From c95e40d598040a15d15156735ccaefd9779a66ed Mon Sep 17 00:00:00 2001 From: Dyldozer Date: Thu, 19 Dec 2019 01:59:09 -0600 Subject: [PATCH] Cannon tracking and Box rework by Owain --- .../java/net/runelite/api/AnimationID.java | 1 + ...annonballFired.java => CannonChanged.java} | 20 +- .../net/runelite/api/events/CannonPlaced.java | 54 ++ .../client/plugins/cannon/CannonPlugin.java | 116 +--- .../plugins/openosrs/OpenOSRSPlugin.java | 162 ++++- .../plugins/suppliestracker/ItemType.java | 20 +- .../plugins/suppliestracker/SuppliesBox.java | 564 ++++++++++++++---- .../SuppliesTrackerConfig.java | 22 + .../suppliestracker/SuppliesTrackerPanel.java | 28 +- .../SuppliesTrackerPlugin.java | 385 +++++++++--- 10 files changed, 1064 insertions(+), 308 deletions(-) rename runelite-api/src/main/java/net/runelite/api/events/{CannonballFired.java => CannonChanged.java} (84%) create mode 100644 runelite-api/src/main/java/net/runelite/api/events/CannonPlaced.java diff --git a/runelite-api/src/main/java/net/runelite/api/AnimationID.java b/runelite-api/src/main/java/net/runelite/api/AnimationID.java index e45020f9bf..7655a749e8 100644 --- a/runelite-api/src/main/java/net/runelite/api/AnimationID.java +++ b/runelite-api/src/main/java/net/runelite/api/AnimationID.java @@ -340,6 +340,7 @@ public final class AnimationID public static final int BARRAGE_ANIMATION = 1979; public static final int BLITZ_ANIMATION = 1978; public static final int CHIN_ANIMATION = 7618; + public static final int SCYTHE_OF_VITUR_ANIMATION = 8056; // Gauntlet Hunleff public static final int HUNLEFF_TRAMPLE = 8420; diff --git a/runelite-api/src/main/java/net/runelite/api/events/CannonballFired.java b/runelite-api/src/main/java/net/runelite/api/events/CannonChanged.java similarity index 84% rename from runelite-api/src/main/java/net/runelite/api/events/CannonballFired.java rename to runelite-api/src/main/java/net/runelite/api/events/CannonChanged.java index 61feeeeecb..4b1dec8b4c 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/CannonballFired.java +++ b/runelite-api/src/main/java/net/runelite/api/events/CannonChanged.java @@ -24,15 +24,23 @@ */ package net.runelite.api.events; +import javax.annotation.Nullable; +import lombok.Value; + /** * an event posted when a cannonball is fired */ -public class CannonballFired implements Event +@Value +public class CannonChanged implements Event { - public static final CannonballFired INSTANCE = new CannonballFired(); + /** + * The projectile id. + */ + @Nullable + private final Integer cannonballId; - private CannonballFired() - { - // noop - } + /** + * The amount of cannonballs left. + */ + private final int cannonballs; } diff --git a/runelite-api/src/main/java/net/runelite/api/events/CannonPlaced.java b/runelite-api/src/main/java/net/runelite/api/events/CannonPlaced.java new file mode 100644 index 0000000000..9cc8a01158 --- /dev/null +++ b/runelite-api/src/main/java/net/runelite/api/events/CannonPlaced.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2019, Davis Cook + * 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 javax.annotation.Nullable; +import lombok.Value; +import net.runelite.api.GameObject; +import net.runelite.api.coords.WorldPoint; + +/** + * an event posted when a cannonball is fired + */ +@Value +public class CannonPlaced implements Event +{ + /** + * Cannon placed or picked up. + */ + private final boolean placed; + + /** + * The location of the cannon. + */ + @Nullable + private final WorldPoint cannonLocation; + + /** + * The cannon object. + */ + @Nullable + private final GameObject cannon; +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonPlugin.java index 64292067e4..f75e66ba3f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonPlugin.java @@ -30,28 +30,19 @@ import java.awt.Color; import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import javax.inject.Inject; import lombok.AccessLevel; import lombok.Getter; -import net.runelite.api.AnimationID; import net.runelite.api.ChatMessageType; import net.runelite.api.Client; import net.runelite.api.GameObject; import net.runelite.api.InventoryID; import net.runelite.api.ItemID; -import static net.runelite.api.ObjectID.CANNON_BASE; -import net.runelite.api.Player; -import net.runelite.api.Projectile; -import static net.runelite.api.ProjectileID.CANNONBALL; -import static net.runelite.api.ProjectileID.GRANITE_CANNONBALL; import net.runelite.api.coords.WorldPoint; +import net.runelite.api.events.CannonChanged; +import net.runelite.api.events.CannonPlaced; import net.runelite.api.events.ChatMessage; -import net.runelite.api.events.GameObjectSpawned; -import net.runelite.api.events.GameTick; import net.runelite.api.events.ItemContainerChanged; -import net.runelite.api.events.ProjectileSpawned; import net.runelite.client.Notifier; import net.runelite.client.callback.ClientThread; import net.runelite.client.config.ConfigManager; @@ -72,13 +63,10 @@ import net.runelite.client.util.ItemUtil; ) public class CannonPlugin extends Plugin { - private static final Pattern NUMBER_PATTERN = Pattern.compile("([0-9]+)"); - private static final int MAX_CBALLS = 30; private static final ImmutableSet CANNON_PARTS = ImmutableSet.of( ItemID.CANNON_BASE, ItemID.CANNON_STAND, ItemID.CANNON_BARRELS, ItemID.CANNON_FURNACE ); private CannonCounter counter; - private boolean skipProjectileCheckThisTick; @Getter(AccessLevel.PACKAGE) private int cballsLeft; @@ -161,7 +149,6 @@ public class CannonPlugin extends Plugin lock = false; cballsLeft = 0; removeCounter(); - skipProjectileCheckThisTick = false; spotPoints.clear(); } @@ -221,34 +208,17 @@ public class CannonPlugin extends Plugin } @Subscribe - private void onGameObjectSpawned(GameObjectSpawned event) + private void onCannonPlaced(CannonPlaced cannonPlacedEvent) { - final GameObject gameObject = event.getGameObject(); - - final Player localPlayer = client.getLocalPlayer(); - if (gameObject.getId() == CANNON_BASE && !cannonPlaced && - localPlayer.getWorldLocation().distanceTo(gameObject.getWorldLocation()) <= 2 && - localPlayer.getAnimation() == AnimationID.BURYING_BONES) - { - cannonPosition = gameObject.getWorldLocation(); - cannon = gameObject; - } + cannonPlaced = cannonPlacedEvent.isPlaced(); + cannonPosition = cannonPlacedEvent.getCannonLocation(); + cannon = cannonPlacedEvent.getCannon(); } @Subscribe - private void onProjectileSpawned(ProjectileSpawned event) + private void onCannonballFired(CannonChanged cannonChangedEvent) { - final Projectile projectile = event.getProjectile(); - - if ((projectile.getId() == CANNONBALL || projectile.getId() == GRANITE_CANNONBALL) && cannonPosition != null) - { - final WorldPoint projectileLoc = WorldPoint.fromLocal(client, projectile.getX1(), projectile.getY1(), client.getPlane()); - - if (projectileLoc.equals(cannonPosition) && !skipProjectileCheckThisTick) - { - cballsLeft--; - } - } + cballsLeft = cannonChangedEvent.getCannonballs(); } @Subscribe @@ -261,84 +231,14 @@ public class CannonPlugin extends Plugin if (event.getMessage().equals("You add the furnace.")) { - cannonPlaced = true; addCounter(); - cballsLeft = 0; } if (event.getMessage().contains("You pick up the cannon") || event.getMessage().contains("Your cannon has decayed. Speak to Nulodion to get a new one!")) { - cannonPlaced = false; - cballsLeft = 0; removeCounter(); } - - if (event.getMessage().startsWith("You load the cannon with")) - { - Matcher m = NUMBER_PATTERN.matcher(event.getMessage()); - if (m.find()) - { - // The cannon will usually refill to MAX_CBALLS, but if the - // player didn't have enough cannonballs in their inventory, - // it could fill up less than that. Filling the cannon to - // cballsLeft + amt is not always accurate though because our - // counter doesn't decrease if the player has been too far away - // from the cannon due to the projectiels not being in memory, - // so our counter can be higher than it is supposed to be. - int amt = Integer.parseInt(m.group()); - if (cballsLeft + amt >= MAX_CBALLS) - { - skipProjectileCheckThisTick = true; - cballsLeft = MAX_CBALLS; - } - else - { - cballsLeft += amt; - } - } - else if (event.getMessage().equals("You load the cannon with one cannonball.")) - { - if (cballsLeft + 1 >= MAX_CBALLS) - { - skipProjectileCheckThisTick = true; - cballsLeft = MAX_CBALLS; - } - else - { - cballsLeft++; - } - } - } - - if (event.getMessage().contains("Your cannon is out of ammo!")) - { - skipProjectileCheckThisTick = true; - - // If the player was out of range of the cannon, some cannonballs - // may have been used without the client knowing, so having this - // extra check is a good idea. - cballsLeft = 0; - - if (this.showEmptyCannonNotification) - { - notifier.notify("Your cannon is out of ammo!"); - } - } - - if (event.getMessage().startsWith("You unload your cannon and receive Cannonball") - || event.getMessage().startsWith("You unload your cannon and receive Granite cannonball")) - { - skipProjectileCheckThisTick = true; - - cballsLeft = 0; - } - } - - @Subscribe - private void onGameTick(GameTick event) - { - skipProjectileCheckThisTick = false; } Color getStateColor() diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/OpenOSRSPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/OpenOSRSPlugin.java index 37b14cf244..0f04abb77c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/OpenOSRSPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/OpenOSRSPlugin.java @@ -29,17 +29,35 @@ package net.runelite.client.plugins.openosrs; import java.awt.event.KeyEvent; import java.util.Arrays; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.inject.Inject; import javax.inject.Singleton; import lombok.extern.slf4j.Slf4j; +import net.runelite.api.AnimationID; +import net.runelite.api.ChatMessageType; import net.runelite.api.Client; +import net.runelite.api.GameObject; +import static net.runelite.api.ObjectID.CANNON_BASE; +import net.runelite.api.Player; +import net.runelite.api.Projectile; +import static net.runelite.api.ProjectileID.CANNONBALL; +import static net.runelite.api.ProjectileID.GRANITE_CANNONBALL; import static net.runelite.api.ScriptID.BANK_PIN_OP; +import net.runelite.api.coords.WorldPoint; +import net.runelite.api.events.CannonChanged; +import net.runelite.api.events.CannonPlaced; +import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.GameObjectSpawned; +import net.runelite.api.events.GameTick; +import net.runelite.api.events.ProjectileSpawned; import net.runelite.api.events.ScriptCallbackEvent; import net.runelite.api.widgets.WidgetID; import static net.runelite.api.widgets.WidgetInfo.*; import net.runelite.client.callback.ClientThread; import net.runelite.client.config.Keybind; import net.runelite.client.config.OpenOSRSConfig; +import net.runelite.client.eventbus.EventBus; import net.runelite.client.eventbus.Subscribe; import net.runelite.client.events.ConfigChanged; import net.runelite.client.input.KeyListener; @@ -60,7 +78,7 @@ import net.runelite.client.util.HotkeyListener; public class OpenOSRSPlugin extends Plugin { private final openosrsKeyListener keyListener = new openosrsKeyListener(); - private final List HidePlugins = Arrays.asList("hidePlugins", "hidePvmPlugins", "hidePvpPlugins", "hideSkillingPlugins", "hideUtilityPlugins", "hideExternalPlugins"); + private static final List HidePlugins = Arrays.asList("hidePlugins", "hidePvmPlugins", "hidePvpPlugins", "hideSkillingPlugins", "hideUtilityPlugins", "hideExternalPlugins"); @Inject private OpenOSRSConfig config; @@ -73,6 +91,18 @@ public class OpenOSRSPlugin extends Plugin @Inject private ClientThread clientThread; + + @Inject + private EventBus eventBus; + + private static final Pattern NUMBER_PATTERN = Pattern.compile("([0-9]+)"); + private static final int MAX_CBALLS = 30; + private int cballsLeft; + private WorldPoint cannonPosition; + private GameObject cannon; + private boolean cannonPlaced; + private boolean skipProjectileCheckThisTick; + private int entered = -1; private int enterIdx; private boolean expectInput; @@ -174,6 +204,136 @@ public class OpenOSRSPlugin extends Plugin } } + @Subscribe + private void onChatMessage(ChatMessage event) + { + if (event.getType() != ChatMessageType.SPAM && event.getType() != ChatMessageType.GAMEMESSAGE) + { + return; + } + + if (event.getMessage().equals("You add the furnace.")) + { + cballsLeft = 0; + eventBus.post(CannonPlaced.class, new CannonPlaced(true, cannonPosition, cannon)); + eventBus.post(CannonChanged.class, new CannonChanged(null, cballsLeft)); + cannonPlaced = true; + } + + if (event.getMessage().contains("You pick up the cannon") + || event.getMessage().contains("Your cannon has decayed. Speak to Nulodion to get a new one!")) + { + cballsLeft = 0; + eventBus.post(CannonPlaced.class, new CannonPlaced(false, null, null)); + eventBus.post(CannonChanged.class, new CannonChanged(null, cballsLeft)); + cannonPlaced = false; + } + + if (event.getMessage().startsWith("You load the cannon with")) + { + Matcher m = NUMBER_PATTERN.matcher(event.getMessage()); + if (m.find()) + { + // The cannon will usually refill to MAX_CBALLS, but if the + // player didn't have enough cannonballs in their inventory, + // it could fill up less than that. Filling the cannon to + // cballsLeft + amt is not always accurate though because our + // counter doesn't decrease if the player has been too far away + // from the cannon due to the projectiels not being in memory, + // so our counter can be higher than it is supposed to be. + int amt = Integer.parseInt(m.group()); + if (cballsLeft + amt >= MAX_CBALLS) + { + skipProjectileCheckThisTick = true; + cballsLeft = MAX_CBALLS; + } + else + { + cballsLeft += amt; + } + } + else if (event.getMessage().equals("You load the cannon with one cannonball.")) + { + if (cballsLeft + 1 >= MAX_CBALLS) + { + skipProjectileCheckThisTick = true; + cballsLeft = MAX_CBALLS; + } + else + { + cballsLeft++; + } + } + + eventBus.post(CannonChanged.class, new CannonChanged(null, cballsLeft)); + } + + if (event.getMessage().contains("Your cannon is out of ammo!")) + { + skipProjectileCheckThisTick = true; + + // If the player was out of range of the cannon, some cannonballs + // may have been used without the client knowing, so having this + // extra check is a good idea. + cballsLeft = 0; + + eventBus.post(CannonChanged.class, new CannonChanged(null, cballsLeft)); + } + + if (event.getMessage().startsWith("You unload your cannon and receive Cannonball") + || event.getMessage().startsWith("You unload your cannon and receive Granite cannonball")) + { + skipProjectileCheckThisTick = true; + + cballsLeft = 0; + + eventBus.post(CannonChanged.class, new CannonChanged(null, cballsLeft)); + } + } + + @Subscribe + private void onGameTick(GameTick event) + { + skipProjectileCheckThisTick = false; + } + + @Subscribe + private void onGameObjectSpawned(GameObjectSpawned event) + { + final GameObject gameObject = event.getGameObject(); + + final Player localPlayer = client.getLocalPlayer(); + if (gameObject.getId() == CANNON_BASE && !cannonPlaced && + localPlayer != null && localPlayer.getWorldLocation().distanceTo(gameObject.getWorldLocation()) <= 2 && + localPlayer.getAnimation() == AnimationID.BURYING_BONES) + { + cannonPosition = gameObject.getWorldLocation(); + cannon = gameObject; + } + } + + @Subscribe + private void onProjectileSpawned(ProjectileSpawned event) + { + if (!cannonPlaced) + { + return; + } + + final Projectile projectile = event.getProjectile(); + + if ((projectile.getId() == CANNONBALL || projectile.getId() == GRANITE_CANNONBALL) && cannonPosition != null) + { + final WorldPoint projectileLoc = WorldPoint.fromLocal(client, projectile.getX1(), projectile.getY1(), client.getPlane()); + + if (projectileLoc.equals(cannonPosition) && !skipProjectileCheckThisTick) + { + cballsLeft--; + eventBus.post(CannonChanged.class, new CannonChanged(projectile.getId(), cballsLeft)); + } + } + } + private void handleKey(char c) { if (client.getWidget(WidgetID.BANK_PIN_GROUP_ID, BANK_PIN_INSTRUCTION_TEXT.getChildId()) == null diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/ItemType.java b/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/ItemType.java index c372f584ea..1a1fbc6928 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/ItemType.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/ItemType.java @@ -28,6 +28,7 @@ package net.runelite.client.plugins.suppliestracker; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Getter; +import static net.runelite.api.ItemID.*; /** * The potential types that supplies can be along with a categorization function @@ -40,7 +41,10 @@ public enum ItemType POTION("Potions"), RUNE("Runes"), AMMO("Ammo"), - TELEPORT("Teleports"); + TELEPORT("Teleports"), + COINS("Coins"), + JEWELLERY("Jewellery"), + CHARGES("Charges"); @Getter(AccessLevel.PACKAGE) private String label; @@ -74,6 +78,20 @@ public enum ItemType { return ItemType.TELEPORT; } + if (item.getId() == COINS_995) + { + return ItemType.COINS; + } + if (item.getName().toLowerCase().contains("ring of") || item.getName().toLowerCase().contains("amulet of") || + item.getName().toLowerCase().contains("bracelet") || item.getName().toLowerCase().contains("necklace")) + { + return ItemType.JEWELLERY; + } + if (item.getId() == SCYTHE_OF_VITUR || item.getId() == SANGUINESTI_STAFF || + item.getId() == TRIDENT_OF_THE_SEAS || item.getId() == TRIDENT_OF_THE_SWAMP) + { + return ItemType.CHARGES; + } return ItemType.FOOD; } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesBox.java b/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesBox.java index fa627ae5ea..a1a31e2813 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesBox.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesBox.java @@ -43,36 +43,38 @@ import net.runelite.api.ItemDefinition; import static net.runelite.api.ItemID.*; import net.runelite.api.util.Text; import net.runelite.client.game.ItemManager; +import static net.runelite.client.plugins.suppliestracker.SuppliesTrackerPlugin.POTION_PATTERN; import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.FontManager; import net.runelite.client.util.AsyncBufferedImage; import net.runelite.client.util.QuantityFormatter; @Singleton -class SuppliesBox extends JPanel +abstract class SuppliesBox extends JPanel { private static final int ITEMS_PER_ROW = 5; - + public final ItemManager itemManager; + protected final SuppliesTrackerPlugin plugin; private final JPanel itemContainer = new JPanel(); private final JLabel priceLabel = new JLabel(); private final JLabel subTitleLabel = new JLabel(); - private final ItemManager itemManager; - @Getter(AccessLevel.PACKAGE) - private final String id; - private final SuppliesTrackerPlugin plugin; private final SuppliesTrackerPanel panel; @Getter(AccessLevel.PACKAGE) - private final List trackedItems = new ArrayList<>(); - - private long totalPrice; - + private final String id; @Getter(AccessLevel.PACKAGE) private final ItemType type; - SuppliesBox(final ItemManager itemManager, final String id, - final SuppliesTrackerPlugin plugin, final SuppliesTrackerPanel panel, - final ItemType type) + @Getter(AccessLevel.PACKAGE) + private final List trackedItems = new ArrayList<>(); + private long totalPrice; + + protected SuppliesBox( + ItemManager itemManager, + String id, + SuppliesTrackerPlugin plugin, + SuppliesTrackerPanel panel, + ItemType type) { this.id = id; this.itemManager = itemManager; @@ -80,6 +82,38 @@ class SuppliesBox extends JPanel this.panel = panel; this.type = type; + render(); + } + + abstract String buildTooltip(int itemId, int qty, SuppliesTrackerItem item); + + public static SuppliesBox of( + ItemManager itemManager, + String id, + SuppliesTrackerPlugin plugin, + SuppliesTrackerPanel panel, + ItemType type) + { + switch (type) + { + case JEWELLERY: + return new JewellerySuppliesBox(itemManager, id, plugin, panel, type); + case CHARGES: + return new ChargesSuppliesBox(itemManager, id, plugin, panel, type); + case FOOD: + return new FoodSuppliesBox(itemManager, id, plugin, panel, type); + case POTION: + return new PotionSuppliesBox(itemManager, id, plugin, panel, type); + } + + return new DefaultSuppliesBox(itemManager, id, plugin, panel, type); + } + + /** + * Builds the box onto the panel + */ + private void render() + { setLayout(new BorderLayout(0, 1)); setBorder(new EmptyBorder(5, 0, 0, 0)); @@ -127,6 +161,11 @@ class SuppliesBox extends JPanel setVisible(false); } + /** + * Adds item to trackedItems + * + * @param item item to be checked + */ void update(SuppliesTrackerItem item) { trackedItems.removeIf(r -> r.getId() == item.getId()); @@ -134,6 +173,11 @@ class SuppliesBox extends JPanel setVisible(trackedItems.size() > 0); } + /** + * Removes item from trackedItems + * + * @param item item to be checked + */ private void remove(SuppliesTrackerItem item) { trackedItems.removeIf(r -> r.getId() == item.getId()); @@ -141,12 +185,20 @@ class SuppliesBox extends JPanel setVisible(trackedItems.size() > 0); } + /** + * Clears trackedItems + */ void clearAll() { trackedItems.clear(); setVisible(false); } + /** + * Adds the total cost of all items to be returned + * + * @return the total cost of all tracked items + */ long getTotalSupplies() { long totalSupplies = 0; @@ -162,6 +214,9 @@ class SuppliesBox extends JPanel return totalPrice; } + /** + * Runs buildItems method and recalculates supplies cost and quantity. + */ void rebuild() { buildItems(); @@ -183,6 +238,19 @@ class SuppliesBox extends JPanel repaint(); } + /** + * Changes itemId to single use variant + * + * @param name name to be checked + * @param itemId is either changed or returned to method + * @return returns updated itemId + */ + abstract int getModifiedItemId(String name, int itemId); + + /** + * Builds an arraylist of items based off trackedItems and populates + * boxes with item information + */ private void buildItems() { final List items = new ArrayList<>(trackedItems); @@ -210,7 +278,7 @@ class SuppliesBox extends JPanel { final SuppliesTrackerItem item = items.get(i); final JLabel imageLabel = new JLabel(); - imageLabel.setToolTipText(buildToolTip(getModifiedItemId(item.getName(), item.getId()), item.getQuantity())); + imageLabel.setToolTipText(buildTooltip(getModifiedItemId(item.getName(), item.getId()), item.getQuantity(), item)); imageLabel.setVerticalAlignment(SwingConstants.CENTER); imageLabel.setHorizontalAlignment(SwingConstants.CENTER); @@ -238,110 +306,396 @@ class SuppliesBox extends JPanel itemContainer.repaint(); } - private int getModifiedItemId(String name, int itemId) + private static class JewellerySuppliesBox extends SuppliesBox { - if (SuppliesTrackerPlugin.isPotion(name)) + protected JewellerySuppliesBox(ItemManager itemManager, String id, SuppliesTrackerPlugin plugin, SuppliesTrackerPanel panel, ItemType type) { - return getSingleDose(name); - } - if (SuppliesTrackerPlugin.isCake(name, itemId)) - { - return getSlice(itemId); - } - if (SuppliesTrackerPlugin.isPizzaPie(name)) - { - return getHalf(itemId); + super(itemManager, id, plugin, panel, type); } - return itemId; + @Override + final String buildTooltip(int itemId, int qty, SuppliesTrackerItem item) + { + ItemDefinition itemDef = itemManager.getItemDefinition(itemId); + final String name = itemDef.getName(); + StringBuilder tooltip = new StringBuilder(); + + if (name.toLowerCase().contains("glory")) + { + tooltip.append("Amulet of Glory(6) x ") + .append(qty) + .append("/6 (") + .append(QuantityFormatter.quantityToStackSize((itemManager.getItemPrice(AMULET_OF_GLORY6) * qty) / 6)) + .append("gp)"); + } + else if (name.toLowerCase().contains("dueling")) + { + tooltip.append("Ring of Dueling(8) x ") + .append(qty) + .append("/8 (") + .append(QuantityFormatter.quantityToStackSize((itemManager.getItemPrice(RING_OF_DUELING8) * qty) / 8)) + .append("gp)"); + } + else if (name.toLowerCase().contains("wealth")) + { + tooltip.append("Ring of Wealth(5) x ") + .append(qty) + .append("/5 (") + .append(QuantityFormatter.quantityToStackSize((itemManager.getItemPrice(RING_OF_WEALTH_5) * qty) / 5)) + .append("gp)"); + } + else if (name.toLowerCase().contains("combat")) + { + tooltip.append("Combat Bracelet(6) x ") + .append(qty) + .append("/6 (") + .append(QuantityFormatter.quantityToStackSize((itemManager.getItemPrice(COMBAT_BRACELET6) * qty) / 6)) + .append("gp)"); + } + else if (name.toLowerCase().contains("games")) + { + tooltip.append("Games Necklace(8) x ") + .append(qty) + .append("/8 (") + .append(QuantityFormatter.quantityToStackSize((itemManager.getItemPrice(GAMES_NECKLACE8) * qty) / 8)) + .append("gp)"); + } + else if (name.toLowerCase().contains("skills")) + { + tooltip.append("Skills Necklace(6) x ") + .append(qty) + .append("/6 (") + .append(QuantityFormatter.quantityToStackSize((itemManager.getItemPrice(SKILLS_NECKLACE6) * qty) / 6)) + .append("gp)"); + } + else if (name.toLowerCase().contains("passage")) + { + tooltip.append("Necklace of Passage(5) x ") + .append(qty) + .append("/5 (") + .append(QuantityFormatter.quantityToStackSize((itemManager.getItemPrice(NECKLACE_OF_PASSAGE5) * qty) / 5)) + .append("gp)"); + } + else if (name.toLowerCase().contains("burning")) + { + tooltip.append("Burning Amulet(5) x ") + .append(qty) + .append("/5 (") + .append(QuantityFormatter.quantityToStackSize((itemManager.getItemPrice(BURNING_AMULET5) * qty) / 5)) + .append("gp)"); + } + return tooltip.toString(); + } + + @Override + int getModifiedItemId(String name, int itemId) + { + return itemId; + } } - //Switches full cake ids to get the image for slice - private int getSlice(int itemId) + private static class ChargesSuppliesBox extends SuppliesBox { - switch (itemId) + protected ChargesSuppliesBox(ItemManager itemManager, String id, SuppliesTrackerPlugin plugin, SuppliesTrackerPanel panel, ItemType type) { - case CAKE: - itemId = SLICE_OF_CAKE; - break; - case CHOCOLATE_CAKE: - itemId = CHOCOLATE_SLICE; - break; - } - return itemId; - } - - //Switches full pizza and pie ids to get the image for half - private int getHalf(int itemId) - { - switch (itemId) - { - case ANCHOVY_PIZZA: - itemId = _12_ANCHOVY_PIZZA; - break; - case MEAT_PIZZA: - itemId = _12_MEAT_PIZZA; - break; - case PINEAPPLE_PIZZA: - itemId = _12_PINEAPPLE_PIZZA; - break; - case PLAIN_PIZZA: - itemId = _12_PLAIN_PIZZA; - break; - case REDBERRY_PIE: - itemId = HALF_A_REDBERRY_PIE; - break; - case GARDEN_PIE: - itemId = HALF_A_GARDEN_PIE; - break; - case SUMMER_PIE: - itemId = HALF_A_SUMMER_PIE; - break; - case FISH_PIE: - itemId = HALF_A_FISH_PIE; - break; - case BOTANICAL_PIE: - itemId = HALF_A_BOTANICAL_PIE; - break; - case MUSHROOM_PIE: - itemId = HALF_A_MUSHROOM_PIE; - break; - case ADMIRAL_PIE: - itemId = HALF_AN_ADMIRAL_PIE; - break; - case WILD_PIE: - itemId = HALF_A_WILD_PIE; - break; - case APPLE_PIE: - itemId = HALF_AN_APPLE_PIE; - break; - case MEAT_PIE: - itemId = HALF_A_MEAT_PIE; - break; - - } - return itemId; - } - - private int getSingleDose(String name) - { - String nameModified = name.replace("(4)", "(1)"); - int itemId = 0; - - if (itemManager.search(nameModified).size() > 0) - { - itemId = itemManager.search(nameModified).get(0).getId(); + super(itemManager, id, plugin, panel, type); } - return itemId; + @Override + final String buildTooltip(int itemId, int qty, SuppliesTrackerItem item) + { + StringBuilder tooltip = new StringBuilder(); + + switch (itemId) + { + case SCYTHE_OF_VITUR: + tooltip.append("") + .append("Blood Rune x ") + .append(qty * 3) + .append(" (") + .append(QuantityFormatter.quantityToStackSize( + itemManager.getItemPrice(BLOOD_RUNE) * qty * 3) + ) + .append("gp)") + .append("
") + .append("Vial of Blood x ") + .append(qty).append("/100 (") + .append(QuantityFormatter.quantityToStackSize( + (itemManager.getItemPrice(VIAL_OF_BLOOD_22446) * qty) / 100) + ) + .append("gp)") + .append("
") + .append(""); + return tooltip.toString(); + + case SANGUINESTI_STAFF: + + tooltip.append("Blood Rune x ") + .append(qty * 3).append(" (") + .append(QuantityFormatter.quantityToStackSize( + itemManager.getItemPrice(BLOOD_RUNE) * qty * 3) + ) + .append("gp)"); + return tooltip.toString(); + + case TRIDENT_OF_THE_SEAS: + tooltip.append("") + .append("Chaos Rune x ") + .append(qty).append(" (") + .append(QuantityFormatter.quantityToStackSize( + itemManager.getItemPrice(CHAOS_RUNE) * qty) + ) + .append("gp)") + .append("
") + .append("Death Rune x ") + .append(qty) + .append(" (") + .append(QuantityFormatter.quantityToStackSize( + itemManager.getItemPrice(DEATH_RUNE) * qty) + ) + .append("gp)") + .append("
") + .append("
") + .append("Fire Rune x ") + .append(qty * 5) + .append(" (") + .append(QuantityFormatter.quantityToStackSize( + (itemManager.getItemPrice(FIRE_RUNE) * qty) * 5) + ) + .append("gp)") + .append("
") + .append("
") + .append("Coins x ") + .append(qty * 10) + .append(" (") + .append(QuantityFormatter.quantityToStackSize( + (itemManager.getItemPrice(COINS_995) * qty) * 10) + ) + .append("gp)") + .append("
") + .append(""); + return tooltip.toString(); + + case TRIDENT_OF_THE_SWAMP: + tooltip.append("") + .append("Chaos Rune x ") + .append(qty) + .append(" (") + .append(QuantityFormatter.quantityToStackSize( + itemManager.getItemPrice(CHAOS_RUNE) * qty) + ) + .append("gp)") + .append("
") + .append("Death Rune x ") + .append(qty) + .append(" (") + .append(QuantityFormatter.quantityToStackSize( + (itemManager.getItemPrice(DEATH_RUNE) * qty)) + ) + .append("gp)") + .append("
") + .append("
") + .append("Fire Rune x ") + .append(qty * 5) + .append(" (") + .append(QuantityFormatter.quantityToStackSize( + (itemManager.getItemPrice(FIRE_RUNE) * qty) * 5) + ) + .append("gp)") + .append("
") + .append("
") + .append("Zulrah's Scales x ") + .append(qty) + .append(" (") + .append(QuantityFormatter.quantityToStackSize( + (itemManager.getItemPrice(ZULRAHS_SCALES) * qty)) + ) + .append("gp)") + .append("
") + .append(""); + return tooltip.toString(); + } + + return tooltip.toString(); + } + + @Override + int getModifiedItemId(String name, int itemId) + { + return itemId; + } } - private String buildToolTip(int itemId, int qty) + private static class FoodSuppliesBox extends SuppliesBox { - ItemDefinition item = this.itemManager.getItemDefinition(itemId); - final String name = item.getName(); - final long price = itemManager.getItemPrice(itemId); - return name + " x " + qty + " (" + QuantityFormatter.quantityToStackSize(price * qty) + ") "; + protected FoodSuppliesBox(ItemManager itemManager, String id, SuppliesTrackerPlugin plugin, SuppliesTrackerPanel panel, ItemType type) + { + super(itemManager, id, plugin, panel, type); + } + + @Override + final String buildTooltip(int itemId, int qty, SuppliesTrackerItem item) + { + ItemDefinition itemDef = itemManager.getItemDefinition(itemId); + final String name = itemDef.getName(); + + final long price = itemManager.getItemPrice(itemId); + return name + " x " + qty + " (" + QuantityFormatter.quantityToStackSize(price * qty) + ") "; + } + + @Override + int getModifiedItemId(String name, int itemId) + { + if (SuppliesTrackerPlugin.isCake(name, itemId)) + { + return getSlice(itemId); + } + if (SuppliesTrackerPlugin.isPizzaPie(name)) + { + return getHalf(itemId); + } + + return itemId; + } + + //Switches full cake ids to get the image for slice + private static int getSlice(int itemId) + { + switch (itemId) + { + case CAKE: + itemId = SLICE_OF_CAKE; + break; + case CHOCOLATE_CAKE: + itemId = CHOCOLATE_SLICE; + break; + } + return itemId; + } + + //Switches full pizza and pie ids to get the image for half + private static int getHalf(int itemId) + { + switch (itemId) + { + case ANCHOVY_PIZZA: + itemId = _12_ANCHOVY_PIZZA; + break; + case MEAT_PIZZA: + itemId = _12_MEAT_PIZZA; + break; + case PINEAPPLE_PIZZA: + itemId = _12_PINEAPPLE_PIZZA; + break; + case PLAIN_PIZZA: + itemId = _12_PLAIN_PIZZA; + break; + case REDBERRY_PIE: + itemId = HALF_A_REDBERRY_PIE; + break; + case GARDEN_PIE: + itemId = HALF_A_GARDEN_PIE; + break; + case SUMMER_PIE: + itemId = HALF_A_SUMMER_PIE; + break; + case FISH_PIE: + itemId = HALF_A_FISH_PIE; + break; + case BOTANICAL_PIE: + itemId = HALF_A_BOTANICAL_PIE; + break; + case MUSHROOM_PIE: + itemId = HALF_A_MUSHROOM_PIE; + break; + case ADMIRAL_PIE: + itemId = HALF_AN_ADMIRAL_PIE; + break; + case WILD_PIE: + itemId = HALF_A_WILD_PIE; + break; + case APPLE_PIE: + itemId = HALF_AN_APPLE_PIE; + break; + case MEAT_PIE: + itemId = HALF_A_MEAT_PIE; + break; + + } + return itemId; + } } + private static class PotionSuppliesBox extends SuppliesBox + { + protected PotionSuppliesBox(ItemManager itemManager, String id, SuppliesTrackerPlugin plugin, SuppliesTrackerPanel panel, ItemType type) + { + super(itemManager, id, plugin, panel, type); + } + + @Override + final String buildTooltip(int itemId, int qty, SuppliesTrackerItem item) + { + ItemDefinition itemDef = itemManager.getItemDefinition(itemId); + final String name = itemDef.getName(); + + final long price = itemManager.getItemPrice(plugin.getPotionID(name.replaceAll(POTION_PATTERN, "(4)"))) / 4; + return name + " x " + qty + " (" + QuantityFormatter.quantityToStackSize(price * qty) + ") "; + } + + @Override + int getModifiedItemId(String name, int itemId) + { + if (SuppliesTrackerPlugin.isPotion(name)) + { + return getSingleDose(name); + } + + return itemId; + } + + /** + * Turns a potion itemid into the single dose id + * + * @param name potion name to be checked + * @return itemid of single dose potion + */ + private int getSingleDose(String name) + { + String nameModified = name.replace("(4)", "(1)"); + int itemId = 0; + + if (itemManager.search(nameModified).size() > 0) + { + itemId = itemManager.search(nameModified).get(0).getId(); + } + + return itemId; + } + } + + private static class DefaultSuppliesBox extends SuppliesBox + { + protected DefaultSuppliesBox(ItemManager itemManager, String id, SuppliesTrackerPlugin plugin, SuppliesTrackerPanel panel, ItemType type) + { + super(itemManager, id, plugin, panel, type); + } + + @Override + final String buildTooltip(int itemId, int qty, SuppliesTrackerItem item) + { + ItemDefinition itemDef = itemManager.getItemDefinition(itemId); + final String name = itemDef.getName(); + + + final long price = itemManager.getItemPrice(itemId); + return name + " x " + qty + " (" + QuantityFormatter.quantityToStackSize(price * qty) + ") "; + } + + @Override + int getModifiedItemId(String name, int itemId) + { + return itemId; + } + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesTrackerConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesTrackerConfig.java index ad50f9d020..9975338ec4 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesTrackerConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesTrackerConfig.java @@ -54,4 +54,26 @@ public interface SuppliesTrackerConfig extends Config { return BlowpipeDartType.MITHRIL; } + + @ConfigTitleSection( + keyName = "chargesBoxTitle", + name = "Charges Box", + description = "", + position = 2 + ) + default Title chargesBoxTitle() + { + return new Title(); + } + + @ConfigItem( + keyName = "chargesBox", + name = "Show weapons charges used box?", + description = "Separates items with charges to show how many of those charges you used.", + titleSection = "chargesBoxTitle" + ) + default boolean chargesBox() + { + return false; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesTrackerPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesTrackerPanel.java index 004c9b1ee6..033c30c88d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesTrackerPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesTrackerPanel.java @@ -53,9 +53,10 @@ class SuppliesTrackerPanel extends PluginPanel private static final String HTML_LABEL_TEMPLATE = "%s%s"; - // Handle loot logs + // Handle supplies logs private final JPanel logsContainer = new JPanel(); + //Boxes for holding supplies private final List boxList = new ArrayList<>(); private final PluginErrorPanel errorPanel = new PluginErrorPanel(); @@ -97,9 +98,10 @@ class SuppliesTrackerPanel extends PluginPanel overallPanel.add(overallIcon, BorderLayout.WEST); overallPanel.add(overallInfo, BorderLayout.CENTER); + //Sorts boxes into usage types for (ItemType type : ItemType.values()) { - SuppliesBox newBox = new SuppliesBox(itemManager, type.getLabel(), plugin, this, type); + SuppliesBox newBox = SuppliesBox.of(itemManager, type.getLabel(), plugin, this, type); logsContainer.add(newBox); boxList.add(newBox); } @@ -135,16 +137,6 @@ class SuppliesTrackerPanel extends PluginPanel overallPanel.setVisible(false); } - /** - * loads an img to the icon on the header - * - * @param img the img for the header icon - */ - void loadHeaderIcon(BufferedImage img) - { - overallIcon.setIcon(new ImageIcon(img)); - } - /** * convert key value pair to html formatting needed to display nicely * @@ -158,6 +150,16 @@ class SuppliesTrackerPanel extends PluginPanel return String.format(HTML_LABEL_TEMPLATE, ColorUtil.toHexColor(ColorScheme.LIGHT_GRAY_COLOR), key, valueStr); } + /** + * loads an img to the icon on the header + * + * @param img the img for the header icon + */ + void loadHeaderIcon(BufferedImage img) + { + overallIcon.setIcon(new ImageIcon(img)); + } + /** * Add an item to the supply panel by placing it into the correct box * @@ -191,6 +193,8 @@ class SuppliesTrackerPanel extends PluginPanel } overallCost = 0; + + //Checks all supply boxes for total price for (SuppliesBox box : boxList) { overallCost += box.getTotalPrice(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesTrackerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesTrackerPlugin.java index 4f14c2e9f9..b8859dbd57 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesTrackerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/suppliestracker/SuppliesTrackerPlugin.java @@ -41,11 +41,8 @@ import javax.inject.Inject; import javax.inject.Singleton; import javax.swing.SwingUtilities; import lombok.extern.slf4j.Slf4j; -import static net.runelite.api.AnimationID.BARRAGE_ANIMATION; -import static net.runelite.api.AnimationID.BLITZ_ANIMATION; -import static net.runelite.api.AnimationID.BLOWPIPE_ATTACK; -import static net.runelite.api.AnimationID.HIGH_LEVEL_MAGIC_ATTACK; -import static net.runelite.api.AnimationID.LOW_LEVEL_MAGIC_ATTACK; +import static net.runelite.api.AnimationID.*; +import net.runelite.api.ChatMessageType; import net.runelite.api.Client; import net.runelite.api.EquipmentInventorySlot; import net.runelite.api.InventoryID; @@ -57,7 +54,8 @@ import static net.runelite.api.ItemID.*; import net.runelite.api.Player; import net.runelite.api.VarPlayer; import net.runelite.api.events.AnimationChanged; -import net.runelite.api.events.CannonballFired; +import net.runelite.api.events.CannonChanged; +import net.runelite.api.events.ChatMessage; import net.runelite.api.events.GameTick; import net.runelite.api.events.ItemContainerChanged; import net.runelite.api.events.MenuOptionClicked; @@ -68,15 +66,12 @@ import net.runelite.client.game.ItemManager; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.plugins.PluginType; -import static net.runelite.client.plugins.suppliestracker.ActionType.CAST; -import static net.runelite.client.plugins.suppliestracker.ActionType.CONSUMABLE; -import static net.runelite.client.plugins.suppliestracker.ActionType.TELEPORT; +import static net.runelite.client.plugins.suppliestracker.ActionType.*; import net.runelite.client.ui.ClientToolbar; import net.runelite.client.ui.NavigationButton; import net.runelite.client.util.ImageUtil; import net.runelite.http.api.item.ItemPrice; - @PluginDescriptor( name = "Supplies Used Tracker", description = "Tracks supplies used during the session", @@ -88,41 +83,54 @@ import net.runelite.http.api.item.ItemPrice; @Slf4j public class SuppliesTrackerPlugin extends Plugin { - private static final String POTION_PATTERN = "[(]\\d[)]"; - + //Regex patterns + static final String POTION_PATTERN = "[(]\\d[)]"; private static final String EAT_PATTERN = "^eat"; private static final String DRINK_PATTERN = "^drink"; private static final String TELEPORT_PATTERN = "^teleport"; private static final String TELETAB_PATTERN = "^break"; + private static final String TELEPORT_SCROLL_BOOK_PATTERN = "teleport scroll$"; private static final String SPELL_PATTERN = "^cast|^grand\\sexchange|^outside|^seers|^yanille"; + //Equipment slot constants private static final int EQUIPMENT_MAINHAND_SLOT = EquipmentInventorySlot.WEAPON.getSlotIdx(); private static final int EQUIPMENT_AMMO_SLOT = EquipmentInventorySlot.AMMO.getSlotIdx(); private static final int EQUIPMENT_CAPE_SLOT = EquipmentInventorySlot.CAPE.getSlotIdx(); + //Ava's calculations private static final double NO_AVAS_PERCENT = 1.0; private static final double ASSEMBLER_PERCENT = 0.20; private static final double ACCUMULATOR_PERCENT = 0.28; private static final double ATTRACTOR_PERCENT = 0.40; + //blowpipe attack timings private static final int BLOWPIPE_TICKS_RAPID_PVM = 2; private static final int BLOWPIPE_TICKS_RAPID_PVP = 3; private static final int BLOWPIPE_TICKS_NORMAL_PVM = 3; private static final int BLOWPIPE_TICKS_NORMAL_PVP = 4; + //blowpipe scale usage private static final double SCALES_PERCENT = 0.66; + //Max use amounts private static final int POTION_DOSES = 4, CAKE_DOSES = 3, PIZZA_PIE_DOSES = 2; private static final Random random = new Random(); + // id array for checking thrown items and runes private static final int[] THROWING_IDS = new int[]{BRONZE_DART, IRON_DART, STEEL_DART, BLACK_DART, MITHRIL_DART, ADAMANT_DART, RUNE_DART, DRAGON_DART, BRONZE_KNIFE, IRON_KNIFE, STEEL_KNIFE, BLACK_KNIFE, MITHRIL_KNIFE, ADAMANT_KNIFE, RUNE_KNIFE, BRONZE_THROWNAXE, IRON_THROWNAXE, STEEL_THROWNAXE, MITHRIL_THROWNAXE, ADAMANT_THROWNAXE, RUNE_THROWNAXE, DRAGON_KNIFE, DRAGON_KNIFE_22812, DRAGON_KNIFE_22814, DRAGON_KNIFEP_22808, DRAGON_KNIFEP_22810, DRAGON_KNIFEP, DRAGON_THROWNAXE, CHINCHOMPA_10033, RED_CHINCHOMPA_10034, BLACK_CHINCHOMPA}; private static final int[] RUNE_IDS = new int[]{AIR_RUNE, WATER_RUNE, EARTH_RUNE, MIND_RUNE, BODY_RUNE, COSMIC_RUNE, CHAOS_RUNE, NATURE_RUNE, LAW_RUNE, DEATH_RUNE, ASTRAL_RUNE, BLOOD_RUNE, SOUL_RUNE, WRATH_RUNE, MIST_RUNE, DUST_RUNE, MUD_RUNE, SMOKE_RUNE, STEAM_RUNE, LAVA_RUNE}; //Hold Supply Data private static final Map suppliesEntry = new HashMap<>(); - private ItemContainer old; private final Deque actionStack = new ArrayDeque<>(); + + //Item arrays + private final String[] RAIDS_CONSUMABLES = new String[]{"xeric's", "elder", "twisted", "revitalisation", "overload", "prayer enhance", "pysk", "suphi", "leckish", "brawk", "mycil", "roqed", "kyren", "guanic", "prael", "giral", "phluxia", "kryket", "murng", "psykk"}; + private final int[] TRIDENT_OF_THE_SEAS_IDS = new int[]{TRIDENT_OF_THE_SEAS, TRIDENT_OF_THE_SEAS_E, TRIDENT_OF_THE_SEAS_FULL}; + private final int[] TRIDENT_OF_THE_SWAMP_IDS = new int[]{TRIDENT_OF_THE_SWAMP_E, TRIDENT_OF_THE_SWAMP, UNCHARGED_TOXIC_TRIDENT_E, UNCHARGED_TOXIC_TRIDENT}; + + private ItemContainer old; private int ammoId = 0; private int ammoAmount = 0; private int thrownId = 0; @@ -133,8 +141,6 @@ public class SuppliesTrackerPlugin extends Plugin private int mainHand = 0; private SuppliesTrackerPanel panel; private NavigationButton navButton; - private final String[] RAIDS_CONSUMABLES = new String[]{"xeric's", "elder", "twisted", "revitalisation", "overload", "prayer enhance", "pysk", "suphi", "leckish", "brawk", "mycil", "roqed", "kyren", "guanic", "prael", "giral", "phluxia", "kryket", "murng", "psykk"}; - private int attackStyleVarbit = -1; private int ticks = 0; private int ticksInAnimation; @@ -151,6 +157,38 @@ public class SuppliesTrackerPlugin extends Plugin @Inject private Client client; + /** + * Checks if item name is potion + * + * @param name the name of the item + * @return if the item is a potion - i.e. has a (1) (2) (3) or (4) in the name + */ + static boolean isPotion(String name) + { + return name.contains("(4)") || + name.contains("(3)") || + name.contains("(2)") || + name.contains("(1)"); + } + + /** + * Checks if item name is pizza or pie + * + * @param name the name of the item + * @return if the item is a pizza or a pie - i.e. has pizza or pie in the name + */ + static boolean isPizzaPie(String name) + { + return name.toLowerCase().contains("pizza") || + name.toLowerCase().contains(" pie"); + } + + static boolean isCake(String name, int itemId) + { + return name.toLowerCase().contains("cake") || + itemId == ItemID.CHOCOLATE_SLICE; + } + @Override protected void startUp() { @@ -190,14 +228,14 @@ public class SuppliesTrackerPlugin extends Plugin { ticks++; } - if (ticks == ticksInAnimation && (player.getAnimation() == BLOWPIPE_ATTACK)) + if (ticks == ticksInAnimation && + (player.getAnimation() == BLOWPIPE_ATTACK)) { double ava_percent = getAccumulatorPercent(); // randomize the usage of supplies since we CANNOT actually get real supplies used if (random.nextDouble() <= ava_percent) { buildEntries(config.blowpipeAmmo().getDartID()); - } if (random.nextDouble() <= SCALES_PERCENT) { @@ -223,10 +261,12 @@ public class SuppliesTrackerPlugin extends Plugin switch (capeID) { case AVAS_ASSEMBLER: + case AVAS_ASSEMBLER_L: case ASSEMBLER_MAX_CAPE: percent = ASSEMBLER_PERCENT; break; case AVAS_ACCUMULATOR: + case AVAS_ACCUMULATOR_23609: case ACCUMULATOR_MAX_CAPE: // TODO: the ranging cape can be used as an attractor so this could be wrong case RANGING_CAPE: @@ -243,10 +283,12 @@ public class SuppliesTrackerPlugin extends Plugin @Subscribe private void onVarbitChanged(VarbitChanged event) { - if (attackStyleVarbit == -1 || attackStyleVarbit != client.getVar(VarPlayer.ATTACK_STYLE)) + if (attackStyleVarbit == -1 || + attackStyleVarbit != client.getVar(VarPlayer.ATTACK_STYLE)) { attackStyleVarbit = client.getVar(VarPlayer.ATTACK_STYLE); - if (attackStyleVarbit == 0 || attackStyleVarbit == 3) + if (attackStyleVarbit == 0 || + attackStyleVarbit == 3) { ticksInAnimation = BLOWPIPE_TICKS_NORMAL_PVM; if (client.getLocalPlayer() != null && @@ -294,7 +336,8 @@ public class SuppliesTrackerPlugin extends Plugin break; } } - if (isRune && (newItem.getId() != oldItem.getId() || newItem.getQuantity() != oldItem.getQuantity())) + if (isRune && (newItem.getId() != oldItem.getId() || + newItem.getQuantity() != oldItem.getQuantity())) { int quantity = oldItem.getQuantity(); if (newItem.getId() == oldItem.getId()) @@ -317,8 +360,12 @@ public class SuppliesTrackerPlugin extends Plugin } @Subscribe - private void onCannonballFired(CannonballFired cannonballFired) + private void onCannonballFired(CannonChanged cannonChanged) { + if (cannonChanged.getCannonballId() == null) + { + return; + } buildEntries(CANNONBALL); } @@ -330,35 +377,53 @@ public class SuppliesTrackerPlugin extends Plugin if (animationChanged.getActor().getAnimation() == HIGH_LEVEL_MAGIC_ATTACK) { //Trident of the seas - if (mainHand == TRIDENT_OF_THE_SEAS || mainHand == TRIDENT_OF_THE_SEAS_E || mainHand == TRIDENT_OF_THE_SEAS_FULL) + for (int tridentOfTheSeas : TRIDENT_OF_THE_SEAS_IDS) { - buildEntries(CHAOS_RUNE); - buildEntries(DEATH_RUNE); - buildEntries(FIRE_RUNE, 5); - buildEntries(COINS_995, 10); + if (mainHand == tridentOfTheSeas) + { + if (config.chargesBox()) + { + buildChargesEntries(TRIDENT_OF_THE_SEAS); + } + else + { + buildEntries(CHAOS_RUNE); + buildEntries(DEATH_RUNE); + buildEntries(FIRE_RUNE, 5); + buildEntries(COINS_995, 10); + } + break; + } } //Trident of the swamp - else if (mainHand == TRIDENT_OF_THE_SWAMP_E || mainHand == TRIDENT_OF_THE_SWAMP || mainHand == UNCHARGED_TOXIC_TRIDENT_E || mainHand == UNCHARGED_TOXIC_TRIDENT) + for (int tridentOfTheSwamp : TRIDENT_OF_THE_SWAMP_IDS) { - buildEntries(CHAOS_RUNE); - buildEntries(DEATH_RUNE); - buildEntries(FIRE_RUNE, 5); - buildEntries(ZULRAHS_SCALES); + if (mainHand == tridentOfTheSwamp) + { + if (config.chargesBox()) + { + buildChargesEntries(TRIDENT_OF_THE_SWAMP); + } + else + { + buildEntries(CHAOS_RUNE); + buildEntries(DEATH_RUNE); + buildEntries(FIRE_RUNE, 5); + buildEntries(ZULRAHS_SCALES); + } + break; + } } //Sang Staff - else if (mainHand == SANGUINESTI_STAFF || mainHand == SANGUINESTI_STAFF_UNCHARGED) + if (mainHand == SANGUINESTI_STAFF) { - buildEntries(BLOOD_RUNE, 3); - } - else - { - old = client.getItemContainer(InventoryID.INVENTORY); - - if (old != null && old.getItems() != null && actionStack.stream().noneMatch(a -> - a.getType() == CAST)) + if (config.chargesBox()) { - MenuAction newAction = new MenuAction(CAST, old.getItems()); - actionStack.push(newAction); + buildChargesEntries(SANGUINESTI_STAFF); + } + else + { + buildEntries(BLOOD_RUNE, 3); } } } @@ -373,17 +438,30 @@ public class SuppliesTrackerPlugin extends Plugin actionStack.push(newAction); } } - else if (animationChanged.getActor().getAnimation() == BARRAGE_ANIMATION || animationChanged.getActor().getAnimation() == BLITZ_ANIMATION ) + else if (animationChanged.getActor().getAnimation() == BARRAGE_ANIMATION || + animationChanged.getActor().getAnimation() == BLITZ_ANIMATION) { old = client.getItemContainer(InventoryID.INVENTORY); if (old != null && old.getItems() != null && actionStack.stream().noneMatch(a -> - a.getType() == CAST)) + a.getType() == CAST)) { MenuAction newAction = new MenuAction(CAST, old.getItems()); actionStack.push(newAction); } } + else if (animationChanged.getActor().getAnimation() == SCYTHE_OF_VITUR_ANIMATION) + { + if (config.chargesBox()) + { + buildChargesEntries(SCYTHE_OF_VITUR); + } + else + { + buildEntries(BLOOD_RUNE, 3); + buildEntries(COINS_995, itemManager.getItemPrice(VIAL_OF_BLOOD_22446) / 100); + } + } } } @@ -392,7 +470,8 @@ public class SuppliesTrackerPlugin extends Plugin { ItemContainer itemContainer = itemContainerChanged.getItemContainer(); - if (itemContainer == client.getItemContainer(InventoryID.INVENTORY) && old != null) + if (itemContainer == client.getItemContainer(InventoryID.INVENTORY) && + old != null) { while (!actionStack.isEmpty()) { @@ -415,7 +494,8 @@ public class SuppliesTrackerPlugin extends Plugin itemFrame = (MenuAction.ItemAction) frame; int teleid = itemFrame.getItemID(); int slot = itemFrame.getSlot(); - if (itemContainer.getItems()[slot].getId() != oldInv[slot].getId() || itemContainer.getItems()[slot].getQuantity() != oldInv[slot].getQuantity()) + if (itemContainer.getItems()[slot].getId() != oldInv[slot].getId() || + itemContainer.getItems()[slot].getQuantity() != oldInv[slot].getQuantity()) { buildEntries(teleid); } @@ -574,8 +654,9 @@ public class SuppliesTrackerPlugin extends Plugin old = client.getItemContainer(InventoryID.INVENTORY); // Makes stack only contains one teleport type to stop from adding multiple of one teleport - if (old != null && old.getItems() != null && actionStack.stream().noneMatch(a -> - a.getType() == TELEPORT)) + if (old != null && old.getItems() != null && + actionStack.stream().noneMatch(a -> + a.getType() == TELEPORT)) { int teleid = event.getIdentifier(); MenuAction newAction = new MenuAction.ItemAction(TELEPORT, old.getItems(), teleid, event.getParam0()); @@ -592,39 +673,116 @@ public class SuppliesTrackerPlugin extends Plugin old = client.getItemContainer(InventoryID.INVENTORY); if (old != null && old.getItems() != null && actionStack.stream().noneMatch(a -> - a.getType() == CAST)) + a.getType() == CAST)) { MenuAction newAction = new MenuAction(CAST, old.getItems()); actionStack.push(newAction); } + + } + + Pattern scrollPattern = Pattern.compile(TELEPORT_SCROLL_BOOK_PATTERN); + + if (event.getOption().toLowerCase().equals("activate")) + { + String target = event.getTarget(); + if (target.toLowerCase().contains("teleport scroll")) + { + switch (target.toLowerCase().substring(target.indexOf(">") + 1)) + { + case "watson teleport scroll": + buildEntries(WATSON_TELEPORT); + break; + case "zul-andra teleport scroll": + buildEntries(ZULANDRA_TELEPORT); + break; + case "nardah teleport scroll": + buildEntries(NARDAH_TELEPORT); + break; + case "digsite teleport scroll": + buildEntries(DIGSITE_TELEPORT); + break; + case "feldip hills teleport scroll": + buildEntries(FELDIP_HILLS_TELEPORT); + break; + case "lunar isle teleport scroll": + buildEntries(LUNAR_ISLE_TELEPORT); + break; + case "mort'ton teleport scroll": + buildEntries(MORTTON_TELEPORT); + break; + case "pest control teleport scroll": + buildEntries(PEST_CONTROL_TELEPORT); + break; + case "piscatoris teleport scroll": + buildEntries(PISCATORIS_TELEPORT); + break; + case "iorwerth camp teleport scroll": + buildEntries(IORWERTH_CAMP_TELEPORT); + break; + case "mos le'harmless teleport scroll": + buildEntries(MOS_LEHARMLESS_TELEPORT); + break; + case "lumberyard teleport scroll": + buildEntries(LUMBERYARD_TELEPORT); + break; + case "revenant cave teleport scroll": + buildEntries(REVENANT_CAVE_TELEPORT); + break; + case "tai bwo wannai teleport scroll": + buildEntries(TAI_BWO_WANNAI_TELEPORT); + break; + } + } } } - /** - * Checks if item name is potion - * - * @param name the name of the item - * @return if the item is a potion - i.e. has a (1) (2) (3) or (4) in the name - */ - static boolean isPotion(String name) + @Subscribe + void onChatMessage(ChatMessage event) { - return name.contains("(4)") || name.contains("(3)") || name.contains("(2)") || name.contains("(1)"); - } - - /** - * Checks if item name is pizza or pie - * - * @param name the name of the item - * @return if the item is a pizza or a pie - i.e. has pizza or pie in the name - */ - static boolean isPizzaPie(String name) - { - return name.toLowerCase().contains("pizza") || name.toLowerCase().contains(" pie"); - } - - static boolean isCake(String name, int itemId) - { - return name.toLowerCase().contains("cake") || itemId == ItemID.CHOCOLATE_SLICE; + String message = event.getMessage(); + if (event.getType() == ChatMessageType.GAMEMESSAGE || event.getType() == ChatMessageType.SPAM) + { + if (message.toLowerCase().contains("your amulet has") || + message.toLowerCase().contains("your amulet's last charge")) + { + buildChargesEntries(AMULET_OF_GLORY6); + } + else if (message.toLowerCase().contains("your ring of dueling has") || + message.toLowerCase().contains("your ring of dueling crumbles")) + { + buildChargesEntries(RING_OF_DUELING8); + } + else if (message.toLowerCase().contains("your ring of wealth has")) + { + buildChargesEntries(RING_OF_WEALTH_5); + } + else if (message.toLowerCase().contains("your combat bracelet has") || + message.toLowerCase().contains("your combat bracelet's last charge")) + { + buildChargesEntries(COMBAT_BRACELET6); + } + else if (message.toLowerCase().contains("your games necklace has") || + message.toLowerCase().contains("your games necklace crumbles")) + { + buildChargesEntries(GAMES_NECKLACE8); + } + else if (message.toLowerCase().contains("your skills necklace has") || + message.toLowerCase().contains("your skills necklace's last charge")) + { + buildChargesEntries(SKILLS_NECKLACE6); + } + else if (message.toLowerCase().contains("your necklace of passage has") || + message.toLowerCase().contains("your necklace of passage crumbles")) + { + buildChargesEntries(NECKLACE_OF_PASSAGE5); + } + else if (message.toLowerCase().contains("your burning amulet has") || + message.toLowerCase().contains("your burning amulet crumbles")) + { + buildChargesEntries(BURNING_AMULET5); + } + } } /** @@ -725,11 +883,89 @@ public class SuppliesTrackerPlugin extends Plugin newQuantity, calculatedPrice); + suppliesEntry.put(itemId, newEntry); SwingUtilities.invokeLater(() -> panel.addItem(newEntry)); } + /** + * Add an item to the supply tracker + * + * @param itemId the id of the item + */ + private void buildChargesEntries(int itemId) + { + final ItemDefinition itemComposition = itemManager.getItemDefinition(itemId); + String name = itemComposition.getName(); + long calculatedPrice = 0; + + + int newQuantity; + if (suppliesEntry.containsKey(itemId)) + { + newQuantity = suppliesEntry.get(itemId).getQuantity() + 1; + } + else + { + newQuantity = 1; + } + + switch (itemId) + { + case AMULET_OF_GLORY6: + calculatedPrice = ((itemManager.getItemPrice(AMULET_OF_GLORY6) * newQuantity) / 6); + break; + case RING_OF_DUELING8: + calculatedPrice = ((itemManager.getItemPrice(RING_OF_DUELING8) * newQuantity) / 8); + break; + case RING_OF_WEALTH_5: + calculatedPrice = ((itemManager.getItemPrice(RING_OF_WEALTH_5) * newQuantity) / 5); + break; + case COMBAT_BRACELET6: + calculatedPrice = ((itemManager.getItemPrice(COMBAT_BRACELET6) * newQuantity) / 6); + break; + case GAMES_NECKLACE8: + calculatedPrice = ((itemManager.getItemPrice(GAMES_NECKLACE8) * newQuantity) / 8); + break; + case SKILLS_NECKLACE6: + calculatedPrice = ((itemManager.getItemPrice(SKILLS_NECKLACE6) * newQuantity) / 6); + break; + case NECKLACE_OF_PASSAGE5: + calculatedPrice = ((itemManager.getItemPrice(NECKLACE_OF_PASSAGE5) * newQuantity) / 5); + break; + case BURNING_AMULET5: + calculatedPrice = ((itemManager.getItemPrice(BURNING_AMULET5) * newQuantity) / 5); + break; + case SCYTHE_OF_VITUR: + calculatedPrice = (itemManager.getItemPrice(BLOOD_RUNE) * newQuantity * 3) + (itemManager.getItemPrice(VIAL_OF_BLOOD_22446) * newQuantity / 100); + break; + case TRIDENT_OF_THE_SWAMP: + calculatedPrice = (itemManager.getItemPrice(CHAOS_RUNE) * newQuantity) + (itemManager.getItemPrice(DEATH_RUNE) * newQuantity) + + (itemManager.getItemPrice(FIRE_RUNE) * newQuantity) + (itemManager.getItemPrice(ZULRAHS_SCALES) * newQuantity); + break; + case TRIDENT_OF_THE_SEAS: + calculatedPrice = (itemManager.getItemPrice(CHAOS_RUNE) * newQuantity) + (itemManager.getItemPrice(DEATH_RUNE) * newQuantity) + + (itemManager.getItemPrice(FIRE_RUNE) * newQuantity) + (itemManager.getItemPrice(COINS_995) * newQuantity * 10); + break; + case SANGUINESTI_STAFF: + calculatedPrice = (itemManager.getItemPrice(BLOOD_RUNE) * newQuantity * 3); + break; + } + + // write the new quantity and calculated price for this entry + SuppliesTrackerItem newEntry = new SuppliesTrackerItem( + itemId, + name, + newQuantity, + calculatedPrice); + + suppliesEntry.put(itemId, newEntry); + SwingUtilities.invokeLater(() -> + panel.addItem(newEntry)); + } + + /** * reset all item stacks */ @@ -754,7 +990,7 @@ public class SuppliesTrackerPlugin extends Plugin * @param name the given name * @return the item id for this name */ - private int getPotionID(String name) + int getPotionID(String name) { int itemId = 0; @@ -822,7 +1058,6 @@ public class SuppliesTrackerPlugin extends Plugin case HALF_A_MEAT_PIE: itemId = MEAT_PIE; break; - // note behavior of case means both below cases return CAKE case _23_CAKE: case SLICE_OF_CAKE: itemId = CAKE;