From 8a4d6172b2e15b78b9321d79bc691577f97fa087 Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 8 Jul 2019 17:12:06 -0400 Subject: [PATCH 01/18] keyremapping: fix escape from chat triggering game keybindings Co-authored-by: Chet Powers --- .../client/plugins/keyremapping/KeyRemappingListener.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingListener.java b/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingListener.java index 56cf3bbedc..a6ad6e8638 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingListener.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingListener.java @@ -190,8 +190,12 @@ class KeyRemappingListener extends MouseAdapter implements KeyListener { switch (e.getKeyCode()) { - case KeyEvent.VK_ENTER: case KeyEvent.VK_ESCAPE: + // When existing typing mode, block the escape key + // so that it doesn't trigger the in-game hotkeys + e.consume(); + // FALLTHROUGH + case KeyEvent.VK_ENTER: plugin.setTyping(false); clientThread.invoke(plugin::lockChat); break; From 3125b74f4fb4cbfb05e90d78a61c9ad216b1b08e Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 8 Jul 2019 18:12:57 -0400 Subject: [PATCH 02/18] keyremapping: allow custom keybinds for F keys Co-authored-by: Chet Powers --- .../keyremapping/KeyRemappingConfig.java | 134 +++++++++++++++++- .../keyremapping/KeyRemappingListener.java | 62 ++++---- 2 files changed, 157 insertions(+), 39 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingConfig.java index 4774bc67e7..9702f6c40c 100755 --- a/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingConfig.java @@ -92,10 +92,142 @@ public interface KeyRemappingConfig extends Config position = 6, keyName = "fkeyRemap", name = "Remap F Keys", - description = "Configures whether F-Keys are Remapped to 1 (F1) through 0 (F10), '-' (F11), and '=' (F12)" + description = "Configures whether F-Keys use remapped keys" ) default boolean fkeyRemap() { return false; } + + @ConfigItem( + position = 7, + keyName = "f1", + name = "F1", + description = "The key which will replace {F1}." + ) + default ModifierlessKeybind f1() + { + return new ModifierlessKeybind(KeyEvent.VK_1, 0); + } + + @ConfigItem( + position = 8, + keyName = "f2", + name = "F2", + description = "The key which will replace {F2}." + ) + default ModifierlessKeybind f2() + { + return new ModifierlessKeybind(KeyEvent.VK_2, 0); + } + + @ConfigItem( + position = 9, + keyName = "f3", + name = "F3", + description = "The key which will replace {F3}." + ) + default ModifierlessKeybind f3() + { + return new ModifierlessKeybind(KeyEvent.VK_3, 0); + } + + @ConfigItem( + position = 10, + keyName = "f4", + name = "F4", + description = "The key which will replace {F4}." + ) + default ModifierlessKeybind f4() + { + return new ModifierlessKeybind(KeyEvent.VK_4, 0); + } + + @ConfigItem( + position = 11, + keyName = "f5", + name = "F5", + description = "The key which will replace {F5}." + ) + default ModifierlessKeybind f5() + { + return new ModifierlessKeybind(KeyEvent.VK_5, 0); + } + + @ConfigItem( + position = 12, + keyName = "f6", + name = "F6", + description = "The key which will replace {F6}." + ) + default ModifierlessKeybind f6() + { + return new ModifierlessKeybind(KeyEvent.VK_6, 0); + } + + @ConfigItem( + position = 13, + keyName = "f7", + name = "F7", + description = "The key which will replace {F7}." + ) + default ModifierlessKeybind f7() + { + return new ModifierlessKeybind(KeyEvent.VK_7, 0); + } + + @ConfigItem( + position = 14, + keyName = "f8", + name = "F8", + description = "The key which will replace {F8}." + ) + default ModifierlessKeybind f8() + { + return new ModifierlessKeybind(KeyEvent.VK_8, 0); + } + + @ConfigItem( + position = 15, + keyName = "f9", + name = "F9", + description = "The key which will replace {F9}." + ) + default ModifierlessKeybind f9() + { + return new ModifierlessKeybind(KeyEvent.VK_9, 0); + } + + @ConfigItem( + position = 16, + keyName = "f10", + name = "F10", + description = "The key which will replace {F10}." + ) + default ModifierlessKeybind f10() + { + return new ModifierlessKeybind(KeyEvent.VK_0, 0); + } + + @ConfigItem( + position = 17, + keyName = "f11", + name = "F11", + description = "The key which will replace {F11}." + ) + default ModifierlessKeybind f11() + { + return new ModifierlessKeybind(KeyEvent.VK_MINUS, 0); + } + + @ConfigItem( + position = 18, + keyName = "f12", + name = "F12", + description = "The key which will replace {F12}." + ) + default ModifierlessKeybind f12() + { + return new ModifierlessKeybind(KeyEvent.VK_EQUALS, 0); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingListener.java b/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingListener.java index a6ad6e8638..e3257f8e73 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingListener.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingListener.java @@ -34,25 +34,11 @@ import net.runelite.api.Client; import net.runelite.api.GameState; import net.runelite.api.VarClientStr; import net.runelite.client.callback.ClientThread; -import net.runelite.client.config.Keybind; -import net.runelite.client.config.ModifierlessKeybind; import net.runelite.client.input.KeyListener; import net.runelite.client.input.MouseAdapter; class KeyRemappingListener extends MouseAdapter implements KeyListener { - private static final Keybind ONE = new ModifierlessKeybind(KeyEvent.VK_1, 0); - private static final Keybind TWO = new ModifierlessKeybind(KeyEvent.VK_2, 0); - private static final Keybind THREE = new ModifierlessKeybind(KeyEvent.VK_3, 0); - private static final Keybind FOUR = new ModifierlessKeybind(KeyEvent.VK_4, 0); - private static final Keybind FIVE = new ModifierlessKeybind(KeyEvent.VK_5, 0); - private static final Keybind SIX = new ModifierlessKeybind(KeyEvent.VK_6, 0); - private static final Keybind SEVEN = new ModifierlessKeybind(KeyEvent.VK_7, 0); - private static final Keybind EIGHT = new ModifierlessKeybind(KeyEvent.VK_8, 0); - private static final Keybind NINE = new ModifierlessKeybind(KeyEvent.VK_9, 0); - private static final Keybind ZERO = new ModifierlessKeybind(KeyEvent.VK_0, 0); - private static final Keybind MINUS = new ModifierlessKeybind(KeyEvent.VK_MINUS, 0); - private static final Keybind EQUALS = new ModifierlessKeybind(KeyEvent.VK_EQUALS, 0); @Inject private KeyRemappingPlugin plugin; @@ -112,62 +98,62 @@ class KeyRemappingListener extends MouseAdapter implements KeyListener // to select options if (config.fkeyRemap() && !plugin.isDialogOpen()) { - if (ONE.matches(e)) + if (config.f1().matches(e)) { modified.put(e.getKeyCode(), KeyEvent.VK_F1); e.setKeyCode(KeyEvent.VK_F1); } - else if (TWO.matches(e)) + else if (config.f2().matches(e)) { modified.put(e.getKeyCode(), KeyEvent.VK_F2); e.setKeyCode(KeyEvent.VK_F2); } - else if (THREE.matches(e)) + else if (config.f3().matches(e)) { modified.put(e.getKeyCode(), KeyEvent.VK_F3); e.setKeyCode(KeyEvent.VK_F3); } - else if (FOUR.matches(e)) + else if (config.f4().matches(e)) { modified.put(e.getKeyCode(), KeyEvent.VK_F4); e.setKeyCode(KeyEvent.VK_F4); } - else if (FIVE.matches(e)) + else if (config.f5().matches(e)) { modified.put(e.getKeyCode(), KeyEvent.VK_F5); e.setKeyCode(KeyEvent.VK_F5); } - else if (SIX.matches(e)) + else if (config.f6().matches(e)) { modified.put(e.getKeyCode(), KeyEvent.VK_F6); e.setKeyCode(KeyEvent.VK_F6); } - else if (SEVEN.matches(e)) + else if (config.f7().matches(e)) { modified.put(e.getKeyCode(), KeyEvent.VK_F7); e.setKeyCode(KeyEvent.VK_F7); } - else if (EIGHT.matches(e)) + else if (config.f8().matches(e)) { modified.put(e.getKeyCode(), KeyEvent.VK_F8); e.setKeyCode(KeyEvent.VK_F8); } - else if (NINE.matches(e)) + else if (config.f9().matches(e)) { modified.put(e.getKeyCode(), KeyEvent.VK_F9); e.setKeyCode(KeyEvent.VK_F9); } - else if (ZERO.matches(e)) + else if (config.f10().matches(e)) { modified.put(e.getKeyCode(), KeyEvent.VK_F10); e.setKeyCode(KeyEvent.VK_F10); } - else if (MINUS.matches(e)) + else if (config.f11().matches(e)) { modified.put(e.getKeyCode(), KeyEvent.VK_F11); e.setKeyCode(KeyEvent.VK_F11); } - else if (EQUALS.matches(e)) + else if (config.f12().matches(e)) { modified.put(e.getKeyCode(), KeyEvent.VK_F12); e.setKeyCode(KeyEvent.VK_F12); @@ -245,51 +231,51 @@ class KeyRemappingListener extends MouseAdapter implements KeyListener if (config.fkeyRemap()) { - if (ONE.matches(e)) + if (config.f1().matches(e)) { e.setKeyCode(KeyEvent.VK_F1); } - else if (TWO.matches(e)) + else if (config.f2().matches(e)) { e.setKeyCode(KeyEvent.VK_F2); } - else if (THREE.matches(e)) + else if (config.f3().matches(e)) { e.setKeyCode(KeyEvent.VK_F3); } - else if (FOUR.matches(e)) + else if (config.f4().matches(e)) { e.setKeyCode(KeyEvent.VK_F4); } - else if (FIVE.matches(e)) + else if (config.f5().matches(e)) { e.setKeyCode(KeyEvent.VK_F5); } - else if (SIX.matches(e)) + else if (config.f6().matches(e)) { e.setKeyCode(KeyEvent.VK_F6); } - else if (SEVEN.matches(e)) + else if (config.f7().matches(e)) { e.setKeyCode(KeyEvent.VK_F7); } - else if (EIGHT.matches(e)) + else if (config.f8().matches(e)) { e.setKeyCode(KeyEvent.VK_F8); } - else if (NINE.matches(e)) + else if (config.f9().matches(e)) { e.setKeyCode(KeyEvent.VK_F9); } - else if (ZERO.matches(e)) + else if (config.f10().matches(e)) { e.setKeyCode(KeyEvent.VK_F10); } - else if (MINUS.matches(e)) + else if (config.f11().matches(e)) { e.setKeyCode(KeyEvent.VK_F11); } - else if (EQUALS.matches(e)) + else if (config.f12().matches(e)) { e.setKeyCode(KeyEvent.VK_F12); } From 39e66af62346949dc77e339ebfb34014b0a3b220 Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 8 Jul 2019 18:10:45 -0400 Subject: [PATCH 03/18] keyremapping: add F-key remap for escape Co-authored-by: Chet Powers --- .../plugins/keyremapping/KeyRemappingConfig.java | 11 +++++++++++ .../plugins/keyremapping/KeyRemappingListener.java | 9 +++++++++ 2 files changed, 20 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingConfig.java index 9702f6c40c..0ee4c58e22 100755 --- a/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingConfig.java @@ -230,4 +230,15 @@ public interface KeyRemappingConfig extends Config { return new ModifierlessKeybind(KeyEvent.VK_EQUALS, 0); } + + @ConfigItem( + position = 19, + keyName = "esc", + name = "ESC", + description = "The key which will replace {ESC}." + ) + default ModifierlessKeybind esc() + { + return new ModifierlessKeybind(KeyEvent.VK_ESCAPE, 0); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingListener.java b/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingListener.java index e3257f8e73..9ffbd27e77 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingListener.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingListener.java @@ -158,6 +158,11 @@ class KeyRemappingListener extends MouseAdapter implements KeyListener modified.put(e.getKeyCode(), KeyEvent.VK_F12); e.setKeyCode(KeyEvent.VK_F12); } + else if (config.esc().matches(e)) + { + modified.put(e.getKeyCode(), KeyEvent.VK_ESCAPE); + e.setKeyCode(KeyEvent.VK_ESCAPE); + } } switch (e.getKeyCode()) @@ -279,6 +284,10 @@ class KeyRemappingListener extends MouseAdapter implements KeyListener { e.setKeyCode(KeyEvent.VK_F12); } + else if (config.esc().matches(e)) + { + e.setKeyCode(KeyEvent.VK_ESCAPE); + } } } else From b874b18abebe14aa375151cefbd3725eb0456155 Mon Sep 17 00:00:00 2001 From: Decorth <38436593+Decorth@users.noreply.github.com> Date: Tue, 9 Jul 2019 10:47:11 +0100 Subject: [PATCH 04/18] Add Sarachnis respawn timer to Boss timers plugin (#9313) --- .../main/java/net/runelite/client/plugins/bosstimer/Boss.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/bosstimer/Boss.java b/runelite-client/src/main/java/net/runelite/client/plugins/bosstimer/Boss.java index 47752f415b..81ae775d12 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/bosstimer/Boss.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/bosstimer/Boss.java @@ -57,7 +57,8 @@ enum Boss KRAKEN(NpcID.KRAKEN, 8400, ChronoUnit.MILLIS, ItemID.PET_KRAKEN), KALPHITE_QUEEN(NpcID.KALPHITE_QUEEN_965, 30, ChronoUnit.SECONDS, ItemID.KALPHITE_PRINCESS), DUSK(NpcID.DUSK_7889, 2, ChronoUnit.MINUTES, ItemID.NOON), - ALCHEMICAL_HYDRA(NpcID.ALCHEMICAL_HYDRA_8622, 25200, ChronoUnit.MILLIS, ItemID.IKKLE_HYDRA); + ALCHEMICAL_HYDRA(NpcID.ALCHEMICAL_HYDRA_8622, 25200, ChronoUnit.MILLIS, ItemID.IKKLE_HYDRA), + SARACHNIS(NpcID.SARACHNIS, 30, ChronoUnit.SECONDS, ItemID.SRARACHA); private static final Map bosses; From 7f7a71eb386acd122948d1b4faaf5757dee59080 Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 9 Jul 2019 09:23:38 -0400 Subject: [PATCH 05/18] api: add item container id to item container changed event --- .../java/net/runelite/api/events/ItemContainerChanged.java | 5 +++++ .../main/java/net/runelite/client/util/GameEventManager.java | 2 +- .../client/plugins/motherlode/MotherlodePluginTest.java | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/runelite-api/src/main/java/net/runelite/api/events/ItemContainerChanged.java b/runelite-api/src/main/java/net/runelite/api/events/ItemContainerChanged.java index f4c5d8163f..f2ff89a679 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/ItemContainerChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/ItemContainerChanged.java @@ -41,6 +41,11 @@ import net.runelite.api.ItemContainer; @Value public class ItemContainerChanged { + /** + * The modified container's ID. + */ + private final int containerId; + /** * The modified item container. */ diff --git a/runelite-client/src/main/java/net/runelite/client/util/GameEventManager.java b/runelite-client/src/main/java/net/runelite/client/util/GameEventManager.java index edaedc856f..e837bb54d9 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/GameEventManager.java +++ b/runelite-client/src/main/java/net/runelite/client/util/GameEventManager.java @@ -118,7 +118,7 @@ public class GameEventManager if (itemContainer != null) { - eventBus.post(new ItemContainerChanged(itemContainer)); + eventBus.post(new ItemContainerChanged(inventory.getId(), itemContainer)); } } diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/motherlode/MotherlodePluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/motherlode/MotherlodePluginTest.java index 9ceaf15247..602a58f3b6 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/motherlode/MotherlodePluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/motherlode/MotherlodePluginTest.java @@ -147,7 +147,7 @@ public class MotherlodePluginTest when(client.getItemContainer(InventoryID.INVENTORY)).thenReturn(inventory); // Trigger comparison - motherlodePlugin.onItemContainerChanged(new ItemContainerChanged(inventory)); + motherlodePlugin.onItemContainerChanged(new ItemContainerChanged(InventoryID.INVENTORY.getId(), inventory)); verify(motherlodeSession).updateOreFound(ItemID.RUNITE_ORE, 1); verify(motherlodeSession).updateOreFound(ItemID.GOLDEN_NUGGET, 4); From 62e877de5ca53d284c1c64165e495e4562bb200e Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 9 Jul 2019 13:53:33 -0400 Subject: [PATCH 06/18] chat filter: add option to filter login notifications Co-authored-by: gregg1494 --- .../client/plugins/chatfilter/ChatFilterConfig.java | 11 +++++++++++ .../client/plugins/chatfilter/ChatFilterPlugin.java | 7 +++++++ 2 files changed, 18 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chatfilter/ChatFilterConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/chatfilter/ChatFilterConfig.java index 264e5ae425..dbdb4d6478 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/chatfilter/ChatFilterConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/chatfilter/ChatFilterConfig.java @@ -86,4 +86,15 @@ public interface ChatFilterConfig extends Config { return false; } + + @ConfigItem( + keyName = "filterLogin", + name = "Filter Logged In/Out Messages", + description = "Filter your private chat to remove logged in/out messages", + position = 6 + ) + default boolean filterLogin() + { + return false; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chatfilter/ChatFilterPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/chatfilter/ChatFilterPlugin.java index 53f4e43ef6..6a3db9266f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/chatfilter/ChatFilterPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/chatfilter/ChatFilterPlugin.java @@ -116,6 +116,13 @@ public class ChatFilterPlugin extends Plugin case MODPRIVATECHAT: case FRIENDSCHAT: break; + case LOGINLOGOUTNOTIFICATION: + if (config.filterLogin()) + { + // Block the message + intStack[intStackSize - 3] = 0; + } + return; default: return; } From 3dc8522d53635042d41c6f7cbbd8594402414520 Mon Sep 17 00:00:00 2001 From: David Date: Tue, 9 Jul 2019 19:28:47 +0100 Subject: [PATCH 07/18] idle notifier: add farming harvest animations --- runelite-api/src/main/java/net/runelite/api/AnimationID.java | 1 + .../client/plugins/idlenotifier/IdleNotifierPlugin.java | 5 +++++ 2 files changed, 6 insertions(+) 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 316feda5d0..1dc49ca455 100644 --- a/runelite-api/src/main/java/net/runelite/api/AnimationID.java +++ b/runelite-api/src/main/java/net/runelite/api/AnimationID.java @@ -171,6 +171,7 @@ public final class AnimationID public static final int FARMING_PLANT_SEED = 2291; public static final int FARMING_HARVEST_FLOWER = 2292; public static final int FARMING_MIX_ULTRACOMPOST = 7699; + public static final int FARMING_HARVEST_ALLOTMENT = 830; // Lunar spellbook public static final int ENERGY_TRANSFER_VENGEANCE_OTHER = 4411; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java index bb7dee8b47..45b37e60f7 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java @@ -217,6 +217,11 @@ public class IdleNotifierPlugin extends Plugin case USING_GILDED_ALTAR: /* Farming */ case FARMING_MIX_ULTRACOMPOST: + case FARMING_HARVEST_BUSH: + case FARMING_HARVEST_HERB: + case FARMING_HARVEST_FRUIT_TREE: + case FARMING_HARVEST_FLOWER: + case FARMING_HARVEST_ALLOTMENT: /* Misc */ case PISCARILIUS_CRANE_REPAIR: case HOME_MAKE_TABLET: From d39bfcb50feadeb782c076131e3598a18dec4b25 Mon Sep 17 00:00:00 2001 From: TheStonedTurtle Date: Thu, 20 Jun 2019 12:46:31 -0700 Subject: [PATCH 08/18] itemskeptondeath: fix many edge cases and add tests This adds support for most degradeable and imbued items, which have death prices that are fixed offsets of the prices of some other reference item, clue boxes, and jewelery. The plugin was refactored largely to be able to add the tests, and now works on our internal DeathItem and ItemStack classes instead of widgets and items. Co-authored-by: Adam --- .../net/runelite/client/game/ItemManager.java | 21 +- .../itemskeptondeath/AlwaysLostItem.java | 4 +- .../itemskeptondeath/DynamicPriceItem.java | 107 +++ .../itemskeptondeath/FixedPriceItem.java | 167 ++++- .../plugins/itemskeptondeath/ItemStack.java | 36 + .../ItemsKeptOnDeathPlugin.java | 342 ++++++---- .../itemskeptondeath/LostIfNotProtected.java | 45 ++ .../ItemsKeptOnDeathPluginTest.java | 616 ++++++++++++++++++ 8 files changed, 1215 insertions(+), 123 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/DynamicPriceItem.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/ItemStack.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/LostIfNotProtected.java create mode 100644 runelite-client/src/test/java/net/runelite/client/plugins/itemskeptondeath/ItemsKeptOnDeathPluginTest.java diff --git a/runelite-client/src/main/java/net/runelite/client/game/ItemManager.java b/runelite-client/src/main/java/net/runelite/client/game/ItemManager.java index bbc07771c2..a257dccba1 100644 --- a/runelite-client/src/main/java/net/runelite/client/game/ItemManager.java +++ b/runelite-client/src/main/java/net/runelite/client/game/ItemManager.java @@ -273,6 +273,18 @@ public class ItemManager * @return item price */ public int getItemPrice(int itemID) + { + return getItemPrice(itemID, false); + } + + /** + * Look up an item's price + * + * @param itemID item id + * @param ignoreUntradeableMap should the price returned ignore the {@link UntradeableItemMapping} + * @return item price + */ + public int getItemPrice(int itemID, boolean ignoreUntradeableMap) { if (itemID == ItemID.COINS_995) { @@ -283,10 +295,13 @@ public class ItemManager return 1000; } - UntradeableItemMapping p = UntradeableItemMapping.map(ItemVariationMapping.map(itemID)); - if (p != null) + if (!ignoreUntradeableMap) { - return getItemPrice(p.getPriceID()) * p.getQuantity(); + UntradeableItemMapping p = UntradeableItemMapping.map(ItemVariationMapping.map(itemID)); + if (p != null) + { + return getItemPrice(p.getPriceID()) * p.getQuantity(); + } } int price = 0; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/AlwaysLostItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/AlwaysLostItem.java index 37066622bc..140b524cfe 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/AlwaysLostItem.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/AlwaysLostItem.java @@ -41,7 +41,9 @@ enum AlwaysLostItem { RUNE_POUCH(ItemID.RUNE_POUCH, true), LOOTING_BAG(ItemID.LOOTING_BAG, false), - CLUE_BOX(ItemID.CLUE_BOX, false); + CLUE_BOX(ItemID.CLUE_BOX, false), + BRACELET_OF_ETHEREUM(ItemID.BRACELET_OF_ETHEREUM, false), + BRACELET_OF_ETHEREUM_UNCHARGED(ItemID.BRACELET_OF_ETHEREUM_UNCHARGED, false); private final int itemID; private final boolean keptOutsideOfWilderness; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/DynamicPriceItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/DynamicPriceItem.java new file mode 100644 index 0000000000..4726555485 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/DynamicPriceItem.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2019, TheStonedTurtle + * 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.itemskeptondeath; + +import com.google.common.collect.ImmutableMap; +import java.util.Map; +import javax.annotation.Nullable; +import lombok.AllArgsConstructor; +import lombok.Getter; +import net.runelite.api.ItemID; + +/** + * Degradable/Non-rechargeable Jewelry death prices are usually determined by the amount of charges the item has left. + * The price of each charge is based on the GE price of the fully charged item divided by the maximum item charges + * Charge price = GE Price / Max Charges + * Death Price = Charge price * Current Charges + */ +@AllArgsConstructor +@Getter +enum DynamicPriceItem +{ + GAMES_NECKLACE1(ItemID.GAMES_NECKLACE1, 1, 8, ItemID.GAMES_NECKLACE8), + GAMES_NECKLACE2(ItemID.GAMES_NECKLACE2, 2, 8, ItemID.GAMES_NECKLACE8), + GAMES_NECKLACE3(ItemID.GAMES_NECKLACE3, 3, 8, ItemID.GAMES_NECKLACE8), + GAMES_NECKLACE4(ItemID.GAMES_NECKLACE4, 4, 8, ItemID.GAMES_NECKLACE8), + GAMES_NECKLACE5(ItemID.GAMES_NECKLACE5, 5, 8, ItemID.GAMES_NECKLACE8), + GAMES_NECKLACE6(ItemID.GAMES_NECKLACE6, 6, 8, ItemID.GAMES_NECKLACE8), + GAMES_NECKLACE7(ItemID.GAMES_NECKLACE7, 7, 8, ItemID.GAMES_NECKLACE8), + + RING_OF_DUELING1(ItemID.RING_OF_DUELING1, 1, 8, ItemID.RING_OF_DUELING8), + RING_OF_DUELING2(ItemID.RING_OF_DUELING2, 2, 8, ItemID.RING_OF_DUELING8), + RING_OF_DUELING3(ItemID.RING_OF_DUELING3, 3, 8, ItemID.RING_OF_DUELING8), + RING_OF_DUELING4(ItemID.RING_OF_DUELING4, 4, 8, ItemID.RING_OF_DUELING8), + RING_OF_DUELING5(ItemID.RING_OF_DUELING5, 5, 8, ItemID.RING_OF_DUELING8), + RING_OF_DUELING6(ItemID.RING_OF_DUELING6, 6, 8, ItemID.RING_OF_DUELING8), + RING_OF_DUELING7(ItemID.RING_OF_DUELING7, 7, 8, ItemID.RING_OF_DUELING8), + + RING_OF_RETURNING1(ItemID.RING_OF_RETURNING1, 1, 5, ItemID.RING_OF_RETURNING5), + RING_OF_RETURNING2(ItemID.RING_OF_RETURNING2, 2, 5, ItemID.RING_OF_RETURNING5), + RING_OF_RETURNING3(ItemID.RING_OF_RETURNING3, 3, 5, ItemID.RING_OF_RETURNING5), + RING_OF_RETURNING4(ItemID.RING_OF_RETURNING4, 4, 5, ItemID.RING_OF_RETURNING5), + + NECKLACE_OF_PASSAGE1(ItemID.NECKLACE_OF_PASSAGE1, 1, 5, ItemID.NECKLACE_OF_PASSAGE5), + NECKLACE_OF_PASSAGE2(ItemID.NECKLACE_OF_PASSAGE2, 2, 5, ItemID.NECKLACE_OF_PASSAGE5), + NECKLACE_OF_PASSAGE3(ItemID.NECKLACE_OF_PASSAGE3, 3, 5, ItemID.NECKLACE_OF_PASSAGE5), + NECKLACE_OF_PASSAGE4(ItemID.NECKLACE_OF_PASSAGE4, 4, 5, ItemID.NECKLACE_OF_PASSAGE5), + + BURNING_AMULET1(ItemID.BURNING_AMULET1, 1, 5, ItemID.BURNING_AMULET5), + BURNING_AMULET2(ItemID.BURNING_AMULET2, 2, 5, ItemID.BURNING_AMULET5), + BURNING_AMULET3(ItemID.BURNING_AMULET3, 3, 5, ItemID.BURNING_AMULET5), + BURNING_AMULET4(ItemID.BURNING_AMULET4, 4, 5, ItemID.BURNING_AMULET5); + + private final int itemId; + private final int currentCharges; + private final int maxCharges; + private final int chargedId; + + private static final Map DYNAMIC_ITEMS; + + static + { + final ImmutableMap.Builder map = ImmutableMap.builder(); + for (final DynamicPriceItem p : values()) + { + map.put(p.itemId, p); + } + DYNAMIC_ITEMS = map.build(); + } + + /** + * Calculates the price off the partially charged jewelry based on the base items price + * @param basePrice price of the base item, usually the trade-able variant + * @return death price of the current DynamicPriceItem + */ + int calculateDeathPrice(final int basePrice) + { + return (basePrice / maxCharges) * currentCharges; + } + + @Nullable + static DynamicPriceItem find(int itemId) + { + return DYNAMIC_ITEMS.get(itemId); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/FixedPriceItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/FixedPriceItem.java index a40851dc65..74120a47d6 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/FixedPriceItem.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/FixedPriceItem.java @@ -28,7 +28,6 @@ package net.runelite.client.plugins.itemskeptondeath; import com.google.common.collect.ImmutableMap; import java.util.Map; import javax.annotation.Nullable; -import lombok.AllArgsConstructor; import lombok.Getter; import net.runelite.api.ItemID; @@ -36,7 +35,6 @@ import net.runelite.api.ItemID; * Some items have a fixed price that is added to its default value when calculating death prices. * These are typically imbued items, such as Berserker ring (i), to help it protect over the non-imbued variants. */ -@AllArgsConstructor @Getter enum FixedPriceItem { @@ -66,10 +64,161 @@ enum FixedPriceItem IMBUED_RING_OF_THE_GODS_I(ItemID.RING_OF_THE_GODS_I, 2000), IMBUED_TREASONOUS_RING_I(ItemID.TREASONOUS_RING_I, 2000), - IMBUED_TYRANNICAL_RING_I(ItemID.TYRANNICAL_RING_I, 2000); + IMBUED_TYRANNICAL_RING_I(ItemID.TYRANNICAL_RING_I, 2000), + + GRACEFUL_HOOD(ItemID.GRACEFUL_HOOD, 1965), + GRACEFUL_CAPE(ItemID.GRACEFUL_CAPE, 2460), + GRACEFUL_TOP(ItemID.GRACEFUL_TOP, 2345), + GRACEFUL_LEGS(ItemID.GRACEFUL_LEGS, 2290), + GRACEFUL_GLOVES(ItemID.GRACEFUL_GLOVES, 1970), + GRACEFUL_BOOTS(ItemID.GRACEFUL_BOOTS, 2060), + + ANGLER_HAT(ItemID.ANGLER_HAT, 2600), + ANGLER_TOP(ItemID.ANGLER_TOP, 3550), + ANGLER_WADERS(ItemID.ANGLER_WADERS, 4400), + ANGLER_BOOTS(ItemID.ANGLER_BOOTS, 5300), + + PROSPECTOR_HELMET(ItemID.PROSPECTOR_HELMET, 2640), + PROSPECTOR_JACKET(ItemID.PROSPECTOR_JACKET, 3550), + PROSPECTOR_LEGS(ItemID.PROSPECTOR_LEGS, 4460), + PROSPECTOR_BOOTS(ItemID.PROSPECTOR_BOOTS, 5370), + + LUMBERJACK_HAT(ItemID.LUMBERJACK_HAT, 19950), + LUMBERJACK_TOP(ItemID.LUMBERJACK_TOP, 19950), + LUMBERJACK_LEGS(ItemID.LUMBERJACK_LEGS, 19950), + LUMBERJACK_BOOTS(ItemID.LUMBERJACK_BOOTS, 19950), + + ROGUE_MASK(ItemID.ROGUE_MASK, 725), + ROGUE_TOP(ItemID.ROGUE_TOP, 575), + ROGUE_TROUSERS(ItemID.ROGUE_TROUSERS, 500), + ROGUE_GLOVES(ItemID.ROGUE_GLOVES, 650), + ROGUE_BOOTS(ItemID.ROGUE_BOOTS, 650), + + RING_OF_WEALTH_1(ItemID.RING_OF_WEALTH_1, 500, ItemID.RING_OF_WEALTH), + RING_OF_WEALTH_2(ItemID.RING_OF_WEALTH_2, 1000, ItemID.RING_OF_WEALTH), + RING_OF_WEALTH_3(ItemID.RING_OF_WEALTH_3, 1500, ItemID.RING_OF_WEALTH), + RING_OF_WEALTH_4(ItemID.RING_OF_WEALTH_4, 2000, ItemID.RING_OF_WEALTH), + + AMULET_OF_GLORY1(ItemID.AMULET_OF_GLORY1, 500, ItemID.AMULET_OF_GLORY), + AMULET_OF_GLORY2(ItemID.AMULET_OF_GLORY2, 1000, ItemID.AMULET_OF_GLORY), + AMULET_OF_GLORY3(ItemID.AMULET_OF_GLORY3, 1500, ItemID.AMULET_OF_GLORY), + AMULET_OF_GLORY5(ItemID.AMULET_OF_GLORY5, 2500, ItemID.AMULET_OF_GLORY), + + COMBAT_BRACELET1(ItemID.COMBAT_BRACELET1, 500, ItemID.COMBAT_BRACELET), + COMBAT_BRACELET2(ItemID.COMBAT_BRACELET2, 1000, ItemID.COMBAT_BRACELET), + COMBAT_BRACELET3(ItemID.COMBAT_BRACELET3, 1500, ItemID.COMBAT_BRACELET), + COMBAT_BRACELET5(ItemID.COMBAT_BRACELET5, 2500, ItemID.COMBAT_BRACELET), + + SKILLS_NECKLACE1(ItemID.SKILLS_NECKLACE1, 500, ItemID.SKILLS_NECKLACE), + SKILLS_NECKLACE2(ItemID.SKILLS_NECKLACE2, 1000, ItemID.SKILLS_NECKLACE), + SKILLS_NECKLACE3(ItemID.SKILLS_NECKLACE3, 1500, ItemID.SKILLS_NECKLACE), + SKILLS_NECKLACE4(ItemID.SKILLS_NECKLACE5, 2500, ItemID.SKILLS_NECKLACE), + + AHRIMS_HOOD_25(ItemID.AHRIMS_HOOD_25, 2500, ItemID.AHRIMS_HOOD_0), + AHRIMS_HOOD_50(ItemID.AHRIMS_HOOD_50, 5000, ItemID.AHRIMS_HOOD_0), + AHRIMS_HOOD_75(ItemID.AHRIMS_HOOD_75, 7500, ItemID.AHRIMS_HOOD_0), + AHRIMS_HOOD_100(ItemID.AHRIMS_HOOD_100, 10000, ItemID.AHRIMS_HOOD_0), + AHRIMS_ROBETOP_25(ItemID.AHRIMS_ROBETOP_25, 2500, ItemID.AHRIMS_ROBETOP_0), + AHRIMS_ROBETOP_50(ItemID.AHRIMS_ROBETOP_50, 5000, ItemID.AHRIMS_ROBETOP_0), + AHRIMS_ROBETOP_75(ItemID.AHRIMS_ROBETOP_75, 7500, ItemID.AHRIMS_ROBETOP_0), + AHRIMS_ROBETOP_100(ItemID.AHRIMS_ROBETOP_100, 10000, ItemID.AHRIMS_ROBETOP_0), + AHRIMS_ROBESKIRT_25(ItemID.AHRIMS_ROBESKIRT_25, 2500, ItemID.AHRIMS_ROBESKIRT_0), + AHRIMS_ROBESKIRT_50(ItemID.AHRIMS_ROBESKIRT_50, 5000, ItemID.AHRIMS_ROBESKIRT_0), + AHRIMS_ROBESKIRT_75(ItemID.AHRIMS_ROBESKIRT_75, 7500, ItemID.AHRIMS_ROBESKIRT_0), + AHRIMS_ROBESKIRT_100(ItemID.AHRIMS_ROBESKIRT_100, 10000, ItemID.AHRIMS_ROBESKIRT_0), + AHRIMS_STAFF_25(ItemID.AHRIMS_STAFF_25, 2500, ItemID.AHRIMS_STAFF_0), + AHRIMS_STAFF_50(ItemID.AHRIMS_STAFF_50, 5000, ItemID.AHRIMS_STAFF_0), + AHRIMS_STAFF_75(ItemID.AHRIMS_STAFF_75, 7500, ItemID.AHRIMS_STAFF_0), + AHRIMS_STAFF_100(ItemID.AHRIMS_STAFF_100, 10000, ItemID.AHRIMS_STAFF_0), + + KARILS_COIF_25(ItemID.KARILS_COIF_25, 2500, ItemID.KARILS_COIF_0), + KARILS_COIF_50(ItemID.KARILS_COIF_50, 5000, ItemID.KARILS_COIF_0), + KARILS_COIF_75(ItemID.KARILS_COIF_75, 7500, ItemID.KARILS_COIF_0), + KARILS_COIF_100(ItemID.KARILS_COIF_100, 10000, ItemID.KARILS_COIF_0), + KARILS_LEATHERTOP_25(ItemID.KARILS_LEATHERTOP_25, 2500, ItemID.KARILS_LEATHERTOP_0), + KARILS_LEATHERTOP_50(ItemID.KARILS_LEATHERTOP_50, 5000, ItemID.KARILS_LEATHERTOP_0), + KARILS_LEATHERTOP_75(ItemID.KARILS_LEATHERTOP_75, 7500, ItemID.KARILS_LEATHERTOP_0), + KARILS_LEATHERTOP_100(ItemID.KARILS_LEATHERTOP_100, 10000, ItemID.KARILS_LEATHERTOP_0), + KARILS_LEATHERSKIRT_25(ItemID.KARILS_LEATHERSKIRT_25, 2500, ItemID.KARILS_LEATHERSKIRT_0), + KARILS_LEATHERSKIRT_50(ItemID.KARILS_LEATHERSKIRT_50, 5000, ItemID.KARILS_LEATHERSKIRT_0), + KARILS_LEATHERSKIRT_75(ItemID.KARILS_LEATHERSKIRT_75, 7500, ItemID.KARILS_LEATHERSKIRT_0), + KARILS_LEATHERSKIRT_100(ItemID.KARILS_LEATHERSKIRT_100, 10000, ItemID.KARILS_LEATHERSKIRT_0), + KARILS_CROSSBOW_25(ItemID.KARILS_CROSSBOW_25, 2500, ItemID.KARILS_CROSSBOW_0), + KARILS_CROSSBOW_50(ItemID.KARILS_CROSSBOW_50, 5000, ItemID.KARILS_CROSSBOW_0), + KARILS_CROSSBOW_75(ItemID.KARILS_CROSSBOW_75, 7500, ItemID.KARILS_CROSSBOW_0), + KARILS_CROSSBOW_100(ItemID.KARILS_CROSSBOW_100, 10000, ItemID.KARILS_CROSSBOW_0), + + DHAROKS_HELM_25(ItemID.DHAROKS_HELM_25, 2500, ItemID.DHAROKS_HELM_0), + DHAROKS_HELM_50(ItemID.DHAROKS_HELM_50, 5000, ItemID.DHAROKS_HELM_0), + DHAROKS_HELM_75(ItemID.DHAROKS_HELM_75, 7500, ItemID.DHAROKS_HELM_0), + DHAROKS_HELM_100(ItemID.DHAROKS_HELM_100, 10000, ItemID.DHAROKS_HELM_0), + DHAROKS_PLATEBODY_25(ItemID.DHAROKS_PLATEBODY_25, 2500, ItemID.DHAROKS_PLATEBODY_0), + DHAROKS_PLATEBODY_50(ItemID.DHAROKS_PLATEBODY_50, 5000, ItemID.DHAROKS_PLATEBODY_0), + DHAROKS_PLATEBODY_75(ItemID.DHAROKS_PLATEBODY_75, 7500, ItemID.DHAROKS_PLATEBODY_0), + DHAROKS_PLATEBODY_100(ItemID.DHAROKS_PLATEBODY_100, 10000, ItemID.DHAROKS_PLATEBODY_0), + DHAROKS_PLATELEGS_25(ItemID.DHAROKS_PLATELEGS_25, 2500, ItemID.DHAROKS_PLATELEGS_0), + DHAROKS_PLATELEGS_50(ItemID.DHAROKS_PLATELEGS_50, 5000, ItemID.DHAROKS_PLATELEGS_0), + DHAROKS_PLATELEGS_75(ItemID.DHAROKS_PLATELEGS_75, 7500, ItemID.DHAROKS_PLATELEGS_0), + DHAROKS_PLATELEGS_100(ItemID.DHAROKS_PLATELEGS_100, 10000, ItemID.DHAROKS_PLATELEGS_0), + DHAROKS_GREATAXE_25(ItemID.DHAROKS_GREATAXE_25, 2500, ItemID.DHAROKS_GREATAXE_0), + DHAROKS_GREATAXE_50(ItemID.DHAROKS_GREATAXE_50, 5000, ItemID.DHAROKS_GREATAXE_0), + DHAROKS_GREATAXE_75(ItemID.DHAROKS_GREATAXE_75, 7500, ItemID.DHAROKS_GREATAXE_0), + DHAROKS_GREATAXE_100(ItemID.DHAROKS_GREATAXE_100, 10000, ItemID.DHAROKS_GREATAXE_0), + + GUTHANS_HELM_25(ItemID.GUTHANS_HELM_25, 2500, ItemID.GUTHANS_HELM_0), + GUTHANS_HELM_50(ItemID.GUTHANS_HELM_50, 5000, ItemID.GUTHANS_HELM_0), + GUTHANS_HELM_75(ItemID.GUTHANS_HELM_75, 7500, ItemID.GUTHANS_HELM_0), + GUTHANS_HELM_100(ItemID.GUTHANS_HELM_100, 10000, ItemID.GUTHANS_HELM_0), + GUTHANS_PLATEBODY_25(ItemID.GUTHANS_PLATEBODY_25, 2500, ItemID.GUTHANS_PLATEBODY_0), + GUTHANS_PLATEBODY_50(ItemID.GUTHANS_PLATEBODY_50, 5000, ItemID.GUTHANS_PLATEBODY_0), + GUTHANS_PLATEBODY_75(ItemID.GUTHANS_PLATEBODY_75, 7500, ItemID.GUTHANS_PLATEBODY_0), + GUTHANS_PLATEBODY_100(ItemID.GUTHANS_PLATEBODY_100, 10000, ItemID.GUTHANS_PLATEBODY_0), + GUTHANS_CHAINSKIRT_25(ItemID.GUTHANS_CHAINSKIRT_25, 2500, ItemID.GUTHANS_CHAINSKIRT_0), + GUTHANS_CHAINSKIRT_50(ItemID.GUTHANS_CHAINSKIRT_50, 5000, ItemID.GUTHANS_CHAINSKIRT_0), + GUTHANS_CHAINSKIRT_75(ItemID.GUTHANS_CHAINSKIRT_75, 7500, ItemID.GUTHANS_CHAINSKIRT_0), + GUTHANS_CHAINSKIRT_100(ItemID.GUTHANS_CHAINSKIRT_100, 10000, ItemID.GUTHANS_CHAINSKIRT_0), + GUTHANS_WARSPEAR_25(ItemID.GUTHANS_WARSPEAR_25, 2500, ItemID.GUTHANS_WARSPEAR_0), + GUTHANS_WARSPEAR_50(ItemID.GUTHANS_WARSPEAR_50, 5000, ItemID.GUTHANS_WARSPEAR_0), + GUTHANS_WARSPEAR_75(ItemID.GUTHANS_WARSPEAR_75, 7500, ItemID.GUTHANS_WARSPEAR_0), + GUTHANS_WARSPEAR_100(ItemID.GUTHANS_WARSPEAR_100, 10000, ItemID.GUTHANS_WARSPEAR_0), + + TORAGS_HELM_25(ItemID.TORAGS_HELM_25, 2500, ItemID.TORAGS_HELM_0), + TORAGS_HELM_50(ItemID.TORAGS_HELM_50, 5000, ItemID.TORAGS_HELM_0), + TORAGS_HELM_75(ItemID.TORAGS_HELM_75, 7500, ItemID.TORAGS_HELM_0), + TORAGS_HELM_100(ItemID.TORAGS_HELM_100, 10000, ItemID.TORAGS_HELM_0), + TORAGS_PLATEBODY_25(ItemID.TORAGS_PLATEBODY_25, 2500, ItemID.TORAGS_PLATEBODY_0), + TORAGS_PLATEBODY_50(ItemID.TORAGS_PLATEBODY_50, 5000, ItemID.TORAGS_PLATEBODY_0), + TORAGS_PLATEBODY_75(ItemID.TORAGS_PLATEBODY_75, 7500, ItemID.TORAGS_PLATEBODY_0), + TORAGS_PLATEBODY_100(ItemID.TORAGS_PLATEBODY_100, 10000, ItemID.TORAGS_PLATEBODY_0), + TORAGS_PLATELEGS_25(ItemID.TORAGS_PLATELEGS_25, 2500, ItemID.TORAGS_PLATELEGS_0), + TORAGS_PLATELEGS_50(ItemID.TORAGS_PLATELEGS_50, 5000, ItemID.TORAGS_PLATELEGS_0), + TORAGS_PLATELEGS_75(ItemID.TORAGS_PLATELEGS_75, 7500, ItemID.TORAGS_PLATELEGS_0), + TORAGS_PLATELEGS_100(ItemID.TORAGS_PLATELEGS_100, 10000, ItemID.TORAGS_PLATELEGS_0), + TORAGS_HAMMERS_25(ItemID.TORAGS_HAMMERS_25, 2500, ItemID.TORAGS_HAMMERS_0), + TORAGS_HAMMERS_50(ItemID.TORAGS_HAMMERS_50, 5000, ItemID.TORAGS_HAMMERS_0), + TORAGS_HAMMERS_75(ItemID.TORAGS_HAMMERS_75, 7500, ItemID.TORAGS_HAMMERS_0), + TORAGS_HAMMERS_100(ItemID.TORAGS_HAMMERS_100, 10000, ItemID.TORAGS_HAMMERS_0), + + VERACS_HELM_25(ItemID.VERACS_HELM_25, 2500, ItemID.VERACS_HELM_0), + VERACS_HELM_50(ItemID.VERACS_HELM_50, 5000, ItemID.VERACS_HELM_0), + VERACS_HELM_75(ItemID.VERACS_HELM_75, 7500, ItemID.VERACS_HELM_0), + VERACS_HELM_100(ItemID.VERACS_HELM_100, 10000, ItemID.VERACS_HELM_0), + VERACS_BRASSARD_25(ItemID.VERACS_BRASSARD_25, 2500, ItemID.VERACS_BRASSARD_0), + VERACS_BRASSARD_50(ItemID.VERACS_BRASSARD_50, 5000, ItemID.VERACS_BRASSARD_0), + VERACS_BRASSARD_75(ItemID.VERACS_BRASSARD_75, 7500, ItemID.VERACS_BRASSARD_0), + VERACS_BRASSARD_100(ItemID.VERACS_BRASSARD_100, 10000, ItemID.VERACS_BRASSARD_0), + VERACS_PLATESKIRT_25(ItemID.VERACS_PLATESKIRT_25, 2500, ItemID.VERACS_PLATESKIRT_0), + VERACS_PLATESKIRT_50(ItemID.VERACS_PLATESKIRT_50, 5000, ItemID.VERACS_PLATESKIRT_0), + VERACS_PLATESKIRT_75(ItemID.VERACS_PLATESKIRT_75, 7500, ItemID.VERACS_PLATESKIRT_0), + VERACS_PLATESKIRT_100(ItemID.VERACS_PLATESKIRT_100, 10000, ItemID.VERACS_PLATESKIRT_0), + VERACS_FLAIL_25(ItemID.VERACS_FLAIL_25, 2500, ItemID.VERACS_FLAIL_0), + VERACS_FLAIL_50(ItemID.VERACS_FLAIL_50, 5000, ItemID.VERACS_FLAIL_0), + VERACS_FLAIL_75(ItemID.VERACS_FLAIL_75, 7500, ItemID.VERACS_FLAIL_0), + VERACS_FLAIL_100(ItemID.VERACS_FLAIL_100, 10000, ItemID.VERACS_FLAIL_0); private final int itemId; private final int offset; + private final int baseId; private static final Map FIXED_ITEMS; @@ -83,6 +232,18 @@ enum FixedPriceItem FIXED_ITEMS = map.build(); } + FixedPriceItem(final int itemId, final int offset, final int baseId) + { + this.itemId = itemId; + this.offset = offset; + this.baseId = baseId; + } + + FixedPriceItem(final int itemId, final int offset) + { + this(itemId, offset, -1); + } + @Nullable static FixedPriceItem find(int itemId) { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/ItemStack.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/ItemStack.java new file mode 100644 index 0000000000..d2f09dd856 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/ItemStack.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2019, TheStonedTurtle + * 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.itemskeptondeath; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +class ItemStack +{ + private int id; + private int qty; +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/ItemsKeptOnDeathPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/ItemsKeptOnDeathPlugin.java index dd9a5403e5..05089bc898 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/ItemsKeptOnDeathPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/ItemsKeptOnDeathPlugin.java @@ -25,6 +25,7 @@ */ package net.runelite.client.plugins.itemskeptondeath; +import com.google.common.annotations.VisibleForTesting; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -32,7 +33,10 @@ import java.util.EnumSet; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; import javax.inject.Inject; +import lombok.AllArgsConstructor; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; import net.runelite.api.Constants; @@ -54,6 +58,7 @@ import net.runelite.api.widgets.WidgetInfo; import net.runelite.api.widgets.WidgetType; import net.runelite.client.eventbus.Subscribe; import net.runelite.client.game.ItemManager; +import net.runelite.client.game.ItemMapping; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.util.StackFormatter; @@ -69,6 +74,16 @@ public class ItemsKeptOnDeathPlugin extends Plugin private static final int DEEP_WILDY = 20; private static final Pattern WILDERNESS_LEVEL_PATTERN = Pattern.compile("^Level: (\\d+).*"); + @AllArgsConstructor + @Getter + @VisibleForTesting + static class DeathItems + { + private final List keptItems; + private final List lostItems; + private final boolean hasAlwaysLost; + } + // Item Container helpers private static final int MAX_ROW_ITEMS = 8; private static final int ITEM_X_OFFSET = 5; @@ -98,9 +113,12 @@ public class ItemsKeptOnDeathPlugin extends Plugin private WidgetButton deepWildyButton; private WidgetButton lowWildyButton; - private boolean isSkulled; - private boolean protectingItem; - private int wildyLevel; + @VisibleForTesting + boolean isSkulled; + @VisibleForTesting + boolean protectingItem; + @VisibleForTesting + int wildyLevel; @Subscribe public void onScriptCallbackEvent(ScriptCallbackEvent event) @@ -223,97 +241,12 @@ public class ItemsKeptOnDeathPlugin extends Plugin final ItemContainer equipment = client.getItemContainer(InventoryID.EQUIPMENT); final Item[] equip = equipment == null ? new Item[0] : equipment.getItems(); - final List items = new ArrayList<>(); - Collections.addAll(items, inv); - Collections.addAll(items, equip); + final DeathItems deathItems = calculateKeptLostItems(inv, equip); - // Sort by item price - items.sort(Comparator.comparing(this::getDeathPrice).reversed()); - - boolean hasAlwaysLost = false; - int keepCount = getDefaultItemsKept(); - - final List keptItems = new ArrayList<>(); - final List lostItems = new ArrayList<>(); - for (final Item i : items) - { - final int id = i.getId(); - int itemQuantity = i.getQuantity(); - - if (id == -1) - { - continue; - } - - final ItemComposition c = itemManager.getItemComposition(i.getId()); - - // Bonds are always kept and do not count towards the limit. - if (id == ItemID.OLD_SCHOOL_BOND || id == ItemID.OLD_SCHOOL_BOND_UNTRADEABLE) - { - final Widget itemWidget = createItemWidget(kept, itemQuantity, c); - itemWidget.setOnOpListener(ScriptID.DEATH_KEEP_ITEM_EXAMINE, 1, itemQuantity, c.getName()); - keptItems.add(itemWidget); - continue; - } - - // Certain items are always lost on death and have a white outline which we need to add - final AlwaysLostItem alwaysLostItem = AlwaysLostItem.getByItemID(i.getId()); - if (alwaysLostItem != null) - { - // Some of these items are kept on death (outside wildy), like the Rune pouch. Ignore them - if (!alwaysLostItem.isKeptOutsideOfWilderness() || wildyLevel > 0) - { - final Widget itemWidget = createItemWidget(lost, itemQuantity, c); - itemWidget.setOnOpListener(ScriptID.DEATH_KEEP_ITEM_EXAMINE, 0, itemQuantity, c.getName()); - itemWidget.setBorderType(2); // white outline - lostItems.add(itemWidget); - hasAlwaysLost = true; - continue; - } - // the rune pouch is "always lost" but its kept outside of pvp, and does not count towards your keep count - } - else if (keepCount > 0) - { - // Keep most valuable items regardless of trade-ability. - if (i.getQuantity() > keepCount) - { - final Widget itemWidget = createItemWidget(kept, keepCount, c); - itemWidget.setOnOpListener(ScriptID.DEATH_KEEP_ITEM_EXAMINE, 1, keepCount, c.getName()); - keptItems.add(itemWidget); - itemQuantity -= keepCount; - keepCount = 0; - // Fall through to below to drop the rest of the stack - } - else - { - final Widget itemWidget = createItemWidget(kept, itemQuantity, c); - itemWidget.setOnOpListener(ScriptID.DEATH_KEEP_ITEM_EXAMINE, 1, itemQuantity, c.getName()); - keptItems.add(itemWidget); - keepCount -= i.getQuantity(); - continue; - } - } - - // Items are kept if: - // 1) is not tradeable - // 2) is under the deep wilderness line - // 3) is outside of the wilderness, or item has a broken form - if (!Pets.isPet(id) - && !isTradeable(c) && wildyLevel <= DEEP_WILDY - && (wildyLevel <= 0 || BrokenOnDeathItem.isBrokenOnDeath(i.getId()))) - { - final Widget itemWidget = createItemWidget(kept, itemQuantity, c); - itemWidget.setOnOpListener(ScriptID.DEATH_KEEP_ITEM_EXAMINE, 1, itemQuantity, c.getName()); - keptItems.add(itemWidget); - } - else - { - // Otherwise, the item is lost - final Widget itemWidget = createItemWidget(lost, itemQuantity, c); - itemWidget.setOnOpListener(ScriptID.DEATH_KEEP_ITEM_EXAMINE, 0, itemQuantity, c.getName()); - lostItems.add(itemWidget); - } - } + final List keptItems = deathItems.getKeptItems().stream() + .map(item -> createItemWidget(kept, item, true)).collect(Collectors.toList()); + final List lostItems = deathItems.getLostItems().stream() + .map(item -> createItemWidget(lost, item, false)).collect(Collectors.toList()); int rows = (keptItems.size() + MAX_ROW_ITEMS - 1) / MAX_ROW_ITEMS; // Show an empty row if there isn't anything @@ -328,36 +261,205 @@ public class ItemsKeptOnDeathPlugin extends Plugin positionWidgetItems(kept, keptItems); positionWidgetItems(lost, lostItems); - updateKeptWidgetInfoText(hasAlwaysLost, keptItems, lostItems); + updateKeptWidgetInfoText(deathItems.isHasAlwaysLost(), keptItems, lostItems); + } + + /** + * Calculates which items will be kept/lost. first list is kept items, second is lost. + * + * @param inv players inventory + * @param equip players equipement + * @return list of items kept followed by a list of items lost + */ + @VisibleForTesting + DeathItems calculateKeptLostItems(final Item[] inv, final Item[] equip) + { + final List items = new ArrayList<>(); + Collections.addAll(items, inv); + Collections.addAll(items, equip); + + // Sort by item price + items.sort(Comparator.comparing(this::getDeathPrice).reversed()); + + boolean hasClueBox = false; + boolean hasAlwaysLost = false; + int keepCount = getDefaultItemsKept(); + + final List keptItems = new ArrayList<>(); + final List lostItems = new ArrayList<>(); + + for (final Item i : items) + { + final int id = i.getId(); + int qty = i.getQuantity(); + if (id == -1) + { + continue; + } + + // Bonds are always kept and do not count towards the limit. + if (id == ItemID.OLD_SCHOOL_BOND || id == ItemID.OLD_SCHOOL_BOND_UNTRADEABLE) + { + keptItems.add(new ItemStack(id, qty)); + continue; + } + + final AlwaysLostItem alwaysLostItem = AlwaysLostItem.getByItemID(id); + if (alwaysLostItem != null && (!alwaysLostItem.isKeptOutsideOfWilderness() || wildyLevel > 0)) + { + hasAlwaysLost = true; + hasClueBox = hasClueBox || id == ItemID.CLUE_BOX; + lostItems.add(new ItemStack(id, qty)); + continue; + } + + if (keepCount > 0) + { + // Keep most valuable items regardless of trade-ability. + if (i.getQuantity() > keepCount) + { + keptItems.add(new ItemStack(id, keepCount)); + qty -= keepCount; + keepCount = 0; + // Fall through to determine if the rest of the stack should drop + } + else + { + keptItems.add(new ItemStack(id, qty)); + keepCount -= qty; + continue; + } + } + + // Items are kept if: + // 1) is not tradeable + // 2) is under the deep wilderness line + // 3) is outside of the wilderness, or item has a broken form + if (!Pets.isPet(id) + && !LostIfNotProtected.isLostIfNotProtected(id) + && !isTradeable(itemManager.getItemComposition(id)) && wildyLevel <= DEEP_WILDY + && (wildyLevel <= 0 || BrokenOnDeathItem.isBrokenOnDeath(i.getId()))) + { + keptItems.add(new ItemStack(id, qty)); + } + else + { + // Otherwise, the item is lost + lostItems.add(new ItemStack(id, qty)); + } + } + + if (hasClueBox) + { + boolean alreadyProtectingClue = false; + for (final ItemStack item : keptItems) + { + if (isClueBoxable(item.getId())) + { + alreadyProtectingClue = true; + break; + } + } + + if (!alreadyProtectingClue) + { + int clueId = -1; + // Clue box protects the last clue in your inventory so loop over the players inv + for (final Item i : inv) + { + final int id = i.getId(); + if (id != -1 && isClueBoxable(id)) + { + clueId = id; + } + } + + if (clueId != -1) + { + // Move the boxed item to the kept items container and remove it from the lost items container + for (final ItemStack boxableItem : lostItems) + { + if (boxableItem.getId() == clueId) + { + if (boxableItem.getQty() > 1) + { + boxableItem.setQty(boxableItem.getQty() - 1); + keptItems.add(new ItemStack(clueId, 1)); + } + else + { + lostItems.remove(boxableItem); + keptItems.add(boxableItem); + } + break; + } + } + } + } + } + + return new DeathItems(keptItems, lostItems, hasAlwaysLost); + } + + @VisibleForTesting + boolean isClueBoxable(final int itemID) + { + final String name = itemManager.getItemComposition(itemID).getName(); + return name.contains("Clue scroll (") || name.contains("Reward casket ("); } /** * Get the price of an item + * * @param item * @return */ - private int getDeathPrice(Item item) + @VisibleForTesting + int getDeathPrice(Item item) { + // 1) Check if the death price is dynamically calculated, if so return that value + // 2) If death price is based off another item default to that price, otherwise apply normal ItemMapping GE price + // 3) If still no price, default to store price + // 4) Apply fixed price offset if applicable + int itemId = item.getId(); // Unnote/unplaceholder item int canonicalizedItemId = itemManager.canonicalize(itemId); - int exchangePrice = itemManager.getItemPrice(canonicalizedItemId); + int exchangePrice = 0; + + final DynamicPriceItem dynamicPrice = DynamicPriceItem.find(canonicalizedItemId); + if (dynamicPrice != null) + { + final int basePrice = itemManager.getItemPrice(dynamicPrice.getChargedId(), true); + return dynamicPrice.calculateDeathPrice(basePrice); + } + + // Some items have artificially offset death prices - such as ring imbues + // which are +2k over the non imbues. Check if the item has a fixed price offset + final FixedPriceItem fixedPrice = FixedPriceItem.find(canonicalizedItemId); + if (fixedPrice != null && fixedPrice.getBaseId() != -1) + { + // Grab base item price + exchangePrice = itemManager.getItemPrice(fixedPrice.getBaseId(), true); + } + else + { + // Account for items whose death value comes from their tradeable variant (barrows) or components (ornate kits) + for (final int mappedID : ItemMapping.map(canonicalizedItemId)) + { + exchangePrice += itemManager.getItemPrice(mappedID, true); + } + } + if (exchangePrice == 0) { final ItemComposition c1 = itemManager.getItemComposition(canonicalizedItemId); exchangePrice = c1.getPrice(); } - else - { - // Some items have artifically applied death prices - such as ring imbues - // which are +2k over the non imbues. Check if the item has a fixed price. - FixedPriceItem fixedPrice = FixedPriceItem.find(canonicalizedItemId); - if (fixedPrice != null) - { - // Apply fixed price offset - exchangePrice += fixedPrice.getOffset(); - } - } + + // Apply fixed price offset + exchangePrice += fixedPrice == null ? 0 : fixedPrice.getOffset(); + return exchangePrice; } @@ -589,21 +691,29 @@ public class ItemsKeptOnDeathPlugin extends Plugin /** * Creates an Item Widget for use inside the Kept on Death Interface * - * @param qty Amount of item - * @param c Items Composition - * @return + * @param parent Widget to add element too as a child + * @param item the TempItem representing the item + * @param kept is the item being shown in the kept items container + * @return the Widget that was added to the `parent` */ - private static Widget createItemWidget(final Widget parent, final int qty, final ItemComposition c) + private Widget createItemWidget(final Widget parent, final ItemStack item, boolean kept) { + final int id = item.getId(); + final int qty = item.getQty(); + final ItemComposition c = itemManager.getItemComposition(id); + final Widget itemWidget = parent.createChild(-1, WidgetType.GRAPHIC); - itemWidget.setItemId(c.getId()); - itemWidget.setItemQuantity(qty); - itemWidget.setHasListener(true); itemWidget.setOriginalWidth(Constants.ITEM_SPRITE_WIDTH); itemWidget.setOriginalHeight(Constants.ITEM_SPRITE_HEIGHT); - itemWidget.setBorderType(1); - + itemWidget.setItemId(id); + itemWidget.setItemQuantity(qty); itemWidget.setAction(1, String.format("Item: %s", c.getName())); + itemWidget.setOnOpListener(ScriptID.DEATH_KEEP_ITEM_EXAMINE, kept ? 1 : 0, qty, c.getName()); + itemWidget.setHasListener(true); + + final AlwaysLostItem alwaysLostItem = AlwaysLostItem.getByItemID(id); + final boolean whiteBorder = alwaysLostItem != null && (!alwaysLostItem.isKeptOutsideOfWilderness() || wildyLevel > 0); + itemWidget.setBorderType(whiteBorder ? 2 : 1); return itemWidget; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/LostIfNotProtected.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/LostIfNotProtected.java new file mode 100644 index 0000000000..56a12c4b79 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/LostIfNotProtected.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019, TheStonedTurtle + * 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.itemskeptondeath; + +import com.google.common.collect.ImmutableSet; +import java.util.Set; +import net.runelite.api.ItemID; + +final class LostIfNotProtected +{ + private static final Set ITEMS = ImmutableSet.of( + ItemID.AMULET_OF_THE_DAMNED, + ItemID.RING_OF_CHAROS, ItemID.RING_OF_CHAROSA, + ItemID.LUNAR_STAFF, + ItemID.SHADOW_SWORD, + ItemID.KERIS, ItemID.KERISP, ItemID.KERISP_10583, ItemID.KERISP_10584 + ); + + public static boolean isLostIfNotProtected(int id) + { + return ITEMS.contains(id); + } +} diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/itemskeptondeath/ItemsKeptOnDeathPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/itemskeptondeath/ItemsKeptOnDeathPluginTest.java new file mode 100644 index 0000000000..37e8a7d99d --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/itemskeptondeath/ItemsKeptOnDeathPluginTest.java @@ -0,0 +1,616 @@ +/* + * Copyright (c) 2019, TheStonedTurtle + * 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.itemskeptondeath; + +import com.google.inject.Guice; +import com.google.inject.Inject; +import com.google.inject.testing.fieldbinder.Bind; +import com.google.inject.testing.fieldbinder.BoundFieldModule; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import net.runelite.api.Client; +import net.runelite.api.Item; +import net.runelite.api.ItemComposition; +import net.runelite.api.ItemID; +import net.runelite.client.game.ItemManager; +import static net.runelite.client.plugins.itemskeptondeath.ItemsKeptOnDeathPlugin.DeathItems; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import org.mockito.runners.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class ItemsKeptOnDeathPluginTest +{ + @Mock + @Bind + private Client client; + + @Mock + @Bind + private ItemManager itemManager; + + @Inject + private ItemsKeptOnDeathPlugin plugin; + + @Before + public void before() + { + Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this); + resetBuffs(); + } + + private void resetBuffs() + { + plugin.isSkulled = false; + plugin.protectingItem = false; + plugin.wildyLevel = -1; + } + + // Mocks an item and the necessary itemManager functions for it + private Item mItem(final int id, final int qty, final String name, final boolean tradeable, final int price) + { + // Mock Item Composition and necessary ItemManager methods for this item + ItemComposition c = mock(ItemComposition.class); + when(c.getId()) + .thenReturn(id); + when(c.getName()) + .thenReturn(name); + when(c.isTradeable()) + .thenReturn(tradeable); + when(c.getPrice()) + .thenReturn(price); + + if (!tradeable) + { + when(c.getNote()).thenReturn(-1); + when(c.getLinkedNoteId()).thenReturn(-1); + } + + when(itemManager.getItemComposition(id)).thenReturn(c); + when(itemManager.canonicalize(id)).thenReturn(id); + when(itemManager.getItemPrice(id, true)).thenReturn(price); + + return mockItem(id, qty); + } + + // Creates a mocked item + private Item mockItem(final int id, final int qty) + { + Item item = mock(Item.class); + + when(item.getId()).thenReturn(id); + when(item.getQuantity()).thenReturn(qty); + + return item; + } + + @Test + public void deathPriceTestRegularItems() + { + final Item acs = mItem(ItemID.ARMADYL_CHAINSKIRT, 1, "Armadyl chainskirt", true, 27837495); + assertEquals(27837495, plugin.getDeathPrice(acs)); + + final Item karambwan = mItem(ItemID.COOKED_KARAMBWAN, 1, "Cooked karambwan", true, 608); + assertEquals(608, plugin.getDeathPrice(karambwan)); + + final Item defender = mItem(ItemID.RUNE_DEFENDER, 1, "Rune defender", false, 35000); + assertEquals(35000, plugin.getDeathPrice(defender)); + } + + @Test + public void deathPriceTestItemMapping() + { + mItem(ItemID.OCCULT_NECKLACE, 1, "Occult necklace", true, 1000000); + mItem(ItemID.OCCULT_ORNAMENT_KIT, 1, "Occult ornament kit", true, 3000000); + final Item occult = mItem(ItemID.OCCULT_NECKLACE_OR, 1, "Occult necklace (or)", false, 0); + assertEquals(4000000, plugin.getDeathPrice(occult)); + + mItem(ItemID.BLACK_MASK, 1, "Black mask", true, 1000000); + final Item blackMask8 = mItem(ItemID.BLACK_MASK_8, 1, "Black mask (8)", false, 0); + assertEquals(1000000, plugin.getDeathPrice(blackMask8)); + final Item slayerHelm = mItem(ItemID.SLAYER_HELMET, 1, "Slayer helmet", false, 0); + assertEquals(1000000, plugin.getDeathPrice(slayerHelm)); + } + + @Test + public void deathPriceTestFixedPriceItems() + { + mItem(ItemID.KARILS_COIF_0, 1, "Karil's coif 0", true, 35000); + final Item coif = mItem(ItemID.KARILS_COIF_100, 1, "Karil's coif 100", false, 0); + final int coifOffset = FixedPriceItem.KARILS_COIF_100.getOffset(); + assertEquals(35000 + coifOffset, plugin.getDeathPrice(coif)); + + mItem(ItemID.AHRIMS_ROBETOP_0, 1, "Ahrim's robetop 0", true, 2500000); + final Item robetop = mItem(ItemID.AHRIMS_ROBETOP_25, 1, "Ahrim's robetop 100", false, 0); + final int robetopOffset = FixedPriceItem.AHRIMS_ROBETOP_25.getOffset(); + assertEquals(2500000 + robetopOffset, plugin.getDeathPrice(robetop)); + + mItem(ItemID.AMULET_OF_GLORY, 1, "Amulet of glory", true, 13000); + final Item glory = mItem(ItemID.AMULET_OF_GLORY3, 1, "Amulet of glory(3)", true, 0); + final int gloryOffset = FixedPriceItem.AMULET_OF_GLORY3.getOffset(); + assertEquals(13000 + gloryOffset, plugin.getDeathPrice(glory)); + + mItem(ItemID.COMBAT_BRACELET, 1, "Combat bracelet", true, 13500); + final Item brace = mItem(ItemID.COMBAT_BRACELET1, 1, "Combat bracelet(1)", true, 0); + final int braceletOffset = FixedPriceItem.COMBAT_BRACELET1.getOffset(); + assertEquals(13500 + braceletOffset, plugin.getDeathPrice(brace)); + } + + @Test + public void deathPriceTestDynamicPriceItems() + { + final Item rod8 = mItem(ItemID.RING_OF_DUELING8, 1, "Ring of dueling(8)", true, 725); + final Item rod3 = mItem(ItemID.RING_OF_DUELING3, 1, "Ring of dueling(3)", true, 0); + final Item rod1 = mItem(ItemID.RING_OF_DUELING1, 1, "Ring of dueling(1)", true, 0); + // Dynamic price items + final int rodPrice = 725 / 8; + assertEquals(rodPrice, plugin.getDeathPrice(rod1)); + assertEquals(725, plugin.getDeathPrice(rod8)); + assertEquals(rodPrice * 3, plugin.getDeathPrice(rod3)); + + final Item nop5 = mItem(ItemID.NECKLACE_OF_PASSAGE5, 1, "Necklace of passage(5)", true, 1250); + final Item nop4 = mItem(ItemID.NECKLACE_OF_PASSAGE4, 1, "Necklace of passage(4)", true, 0); + final Item nop2 = mItem(ItemID.NECKLACE_OF_PASSAGE2, 1, "Necklace of passage(2)", true, 0); + + final int nopPrice = 1250 / 5; + assertEquals(nopPrice * 2, plugin.getDeathPrice(nop2)); + assertEquals(nopPrice * 4, plugin.getDeathPrice(nop4)); + assertEquals(1250, plugin.getDeathPrice(nop5)); + } + + private Item[] getFourExpensiveItems() + { + return new Item[] + { + mItem(ItemID.TWISTED_BOW, 1, "Twister bow", true, Integer.MAX_VALUE), + mItem(ItemID.SCYTHE_OF_VITUR, 1, "Scythe of vitur", true, Integer.MAX_VALUE), + mItem(ItemID.ELYSIAN_SPIRIT_SHIELD, 1, "Elysian spirit shield", true, 800000000), + mItem(ItemID.ARCANE_SPIRIT_SHIELD, 1, "Arcane spirit shield", true, 250000000) + }; + } + + @Test + public void alwaysLostTestRunePouch() + { + final Item[] inv = getFourExpensiveItems(); + final Item[] equip = new Item[] + { + mItem(ItemID.RUNE_POUCH, 1, "Rune pouch", false, 1) + }; + + final DeathItems deathItems = plugin.calculateKeptLostItems(inv, equip); + assertFalse(deathItems.isHasAlwaysLost()); + } + + @Test + public void alwaysLostTestRunePouchWildy() + { + final Item[] inv = getFourExpensiveItems(); + final Item[] equip = new Item[] + { + mItem(ItemID.RUNE_POUCH, 1, "Rune pouch", false, 1) + }; + + plugin.wildyLevel = 1; + final DeathItems deathItems = plugin.calculateKeptLostItems(inv, equip); + assertTrue(deathItems.isHasAlwaysLost()); + } + + @Test + public void alwaysLostTestLootBag() + { + final Item[] inv = getFourExpensiveItems(); + final Item[] equip = new Item[] + { + mItem(ItemID.LOOTING_BAG, 1, "Looting bag", false, 1) + }; + + final DeathItems deathItems = plugin.calculateKeptLostItems(inv, equip); + assertTrue(deathItems.isHasAlwaysLost()); + + } + + @Test + public void alwaysLostTestLootBagWildy() + { + final Item[] inv = getFourExpensiveItems(); + final Item[] equip = new Item[] + { + mItem(ItemID.LOOTING_BAG, 1, "Looting bag", false, 1) + }; + + plugin.wildyLevel = 1; + final DeathItems deathItems = plugin.calculateKeptLostItems(inv, equip); + assertTrue(deathItems.isHasAlwaysLost()); + } + + private Item[] getClueBoxTestInventory() + { + return new Item[] + { + mItem(ItemID.BLACK_DHIDE_BODY, 1, "Black d'hide body", true, 7552), + mItem(ItemID.ARMADYL_CHAINSKIRT, 1, "Armadyl chainskirt", true, 27837495), + mItem(ItemID.PEGASIAN_BOOTS, 1, "Pegasian boots", true, 30542187), + mItem(ItemID.DRAGON_SCIMITAR, 1, "Dragon scimitar", true, 63123), + + mItem(ItemID.HELM_OF_NEITIZNOT, 1, "Helm of neitiznot", true, 45519), + mItem(ItemID.RUNE_DEFENDER, 1, "Rune defender", false, 35000), + mItem(ItemID.SPADE, 1, "Spade", true, 104), + mItem(ItemID.CLUE_SCROLL_EASY, 1, "Clue scroll (easy)", false, 50), + + mItem(ItemID.CLUE_BOX, 1, "Clue box", false, 50), + mItem(ItemID.COOKED_KARAMBWAN, 1, "Cooked karambwan", true, 608), + mItem(ItemID.COOKED_KARAMBWAN, 1, "Cooked karambwan", true, 608), + mItem(ItemID.COOKED_KARAMBWAN, 1, "Cooked karambwan", true, 608), + + mItem(ItemID.COOKED_KARAMBWAN, 1, "Cooked karambwan", true, 608), + mItem(ItemID.COOKED_KARAMBWAN, 1, "Cooked karambwan", true, 608), + mItem(ItemID.LAW_RUNE, 200, "Law rune", true, 212), + mItem(ItemID.DUST_RUNE, 200, "Dust rune", true, 3), + + mItem(ItemID.CLUE_SCROLL_MASTER, 1, "Clue scroll (master)", false, 50), + mItem(ItemID.CLUELESS_SCROLL, 1, "Clueless scroll", false, 50), + }; + } + + @Test + public void isClueBoxableTest() + { + getClueBoxTestInventory(); + mItem(ItemID.REWARD_CASKET_EASY, 1, "Reward casket (easy)", false, 50); + + assertTrue(plugin.isClueBoxable(ItemID.CLUE_SCROLL_EASY)); + assertTrue(plugin.isClueBoxable(ItemID.CLUE_SCROLL_MASTER)); + assertTrue(plugin.isClueBoxable(ItemID.REWARD_CASKET_EASY)); + + assertFalse(plugin.isClueBoxable(ItemID.CLUELESS_SCROLL)); + assertFalse(plugin.isClueBoxable(ItemID.LAW_RUNE)); + assertFalse(plugin.isClueBoxable(ItemID.SPADE)); + } + + @Test + public void clueBoxTestDefault() + { + final Item[] inv = getClueBoxTestInventory(); + final Item[] equip = new Item[0]; + + final DeathItems deathItems = plugin.calculateKeptLostItems(inv, equip); + + final List kept = deathItems.getKeptItems(); + final List expectedKept = Arrays.asList( + new ItemStack(ItemID.PEGASIAN_BOOTS, 1), + new ItemStack(ItemID.ARMADYL_CHAINSKIRT, 1), + new ItemStack(ItemID.DRAGON_SCIMITAR, 1), + new ItemStack(ItemID.RUNE_DEFENDER, 1), + new ItemStack(ItemID.CLUE_SCROLL_EASY, 1), + new ItemStack(ItemID.CLUE_SCROLL_MASTER, 1), + new ItemStack(ItemID.CLUELESS_SCROLL, 1) + ); + assertEquals(expectedKept, kept); + + final List lost = deathItems.getLostItems(); + assertEquals((inv.length + equip.length) - expectedKept.size(), lost.size()); + } + + @Test + public void clueBoxTestDeepWildy() + { + final Item[] inv = getClueBoxTestInventory(); + final Item[] equip = new Item[0]; + + plugin.wildyLevel = 21; + + final DeathItems deathItems = plugin.calculateKeptLostItems(inv, equip); + + final List kept = deathItems.getKeptItems(); + final List expectedKept = Arrays.asList( + new ItemStack(ItemID.PEGASIAN_BOOTS, 1), + new ItemStack(ItemID.ARMADYL_CHAINSKIRT, 1), + new ItemStack(ItemID.DRAGON_SCIMITAR, 1), + new ItemStack(ItemID.CLUE_SCROLL_MASTER, 1) + ); + assertEquals(expectedKept, kept); + + final List lost = deathItems.getLostItems(); + final int keptOffset = expectedKept.size(); + assertEquals((inv.length + equip.length) - keptOffset, lost.size()); + } + + @Test + public void clueBoxTestDeepWildyProtectItem() + { + final Item[] inv = getClueBoxTestInventory(); + final Item[] equip = new Item[0]; + + plugin.wildyLevel = 21; + plugin.protectingItem = true; + + final DeathItems deathItems = plugin.calculateKeptLostItems(inv, equip); + + final List kept = deathItems.getKeptItems(); + final List expectedKept = Arrays.asList( + new ItemStack(ItemID.PEGASIAN_BOOTS, 1), + new ItemStack(ItemID.ARMADYL_CHAINSKIRT, 1), + new ItemStack(ItemID.DRAGON_SCIMITAR, 1), + new ItemStack(ItemID.HELM_OF_NEITIZNOT, 1), + new ItemStack(ItemID.CLUE_SCROLL_MASTER, 1) // Clue box + ); + assertEquals(expectedKept, kept); + + final List lost = deathItems.getLostItems(); + final int keptOffset = expectedKept.size(); + assertEquals((inv.length + equip.length) - keptOffset, lost.size()); + } + + @Test + public void clueBoxTestDeepWildySkulled() + { + final Item[] inv = getClueBoxTestInventory(); + final Item[] equip = new Item[0]; + + plugin.wildyLevel = 21; + plugin.isSkulled = true; + + final DeathItems deathItems = plugin.calculateKeptLostItems(inv, equip); + + final List kept = deathItems.getKeptItems(); + final List expectedKept = Collections.singletonList( + new ItemStack(ItemID.CLUE_SCROLL_MASTER, 1) + ); + assertEquals(expectedKept, kept); + + final List lost = deathItems.getLostItems(); + final int keptOffset = expectedKept.size(); + assertEquals(lost.size(), (inv.length + equip.length) - keptOffset); + } + + @Test + public void clueBoxTestLowWildy() + { + final Item[] inv = getClueBoxTestInventory(); + final Item[] equip = new Item[0]; + + plugin.wildyLevel = 1; + + final DeathItems deathItems = plugin.calculateKeptLostItems(inv, equip); + + final List kept = deathItems.getKeptItems(); + final List expectedKept = Arrays.asList( + new ItemStack(ItemID.PEGASIAN_BOOTS, 1), + new ItemStack(ItemID.ARMADYL_CHAINSKIRT, 1), + new ItemStack(ItemID.DRAGON_SCIMITAR, 1), + new ItemStack(ItemID.RUNE_DEFENDER, 1), // Rune defender protected because of broken variant + new ItemStack(ItemID.CLUE_SCROLL_MASTER, 1) + ); + assertEquals(expectedKept, kept); + + final List lost = deathItems.getLostItems(); + final int keptOffset = expectedKept.size(); + assertEquals(lost.size(), (inv.length + equip.length) - keptOffset); + } + + @Test + public void clueBoxTestLowWildyProtectItem() + { + final Item[] inv = getClueBoxTestInventory(); + final Item[] equip = new Item[0]; + + plugin.wildyLevel = 1; + plugin.protectingItem = true; + + final DeathItems deathItems = plugin.calculateKeptLostItems(inv, equip); + + final List kept = deathItems.getKeptItems(); + final List expectedKept = Arrays.asList( + new ItemStack(ItemID.PEGASIAN_BOOTS, 1), + new ItemStack(ItemID.ARMADYL_CHAINSKIRT, 1), + new ItemStack(ItemID.DRAGON_SCIMITAR, 1), + new ItemStack(ItemID.HELM_OF_NEITIZNOT, 1), + new ItemStack(ItemID.RUNE_DEFENDER, 1), // Rune defender protected because of broken variant + new ItemStack(ItemID.CLUE_SCROLL_MASTER, 1) + ); + assertEquals(expectedKept, kept); + + final List lost = deathItems.getLostItems(); + final int keptOffset = expectedKept.size(); + assertEquals((inv.length + equip.length) - keptOffset, lost.size()); + } + + @Test + public void clueBoxTestLowWildySkulled() + { + final Item[] inv = getClueBoxTestInventory(); + final Item[] equip = new Item[0]; + + plugin.wildyLevel = 1; + plugin.isSkulled = true; + + final DeathItems deathItems = plugin.calculateKeptLostItems(inv, equip); + + final List kept = deathItems.getKeptItems(); + final List expectedKept = Arrays.asList( + new ItemStack(ItemID.RUNE_DEFENDER, 1), // Rune defender protected because of broken variant + new ItemStack(ItemID.CLUE_SCROLL_MASTER, 1) + ); + assertEquals(expectedKept, kept); + + final List lost = deathItems.getLostItems(); + final int keptOffset = expectedKept.size(); + assertEquals((inv.length + equip.length) - keptOffset, lost.size()); + } + + private Item[] getClueBoxCasketTestInventory() + { + // Reward caskets can stack but the clue box should only protect one + return new Item[] + { + mItem(ItemID.BLACK_DHIDE_BODY, 1, "Black d'hide body", true, 7552), + mItem(ItemID.ARMADYL_CHAINSKIRT, 1, "Armadyl chainskirt", true, 27837495), + mItem(ItemID.PEGASIAN_BOOTS, 1, "Pegasian boots", true, 30542187), + mItem(ItemID.DRAGON_SCIMITAR, 1, "Dragon scimitar", true, 63123), + + mItem(ItemID.SPADE, 1, "Spade", true, 104), + mItem(ItemID.CLUE_SCROLL_EASY, 1, "Clue scroll (easy)", false, 50), + mItem(ItemID.REWARD_CASKET_EASY, 20, "Reward casket (easy)", false, 50), + mItem(ItemID.CLUE_BOX, 1, "Clue box", false, 50), + + mItem(ItemID.COOKED_KARAMBWAN, 1, "Cooked karambwan", true, 608), + mItem(ItemID.COOKED_KARAMBWAN, 1, "Cooked karambwan", true, 608), + mItem(ItemID.COOKED_KARAMBWAN, 1, "Cooked karambwan", true, 608), + mItem(ItemID.COOKED_KARAMBWAN, 1, "Cooked karambwan", true, 608), + + mItem(ItemID.LAW_RUNE, 200, "Law rune", true, 212), + mItem(ItemID.DUST_RUNE, 200, "Dust rune", true, 3), + }; + } + + @Test + public void clueBoxTestCasketProtect() + { + final Item[] inv = getClueBoxCasketTestInventory(); + final Item[] equip = new Item[0]; + + plugin.wildyLevel = 1; + + final DeathItems deathItems = plugin.calculateKeptLostItems(inv, equip); + + final List kept = deathItems.getKeptItems(); + final List expectedKept = Arrays.asList( + new ItemStack(ItemID.PEGASIAN_BOOTS, 1), + new ItemStack(ItemID.ARMADYL_CHAINSKIRT, 1), + new ItemStack(ItemID.DRAGON_SCIMITAR, 1), + new ItemStack(ItemID.REWARD_CASKET_EASY, 1) // Clue box + ); + assertEquals(expectedKept, kept); + + final List lost = deathItems.getLostItems(); + final int keptOffset = expectedKept.size() - 1; // We are still losing some reward caskets. + assertEquals((inv.length + equip.length) - keptOffset, lost.size()); + } + + private Item[] getFullGracefulItems() + { + return new Item[] + { + mItem(ItemID.GRACEFUL_HOOD, 1, "Graceful hood", false, 35), + mItem(ItemID.GRACEFUL_CAPE, 1, "Graceful cape", false, 40), + mItem(ItemID.GRACEFUL_TOP, 1, "Graceful top", false, 55), + mItem(ItemID.GRACEFUL_LEGS, 1, "Graceful legs", false, 60), + mItem(ItemID.GRACEFUL_BOOTS, 1, "Graceful boots", false, 40), + mItem(ItemID.GRACEFUL_GLOVES, 1, "Graceful gloves", false, 30), + }; + } + + @Test + public void gracefulValueTest() + { + final Item[] inv = getFullGracefulItems(); + final Item[] equip = new Item[] + { + mItem(ItemID.AMULET_OF_GLORY6, 1, "Amulet of glory (6)", true, 20000) + }; + + + final DeathItems deathItems = plugin.calculateKeptLostItems(inv, equip); + + final List kept = deathItems.getKeptItems(); + final List expectedKept = Arrays.asList( + new ItemStack(ItemID.AMULET_OF_GLORY6, 1), + new ItemStack(ItemID.GRACEFUL_CAPE, 1), + new ItemStack(ItemID.GRACEFUL_TOP, 1), + new ItemStack(ItemID.GRACEFUL_LEGS, 1), + new ItemStack(ItemID.GRACEFUL_BOOTS, 1), + new ItemStack(ItemID.GRACEFUL_HOOD, 1), + new ItemStack(ItemID.GRACEFUL_GLOVES, 1) + ); + assertEquals(expectedKept, kept); + + final List lost = deathItems.getLostItems(); + assertEquals((inv.length + equip.length) - expectedKept.size(), lost.size()); + } + + @Test + public void gracefulValueTestWildy() + { + final Item[] inv = getFullGracefulItems(); + final Item[] equip = new Item[] + { + mItem(ItemID.AMULET_OF_GLORY6, 1, "Amulet of glory (6)", true, 20000) + }; + + plugin.wildyLevel = 1; + + final DeathItems deathItems = plugin.calculateKeptLostItems(inv, equip); + + final List kept = deathItems.getKeptItems(); + final List expectedKept = Arrays.asList( + new ItemStack(ItemID.AMULET_OF_GLORY6, 1), + new ItemStack(ItemID.GRACEFUL_CAPE, 1), + new ItemStack(ItemID.GRACEFUL_TOP, 1) + ); + assertEquals(expectedKept, kept); + + final List lost = deathItems.getLostItems(); + assertEquals((inv.length + equip.length) - expectedKept.size(), lost.size()); + } + + @Test + public void lostIfNotProtectedTestLost() + { + final Item[] inv = getFourExpensiveItems(); + final Item[] equip = new Item[] + { + mItem(ItemID.SHADOW_SWORD, 1, "Shadow sword", false, 1) + }; + + final DeathItems deathItems = plugin.calculateKeptLostItems(inv, equip); + + final List lost = deathItems.getLostItems(); + assertTrue(lost.contains(new ItemStack(ItemID.SHADOW_SWORD, 1))); + } + + @Test + public void lostIfNotProtectedTestKept() + { + final Item[] inv = new Item[] + { + mItem(ItemID.SHADOW_SWORD, 1, "Shadow sword", false, 1) + }; + final Item[] equip = new Item[0]; + + final DeathItems deathItems = plugin.calculateKeptLostItems(inv, equip); + + final List kept = deathItems.getKeptItems(); + assertTrue(kept.contains(new ItemStack(ItemID.SHADOW_SWORD, 1))); + } +} From 40e572c731f372aaab0bc2fb5fa1373d8fd7f68a Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 10 Jul 2019 13:29:44 +1000 Subject: [PATCH 09/18] fixed ping not working on current build pr #941 breaks ping indicator ingame, thanks to 789 for finding this --- .../main/java/net/runelite/client/util/ping/IcmpEchoReply.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/util/ping/IcmpEchoReply.java b/runelite-client/src/main/java/net/runelite/client/util/ping/IcmpEchoReply.java index 0e4af9137e..2756320990 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/ping/IcmpEchoReply.java +++ b/runelite-client/src/main/java/net/runelite/client/util/ping/IcmpEchoReply.java @@ -30,7 +30,7 @@ import com.sun.jna.platform.win32.WinDef; import java.util.Arrays; import java.util.List; -class IcmpEchoReply extends Structure +public class IcmpEchoReply extends Structure { private static final int IP_OPTION_INFO_SIZE = 1 + 1 + 1 + 1 + (Pointer.SIZE == 8 ? 12 : 4); // on 64bit vms add 4 byte padding public static final int SIZE = 4 + 4 + 4 + 2 + 2 + Pointer.SIZE + IP_OPTION_INFO_SIZE; From e7b15d9cfdc19f3cc311c47227984941fe8bce53 Mon Sep 17 00:00:00 2001 From: James <38226001+f0rmatme@users.noreply.github.com> Date: Wed, 10 Jul 2019 00:00:42 -0700 Subject: [PATCH 10/18] fix for http-service (#952) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d9a35dbab6..50bfcfb90f 100644 --- a/pom.xml +++ b/pom.xml @@ -50,7 +50,7 @@ 1.10.19 1.5.4 3.0.6 - 4.0.0 + 3.14.0 0.7 3.0.2 From c5d2c29bec59a3fb9fc0fd010aea7f9e32a6a3e7 Mon Sep 17 00:00:00 2001 From: Owain van Brakel Date: Wed, 10 Jul 2019 17:29:02 +0200 Subject: [PATCH 11/18] lizardman: Fix plugin --- runelite-api/src/main/java/net/runelite/api/AnimationID.java | 2 +- .../client/plugins/lizardmenshaman/LizardmenShamanPlugin.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) 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 07a2f68ce6..76dfca0deb 100644 --- a/runelite-api/src/main/java/net/runelite/api/AnimationID.java +++ b/runelite-api/src/main/java/net/runelite/api/AnimationID.java @@ -315,7 +315,7 @@ public final class AnimationID public static final int DAG_SUPREME = 2855; // Lizardman shaman - public static final int LIZARDMAN_SHAMAN_SPAWN = 2855; + public static final int LIZARDMAN_SHAMAN_SPAWN = 7157; // Combat counter public static final int BARRAGE_ANIMATION = 1979; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/lizardmenshaman/LizardmenShamanPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/lizardmenshaman/LizardmenShamanPlugin.java index 7e76be3560..64cc513f22 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/lizardmenshaman/LizardmenShamanPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/lizardmenshaman/LizardmenShamanPlugin.java @@ -34,7 +34,6 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; import net.runelite.api.Actor; import static net.runelite.api.AnimationID.LIZARDMAN_SHAMAN_SPAWN; -import net.runelite.api.ChatMessageType; import net.runelite.api.coords.LocalPoint; import net.runelite.api.events.AnimationChanged; import net.runelite.api.events.ChatMessage; @@ -104,7 +103,8 @@ public class LizardmenShamanPlugin extends Plugin @Subscribe public void onChatMessage(ChatMessage event) { - if (this.notifyOnSpawn && event.getType() == ChatMessageType.GAMEMESSAGE && event.getMessage().contains(MESSAGE)) + if (this.notifyOnSpawn && /* event.getType() == ChatMessageType.GAMEMESSAGE && */event.getMessage().contains(MESSAGE)) + // ChatMessageType should probably be SPAM <- should be tested first though { notifier.notify(MESSAGE); } From a2afc8d65d9817a9b0750c68189f5fddd881314c Mon Sep 17 00:00:00 2001 From: Owain van Brakel Date: Wed, 10 Jul 2019 19:18:45 +0200 Subject: [PATCH 12/18] skillcalc: New UI and panel --- .../skillcalculator/BankedCalculator.java | 675 ------------- .../skillcalculator/CacheSkillData.java | 2 +- .../skillcalculator/CalculatorType.java | 54 +- .../skillcalculator/SkillCalculator.java | 68 +- .../SkillCalculatorConfig.java | 18 +- .../skillcalculator/SkillCalculatorPanel.java | 163 +--- .../SkillCalculatorPlugin.java | 158 ++-- .../plugins/skillcalculator/UIActionSlot.java | 4 +- .../UICalculatorInputArea.java | 21 +- .../skillcalculator/UICombinedActionSlot.java | 6 +- .../banked/BankedCalculator.java | 462 +++++++++ .../banked/BankedCalculatorPanel.java | 120 +++ .../skillcalculator/banked/CriticalItem.java | 418 --------- .../banked/beans/Activity.java | 885 ++++++++++++------ .../banked/beans/ActivitySecondaries.java | 112 --- .../banked/beans/BankedItem.java | 53 ++ .../banked/beans/CriticalItem.java | 355 +++++++ .../{SecondaryItem.java => ItemInfo.java} | 28 +- .../banked/beans/ItemStack.java | 36 + .../banked/beans/Secondaries.java | 118 +++ .../banked/beans/XpModifiers.java | 64 ++ .../banked/components/GridItem.java | 201 ++++ .../banked/components/ModifyPanel.java | 354 +++++++ .../banked/components/SelectionGrid.java | 142 +++ .../banked/ui/CriticalItemPanel.java | 402 -------- .../skillcalculator/beans/SkillData.java | 3 +- .../skillcalculator/beans/SkillDataBonus.java | 3 +- .../skillcalculator/beans/SkillDataEntry.java | 3 +- .../ui/components/ComboBoxIconEntry.java | 44 + .../ui/components/ComboBoxListRenderer.java | 17 +- 30 files changed, 2708 insertions(+), 2281 deletions(-) delete mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/BankedCalculator.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/BankedCalculator.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/BankedCalculatorPanel.java delete mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/CriticalItem.java delete mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/ActivitySecondaries.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/BankedItem.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/CriticalItem.java rename runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/{SecondaryItem.java => ItemInfo.java} (80%) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/ItemStack.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/Secondaries.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/XpModifiers.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/components/GridItem.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/components/ModifyPanel.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/components/SelectionGrid.java delete mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/ui/CriticalItemPanel.java create mode 100644 runelite-client/src/main/java/net/runelite/client/ui/components/ComboBoxIconEntry.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/BankedCalculator.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/BankedCalculator.java deleted file mode 100644 index ac7d985013..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/BankedCalculator.java +++ /dev/null @@ -1,675 +0,0 @@ -/* - * Copyright (c) 2018, TheStonedTurtle - * 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.skillcalculator; - -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Dimension; -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import javax.inject.Singleton; -import javax.swing.BorderFactory; -import javax.swing.Box; -import javax.swing.BoxLayout; -import javax.swing.JCheckBox; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.SwingUtilities; -import lombok.extern.slf4j.Slf4j; -import net.runelite.api.Client; -import net.runelite.api.Experience; -import net.runelite.api.Skill; -import net.runelite.client.game.ItemManager; -import net.runelite.client.plugins.skillcalculator.banked.CriticalItem; -import net.runelite.client.plugins.skillcalculator.banked.beans.Activity; -import net.runelite.client.plugins.skillcalculator.banked.beans.SecondaryItem; -import net.runelite.client.plugins.skillcalculator.banked.ui.CriticalItemPanel; -import net.runelite.client.plugins.skillcalculator.beans.SkillDataBonus; -import net.runelite.client.ui.ColorScheme; -import net.runelite.client.ui.DynamicGridLayout; -import net.runelite.client.ui.FontManager; - -@Slf4j -@Singleton -public class BankedCalculator extends JPanel -{ - private static final DecimalFormat XP_FORMAT_COMMA = new DecimalFormat("#,###.#"); - - private final SkillCalculatorPanel parent; - private final Client client; - private final UICalculatorInputArea uiInput; - private final SkillCalculatorConfig config; - private final ItemManager itemManager; - - private final CacheSkillData skillData = new CacheSkillData(); - private final List bonusCheckBoxes = new ArrayList<>(); - - // UI Input data - private float xpFactor = 1.0f; - private CalculatorType currentCalc; - private Skill currentSkill; - - private double totalBankedXp = 0.0f; - private final JLabel totalLabel = new JLabel(); - private final JPanel detailConfigContainer; - private final JPanel detailContainer; - - // Banked Experience magic - private Map bankMap = new HashMap<>(); - private final Map categoryMap = new HashMap<>(); // Check if CriticalItem Category is enabled - private final Map panelMap = new HashMap<>(); - private final Map criticalMap = new HashMap<>(); // Quantity of CriticalItem inside bankMap - private final Map activityMap = new HashMap<>(); // Selected Activity used for calculating xp - private final Map linkedMap = new HashMap<>(); // ItemID of item that links to the CriticalItem - - BankedCalculator( - final SkillCalculatorPanel parent, - final Client client, - final UICalculatorInputArea uiInput, - final SkillCalculatorConfig config, - final ItemManager itemManager) - { - this.parent = parent; - this.client = client; - this.uiInput = uiInput; - this.config = config; - this.itemManager = itemManager; - - setLayout(new DynamicGridLayout(0, 1, 0, 5)); - - detailContainer = new JPanel(); - detailContainer.setLayout(new BoxLayout(detailContainer, BoxLayout.Y_AXIS)); - - detailConfigContainer = new JPanel(); - detailConfigContainer.setLayout(new BoxLayout(detailConfigContainer, BoxLayout.Y_AXIS)); - } - - private void reset() - { - criticalMap.clear(); - linkedMap.clear(); - xpFactor = 1f; - } - - /** - * Update target Xp and Level inputs to match current Xp + total banked XP - */ - private void syncInputFields() - { - // Update Target XP & Level to include total banked xp - int newTotal = (int) (uiInput.getCurrentXPInput() + totalBankedXp); - uiInput.setTargetXPInput(newTotal); - uiInput.setTargetLevelInput(Experience.getLevelForXp(newTotal)); - } - - /* - * Banked Experience Logic - */ - - /** - * Shows the Banked Xp tab for the CalculatorType - * - * @param calculatorType Selected Calculator Type - */ - void openBanked(CalculatorType calculatorType) - { - // clean slate for creating the required panel - removeAll(); - reset(); - if (calculatorType.getSkill() != currentSkill) - { - // Only clear Category and Activity map on skill change. - activityMap.clear(); - categoryMap.clear(); - } - currentCalc = calculatorType; - currentSkill = calculatorType.getSkill(); - bankMap = parent.getBankMap(); - - uiInput.setCurrentLevelInput(client.getRealSkillLevel(currentSkill)); - uiInput.setCurrentXPInput(client.getSkillExperience(currentSkill)); - - // Only adds Banked Experience portion if enabled for this SkillCalc and have seen their bank - if (!calculatorType.isBankedXpFlag()) - { - add(new JLabel("
Banked Experience is not enabled for this skill.
", JLabel.CENTER)); - } - else if (bankMap.size() <= 0) - { - add(new JLabel("Please visit a bank!", JLabel.CENTER)); - } - else - { - // Prevent editing of the target level/exp since we automagically adjust them - uiInput.getUiFieldTargetLevel().setEditable(false); - uiInput.getUiFieldTargetXP().setEditable(false); - - // Now we can actually show the Banked Experience Panel - // Adds Config Options for this panel - renderBankedXpOptions(); - - renderBonusXpOptions(); - - // sprite 202 - calculatedBankedMaps(); - - // Calculate total banked experience and create detail container - refreshDetailContainer(); - - // Add back all necessary content - add(detailConfigContainer); - add(totalLabel); - add(detailContainer); - } - - revalidate(); - repaint(); - - // Update the input fields. - syncInputFields(); - } - - /** - * Add the config options for toggling each Item Category - */ - private void renderBankedXpOptions() - { - Set categories = CriticalItem.getSkillCategories(currentSkill); - if (categories == null) - { - return; - } - - add(new JLabel("Configs:")); - - for (String category : categories) - { - JPanel uiOption = new JPanel(new BorderLayout()); - JLabel uiLabel = new JLabel(category); - JCheckBox uiCheckbox = new JCheckBox(); - - uiLabel.setForeground(Color.WHITE); - uiLabel.setFont(FontManager.getRunescapeSmallFont()); - - uiOption.setBorder(BorderFactory.createEmptyBorder(3, 7, 3, 0)); - uiOption.setBackground(ColorScheme.DARKER_GRAY_COLOR); - - // Everything is enabled by default - uiCheckbox.setSelected(true); - categoryMap.put(category, true); - - // Adjust Total Banked XP check-state of the box. - uiCheckbox.addActionListener(e -> toggleCategory(category, uiCheckbox.isSelected())); - uiCheckbox.setBackground(ColorScheme.MEDIUM_GRAY_COLOR); - - uiOption.add(uiLabel, BorderLayout.WEST); - uiOption.add(uiCheckbox, BorderLayout.EAST); - - add(uiOption); - add(Box.createRigidArea(new Dimension(0, 5))); - } - } - - /** - * Used to toggle Categories of Items inside the Banked Xp tab - * - * @param category Category Name - * @param enabled is enabled - */ - private void toggleCategory(String category, boolean enabled) - { - categoryMap.put(category, enabled); - refreshDetailContainer(); - } - - - /** - * Creates the Maps used for easy access when calculating Banked Xp - */ - private void calculatedBankedMaps() - { - // Grab all CriticalItems for this skill - List items = CriticalItem.getBySkillName(currentSkill); - - // Loop over all Critical Items for this skill and determine how many are in the bank - for (CriticalItem item : items) - { - Integer qty = bankMap.get(item.getItemID()); - if (qty != null && qty > 0) - { - if (criticalMap.containsKey(item)) - { - criticalMap.put(item, criticalMap.get(item) + qty); - } - else - { - criticalMap.put(item, qty); - } - - // Ensure the item this is linked to maps back to us. - if (item.getLinkedItemId() != -1) - { - CriticalItem i = CriticalItem.getByItemId(item.getLinkedItemId()); - if (i != null) - { - linkedMap.put(i, item.getItemID()); - } - } - } - } - } - - /** - * Populates the detailContainer with the necessary CriticalItemPanels - */ - private void refreshDetailContainer() - { - detailContainer.removeAll(); - panelMap.clear(); - - Map map = getBankedXpBreakdown(); - for (Map.Entry entry : map.entrySet()) - { - CriticalItem item = entry.getKey(); - createItemPanel(item); - } - - detailContainer.revalidate(); - detailContainer.repaint(); - - calculateBankedXpTotal(); - } - - /** - * Creates an Individual Item Panel if it should be displayed - * - * @param item CriticalItem this information is tied too - */ - private void createItemPanel(CriticalItem item) - { - // Category Included? - if (categoryMap.get(item.getCategory())) - { - // Get possible activities limited to current level - List activities = Activity.getByCriticalItem(item, uiInput.getCurrentLevelInput()); - - // Check if this should count as another item. - if (item.getLinkedItemId() != -1) - { - // Ensure the linked item panel is created even if there are none in bank. - CriticalItem linked = CriticalItem.getByItemId(item.getLinkedItemId()); - if (!criticalMap.containsKey(linked)) - { - createItemPanel(linked); - } - - // One activity and rewards no xp ignore. - if (activities.size() == 1 && activities.get(0).getXp() <= 0) - { - return; - } - } - - // If it doesn't have any activities ignore it in the breakdown. - if (activities.size() <= 0) - { - return; - } - // Either this item has multiple activities or the single activity rewards xp, create the item panel. - - // Determine xp rate for this item - Activity a = getSelectedActivity(item); - double activityXp = a == null ? 0 : a.getXp(); - double xp = activityXp * (item.isIgnoreBonus() ? 1.0f : xpFactor); - int amount = 0; - - // If it has linked items figure out the working total. - Map links = getLinkedTotalMap(item); - for (Integer num : links.values()) - { - amount += num; - } - - // Actually create the panel displaying banked experience for this item - CriticalItemPanel panel = new CriticalItemPanel(this, itemManager, item, xp, amount, links); - - // Limit to Banked Secondaries - if (config.limitedBankedSecondaries() && a != null) - { - panel.updateAmount(limitToActivitySecondaries(a, amount), true); - panel.recalculate(); - } - panelMap.put(item, panel); - detailContainer.add(panel); - } - - } - - /** - * Return the Activity the player selected for this Item. Defaults to First activity - * - * @param i CriticalItem to check for - * @return selected Activity - */ - public Activity getSelectedActivity(CriticalItem i) - { - // Pull from memory if available - Activity a = activityMap.get(i); - if (a != null) - { - return a; - } - - // If not in memory select the first Activity and add to memory - List activities = Activity.getByCriticalItem(i); - if (activities.size() == 0) - { - // If you can't find an activity it means this item must link to one and give 0 xp - return null; - } - - Activity selected = activities.get(0); - activityMap.put(i, selected); - return selected; - } - - /** - * Creates a Map of Item ID and QTY for this Skill by Category. Keeps order for better UI display - * - * @return Map of Item ID and QTY for this Skill by Category - */ - private Map getBankedXpBreakdown() - { - Map map = new LinkedHashMap<>(); - - for (String category : CriticalItem.getSkillCategories(currentSkill)) - { - List items = CriticalItem.getItemsForSkillCategories(currentSkill, category); - for (CriticalItem item : items) - { - Integer amount = bankMap.get(item.getItemID()); - if (amount != null && amount > 0) - { - map.put(item, amount); - } - } - } - - return map; - } - - /** - * Used to select an Activity for an item - * - * @param i CriticalItem - * @param a Activity selected - */ - public void activitySelected(CriticalItem i, Activity a) - { - // This is triggered on every click so don't update if activity didn't actually change - Activity cur = activityMap.get(i); - if (cur != null && cur.equals(a)) - { - return; - } - - // Update selected activity in map - activityMap.put(i, a); - - // If had a previous selection and this item links to another check for item prevention change. - // If there are changes adjust the Linked panel quantity as well - if (cur != null && i.getLinkedItemId() != -1 && cur.isPreventLinked() != a.isPreventLinked()) - { - CriticalItem linked = CriticalItem.getByItemId(i.getLinkedItemId()); - CriticalItemPanel l = panelMap.get(linked); - if (l != null) - { - l.updateLinkedMap(getLinkedTotalMap(linked)); - int amount = config.limitedBankedSecondaries() ? limitToActivitySecondaries(a, l.getAmount()) : l.getAmount(); - l.updateAmount(amount, false); - l.recalculate(); - } - } - - // Total banked experience - CriticalItemPanel p = panelMap.get(i); - if (p != null) - { - p.updateLinkedMap(getLinkedTotalMap(i)); - int amount = config.limitedBankedSecondaries() ? limitToActivitySecondaries(a, p.getAmount()) : p.getAmount(); - p.updateAmount(amount, true); - p.updateXp(a.getXp() * (i.isIgnoreBonus() ? 1.0f : xpFactor)); - } - - // Update total banked xp value based on updated panels - calculateBankedXpTotal(); - } - - private Map getLinkedTotalMap(CriticalItem i) - { - return getLinkedTotalMap(i, true); - } - - /** - * Creates a Map of CriticalItem and Qty for all items that link to the passed CriticalItem - * - * @param i CriticalItem to base Map off of - * @param first Since this is called recursively we want to ensure the original CriticalItem is always added - * @return Map of Linked CriticalItems and their Qty - */ - private Map getLinkedTotalMap(CriticalItem i, boolean first) - { - Map map = new LinkedHashMap<>(); - if (!categoryMap.get(i.getCategory())) - { - return map; - } - - // This item has an activity selected and its preventing linked functionality? - Activity selected = activityMap.get(i); - if (selected != null && selected.isPreventLinked() - // If initial request is for this item - && !first) - { - return map; - } - - // Add self to map - int amount = criticalMap.getOrDefault(i, 0); - if (amount > 0) - { - map.put(i, amount); - } - - // This item doesn't link to anything, all done. - if (linkedMap.get(i) == null) - { - return map; - } - - CriticalItem item = CriticalItem.getByItemId(linkedMap.get(i)); - if (item == null) - { - log.warn("Error finding Critical Item for Item ID: {}", linkedMap.get(i)); - return map; - } - - map.putAll(getLinkedTotalMap(item, false)); - - return map; - } - - /** - * SkillCalculatorPlugin sends the Bank Map when the bank contents change - * - * @param map Map of Item IDs and Quantity - */ - void updateBankMap(Map map) - { - boolean oldMapFlag = (bankMap.size() <= 0); - bankMap = map; - // Refresh entire panel if old map was empty - if (oldMapFlag) - { - CalculatorType calc = CalculatorType.getBySkill(currentSkill); - SwingUtilities.invokeLater(() -> - { - if (calc != null) - { - openBanked(calc); - } - }); - return; - } - - // recalculate all data related to banked experience except for activity selections - criticalMap.clear(); - linkedMap.clear(); - calculatedBankedMaps(); - - // Update the Total XP banked and the details panel - SwingUtilities.invokeLater(this::refreshDetailContainer); - } - - /** - * Loops over all ItemPanels too sum their total xp and updates the label with the new value - */ - private void calculateBankedXpTotal() - { - double total = 0.0; - for (CriticalItemPanel p : panelMap.values()) - { - total += p.getTotal(); - } - - totalBankedXp = total; - - syncBankedXp(); - } - - /** - * Used to update the UI to reflect the new Banked XP amount - */ - private void syncBankedXp() - { - totalLabel.setText("Total Banked xp: " + XP_FORMAT_COMMA.format(totalBankedXp)); - - syncInputFields(); - - revalidate(); - repaint(); - } - - /** - * Check Bank for Activity Secondaries and Limits to possible Activity amounts - * - * @param a Selected Activity - * @param possible Amount of Critical Item available - * @return possible Limited to Banked Secondaries - */ - private int limitToActivitySecondaries(Activity a, int possible) - { - for (SecondaryItem i : a.getSecondaries()) - { - int banked = bankMap.getOrDefault(i.getId(), 0); - int newPossible = banked / i.getQty(); - possible = newPossible < possible ? newPossible : possible; - } - - return possible; - } - - /** - * Renders the Xp Modifier options - */ - - private void renderBonusXpOptions() - { - SkillDataBonus[] bonuses = skillData.getSkillData(currentCalc.getDataFile()).getBonuses(); - if (bonuses != null) - { - add(new JLabel("Bonus Experience:")); - for (SkillDataBonus bonus : bonuses) - { - JPanel checkboxPanel = buildCheckboxPanel(bonus); - - add(checkboxPanel); - add(Box.createRigidArea(new Dimension(0, 5))); - } - } - } - - private JPanel buildCheckboxPanel(SkillDataBonus bonus) - { - JPanel uiOption = new JPanel(new BorderLayout()); - JLabel uiLabel = new JLabel(bonus.getName()); - JCheckBox uiCheckbox = new JCheckBox(); - - uiLabel.setForeground(Color.WHITE); - uiLabel.setFont(FontManager.getRunescapeSmallFont()); - - uiOption.setBorder(BorderFactory.createEmptyBorder(3, 7, 3, 0)); - uiOption.setBackground(ColorScheme.DARKER_GRAY_COLOR); - - // Adjust XP bonus depending on check-state of the boxes. - uiCheckbox.addActionListener(event -> adjustCheckboxes(uiCheckbox, bonus)); - - uiCheckbox.setBackground(ColorScheme.MEDIUM_GRAY_COLOR); - - uiOption.add(uiLabel, BorderLayout.WEST); - uiOption.add(uiCheckbox, BorderLayout.EAST); - bonusCheckBoxes.add(uiCheckbox); - - return uiOption; - } - - private void adjustCheckboxes(JCheckBox target, SkillDataBonus bonus) - { - adjustXPBonus(0); - bonusCheckBoxes.forEach(otherSelectedCheckbox -> - { - if (otherSelectedCheckbox != target) - { - otherSelectedCheckbox.setSelected(false); - } - }); - - if (target.isSelected()) - { - adjustXPBonus(bonus.getValue()); - } - } - - private void adjustXPBonus(float value) - { - xpFactor = 1f + value; - refreshDetailContainer(); - } -} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/CacheSkillData.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/CacheSkillData.java index a427b602ac..f4f54e0f52 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/CacheSkillData.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/CacheSkillData.java @@ -35,7 +35,7 @@ class CacheSkillData { private final Map cache = new HashMap<>(); - SkillData getSkillData(final String dataFile) + SkillData getSkillData(String dataFile) { if (cache.containsKey(dataFile)) { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/CalculatorType.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/CalculatorType.java index 8874563113..e102988f50 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/CalculatorType.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/CalculatorType.java @@ -1,6 +1,5 @@ /* * Copyright (c) 2018, Kruithne - * Copyright (c) 2018, TheStonedTurtle * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,47 +24,32 @@ */ package net.runelite.client.plugins.skillcalculator; -import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Getter; import net.runelite.api.Skill; @AllArgsConstructor -@Getter(AccessLevel.PACKAGE) -public enum CalculatorType +@Getter +enum CalculatorType { - AGILITY(Skill.AGILITY, "skill_agility.json", false), - CONSTRUCTION(Skill.CONSTRUCTION, "skill_construction.json", true), - COOKING(Skill.COOKING, "skill_cooking.json", true), - CRAFTING(Skill.CRAFTING, "skill_crafting.json", true), - FARMING(Skill.FARMING, "skill_farming.json", true), - FIREMAKING(Skill.FIREMAKING, "skill_firemaking.json", false), - FLETCHING(Skill.FLETCHING, "skill_fletching.json", false), - FISHING(Skill.FISHING, "skill_fishing.json", false), - HERBLORE(Skill.HERBLORE, "skill_herblore.json", true), - HUNTER(Skill.HUNTER, "skill_hunter.json", false), - MAGIC(Skill.MAGIC, "skill_magic.json", false), - MINING(Skill.MINING, "skill_mining.json", false), - PRAYER(Skill.PRAYER, "skill_prayer.json", true), - RUNECRAFT(Skill.RUNECRAFT, "skill_runecraft.json", false), - SMITHING(Skill.SMITHING, "skill_smithing.json", true), - THIEVING(Skill.THIEVING, "skill_thieving.json", false), - WOODCUTTING(Skill.WOODCUTTING, "skill_woodcutting.json", false); + MINING(Skill.MINING, "skill_mining.json"), + AGILITY(Skill.AGILITY, "skill_agility.json"), + SMITHING(Skill.SMITHING, "skill_smithing.json"), + HERBLORE(Skill.HERBLORE, "skill_herblore.json"), + FISHING(Skill.FISHING, "skill_fishing.json"), + THIEVING(Skill.THIEVING, "skill_thieving.json"), + COOKING(Skill.COOKING, "skill_cooking.json"), + PRAYER(Skill.PRAYER, "skill_prayer.json"), + CRAFTING(Skill.CRAFTING, "skill_crafting.json"), + FIREMAKING(Skill.FIREMAKING, "skill_firemaking.json"), + MAGIC(Skill.MAGIC, "skill_magic.json"), + FLETCHING(Skill.FLETCHING, "skill_fletching.json"), + WOODCUTTING(Skill.WOODCUTTING, "skill_woodcutting.json"), + RUNECRAFT(Skill.RUNECRAFT, "skill_runecraft.json"), + FARMING(Skill.FARMING, "skill_farming.json"), + CONSTRUCTION(Skill.CONSTRUCTION, "skill_construction.json"), + HUNTER(Skill.HUNTER, "skill_hunter.json"); private final Skill skill; private final String dataFile; - private final boolean bankedXpFlag; - - public static CalculatorType getBySkill(Skill skill) - { - for (CalculatorType c : values()) - { - if (c.getSkill().equals(skill)) - { - return c; - } - } - - return null; - } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculator.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculator.java index e0b614a95a..4434aa92fd 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculator.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculator.java @@ -34,18 +34,13 @@ import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.ArrayList; import java.util.List; -import java.util.Optional; -import javax.inject.Singleton; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JPanel; -import lombok.AccessLevel; -import lombok.Getter; import net.runelite.api.Client; import net.runelite.api.Experience; -import net.runelite.api.Skill; import net.runelite.client.game.ItemManager; import net.runelite.client.game.SpriteManager; import net.runelite.client.plugins.skillcalculator.beans.SkillData; @@ -59,7 +54,6 @@ import net.runelite.client.ui.components.IconTextField; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; -@Singleton class SkillCalculator extends JPanel { private static final int MAX_XP = 200_000_000; @@ -71,9 +65,7 @@ class SkillCalculator extends JPanel private final ItemManager itemManager; private final List uiActionSlots = new ArrayList<>(); private final CacheSkillData cacheSkillData = new CacheSkillData(); - @Getter(AccessLevel.PACKAGE) private final UICombinedActionSlot combinedActionSlot; - @Getter(AccessLevel.PACKAGE) private final List combinedActionSlots = new ArrayList<>(); private final List bonusCheckBoxes = new ArrayList<>(); private final IconTextField searchBar = new IconTextField(); @@ -85,9 +77,8 @@ class SkillCalculator extends JPanel private int targetXP = Experience.getXpForLevel(targetLevel); private float xpFactor = 1.0f; private float lastBonus = 0.0f; - private CalculatorType calculatorType; - SkillCalculator(final Client client, final UICalculatorInputArea uiInput, final SpriteManager spriteManager, final ItemManager itemManager) + SkillCalculator(Client client, UICalculatorInputArea uiInput, SpriteManager spriteManager, ItemManager itemManager) { this.client = client; this.uiInput = uiInput; @@ -123,8 +114,6 @@ class SkillCalculator extends JPanel void openCalculator(CalculatorType calculatorType) { - this.calculatorType = calculatorType; - // Load the skill data. skillData = cacheSkillData.getSkillData(calculatorType.getDataFile()); @@ -132,11 +121,10 @@ class SkillCalculator extends JPanel xpFactor = 1.0f; // Update internal skill/XP values. - updateInternalValues(); - - // BankedCalculator prevents these from being editable so just ensure they are editable. - uiInput.getUiFieldTargetLevel().setEditable(true); - uiInput.getUiFieldTargetXP().setEditable(true); + currentXP = client.getSkillExperience(calculatorType.getSkill()); + currentLevel = Experience.getLevelForXp(currentXP); + targetLevel = enforceSkillBounds(currentLevel + 1); + targetXP = Experience.getXpForLevel(targetLevel); // Remove all components (action slots) from this panel. removeAll(); @@ -144,9 +132,6 @@ class SkillCalculator extends JPanel // Clear the search bar searchBar.setText(null); - // Clear the search bar - searchBar.setText(null); - // Add in checkboxes for available skill bonuses. renderBonusOptions(); @@ -163,24 +148,6 @@ class SkillCalculator extends JPanel updateInputFields(); } - private void updateInternalValues() - { - updateCurrentValues(); - updateTargetValues(); - } - - private void updateCurrentValues() - { - currentXP = client.getSkillExperience(calculatorType.getSkill()); - currentLevel = Experience.getLevelForXp(currentXP); - } - - private void updateTargetValues() - { - targetLevel = enforceSkillBounds(currentLevel + 1); - targetXP = Experience.getXpForLevel(targetLevel); - } - private void updateCombinedAction() { int size = combinedActionSlots.size(); @@ -254,7 +221,7 @@ class SkillCalculator extends JPanel JCheckBox uiCheckbox = new JCheckBox(); uiLabel.setForeground(Color.WHITE); - uiLabel.setFont(FontManager.getSmallFont(getFont())); + uiLabel.setFont(FontManager.getRunescapeSmallFont()); uiOption.setBorder(BorderFactory.createEmptyBorder(3, 7, 3, 0)); uiOption.setBackground(ColorScheme.DARKER_GRAY_COLOR); @@ -270,7 +237,7 @@ class SkillCalculator extends JPanel for (JCheckBox checkBox : uiCheckBoxList) { - if (checkBox != null && !checkBox.equals(uiCheckBox)) + if (checkBox != uiCheckBox) { checkBox.setSelected(false); } @@ -466,25 +433,4 @@ class SkillCalculator extends JPanel return slot.getAction().getName().toLowerCase().contains(text.toLowerCase()); } - /** - * Updates the current skill calculator (if present) - *

- * This method is invoked by the {@link SkillCalculatorPlugin} event subscriber - * when an {@link ExperienceChanged} object is posted to the event bus - */ - void updateSkillCalculator(Skill skill) - { - // If the user has selected a calculator, update its fields - Optional.ofNullable(calculatorType).ifPresent(calc -> - { - if (skill.equals(calculatorType.getSkill())) - { - // Update our model "current" values - updateCurrentValues(); - - // Update the UI to reflect our new model - updateInputFields(); - } - }); - } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorConfig.java index 9fd11ba13f..825c620459 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorConfig.java @@ -32,24 +32,24 @@ import net.runelite.client.config.ConfigItem; public interface SkillCalculatorConfig extends Config { @ConfigItem( - keyName = "showBankedXp", - name = "Show Banked xp Tab", - description = "Shows the Banked xp tab inside the Calculator Panel", + keyName = "enabledBankedXp", + name = "Add Banked XP Panel", + description = "Adds the Banked XP Panel to the side bar", position = 0 ) default boolean showBankedXp() { - return true; + return false; } @ConfigItem( - keyName = "limitedBankedSecondaries", - name = "Limit Banked xp to Secondaries", - description = "Limits the Banked xp shown based on secondaries banked as well", + keyName = "cascadeBankedXp", + name = "Include output items", + description = "Includes output items in the item quantity calculations", position = 1 ) - default boolean limitedBankedSecondaries() + default boolean cascadeBankedXp() { - return false; + return true; } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorPanel.java index 79684db069..b6abe4577f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorPanel.java @@ -1,7 +1,6 @@ /* * Copyright (c) 2018, Kruithne * Copyright (c) 2018, Psikoi - * Copyright (c) 2018, TheStonedTurtle * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,20 +29,10 @@ package net.runelite.client.plugins.skillcalculator; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.GridLayout; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import javax.inject.Singleton; import javax.swing.ImageIcon; import javax.swing.JScrollPane; -import javax.swing.SwingUtilities; import javax.swing.border.EmptyBorder; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; -import net.runelite.api.Skill; import net.runelite.client.game.ItemManager; import net.runelite.client.game.SkillIconManager; import net.runelite.client.game.SpriteManager; @@ -52,43 +41,30 @@ import net.runelite.client.ui.PluginPanel; import net.runelite.client.ui.components.materialtabs.MaterialTab; import net.runelite.client.ui.components.materialtabs.MaterialTabGroup; -@Slf4j -@Singleton class SkillCalculatorPanel extends PluginPanel { private final SkillCalculator uiCalculator; private final SkillIconManager iconManager; - private final SkillCalculatorConfig config; - private final BankedCalculator bankedCalculator; - - private CalculatorType currentCalc; - private final MaterialTabGroup skillGroup; private final MaterialTabGroup tabGroup; - private String currentTab; - private final List tabs = new ArrayList<>(); - @Getter - private Map bankMap = new HashMap<>(); - private final GridBagConstraints c; - SkillCalculatorPanel(final SkillIconManager iconManager, final Client client, final SkillCalculatorConfig config, final SpriteManager spriteManager, final ItemManager itemManager) + SkillCalculatorPanel(SkillIconManager iconManager, Client client, SpriteManager spriteManager, ItemManager itemManager) { super(); getScrollPane().setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); this.iconManager = iconManager; - this.config = config; setBorder(new EmptyBorder(10, 10, 10, 10)); setLayout(new GridBagLayout()); - c = new GridBagConstraints(); + GridBagConstraints c = new GridBagConstraints(); c.fill = GridBagConstraints.HORIZONTAL; c.weightx = 1; c.gridx = 0; c.gridy = 0; - skillGroup = new MaterialTabGroup(); - skillGroup.setLayout(new GridLayout(0, 6, 7, 7)); + tabGroup = new MaterialTabGroup(); + tabGroup.setLayout(new GridLayout(0, 6, 7, 7)); addCalculatorButtons(); @@ -97,23 +73,14 @@ class SkillCalculatorPanel extends PluginPanel uiInput.setBackground(ColorScheme.DARK_GRAY_COLOR); uiCalculator = new SkillCalculator(client, uiInput, spriteManager, itemManager); - bankedCalculator = new BankedCalculator(this, client, uiInput, config, itemManager); - - tabGroup = new MaterialTabGroup(); - tabGroup.setBorder(new EmptyBorder(0, 0, 10, 0)); - - addTabButtons(); - - add(skillGroup, c); + add(tabGroup, c); c.gridy++; add(uiInput, c); c.gridy++; - add(tabGroup, c); - c.gridy++; - add(uiCalculator, c); + c.gridy++; } private void addCalculatorButtons() @@ -121,126 +88,14 @@ class SkillCalculatorPanel extends PluginPanel for (CalculatorType calculatorType : CalculatorType.values()) { ImageIcon icon = new ImageIcon(iconManager.getSkillImage(calculatorType.getSkill(), true)); - MaterialTab tab = new MaterialTab(icon, skillGroup, null); + MaterialTab tab = new MaterialTab(icon, tabGroup, null); tab.setOnSelectEvent(() -> { - if (currentCalc != null && currentCalc.equals(calculatorType)) - { - return true; - } - currentCalc = calculatorType; - selectedTab(currentTab, true); + uiCalculator.openCalculator(calculatorType); return true; }); - skillGroup.addTab(tab); + tabGroup.addTab(tab); } } - - private void addTabButtons() - { - tabGroup.removeAll(); - tabs.clear(); - - tabs.add("Calculator"); - if (config.showBankedXp()) - { - tabs.add("Banked Xp"); - } - // Only show if both options are visible - tabGroup.setVisible(tabs.size() > 1); - - tabGroup.setLayout(new GridLayout(0, tabs.size(), 7, 7)); - - for (String s : tabs) - { - MaterialTab matTab = new MaterialTab(s, tabGroup, null); - - matTab.setHorizontalAlignment(SwingUtilities.CENTER); - - // Ensure Background is applied - matTab.setOpaque(true); - matTab.setBackground(ColorScheme.DARKER_GRAY_COLOR); - - // When Clicked - matTab.setOnSelectEvent(() -> - { - selectedTab(s, false); - return true; - }); - - tabGroup.addTab(matTab); - } - - MaterialTab selected = tabGroup.getTab(0); - if (tabs.contains(currentTab)) - { - selected = tabGroup.getTab(tabs.indexOf(currentTab)); - } - - tabGroup.select(selected); - currentTab = selected.getText(); - } - - private void selectedTab(String s, boolean force) - { - // Do not refresh the panel if they clicked the same tab, unless they selected a new skill - if (Objects.equals(currentTab, s) && !force) - { - return; - } - - currentTab = s; - - // Only open a panel if a skill is selected - if (currentCalc == null) - { - return; - } - - switch (s) - { - case "Calculator": - remove(bankedCalculator); - add(uiCalculator, c); - uiCalculator.openCalculator(currentCalc); - break; - case "Banked Xp": - remove(uiCalculator); - add(bankedCalculator, c); - bankedCalculator.openBanked(currentCalc); - break; - } - - this.revalidate(); - this.repaint(); - } - - // Refresh entire panel - void refreshPanel() - { - // Recreate Tabs (in case of Config change) and selects the first tab - addTabButtons(); - - // Ensure reload - selectedTab(currentTab, true); - - this.revalidate(); - this.repaint(); - } - - // Wrapper function for updating SkillCalculator's bankMap - void updateBankMap(Map bank) - { - bankMap = bank; - if (currentCalc != null & currentTab.equals("Banked Xp")) - { - bankedCalculator.updateBankMap(bankMap); - } - } - - void updateSkillCalculator(Skill skill) - { - uiCalculator.updateSkillCalculator(skill); - } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorPlugin.java index e6a5316cf3..147420e7b1 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorPlugin.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2018, Kruithne - * Copyright (c) 2018, TheStonedTurtle + * Copyright (c) 2019, TheStonedTurtle * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,18 +31,13 @@ import java.awt.image.BufferedImage; import java.util.HashMap; import java.util.Map; import javax.inject.Inject; -import javax.inject.Singleton; import javax.swing.SwingUtilities; -import lombok.Getter; import net.runelite.api.Client; import net.runelite.api.InventoryID; import net.runelite.api.Item; import net.runelite.api.ItemContainer; import net.runelite.api.events.ConfigChanged; -import net.runelite.api.events.ExperienceChanged; -import net.runelite.api.events.GameTick; -import net.runelite.api.widgets.Widget; -import net.runelite.api.widgets.WidgetInfo; +import net.runelite.api.events.ScriptCallbackEvent; import net.runelite.client.callback.ClientThread; import net.runelite.client.config.ConfigManager; import net.runelite.client.eventbus.Subscribe; @@ -51,7 +46,9 @@ import net.runelite.client.game.SkillIconManager; import net.runelite.client.game.SpriteManager; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; -import net.runelite.client.plugins.skillcalculator.banked.CriticalItem; +import net.runelite.client.plugins.skillcalculator.banked.BankedCalculatorPanel; +import net.runelite.client.plugins.skillcalculator.banked.beans.Activity; +import net.runelite.client.plugins.skillcalculator.banked.beans.CriticalItem; import net.runelite.client.ui.ClientToolbar; import net.runelite.client.ui.NavigationButton; import net.runelite.client.util.ImageUtil; @@ -61,7 +58,6 @@ import net.runelite.client.util.ImageUtil; description = "Enable the Skill Calculator panel", tags = {"panel", "skilling"} ) -@Singleton public class SkillCalculatorPlugin extends Plugin { @Inject @@ -86,12 +82,10 @@ public class SkillCalculatorPlugin extends Plugin private SkillCalculatorConfig skillCalculatorConfig; private NavigationButton uiNavigationButton; - private SkillCalculatorPanel uiPanel; + private NavigationButton bankedUiNavigationButton; - @Getter - private Map bankMap = new HashMap<>(); - - private int bankHash; + private BankedCalculatorPanel bankedUiPanel; + private int bankHash = -1; @Provides SkillCalculatorConfig getConfig(ConfigManager configManager) @@ -103,7 +97,7 @@ public class SkillCalculatorPlugin extends Plugin protected void startUp() throws Exception { final BufferedImage icon = ImageUtil.getResourceStreamFromClass(getClass(), "calc.png"); - this.uiPanel = new SkillCalculatorPanel(skillIconManager, client, skillCalculatorConfig, spriteManager, itemManager); + final SkillCalculatorPanel uiPanel = new SkillCalculatorPanel(skillIconManager, client, spriteManager, itemManager); uiNavigationButton = NavigationButton.builder() .tooltip("Skill Calculator") @@ -114,56 +108,32 @@ public class SkillCalculatorPlugin extends Plugin clientToolbar.addNavigation(uiNavigationButton); - clientThread.invokeLater(() -> - { - switch (client.getGameState()) - { - case STARTING: - case UNKNOWN: - return false; - } - - CriticalItem.prepareItemDefinitions(itemManager); - return true; - }); + toggleBankedXpPanel(); } @Override protected void shutDown() throws Exception { clientToolbar.removeNavigation(uiNavigationButton); - bankMap.clear(); - bankHash = -1; + if (bankedUiNavigationButton != null) + { + clientToolbar.removeNavigation(bankedUiNavigationButton); + } } @Subscribe public void onConfigChanged(ConfigChanged event) { - if (event.getGroup().equals("skillCalculator")) + if (event.getGroup().equals("skillCalculator") && event.getKey().equals("enabledBankedXp")) { - if (event.getKey().equals("showBankedXp")) - { - bankMap.clear(); - bankHash = -1; - } - - SwingUtilities.invokeLater(() -> uiPanel.refreshPanel()); + toggleBankedXpPanel(); } } - // Pulled from bankvalue plugin to check if bank is open @Subscribe - public void onGameTick(GameTick event) + public void onScriptCallbackEvent(ScriptCallbackEvent event) { - if (!skillCalculatorConfig.showBankedXp()) - { - return; - } - - Widget widgetBankTitleBar = client.getWidget(WidgetInfo.BANK_TITLE_BAR); - - // Don't update on a search because rs seems to constantly update the title - if (widgetBankTitleBar == null || widgetBankTitleBar.isHidden() || widgetBankTitleBar.getText().contains("Showing")) + if (!event.getEventName().equals("setBankTitle") || !skillCalculatorConfig.showBankedXp()) { return; } @@ -171,54 +141,80 @@ public class SkillCalculatorPlugin extends Plugin updateBankItems(); } + private void toggleBankedXpPanel() + { + if (skillCalculatorConfig.showBankedXp()) + { + final BufferedImage icon = ImageUtil.getResourceStreamFromClass(getClass(), "banked.png"); + + bankedUiPanel = new BankedCalculatorPanel(client, skillCalculatorConfig, skillIconManager, itemManager); + bankedUiNavigationButton = NavigationButton.builder() + .tooltip("Banked XP") + .icon(icon) + .priority(6) + .panel(bankedUiPanel) + .build(); + + clientToolbar.addNavigation(bankedUiNavigationButton); + + clientThread.invoke(() -> + { + switch (client.getGameState()) + { + case LOGIN_SCREEN: + case LOGIN_SCREEN_AUTHENTICATOR: + case LOGGING_IN: + case LOADING: + case LOGGED_IN: + case CONNECTION_LOST: + case HOPPING: + CriticalItem.prepareItemDefinitions(itemManager); + Activity.prepareItemDefinitions(itemManager); + return true; + default: + return false; + } + }); + } + else + { + if (bankedUiNavigationButton == null) + { + return; + } + + clientToolbar.removeNavigation(bankedUiNavigationButton); + + bankedUiNavigationButton = null; + } + } + // Check if bank contents changed and if so send to UI private void updateBankItems() { - ItemContainer c = client.getItemContainer(InventoryID.BANK); - Item[] widgetItems = (c == null ? new Item[0] : c.getItems()); + final ItemContainer c = client.getItemContainer(InventoryID.BANK); + if (c == null) + { + return; + } - // Couldn't find any items in bank, do nothing. + final Item[] widgetItems = c.getItems(); if (widgetItems == null || widgetItems.length == 0) { return; } - Map newBankMap = getBankMapIfDiff(widgetItems); - - // Bank didn't change - if (newBankMap.size() == 0) - { - return; - } - - bankMap = newBankMap; - // send updated bank map to ui - uiPanel.updateBankMap(bankMap); - } - - // Recreates the bankMap and checks if the hashCode is different (the map has changed). Sends an empty map if no changes - private Map getBankMapIfDiff(Item[] widgetItems) - { - Map mapCheck = new HashMap<>(); + final Map m = new HashMap<>(); for (Item widgetItem : widgetItems) { - mapCheck.put(widgetItem.getId(), widgetItem.getQuantity()); + m.put(widgetItem.getId(), widgetItem.getQuantity()); } - int curHash = mapCheck.hashCode(); - - if (curHash != bankHash) + final int curHash = m.hashCode(); + if (bankHash != curHash) { bankHash = curHash; - return mapCheck; + SwingUtilities.invokeLater(() -> bankedUiPanel.setBankMap(m)); } - - return new HashMap<>(); - } - - @Subscribe - public void onExperienceChanged(ExperienceChanged changeEvent) - { - uiPanel.updateSkillCalculator(changeEvent.getSkill()); } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UIActionSlot.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UIActionSlot.java index 819e75ad4b..2051a0ddcd 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UIActionSlot.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UIActionSlot.java @@ -33,7 +33,6 @@ import java.awt.GridLayout; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; -import javax.inject.Singleton; import javax.swing.BorderFactory; import javax.swing.JLabel; import javax.swing.JPanel; @@ -48,7 +47,6 @@ import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.FontManager; import net.runelite.client.ui.components.shadowlabel.JShadowedLabel; -@Singleton class UIActionSlot extends JPanel { private static final Border GREEN_BORDER = new CompoundBorder( @@ -127,7 +125,7 @@ class UIActionSlot extends JPanel uiLabelName.setForeground(Color.WHITE); uiLabelActions = new JShadowedLabel("Unknown"); - uiLabelActions.setFont(FontManager.getSmallFont(getFont())); + uiLabelActions.setFont(FontManager.getRunescapeSmallFont()); uiLabelActions.setForeground(ColorScheme.LIGHT_GRAY_COLOR); uiInfo.add(uiLabelName); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UICalculatorInputArea.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UICalculatorInputArea.java index 515b3997d0..6584c85bf9 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UICalculatorInputArea.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UICalculatorInputArea.java @@ -28,27 +28,24 @@ package net.runelite.client.plugins.skillcalculator; import java.awt.BorderLayout; import java.awt.Color; import java.awt.GridLayout; -import javax.inject.Singleton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.border.EmptyBorder; -import lombok.AccessLevel; import lombok.Getter; import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.FontManager; import net.runelite.client.ui.components.FlatTextField; -@Getter(AccessLevel.PACKAGE) -@Singleton -class UICalculatorInputArea extends JPanel +@Getter +public class UICalculatorInputArea extends JPanel { private final JTextField uiFieldCurrentLevel; private final JTextField uiFieldCurrentXP; private final JTextField uiFieldTargetLevel; private final JTextField uiFieldTargetXP; - UICalculatorInputArea() + public UICalculatorInputArea() { setLayout(new GridLayout(2, 2, 7, 7)); uiFieldCurrentLevel = addComponent("Current Level"); @@ -62,7 +59,7 @@ class UICalculatorInputArea extends JPanel return getInput(uiFieldCurrentLevel); } - void setCurrentLevelInput(int value) + public void setCurrentLevelInput(int value) { setInput(uiFieldCurrentLevel, value); } @@ -72,7 +69,7 @@ class UICalculatorInputArea extends JPanel return getInput(uiFieldCurrentXP); } - void setCurrentXPInput(Object value) + public void setCurrentXPInput(Object value) { setInput(uiFieldCurrentXP, value); } @@ -82,7 +79,7 @@ class UICalculatorInputArea extends JPanel return getInput(uiFieldTargetLevel); } - void setTargetLevelInput(Object value) + public void setTargetLevelInput(Object value) { setInput(uiFieldTargetLevel, value); } @@ -92,7 +89,7 @@ class UICalculatorInputArea extends JPanel return getInput(uiFieldTargetXP); } - void setTargetXPInput(Object value) + public void setTargetXPInput(Object value) { setInput(uiFieldTargetXP, value); } @@ -126,7 +123,7 @@ class UICalculatorInputArea extends JPanel uiInput.setHoverBackgroundColor(ColorScheme.DARK_GRAY_HOVER_COLOR); uiInput.setBorder(new EmptyBorder(5, 7, 5, 7)); - uiLabel.setFont(FontManager.getSmallFont(getFont())); + uiLabel.setFont(FontManager.getRunescapeSmallFont()); uiLabel.setBorder(new EmptyBorder(0, 0, 4, 0)); uiLabel.setForeground(Color.WHITE); @@ -137,4 +134,4 @@ class UICalculatorInputArea extends JPanel return uiInput.getTextField(); } -} +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UICombinedActionSlot.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UICombinedActionSlot.java index f84cdecb23..fa1614c91b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UICombinedActionSlot.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/UICombinedActionSlot.java @@ -30,7 +30,6 @@ import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.GridLayout; -import javax.inject.Singleton; import javax.swing.BorderFactory; import javax.swing.JLabel; import javax.swing.JPanel; @@ -40,14 +39,13 @@ import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.FontManager; import net.runelite.client.ui.components.shadowlabel.JShadowedLabel; -@Singleton class UICombinedActionSlot extends JPanel { private static final Dimension ICON_SIZE = new Dimension(32, 32); private final JShadowedLabel uiLabelActions; private final JShadowedLabel uiLabelTitle; - UICombinedActionSlot(final SpriteManager spriteManager) + UICombinedActionSlot(SpriteManager spriteManager) { setLayout(new BorderLayout()); setBackground(ColorScheme.DARKER_GRAY_COLOR); @@ -70,7 +68,7 @@ class UICombinedActionSlot extends JPanel uiLabelTitle.setForeground(Color.WHITE); uiLabelActions = new JShadowedLabel("Shift-click to select multiple"); - uiLabelActions.setFont(FontManager.getSmallFont(getFont())); + uiLabelActions.setFont(FontManager.getRunescapeSmallFont()); uiLabelActions.setForeground(ColorScheme.LIGHT_GRAY_COLOR); uiInfo.add(uiLabelTitle); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/BankedCalculator.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/BankedCalculator.java new file mode 100644 index 0000000000..821fd23fc4 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/BankedCalculator.java @@ -0,0 +1,462 @@ +/* + * Copyright (c) 2019, TheStonedTurtle + * 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.skillcalculator.banked; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.event.ItemEvent; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import javax.swing.BorderFactory; +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.SwingConstants; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import net.runelite.api.Experience; +import net.runelite.api.Skill; +import net.runelite.client.game.AsyncBufferedImage; +import net.runelite.client.game.ItemManager; +import net.runelite.client.plugins.skillcalculator.SkillCalculatorConfig; +import net.runelite.client.plugins.skillcalculator.UICalculatorInputArea; +import net.runelite.client.plugins.skillcalculator.banked.beans.Activity; +import net.runelite.client.plugins.skillcalculator.banked.beans.BankedItem; +import net.runelite.client.plugins.skillcalculator.banked.beans.CriticalItem; +import net.runelite.client.plugins.skillcalculator.banked.beans.XpModifiers; +import net.runelite.client.plugins.skillcalculator.banked.components.GridItem; +import net.runelite.client.plugins.skillcalculator.banked.components.ModifyPanel; +import net.runelite.client.plugins.skillcalculator.banked.components.SelectionGrid; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.DynamicGridLayout; +import net.runelite.client.ui.FontManager; + +@Slf4j +public class BankedCalculator extends JPanel +{ + public static final DecimalFormat XP_FORMAT_COMMA = new DecimalFormat("#,###.#"); + + private final Client client; + @Getter + private final SkillCalculatorConfig config; + private final UICalculatorInputArea uiInput; + private final ItemManager itemManager; + + // Some activities output a CriticalItem and may need to be included in the calculable qty + // Using multimap for cases where there are multiple items linked directly to one item, use recursion for otherwise + private final Multimap linkedMap = ArrayListMultimap.create(); + + private final Map bankedItemMap = new LinkedHashMap<>(); + private final JLabel totalXpLabel = new JLabel(); + private final ModifyPanel modifyPanel; + private SelectionGrid itemGrid; + + @Setter + private Map bankMap = new HashMap<>(); + + @Getter + private Skill currentSkill; + + @Getter + private int skillLevel, skillExp, endLevel, endExp; + + private final Collection xpModifierButtons = new ArrayList<>(); + @Getter + private float xpFactor = 1.0f; + + BankedCalculator(UICalculatorInputArea uiInput, Client client, SkillCalculatorConfig config, ItemManager itemManager) + { + this.uiInput = uiInput; + this.client = client; + this.config = config; + this.itemManager = itemManager; + + setLayout(new DynamicGridLayout(0, 1, 0, 5)); + + // Panel used to modify banked item values + this.modifyPanel = new ModifyPanel(this, itemManager); + } + + /** + * opens the Banked Calculator for this skill + */ + void open(final Skill newSkill) + { + if (newSkill.equals(currentSkill)) + { + return; + } + + this.currentSkill = newSkill; + removeAll(); + xpFactor = 1.0f; + + if (bankMap.size() <= 0) + { + add(new JLabel("Please visit a bank!", JLabel.CENTER)); + revalidate(); + repaint(); + return; + } + + skillLevel = client.getRealSkillLevel(currentSkill); + skillExp = client.getSkillExperience(currentSkill); + endLevel = skillLevel; + endExp = skillExp; + + uiInput.setCurrentLevelInput(skillLevel); + uiInput.setCurrentXPInput(skillExp); + uiInput.setTargetLevelInput(endLevel); + uiInput.setTargetXPInput(endExp); + + recreateBankedItemMap(); + + final Collection modifiers = XpModifiers.getModifiersBySkill(this.currentSkill); + for (final XpModifiers modifier : modifiers) + { + JPanel uiOption = new JPanel(new BorderLayout()); + JLabel uiLabel = new JLabel(modifier.getName()); + JCheckBox btn = new JCheckBox(); + + uiLabel.setForeground(Color.WHITE); + uiLabel.setFont(FontManager.getRunescapeSmallFont()); + uiLabel.setHorizontalAlignment(SwingConstants.CENTER); + + uiOption.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 0)); + uiOption.setBackground(ColorScheme.DARKER_GRAY_COLOR); + + btn.addItemListener((event) -> + { + switch (event.getStateChange()) + { + case ItemEvent.DESELECTED: + xpFactor = 1.0f; + break; + case ItemEvent.SELECTED: + // Deselects all but the current item + final JCheckBox box = (JCheckBox) event.getItem(); + xpModifierButtons.forEach(b -> b.setSelected(b == box)); + + xpFactor = modifier.getModifier(); + break; + default: + return; + + } + + modifierUpdated(); + }); + xpModifierButtons.add(btn); + + uiOption.add(uiLabel, BorderLayout.WEST); + uiOption.add(btn, BorderLayout.EAST); + add(uiOption); + } + + recreateItemGrid(); + + // This should only be null if there are no items in their bank for this skill + if (itemGrid.getSelectedItem() == null) + { + add(new JLabel("Couldn't find any items for this skill.", JLabel.CENTER)); + } + else + { + add(totalXpLabel); + add(modifyPanel); + add(itemGrid); + } + + revalidate(); + repaint(); + } + + private void recreateBankedItemMap() + { + bankedItemMap.clear(); + linkedMap.clear(); + + final Collection items = CriticalItem.getBySkill(currentSkill); + log.debug("Critical Items for the {} Skill: {}", currentSkill.getName(), items); + + for (final CriticalItem item : items) + { + final BankedItem banked = new BankedItem(item, bankMap.getOrDefault(item.getItemID(), 0)); + bankedItemMap.put(item, banked); + + Activity a = item.getSelectedActivity(); + if (a == null) + { + final List activities = Activity.getByCriticalItem(item); + if (activities.size() == 0) + { + continue; + } + + item.setSelectedActivity(activities.get(0)); + a = activities.get(0); + } + + if (a.getLinkedItem() != null) + { + linkedMap.put(a.getLinkedItem(), banked); + } + } + log.debug("Banked Item Map: {}", bankedItemMap); + log.debug("Linked Map: {}", linkedMap); + } + + /** + * Populates the detailContainer with the necessary BankedItemPanels + */ + private void recreateItemGrid() + { + // Selection grid will only display values with > 0 items + itemGrid = new SelectionGrid(this, bankedItemMap.values(), itemManager); + itemGrid.setOnSelectEvent(() -> + { + modifyPanel.setBankedItem(itemGrid.getSelectedItem()); + return true; + }); + + itemGrid.setOnIgnoreEvent(() -> + { + CriticalItem item = itemGrid.getLastIgnoredItem().getItem(); + updateLinkedItems(item.getSelectedActivity()); + calculateBankedXpTotal(); + return true; + }); + + // Select the first item in the list + modifyPanel.setBankedItem(itemGrid.getSelectedItem()); + + calculateBankedXpTotal(); + } + + public double getItemXpRate(final BankedItem bankedItem) + { + return bankedItem.getXpRate() * (bankedItem.getItem().isIgnoreBonus() ? 1.0f : xpFactor); + } + + /** + * Calculates total item quantity accounting for backwards linked items + * + * @param item starting item + * @return item qty including linked items + */ + public int getItemQty(final BankedItem item) + { + int qty = item.getQty(); + + if (!config.cascadeBankedXp()) + { + return qty; + } + + final Map linked = createLinksMap(item); + final int linkedQty = linked.values().stream().mapToInt(Integer::intValue).sum(); + + return qty + linkedQty; + } + + private void calculateBankedXpTotal() + { + double total = 0.0; + for (final GridItem i : itemGrid.getPanelMap().values()) + { + if (i.isIgnored()) + { + continue; + } + + final BankedItem bi = i.getBankedItem(); + total += getItemQty(bi) * getItemXpRate(bi); + } + + endExp = (int) (skillExp + total); + endLevel = Experience.getLevelForXp(endExp); + + totalXpLabel.setText("Total Banked xp: " + XP_FORMAT_COMMA.format(total)); + uiInput.setTargetLevelInput(endLevel); + uiInput.setTargetXPInput(Math.min(Experience.MAX_SKILL_XP, endExp)); + + revalidate(); + repaint(); + } + + /** + * Used to select an Activity for an item + * + * @param i BankedItem item the activity is tied to + * @param a Activity the selected activity + */ + public void activitySelected(final BankedItem i, final Activity a) + { + final CriticalItem item = i.getItem(); + final Activity old = item.getSelectedActivity(); + if (a.equals(old)) + { + return; + } + + item.setSelectedActivity(a); + + // Cascade activity changes if necessary. + if (config.cascadeBankedXp() && (old.getLinkedItem() != a.getLinkedItem())) + { + // Update Linked Map + linkedMap.remove(old.getLinkedItem(), i); + linkedMap.put(a.getLinkedItem(), i); + // Update all items the old activity effects + updateLinkedItems(old); + // Update all the items the new activity effects + updateLinkedItems(a); + } + + modifyPanel.setBankedItem(i); + itemGrid.getPanelMap().get(i).updateToolTip(); + + // recalculate total xp + calculateBankedXpTotal(); + } + + /** + * Updates the item quantities of all forward linked items + * + * @param activity the starting {@link Activity} to start the cascade from + */ + private void updateLinkedItems(final Activity activity) + { + if (activity == null) + { + return; + } + + boolean foundSelected = false; + boolean panelAmountChange = false; + + CriticalItem i = activity.getLinkedItem(); + while (i != null) + { + final BankedItem bi = bankedItemMap.get(i); + if (bi == null) + { + break; + } + + final int qty = getItemQty(bi); + final boolean stackable = bi.getItem().getItemInfo().isStackable() || qty > 1; + final AsyncBufferedImage img = itemManager.getImage(bi.getItem().getItemID(), qty, stackable); + + final GridItem gridItem = itemGrid.getPanelMap().get(bi); + final int oldQty = gridItem.getAmount(); + panelAmountChange = panelAmountChange || ((oldQty == 0 && qty > 0) || (oldQty > 0 && qty == 0)); + gridItem.updateIcon(img, qty); + gridItem.updateToolTip(); + + foundSelected = foundSelected || itemGrid.getSelectedItem().equals(bi); + + final Activity a = bi.getItem().getSelectedActivity(); + if (a == null) + { + break; + } + + i = a.getLinkedItem(); + } + + if (panelAmountChange) + { + itemGrid.refreshGridDisplay(); + } + + if (foundSelected) + { + // Refresh current modify panel if the cascade effects it + modifyPanel.setBankedItem(itemGrid.getSelectedItem()); + } + } + + /** + * Creates a Map of CriticalItem to bank qty for all items that are being linked to this one + * + * @param item starting item + * @return Map of CriticalItem to bank qty + */ + public Map createLinksMap(final BankedItem item) + { + final Map qtyMap = new HashMap<>(); + + final Activity a = item.getItem().getSelectedActivity(); + if (a == null) + { + return qtyMap; + } + + final Collection linkedBank = linkedMap.get(item.getItem()); + if (linkedBank == null || linkedBank.size() == 0) + { + return qtyMap; + } + + for (final BankedItem linked : linkedBank) + { + // Check if the item is ignored in the grid + if (itemGrid != null) + { + final GridItem grid = itemGrid.getPanelMap().get(linked); + if (grid != null && grid.isIgnored()) + { + continue; + } + } + + final int qty = linked.getQty(); + if (qty > 0) + { + qtyMap.put(linked.getItem(), qty); + } + qtyMap.putAll(createLinksMap(linked)); + } + + return qtyMap; + } + + private void modifierUpdated() + { + itemGrid.getPanelMap().values().forEach(GridItem::updateToolTip); + modifyPanel.setBankedItem(modifyPanel.getBankedItem()); + calculateBankedXpTotal(); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/BankedCalculatorPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/BankedCalculatorPanel.java new file mode 100644 index 0000000000..f017040e2a --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/BankedCalculatorPanel.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2019, TheStonedTurtle + * 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.skillcalculator.banked; + +import com.google.common.collect.ImmutableSet; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.event.ItemEvent; +import java.awt.image.BufferedImage; +import java.util.Map; +import javax.swing.ImageIcon; +import javax.swing.JComboBox; +import javax.swing.border.EmptyBorder; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import net.runelite.api.Skill; +import net.runelite.client.game.ItemManager; +import net.runelite.client.game.SkillIconManager; +import net.runelite.client.plugins.skillcalculator.SkillCalculatorConfig; +import net.runelite.client.plugins.skillcalculator.UICalculatorInputArea; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.PluginPanel; +import net.runelite.client.ui.components.ComboBoxIconEntry; +import net.runelite.client.ui.components.ComboBoxListRenderer; + +@Slf4j +public class BankedCalculatorPanel extends PluginPanel +{ + private final static ImmutableSet BANKABLE_SKILLS = ImmutableSet.of( + Skill.CONSTRUCTION, Skill.COOKING, Skill.CRAFTING, Skill.FARMING, Skill.HERBLORE, Skill.PRAYER, Skill.SMITHING + ); + + private final BankedCalculator calculator; + + public BankedCalculatorPanel(Client client, SkillCalculatorConfig config, SkillIconManager skillIconManager, ItemManager itemManager) + { + super(); + + setBorder(new EmptyBorder(10, 10, 10, 10)); + setLayout(new GridBagLayout()); + + final UICalculatorInputArea inputs = new UICalculatorInputArea(); + inputs.setBorder(new EmptyBorder(15, 0, 15, 0)); + inputs.setBackground(ColorScheme.DARK_GRAY_COLOR); + + inputs.getUiFieldTargetXP().setEditable(false); + inputs.getUiFieldTargetLevel().setEditable(false); + + calculator = new BankedCalculator(inputs, client, config, itemManager); + + // Create the Skill dropdown with icons + final JComboBox dropdown = new JComboBox<>(); + + final ComboBoxListRenderer renderer = new ComboBoxListRenderer(); + renderer.setDefaultText("Select a Skill..."); + dropdown.setRenderer(renderer); + + for (final Skill skill : BANKABLE_SKILLS) + { + final BufferedImage img = skillIconManager.getSkillImage(skill, true); + final ComboBoxIconEntry entry = new ComboBoxIconEntry(new ImageIcon(img), skill.getName(), skill); + dropdown.addItem(entry); + } + + // Add click event handler now to prevent above code from triggering it. + dropdown.addItemListener(e -> + { + if (e.getStateChange() == ItemEvent.SELECTED) + { + final ComboBoxIconEntry source = (ComboBoxIconEntry) e.getItem(); + if (source.getData() instanceof Skill) + { + final Skill skill = (Skill) source.getData(); + this.calculator.open(skill); + } + } + }); + + dropdown.setSelectedIndex(-1); + + GridBagConstraints c = new GridBagConstraints(); + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.gridx = 0; + c.gridy = 0; + + add(dropdown, c); + c.gridy++; + add(inputs, c); + c.gridy++; + add(calculator, c); + } + + public void setBankMap(final Map bankMap) + { + calculator.setBankMap(bankMap); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/CriticalItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/CriticalItem.java deleted file mode 100644 index 983526c59a..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/CriticalItem.java +++ /dev/null @@ -1,418 +0,0 @@ -/* - * Copyright (c) 2018, TheStonedTurtle - * 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.skillcalculator.banked; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import lombok.Getter; -import net.runelite.api.ItemDefinition; -import net.runelite.api.ItemID; -import net.runelite.api.Skill; -import net.runelite.client.game.ItemManager; - -public enum CriticalItem -{ - /** - * Construction Items - */ - // Planks - PLANK(ItemID.PLANK, "Planks", Skill.CONSTRUCTION), - OAK_PLANK(ItemID.OAK_PLANK, "Planks", Skill.CONSTRUCTION), - TEAK_PLANK(ItemID.TEAK_PLANK, "Planks", Skill.CONSTRUCTION), - MAHOGANY_PLANK(ItemID.MAHOGANY_PLANK, "Planks", Skill.CONSTRUCTION), - // Logs - LOGS(ItemID.LOGS, "Logs", Skill.CONSTRUCTION, ItemID.PLANK), - OAK_LOGS(ItemID.OAK_LOGS, "Logs", Skill.CONSTRUCTION, ItemID.OAK_PLANK), - TEAK_LOGS(ItemID.TEAK_LOGS, "Logs", Skill.CONSTRUCTION, ItemID.TEAK_PLANK), - MAHOGANY_LOGS(ItemID.MAHOGANY_LOGS, "Logs", Skill.CONSTRUCTION, ItemID.MAHOGANY_PLANK), - - /** - * Herblore Items - */ - // Grimy Herbs - GRIMY_GUAM_LEAF(ItemID.GRIMY_GUAM_LEAF, "Grimy Herbs", Skill.HERBLORE, ItemID.GUAM_LEAF), - GRIMY_MARRENTILL(ItemID.GRIMY_MARRENTILL, "Grimy Herbs", Skill.HERBLORE, ItemID.MARRENTILL), - GRIMY_TARROMIN(ItemID.GRIMY_TARROMIN, "Grimy Herbs", Skill.HERBLORE, ItemID.TARROMIN), - GRIMY_HARRALANDER(ItemID.GRIMY_HARRALANDER, "Grimy Herbs", Skill.HERBLORE, ItemID.HARRALANDER), - GRIMY_RANARR_WEED(ItemID.GRIMY_RANARR_WEED, "Grimy Herbs", Skill.HERBLORE, ItemID.RANARR_WEED), - GRIMY_TOADFLAX(ItemID.GRIMY_TOADFLAX, "Grimy Herbs", Skill.HERBLORE, ItemID.TOADFLAX), - GRIMY_IRIT_LEAF(ItemID.GRIMY_IRIT_LEAF, "Grimy Herbs", Skill.HERBLORE, ItemID.IRIT_LEAF), - GRIMY_AVANTOE(ItemID.GRIMY_AVANTOE, "Grimy Herbs", Skill.HERBLORE, ItemID.AVANTOE), - GRIMY_KWUARM(ItemID.GRIMY_KWUARM, "Grimy Herbs", Skill.HERBLORE, ItemID.KWUARM), - GRIMY_SNAPDRAGON(ItemID.GRIMY_SNAPDRAGON, "Grimy Herbs", Skill.HERBLORE, ItemID.SNAPDRAGON), - GRIMY_CADANTINE(ItemID.GRIMY_CADANTINE, "Grimy Herbs", Skill.HERBLORE, ItemID.CADANTINE), - GRIMY_LANTADYME(ItemID.GRIMY_LANTADYME, "Grimy Herbs", Skill.HERBLORE, ItemID.LANTADYME), - GRIMY_DWARF_WEED(ItemID.GRIMY_DWARF_WEED, "Grimy Herbs", Skill.HERBLORE, ItemID.DWARF_WEED), - GRIMY_TORSTOL(ItemID.GRIMY_TORSTOL, "Grimy Herbs", Skill.HERBLORE, ItemID.TORSTOL), - // Clean Herbs - GUAM_LEAF(ItemID.GUAM_LEAF, "Cleaned Herbs", Skill.HERBLORE, ItemID.GUAM_POTION_UNF), - MARRENTILL(ItemID.MARRENTILL, "Cleaned Herbs", Skill.HERBLORE, ItemID.MARRENTILL_POTION_UNF), - TARROMIN(ItemID.TARROMIN, "Cleaned Herbs", Skill.HERBLORE, ItemID.TARROMIN_POTION_UNF), - HARRALANDER(ItemID.HARRALANDER, "Cleaned Herbs", Skill.HERBLORE, ItemID.HARRALANDER_POTION_UNF), - RANARR_WEED(ItemID.RANARR_WEED, "Cleaned Herbs", Skill.HERBLORE, ItemID.RANARR_POTION_UNF), - TOADFLAX(ItemID.TOADFLAX, "Cleaned Herbs", Skill.HERBLORE, ItemID.TOADFLAX_POTION_UNF), - IRIT_LEAF(ItemID.IRIT_LEAF, "Cleaned Herbs", Skill.HERBLORE, ItemID.IRIT_POTION_UNF), - AVANTOE(ItemID.AVANTOE, "Cleaned Herbs", Skill.HERBLORE, ItemID.AVANTOE_POTION_UNF), - KWUARM(ItemID.KWUARM, "Cleaned Herbs", Skill.HERBLORE, ItemID.KWUARM_POTION_UNF), - SNAPDRAGON(ItemID.SNAPDRAGON, "Cleaned Herbs", Skill.HERBLORE, ItemID.SNAPDRAGON_POTION_UNF), - CADANTINE(ItemID.CADANTINE, "Cleaned Herbs", Skill.HERBLORE, ItemID.CADANTINE_POTION_UNF), - LANTADYME(ItemID.LANTADYME, "Cleaned Herbs", Skill.HERBLORE, ItemID.LANTADYME_POTION_UNF), - DWARF_WEED(ItemID.DWARF_WEED, "Cleaned Herbs", Skill.HERBLORE, ItemID.DWARF_WEED_POTION_UNF), - TORSTOL(ItemID.TORSTOL, "Cleaned Herbs", Skill.HERBLORE, ItemID.TORSTOL_POTION_UNF), - // Unfinished Potions - GUAM_LEAF_POTION_UNF(ItemID.GUAM_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), - MARRENTILL_POTION_UNF(ItemID.MARRENTILL_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), - TARROMIN_POTION_UNF(ItemID.TARROMIN_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), - HARRALANDER_POTION_UNF(ItemID.HARRALANDER_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), - RANARR_POTION_UNF(ItemID.RANARR_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), - TOADFLAX_POTION_UNF(ItemID.TOADFLAX_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), - IRIT_POTION_UNF(ItemID.IRIT_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), - AVANTOE_POTION_UNF(ItemID.AVANTOE_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), - KWUARM_POTION_UNF(ItemID.KWUARM_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), - SNAPDRAGON_POTION_UNF(ItemID.SNAPDRAGON_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), - CADANTINE_POTION_UNF(ItemID.CADANTINE_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), - LANTADYME_POTION_UNF(ItemID.LANTADYME_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), - DWARF_WEED_POTION_UNF(ItemID.DWARF_WEED_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), - TORSTOL_POTION_UNF(ItemID.TORSTOL_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), - - /** - * Prayer Items - */ - // Bones - BONES(ItemID.BONES, "Bones", Skill.PRAYER), - WOLF_BONES(ItemID.WOLF_BONES, "Bones", Skill.PRAYER), - BURNT_BONES(ItemID.BURNT_BONES, "Bones", Skill.PRAYER), - MONKEY_BONES(ItemID.MONKEY_BONES, "Bones", Skill.PRAYER), - BAT_BONES(ItemID.BAT_BONES, "Bones", Skill.PRAYER), - JOGRE_BONES(ItemID.JOGRE_BONES, "Bones", Skill.PRAYER), - BIG_BONES(ItemID.BIG_BONES, "Bones", Skill.PRAYER), - ZOGRE_BONES(ItemID.ZOGRE_BONES, "Bones", Skill.PRAYER), - SHAIKAHAN_BONES(ItemID.SHAIKAHAN_BONES, "Bones", Skill.PRAYER), - BABYDRAGON_BONES(ItemID.BABYDRAGON_BONES, "Bones", Skill.PRAYER), - WYVERN_BONES(ItemID.WYVERN_BONES, "Bones", Skill.PRAYER), - DRAGON_BONES(ItemID.DRAGON_BONES, "Bones", Skill.PRAYER), - FAYRG_BONES(ItemID.FAYRG_BONES, "Bones", Skill.PRAYER), - LAVA_DRAGON_BONES(ItemID.LAVA_DRAGON_BONES, "Bones", Skill.PRAYER), - RAURG_BONES(ItemID.RAURG_BONES, "Bones", Skill.PRAYER), - DAGANNOTH_BONES(ItemID.DAGANNOTH_BONES, "Bones", Skill.PRAYER), - OURG_BONES(ItemID.OURG_BONES, "Bones", Skill.PRAYER), - SUPERIOR_DRAGON_BONES(ItemID.SUPERIOR_DRAGON_BONES, "Bones", Skill.PRAYER), - // Shade Remains (Pyre Logs) - LOAR_REMAINS(ItemID.LOAR_REMAINS, "Shades", Skill.PRAYER, true), - PHRIN_REMAINS(ItemID.PHRIN_REMAINS, "Shades", Skill.PRAYER, true), - RIYL_REMAINS(ItemID.RIYL_REMAINS, "Shades", Skill.PRAYER, true), - ASYN_REMAINS(ItemID.ASYN_REMAINS, "Shades", Skill.PRAYER, true), - FIYR_REMAINS(ItemID.FIYR_REMAINS, "Shades", Skill.PRAYER, true), - // Ensouled Heads - ENSOULED_GOBLIN_HEAD(ItemID.ENSOULED_GOBLIN_HEAD_13448, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_MONKEY_HEAD(ItemID.ENSOULED_MONKEY_HEAD_13451, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_IMP_HEAD(ItemID.ENSOULED_IMP_HEAD_13454, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_MINOTAUR_HEAD(ItemID.ENSOULED_MINOTAUR_HEAD_13457, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_SCORPION_HEAD(ItemID.ENSOULED_SCORPION_HEAD_13460, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_BEAR_HEAD(ItemID.ENSOULED_BEAR_HEAD_13463, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_UNICORN_HEAD(ItemID.ENSOULED_UNICORN_HEAD_13466, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_DOG_HEAD(ItemID.ENSOULED_DOG_HEAD_13469, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_CHAOS_DRUID_HEAD(ItemID.ENSOULED_CHAOS_DRUID_HEAD_13472, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_GIANT_HEAD(ItemID.ENSOULED_GIANT_HEAD_13475, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_OGRE_HEAD(ItemID.ENSOULED_OGRE_HEAD_13478, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_ELF_HEAD(ItemID.ENSOULED_ELF_HEAD_13481, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_TROLL_HEAD(ItemID.ENSOULED_TROLL_HEAD_13484, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_HORROR_HEAD(ItemID.ENSOULED_HORROR_HEAD_13487, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_KALPHITE_HEAD(ItemID.ENSOULED_KALPHITE_HEAD_13490, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_DAGANNOTH_HEAD(ItemID.ENSOULED_DAGANNOTH_HEAD_13493, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_BLOODVELD_HEAD(ItemID.ENSOULED_BLOODVELD_HEAD_13496, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_TZHAAR_HEAD(ItemID.ENSOULED_TZHAAR_HEAD_13499, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_DEMON_HEAD(ItemID.ENSOULED_DEMON_HEAD_13502, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_AVIANSIE_HEAD(ItemID.ENSOULED_AVIANSIE_HEAD_13505, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_ABYSSAL_HEAD(ItemID.ENSOULED_ABYSSAL_HEAD_13508, "Ensouled Heads", Skill.PRAYER, true), - ENSOULED_DRAGON_HEAD(ItemID.ENSOULED_DRAGON_HEAD_13511, "Ensouled Heads", Skill.PRAYER, true), - - /** - * Cooking Items - */ - RAW_HERRING(ItemID.RAW_HERRING, "Fish", Skill.COOKING), - RAW_MACKEREL(ItemID.RAW_MACKEREL, "Fish", Skill.COOKING), - RAW_TROUT(ItemID.RAW_TROUT, "Fish", Skill.COOKING), - RAW_COD(ItemID.RAW_COD, "Fish", Skill.COOKING), - RAW_PIKE(ItemID.RAW_PIKE, "Fish", Skill.COOKING), - RAW_SALMON(ItemID.RAW_SALMON, "Fish", Skill.COOKING), - RAW_TUNA(ItemID.RAW_TUNA, "Fish", Skill.COOKING), - RAW_KARAMBWAN(ItemID.RAW_KARAMBWAN, "Fish", Skill.COOKING), - RAW_LOBSTER(ItemID.RAW_LOBSTER, "Fish", Skill.COOKING), - RAW_BASS(ItemID.RAW_BASS, "Fish", Skill.COOKING), - RAW_SWORDFISH(ItemID.RAW_SWORDFISH, "Fish", Skill.COOKING), - RAW_MONKFISH(ItemID.RAW_MONKFISH, "Fish", Skill.COOKING), - RAW_SHARK(ItemID.RAW_SHARK, "Fish", Skill.COOKING), - RAW_SEA_TURTLE(ItemID.RAW_SEA_TURTLE, "Fish", Skill.COOKING), - RAW_ANGLERFISH(ItemID.RAW_ANGLERFISH, "Fish", Skill.COOKING), - RAW_DARK_CRAB(ItemID.RAW_DARK_CRAB, "Fish", Skill.COOKING), - RAW_MANTA_RAY(ItemID.RAW_MANTA_RAY, "Fish", Skill.COOKING), - - GRAPES(ItemID.GRAPES, "Other", Skill.COOKING), - - /** - * Crafting Items - */ - WOOL(ItemID.WOOL, "Misc", Skill.CRAFTING), - FLAX(ItemID.FLAX, "Misc", Skill.CRAFTING), - MOLTEN_GLASS(ItemID.MOLTEN_GLASS, "Misc", Skill.CRAFTING), - BATTLESTAFF(ItemID.BATTLESTAFF, "Misc", Skill.CRAFTING), - - // D'hide/Dragon Leather - GREEN_DRAGONHIDE(ItemID.GREEN_DRAGONHIDE, "D'hide", Skill.CRAFTING, ItemID.GREEN_DRAGON_LEATHER), - GREEN_DRAGON_LEATHER(ItemID.GREEN_DRAGON_LEATHER, "D'hide", Skill.CRAFTING), - BLUE_DRAGONHIDE(ItemID.BLUE_DRAGONHIDE, "D'hide", Skill.CRAFTING, ItemID.BLUE_DRAGON_LEATHER), - BLUE_DRAGON_LEATHER(ItemID.BLUE_DRAGON_LEATHER, "D'hide", Skill.CRAFTING), - RED_DRAGONHIDE(ItemID.RED_DRAGONHIDE, "D'hide", Skill.CRAFTING, ItemID.RED_DRAGON_LEATHER), - RED_DRAGON_LEATHER(ItemID.RED_DRAGON_LEATHER, "D'hide", Skill.CRAFTING), - BLACK_DRAGONHIDE(ItemID.BLACK_DRAGONHIDE, "D'hide", Skill.CRAFTING, ItemID.BLACK_DRAGON_LEATHER), - BLACK_DRAGON_LEATHER(ItemID.BLACK_DRAGON_LEATHER, "D'hide", Skill.CRAFTING), - - // Uncut Gems - UNCUT_OPAL(ItemID.UNCUT_OPAL, "Gems", Skill.CRAFTING, ItemID.OPAL), - UNCUT_JADE(ItemID.UNCUT_JADE, "Gems", Skill.CRAFTING, ItemID.JADE), - UNCUT_RED_TOPAZ(ItemID.UNCUT_RED_TOPAZ, "Gems", Skill.CRAFTING, ItemID.RED_TOPAZ), - UNCUT_SAPPHIRE(ItemID.UNCUT_SAPPHIRE, "Gems", Skill.CRAFTING, ItemID.SAPPHIRE), - UNCUT_EMERALD(ItemID.UNCUT_EMERALD, "Gems", Skill.CRAFTING, ItemID.EMERALD), - UNCUT_RUBY(ItemID.UNCUT_RUBY, "Gems", Skill.CRAFTING, ItemID.RUBY), - UNCUT_DIAMOND(ItemID.UNCUT_DIAMOND, "Gems", Skill.CRAFTING, ItemID.DIAMOND), - UNCUT_DRAGONSTONE(ItemID.UNCUT_DRAGONSTONE, "Gems", Skill.CRAFTING, ItemID.DRAGONSTONE), - UNCUT_ONYX(ItemID.UNCUT_ONYX, "Gems", Skill.CRAFTING, ItemID.ONYX), - UNCUT_ZENYTE(ItemID.UNCUT_ZENYTE, "Gems", Skill.CRAFTING, ItemID.ZENYTE), - - // Cut Gems - OPAL(ItemID.OPAL, "Gems", Skill.CRAFTING), - JADE(ItemID.JADE, "Gems", Skill.CRAFTING), - RED_TOPAZ(ItemID.RED_TOPAZ, "Gems", Skill.CRAFTING), - SAPPHIRE(ItemID.SAPPHIRE, "Gems", Skill.CRAFTING), - EMERALD(ItemID.EMERALD, "Gems", Skill.CRAFTING), - RUBY(ItemID.RUBY, "Gems", Skill.CRAFTING), - DIAMOND(ItemID.DIAMOND, "Gems", Skill.CRAFTING), - DRAGONSTONE(ItemID.DRAGONSTONE, "Gems", Skill.CRAFTING), - ONYX(ItemID.ONYX, "Gems", Skill.CRAFTING), - ZENYTE(ItemID.ZENYTE, "Gems", Skill.CRAFTING), - - /** - * Smithing Items - */ - - // Ores - IRON_ORE(ItemID.IRON_ORE, "Ore", Skill.SMITHING), - SILVER_ORE(ItemID.SILVER_ORE, "Ore", Skill.SMITHING), - GOLD_ORE(ItemID.GOLD_ORE, "Ore", Skill.SMITHING), - MITHRIL_ORE(ItemID.MITHRIL_ORE, "Ore", Skill.SMITHING), - ADAMANTITE_ORE(ItemID.ADAMANTITE_ORE, "Ore", Skill.SMITHING), - RUNITE_ORE(ItemID.RUNITE_ORE, "Ore", Skill.SMITHING), - - // Bars - BRONZE_BAR(ItemID.BRONZE_BAR, "Bars", Skill.SMITHING), - IRON_BAR(ItemID.IRON_BAR, "Bars", Skill.SMITHING), - STEEL_BAR(ItemID.STEEL_BAR, "Bars", Skill.SMITHING), - MITHRIL_BAR(ItemID.MITHRIL_BAR, "Bars", Skill.SMITHING), - ADAMANTITE_BAR(ItemID.ADAMANTITE_BAR, "Bars", Skill.SMITHING), - RUNITE_BAR(ItemID.RUNITE_BAR, "Bars", Skill.SMITHING), - - /** - * Farming Items - */ - // Seeds - ACORN(ItemID.ACORN, "Seeds", Skill.FARMING), - WILLOW_SEED(ItemID.WILLOW_SEED, "Seeds", Skill.FARMING), - MAPLE_SEED(ItemID.MAPLE_SEED, "Seeds", Skill.FARMING), - YEW_SEED(ItemID.YEW_SEED, "Seeds", Skill.FARMING), - MAGIC_SEED(ItemID.MAGIC_SEED, "Seeds", Skill.FARMING), - APPLE_TREE_SEED(ItemID.APPLE_TREE_SEED, "Seeds", Skill.FARMING), - BANANA_TREE_SEED(ItemID.BANANA_TREE_SEED, "Seeds", Skill.FARMING), - ORANGE_TREE_SEED(ItemID.ORANGE_TREE_SEED, "Seeds", Skill.FARMING), - CURRY_TREE_SEED(ItemID.CURRY_TREE_SEED, "Seeds", Skill.FARMING), - PINEAPPLE_SEED(ItemID.PINEAPPLE_SEED, "Seeds", Skill.FARMING), - PAPAYA_TREE_SEED(ItemID.PAPAYA_TREE_SEED, "Seeds", Skill.FARMING), - PALM_TREE_SEED(ItemID.PALM_TREE_SEED, "Seeds", Skill.FARMING), - CALQUAT_TREE_SEED(ItemID.CALQUAT_TREE_SEED, "Seeds", Skill.FARMING), - TEAK_SEED(ItemID.TEAK_SEED, "Seeds", Skill.FARMING), - MAHOGANY_SEED(ItemID.MAHOGANY_SEED, "Seeds", Skill.FARMING), - SPIRIT_SEED(ItemID.SPIRIT_SEED, "Seeds", Skill.FARMING), - - // Saplings - OAK_SAPLING(ItemID.OAK_SAPLING, "Saplings", Skill.FARMING, ItemID.ACORN), - WILLOW_SAPLING(ItemID.WILLOW_SAPLING, "Saplings", Skill.FARMING, ItemID.WILLOW_SEED), - MAPLE_SAPLING(ItemID.MAPLE_SAPLING, "Saplings", Skill.FARMING, ItemID.MAPLE_SEED), - YEW_SAPLING(ItemID.YEW_SAPLING, "Saplings", Skill.FARMING, ItemID.YEW_SEED), - MAGIC_SAPLING(ItemID.MAGIC_SAPLING, "Saplings", Skill.FARMING, ItemID.MAGIC_SEED), - APPLE_TREE_SAPLING(ItemID.APPLE_SAPLING, "Saplings", Skill.FARMING, ItemID.APPLE_TREE_SEED), - BANANA_TREE_SAPLING(ItemID.BANANA_SAPLING, "Saplings", Skill.FARMING, ItemID.BANANA_TREE_SEED), - ORANGE_TREE_SAPLING(ItemID.ORANGE_SAPLING, "Saplings", Skill.FARMING, ItemID.ORANGE_TREE_SEED), - CURRY_TREE_SAPLING(ItemID.CURRY_SAPLING, "Saplings", Skill.FARMING, ItemID.CURRY_TREE_SEED), - PINEAPPLE_SAPLING(ItemID.PINEAPPLE_SAPLING, "Saplings", Skill.FARMING, ItemID.PINEAPPLE_SEED), - PAPAYA_TREE_SAPLING(ItemID.PAPAYA_SAPLING, "Saplings", Skill.FARMING, ItemID.PAPAYA_TREE_SEED), - PALM_TREE_SAPLING(ItemID.PALM_SAPLING, "Saplings", Skill.FARMING, ItemID.PALM_TREE_SEED), - CALQUAT_TREE_SAPLING(ItemID.CALQUAT_SAPLING, "Saplings", Skill.FARMING, ItemID.CALQUAT_TREE_SEED), - TEAK_SAPLING(ItemID.TEAK_SAPLING, "Saplings", Skill.FARMING, ItemID.TEAK_SEED), - MAHOGANY_SAPLING(ItemID.MAHOGANY_SAPLING, "Saplings", Skill.FARMING, ItemID.MAHOGANY_SEED), - SPIRIT_SAPLING(ItemID.SPIRIT_SAPLING, "Saplings", Skill.FARMING, ItemID.SPIRIT_SEED), - ; - - @Getter - private final int itemID; - @Getter - private final String category; - @Getter - private final Skill skill; - - /** - * Should be operated on and then treated like this item or does nothing if null. - * Used mostly for things like herblore where you want Grimy, Clean, and UNF to count for creating potions. - * To do this GRIMY links to CLEAN which links to UNFINISHED which links to null - */ - @Getter - private final int linkedItemId; - - @Getter - private boolean ignoreBonus; - - @Getter - private ItemDefinition definition; - - CriticalItem(int itemID, String category, Skill skill, int linkedItem) - { - this.itemID = itemID; - this.category = category; - this.skill = skill; - this.linkedItemId = linkedItem; - this.definition = null; - this.ignoreBonus = false; - } - - CriticalItem(int itemID, String category, Skill skill) - { - this(itemID, category, skill, -1); - } - - CriticalItem(int itemID, String category, Skill skill, boolean ignoreBonusXp) - { - this(itemID, category, skill, -1); - this.ignoreBonus = ignoreBonusXp; - } - - // Builds a Map to reduce looping frequency - private static Map> buildSkillItemMap() - { - Map> map = new HashMap<>(); - for (CriticalItem item : values()) - { - map.computeIfAbsent(item.getSkill(), e -> new ArrayList<>()).add(item); - } - - return map; - } - - private static final Map> bySkillName = buildSkillItemMap(); - - public static List getBySkillName(Skill skill) - { - return bySkillName.get(skill); - } - - // Builds a Map to reduce looping frequency - private static Map> buildSkillCategoryMap() - { - Map> map = new HashMap<>(); - for (CriticalItem item : values()) - { - map.computeIfAbsent(item.getSkill(), k -> new HashSet<>()).add(item.category); - } - - return map; - } - - private static final Map> bySkillCategory = buildSkillCategoryMap(); - - public static Set getSkillCategories(Skill skill) - { - return bySkillCategory.get(skill); - } - - // Builds a Map to reduce looping frequency - private static Map> buildItemSkillCategoryMap() - { - Map> map = new HashMap<>(); - for (CriticalItem item : values()) - { - String key = item.getCategory() + item.skill.getName(); - map.computeIfAbsent(key, e -> new ArrayList<>()).add(item); - } - - return map; - } - - private static final Map> itemsBySkillCategory = buildItemSkillCategoryMap(); - - public static List getItemsForSkillCategories(Skill skill, String category) - { - return itemsBySkillCategory.get(category + skill.getName()); - } - - // Builds a Map to reduce looping frequency - private static Map buildItemsByIdMap() - { - Map map = new HashMap<>(); - for (CriticalItem item : values()) - { - map.put(item.getItemID(), item); - } - - return map; - } - - private static final Map itemsById = buildItemsByIdMap(); - - public static CriticalItem getByItemId(int id) - { - return itemsById.get(id); - } - - /** - * Attaches the Item Composition to each Critical Item on client initial load - * - * @param m ItemManager - */ - public static void prepareItemDefinitions(ItemManager m) - { - for (CriticalItem i : values()) - { - i.definition = m.getItemDefinition(i.getItemID()); - } - } - - @Override - public String toString() - { - return "CriticalItem=(name=" + this.name() + ",id=" + this.itemID + ",category=" + this.category + ")"; - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/Activity.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/Activity.java index 080dad9cb9..16aee82217 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/Activity.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/Activity.java @@ -24,17 +24,19 @@ */ package net.runelite.client.plugins.skillcalculator.banked.beans; +import com.google.common.collect.ImmutableMultimap; import java.util.ArrayList; -import java.util.HashMap; +import java.util.Collection; import java.util.List; -import java.util.Map; -import lombok.AccessLevel; +import java.util.stream.Collectors; +import javax.annotation.Nullable; import lombok.Getter; +import net.runelite.api.ItemDefinition; import net.runelite.api.ItemID; import net.runelite.api.Skill; -import net.runelite.client.plugins.skillcalculator.banked.CriticalItem; +import net.runelite.client.game.ItemManager; -@Getter(AccessLevel.PUBLIC) +@Getter public enum Activity { /** @@ -42,372 +44,679 @@ public enum Activity */ // Creating Potions // Guam - GUAM_POTION_UNF(ItemID.GUAM_POTION_UNF, "Unfinished Potion", Skill.HERBLORE, 1, 0, CriticalItem.GUAM_LEAF, ActivitySecondaries.UNFINISHED_POTION), - GUAM_TAR(ItemID.GUAM_TAR, "Guam tar", Skill.HERBLORE, 19, 30, CriticalItem.GUAM_LEAF, ActivitySecondaries.SWAMP_TAR, true), + GUAM_POTION_UNF(ItemID.GUAM_POTION_UNF, "Unfinished Potion", Skill.HERBLORE, 1, 0, + CriticalItem.GUAM_LEAF, Secondaries.UNFINISHED_POTION, new ItemStack(ItemID.GUAM_POTION_UNF, 1)), + GUAM_TAR(ItemID.GUAM_TAR, "Guam tar", Skill.HERBLORE, 19, 30, + CriticalItem.GUAM_LEAF, Secondaries.SWAMP_TAR, new ItemStack(ItemID.GUAM_TAR, 15)), - ATTACK_POTION(ItemID.ATTACK_POTION4, "Attack Potion", Skill.HERBLORE, 3, 25, CriticalItem.GUAM_LEAF_POTION_UNF, ActivitySecondaries.ATTACK_POTION), + ATTACK_POTION(ItemID.ATTACK_POTION3, "Attack potion", Skill.HERBLORE, 3, 25, + CriticalItem.GUAM_LEAF_POTION_UNF, Secondaries.ATTACK_POTION, new ItemStack(ItemID.ATTACK_POTION3, 1)), // Marrentil - MARRENTILL_POTION_UNF(ItemID.MARRENTILL_POTION_UNF, "Unfinished Potion", Skill.HERBLORE, 1, 0, CriticalItem.MARRENTILL, ActivitySecondaries.UNFINISHED_POTION), - MARRENTILL_TAR(ItemID.MARRENTILL_TAR, "Marrentill tar", Skill.HERBLORE, 31, 42.5, CriticalItem.MARRENTILL, ActivitySecondaries.SWAMP_TAR, true), + MARRENTILL_POTION_UNF(ItemID.MARRENTILL_POTION_UNF, "Unfinished potion", Skill.HERBLORE, 1, 0, + CriticalItem.MARRENTILL, Secondaries.UNFINISHED_POTION, new ItemStack(ItemID.MARRENTILL_POTION_UNF, 1)), + MARRENTILL_TAR(ItemID.MARRENTILL_TAR, "Marrentill tar", Skill.HERBLORE, 31, 42.5, + CriticalItem.MARRENTILL, Secondaries.SWAMP_TAR, new ItemStack(ItemID.MARRENTILL_TAR, 15)), - ANTIPOISON(ItemID.ANTIPOISON4, "Antipoison", Skill.HERBLORE, 5, 37.5, CriticalItem.MARRENTILL_POTION_UNF, ActivitySecondaries.ANTIPOISON), + ANTIPOISON(ItemID.ANTIPOISON3, "Antipoison", Skill.HERBLORE, 5, 37.5, + CriticalItem.MARRENTILL_POTION_UNF, Secondaries.ANTIPOISON, new ItemStack(ItemID.ANTIPOISON3, 1)), // Tarromin - TARROMIN_POTION_UNF(ItemID.TARROMIN_POTION_UNF, "Unfinished Potion", Skill.HERBLORE, 1, 0, CriticalItem.TARROMIN, ActivitySecondaries.UNFINISHED_POTION), - TARROMIN_TAR(ItemID.TARROMIN_TAR, "Tarromin tar", Skill.HERBLORE, 39, 55, CriticalItem.TARROMIN, ActivitySecondaries.SWAMP_TAR, true), + TARROMIN_POTION_UNF(ItemID.TARROMIN_POTION_UNF, "Unfinished potion", Skill.HERBLORE, 1, 0, + CriticalItem.TARROMIN, Secondaries.UNFINISHED_POTION, new ItemStack(ItemID.TARROMIN_POTION_UNF, 1)), + TARROMIN_TAR(ItemID.TARROMIN_TAR, "Tarromin tar", Skill.HERBLORE, 39, 55, + CriticalItem.TARROMIN, Secondaries.SWAMP_TAR, new ItemStack(ItemID.TARROMIN_TAR, 15)), - STRENGTH_POTION(ItemID.STRENGTH_POTION4, "Strength potion", Skill.HERBLORE, 12, 50, CriticalItem.TARROMIN_POTION_UNF, ActivitySecondaries.STRENGTH_POTION), - SERUM_207(ItemID.SERUM_207_4, "Serum 207", Skill.HERBLORE, 15, 50, CriticalItem.TARROMIN_POTION_UNF, ActivitySecondaries.SERUM_207), + STRENGTH_POTION(ItemID.STRENGTH_POTION3, "Strength potion", Skill.HERBLORE, 12, 50, + CriticalItem.TARROMIN_POTION_UNF, Secondaries.STRENGTH_POTION, new ItemStack(ItemID.STRENGTH_POTION3, 1)), + SERUM_207(ItemID.SERUM_207_3, "Serum 207", Skill.HERBLORE, 15, 50, + CriticalItem.TARROMIN_POTION_UNF, Secondaries.SERUM_207, new ItemStack(ItemID.SERUM_207_3, 1)), // Harralander - HARRALANDER_POTION_UNF(ItemID.HARRALANDER_POTION_UNF, "Unfinished Potion", Skill.HERBLORE, 1, 0, CriticalItem.HARRALANDER, ActivitySecondaries.UNFINISHED_POTION), - HARRALANDER_TAR(ItemID.HARRALANDER_TAR, "Harralander tar", Skill.HERBLORE, 44, 72.5, CriticalItem.HARRALANDER, ActivitySecondaries.SWAMP_TAR, true), + HARRALANDER_POTION_UNF(ItemID.HARRALANDER_POTION_UNF, "Unfinished potion", Skill.HERBLORE, 1, 0, + CriticalItem.HARRALANDER, Secondaries.UNFINISHED_POTION, new ItemStack(ItemID.HARRALANDER_POTION_UNF, 1)), + HARRALANDER_TAR(ItemID.HARRALANDER_TAR, "Harralander tar", Skill.HERBLORE, 44, 72.5, + CriticalItem.HARRALANDER, Secondaries.SWAMP_TAR, new ItemStack(ItemID.HARRALANDER_TAR, 15)), - COMPOST_POTION(ItemID.COMPOST_POTION4, "Compost potion", Skill.HERBLORE, 21, 60, CriticalItem.HARRALANDER_POTION_UNF, ActivitySecondaries.COMPOST_POTION), - RESTORE_POTION(ItemID.RESTORE_POTION4, "Restore potion", Skill.HERBLORE, 22, 62.5, CriticalItem.HARRALANDER_POTION_UNF, ActivitySecondaries.RESTORE_POTION), - ENERGY_POTION(ItemID.ENERGY_POTION4, "Energy potion", Skill.HERBLORE, 26, 67.5, CriticalItem.HARRALANDER_POTION_UNF, ActivitySecondaries.ENERGY_POTION), - COMBAT_POTION(ItemID.COMBAT_POTION4, "Combat potion", Skill.HERBLORE, 36, 84, CriticalItem.HARRALANDER_POTION_UNF, ActivitySecondaries.COMBAT_POTION), + COMPOST_POTION(ItemID.COMPOST_POTION3, "Compost potion", Skill.HERBLORE, 21, 60, + CriticalItem.HARRALANDER_POTION_UNF, Secondaries.COMPOST_POTION, new ItemStack(ItemID.COMPOST_POTION3, 1)), + RESTORE_POTION(ItemID.RESTORE_POTION3, "Restore potion", Skill.HERBLORE, 22, 62.5, + CriticalItem.HARRALANDER_POTION_UNF, Secondaries.RESTORE_POTION, new ItemStack(ItemID.RESTORE_POTION3, 1)), + ENERGY_POTION(ItemID.ENERGY_POTION3, "Energy potion", Skill.HERBLORE, 26, 67.5, + CriticalItem.HARRALANDER_POTION_UNF, Secondaries.ENERGY_POTION, new ItemStack(ItemID.ENERGY_POTION3, 1)), + COMBAT_POTION(ItemID.COMBAT_POTION3, "Combat potion", Skill.HERBLORE, 36, 84, + CriticalItem.HARRALANDER_POTION_UNF, Secondaries.COMBAT_POTION, new ItemStack(ItemID.COMBAT_POTION3, 1)), // Ranarr Weed - DEFENCE_POTION(ItemID.DEFENCE_POTION4, "Defence potion", Skill.HERBLORE, 30, 75, CriticalItem.RANARR_POTION_UNF, ActivitySecondaries.DEFENCE_POTION), - PRAYER_POTION(ItemID.PRAYER_POTION4, "Prayer potion", Skill.HERBLORE, 38, 87.5, CriticalItem.RANARR_POTION_UNF, ActivitySecondaries.PRAYER_POTION), + RANARR_POTION_UNF(ItemID.RANARR_POTION_UNF, "Unfinished potion", Skill.HERBLORE, 30, 0, + CriticalItem.RANARR_WEED, Secondaries.UNFINISHED_POTION, new ItemStack(ItemID.RANARR_POTION_UNF, 1)), + DEFENCE_POTION(ItemID.DEFENCE_POTION3, "Defence potion", Skill.HERBLORE, 30, 75, + CriticalItem.RANARR_POTION_UNF, Secondaries.DEFENCE_POTION, new ItemStack(ItemID.DEFENCE_POTION3, 1)), + PRAYER_POTION(ItemID.PRAYER_POTION3, "Prayer potion", Skill.HERBLORE, 38, 87.5, + CriticalItem.RANARR_POTION_UNF, Secondaries.PRAYER_POTION, new ItemStack(ItemID.PRAYER_POTION3, 1)), // Toadflax - AGILITY_POTION(ItemID.AGILITY_POTION4, "Agility potion", Skill.HERBLORE, 34, 80, CriticalItem.TOADFLAX_POTION_UNF, ActivitySecondaries.AGILITY_POTION), - SARADOMIN_BREW(ItemID.SARADOMIN_BREW4, "Saradomin brew", Skill.HERBLORE, 81, 180, CriticalItem.TOADFLAX_POTION_UNF, ActivitySecondaries.SARADOMIN_BREW), + TOADFLAX_POTION_UNF(ItemID.TOADFLAX_POTION_UNF, "Unfinished potion", Skill.HERBLORE, 34, 0, + CriticalItem.TOADFLAX, Secondaries.UNFINISHED_POTION, new ItemStack(ItemID.TOADFLAX_POTION_UNF, 1)), + AGILITY_POTION(ItemID.AGILITY_POTION3, "Agility potion", Skill.HERBLORE, 34, 80, + CriticalItem.TOADFLAX_POTION_UNF, Secondaries.AGILITY_POTION, new ItemStack(ItemID.AGILITY_POTION3, 1)), + SARADOMIN_BREW(ItemID.SARADOMIN_BREW3, "Saradomin brew", Skill.HERBLORE, 81, 180, + CriticalItem.TOADFLAX_POTION_UNF, Secondaries.SARADOMIN_BREW, new ItemStack(ItemID.SARADOMIN_BREW3, 1)), // Irit - SUPER_ATTACK(ItemID.SUPER_ATTACK4, "Super attack", Skill.HERBLORE, 45, 100, CriticalItem.IRIT_POTION_UNF, ActivitySecondaries.SUPER_ATTACK), - SUPERANTIPOISON(ItemID.SUPERANTIPOISON4, "Superantipoison", Skill.HERBLORE, 48, 106.3, CriticalItem.IRIT_POTION_UNF, ActivitySecondaries.SUPERANTIPOISON), + IRIT_POTION_UNF(ItemID.IRIT_POTION_UNF, "Unfinished potion", Skill.HERBLORE, 45, 0, + CriticalItem.IRIT_LEAF, Secondaries.UNFINISHED_POTION, new ItemStack(ItemID.IRIT_POTION_UNF, 1)), + SUPER_ATTACK(ItemID.SUPER_ATTACK3, "Super attack", Skill.HERBLORE, 45, 100, + CriticalItem.IRIT_POTION_UNF, Secondaries.SUPER_ATTACK, new ItemStack(ItemID.SUPER_ATTACK3, 1)), + SUPERANTIPOISON(ItemID.SUPERANTIPOISON3, "Superantipoison", Skill.HERBLORE, 48, 106.3, + CriticalItem.IRIT_POTION_UNF, Secondaries.SUPERANTIPOISON, new ItemStack(ItemID.SUPERANTIPOISON3, 1)), // Avantoe - FISHING_POTION(ItemID.FISHING_POTION4, "Fishing potion", Skill.HERBLORE, 50, 112.5, CriticalItem.AVANTOE_POTION_UNF, ActivitySecondaries.FISHING_POTION), - SUPER_ENERGY_POTION(ItemID.SUPER_ENERGY3_20549, "Super energy potion", Skill.HERBLORE, 52, 117.5, CriticalItem.AVANTOE_POTION_UNF, ActivitySecondaries.SUPER_ENERGY_POTION), - HUNTER_POTION(ItemID.HUNTER_POTION4, "Hunter potion", Skill.HERBLORE, 53, 120, CriticalItem.AVANTOE_POTION_UNF, ActivitySecondaries.HUNTER_POTION), + AVANTOE_POTION_UNF(ItemID.AVANTOE_POTION_UNF, "Unfinished potion", Skill.HERBLORE, 50, 0, + CriticalItem.AVANTOE, Secondaries.UNFINISHED_POTION, new ItemStack(ItemID.AVANTOE_POTION_UNF, 1)), + FISHING_POTION(ItemID.FISHING_POTION3, "Fishing potion", Skill.HERBLORE, 50, 112.5, + CriticalItem.AVANTOE_POTION_UNF, Secondaries.FISHING_POTION, new ItemStack(ItemID.FISHING_POTION3, 1)), + SUPER_ENERGY_POTION(ItemID.SUPER_ENERGY3_20549, "Super energy potion", Skill.HERBLORE, 52, 117.5, + CriticalItem.AVANTOE_POTION_UNF, Secondaries.SUPER_ENERGY_POTION, new ItemStack(ItemID.SUPER_ENERGY3_20549, 1)), + HUNTER_POTION(ItemID.HUNTER_POTION3, "Hunter potion", Skill.HERBLORE, 53, 120, + CriticalItem.AVANTOE_POTION_UNF, Secondaries.HUNTER_POTION, new ItemStack(ItemID.HUNTER_POTION3, 1)), // Kwuarm - SUPER_STRENGTH(ItemID.SUPER_STRENGTH4, "Super strength", Skill.HERBLORE, 55, 125, CriticalItem.KWUARM_POTION_UNF, ActivitySecondaries.SUPER_STRENGTH), + KWUARM_POTION_UNF(ItemID.KWUARM_POTION_UNF, "Unfinished potion", Skill.HERBLORE, 55, 0, + CriticalItem.KWUARM, Secondaries.UNFINISHED_POTION, new ItemStack(ItemID.KWUARM_POTION_UNF, 1)), + SUPER_STRENGTH(ItemID.SUPER_STRENGTH3, "Super strength", Skill.HERBLORE, 55, 125, + CriticalItem.KWUARM_POTION_UNF, Secondaries.SUPER_STRENGTH, new ItemStack(ItemID.SUPER_STRENGTH3, 1)), // Snapdragon - SUPER_RESTORE(ItemID.SUPER_RESTORE4, "Super restore", Skill.HERBLORE, 63, 142.5, CriticalItem.SNAPDRAGON_POTION_UNF, ActivitySecondaries.SUPER_RESTORE), - SANFEW_SERUM(ItemID.SANFEW_SERUM4, "Sanfew serum", Skill.HERBLORE, 65, 160, CriticalItem.SNAPDRAGON_POTION_UNF, ActivitySecondaries.SANFEW_SERUM), + SNAPDRAGON_POTION_UNF(ItemID.SNAPDRAGON_POTION_UNF, "Unfinished potion", Skill.HERBLORE, 63, 0, + CriticalItem.SNAPDRAGON, Secondaries.UNFINISHED_POTION, new ItemStack(ItemID.SNAPDRAGON_POTION_UNF, 1)), + SUPER_RESTORE(ItemID.SUPER_RESTORE3, "Super restore", Skill.HERBLORE, 63, 142.5, + CriticalItem.SNAPDRAGON_POTION_UNF, Secondaries.SUPER_RESTORE, new ItemStack(ItemID.SUPER_RESTORE3, 1)), + SANFEW_SERUM(ItemID.SANFEW_SERUM3, "Sanfew serum", Skill.HERBLORE, 65, 160, + CriticalItem.SNAPDRAGON_POTION_UNF, Secondaries.SANFEW_SERUM, new ItemStack(ItemID.SANFEW_SERUM3, 1)), // Cadantine - SUPER_DEFENCE_POTION(ItemID.SUPER_DEFENCE4, "Super defence", Skill.HERBLORE, 66, 150, CriticalItem.CADANTINE_POTION_UNF, ActivitySecondaries.SUPER_DEFENCE_POTION), + CADANTINE_POTION_UNF(ItemID.CADANTINE_POTION_UNF, "Unfinished potion", Skill.HERBLORE, 66, 0, + CriticalItem.CADANTINE, Secondaries.UNFINISHED_POTION, new ItemStack(ItemID.CADANTINE_POTION_UNF, 1)), + SUPER_DEFENCE_POTION(ItemID.SUPER_DEFENCE3, "Super defence", Skill.HERBLORE, 66, 150, + CriticalItem.CADANTINE_POTION_UNF, Secondaries.SUPER_DEFENCE_POTION, new ItemStack(ItemID.SUPER_DEFENCE3, 1)), // Lantadyme - ANTIFIRE_POTION(ItemID.ANTIFIRE_POTION4, "Anti-fire potion", Skill.HERBLORE, 69, 157.5, CriticalItem.LANTADYME_POTION_UNF, ActivitySecondaries.ANTIFIRE_POTION), - MAGIC_POTION(ItemID.MAGIC_POTION4, "Magic potion", Skill.HERBLORE, 76, 172.5, CriticalItem.LANTADYME_POTION_UNF, ActivitySecondaries.MAGIC_POTION), + LANTADYME_POTION_UNF(ItemID.LANTADYME_POTION_UNF, "Unfinished potion", Skill.HERBLORE, 69, 0, + CriticalItem.LANTADYME, Secondaries.UNFINISHED_POTION, new ItemStack(ItemID.LANTADYME_POTION_UNF, 1)), + ANTIFIRE_POTION(ItemID.ANTIFIRE_POTION3, "Anti-fire potion", Skill.HERBLORE, 69, 157.5, + CriticalItem.LANTADYME_POTION_UNF, Secondaries.ANTIFIRE_POTION, new ItemStack(ItemID.ANTIFIRE_POTION3, 1)), + MAGIC_POTION(ItemID.MAGIC_POTION3, "Magic potion", Skill.HERBLORE, 76, 172.5, + CriticalItem.LANTADYME_POTION_UNF, Secondaries.MAGIC_POTION, new ItemStack(ItemID.MAGIC_POTION3, 1)), // Dwarf Weed - RANGING_POTION(ItemID.RANGING_POTION4, "Ranging potion", Skill.HERBLORE, 72, 162.5, CriticalItem.DWARF_WEED_POTION_UNF, ActivitySecondaries.RANGING_POTION), + DWARF_WEED_POTION_UNF(ItemID.DWARF_WEED_POTION_UNF, "Unfinished potion", Skill.HERBLORE, 72, 0, + CriticalItem.DWARF_WEED, Secondaries.UNFINISHED_POTION, new ItemStack(ItemID.DWARF_WEED_POTION_UNF, 1)), + RANGING_POTION(ItemID.RANGING_POTION3, "Ranging potion", Skill.HERBLORE, 72, 162.5, + CriticalItem.DWARF_WEED_POTION_UNF, Secondaries.RANGING_POTION, new ItemStack(ItemID.RANGING_POTION3, 1)), // Torstol - TORSTOL_POTION_UNF(ItemID.TORSTOL_POTION_UNF, "Unfinished Potion", Skill.HERBLORE, 78, 0, CriticalItem.TORSTOL, ActivitySecondaries.UNFINISHED_POTION), - SUPER_COMBAT_POTION(ItemID.SUPER_COMBAT_POTION4, "Super combat", Skill.HERBLORE, 90, 150, CriticalItem.TORSTOL, ActivitySecondaries.SUPER_COMBAT_POTION, true), - ANTIVENOM_PLUS(ItemID.ANTIVENOM4_12913, "Anti-venom+", Skill.HERBLORE, 94, 125, CriticalItem.TORSTOL, ActivitySecondaries.ANTIVENOM_PLUS, true), + TORSTOL_POTION_UNF(ItemID.TORSTOL_POTION_UNF, "Unfinished potion", Skill.HERBLORE, 78, 0, + CriticalItem.TORSTOL, Secondaries.UNFINISHED_POTION, new ItemStack(ItemID.TORSTOL_POTION_UNF, 1)), + SUPER_COMBAT_POTION(ItemID.SUPER_COMBAT_POTION4, "Super combat", Skill.HERBLORE, 90, 150, + CriticalItem.TORSTOL, Secondaries.SUPER_COMBAT_POTION, new ItemStack(ItemID.SUPER_COMBAT_POTION4, 1)), + ANTIVENOM_PLUS(ItemID.ANTIVENOM3_12915, "Anti-venom+", Skill.HERBLORE, 94, 125, + CriticalItem.TORSTOL, Secondaries.ANTIVENOM_PLUS, new ItemStack(ItemID.ANTIVENOM3_12915, 1)), - ZAMORAK_BREW(ItemID.ZAMORAK_BREW4, "Zamorak brew", Skill.HERBLORE, 78, 175, CriticalItem.TORSTOL_POTION_UNF, ActivitySecondaries.ZAMORAK_BREW), + ZAMORAK_BREW(ItemID.ZAMORAK_BREW3, "Zamorak brew", Skill.HERBLORE, 78, 175, + CriticalItem.TORSTOL_POTION_UNF, Secondaries.ZAMORAK_BREW, new ItemStack(ItemID.ZAMORAK_BREW3, 1)), // Cleaning Grimy Herbs - CLEAN_GUAM(ItemID.GUAM_LEAF, "Clean guam", Skill.HERBLORE, 3, 2.5, CriticalItem.GRIMY_GUAM_LEAF), - CLEAN_MARRENTILL(ItemID.MARRENTILL, "Clean marrentill", Skill.HERBLORE, 5, 3.8, CriticalItem.GRIMY_MARRENTILL), - CLEAN_TARROMIN(ItemID.TARROMIN, "Clean tarromin", Skill.HERBLORE, 11, 5, CriticalItem.GRIMY_TARROMIN), - CLEAN_HARRALANDER(ItemID.HARRALANDER, "Clean harralander", Skill.HERBLORE, 20, 6.3, CriticalItem.GRIMY_HARRALANDER), - CLEAN_RANARR_WEED(ItemID.RANARR_WEED, "Clean ranarr weed", Skill.HERBLORE, 25, 7.5, CriticalItem.GRIMY_RANARR_WEED), - CLEAN_TOADFLAX(ItemID.TOADFLAX, "Clean toadflax", Skill.HERBLORE, 30, 8, CriticalItem.GRIMY_TOADFLAX), - CLEAN_IRIT_LEAF(ItemID.IRIT_LEAF, "Clean irit leaf", Skill.HERBLORE, 40, 8.8, CriticalItem.GRIMY_IRIT_LEAF), - CLEAN_AVANTOE(ItemID.AVANTOE, "Clean avantoe", Skill.HERBLORE, 48, 10, CriticalItem.GRIMY_AVANTOE), - CLEAN_KWUARM(ItemID.KWUARM, "Clean kwuarm", Skill.HERBLORE, 54, 11.3, CriticalItem.GRIMY_KWUARM), - CLEAN_SNAPDRAGON(ItemID.SNAPDRAGON, "Clean snapdragon", Skill.HERBLORE, 59, 11.8, CriticalItem.GRIMY_SNAPDRAGON), - CLEAN_CADANTINE(ItemID.CADANTINE, "Clean cadantine", Skill.HERBLORE, 65, 12.5, CriticalItem.GRIMY_CADANTINE), - CLEAN_LANTADYME(ItemID.LANTADYME, "Clean lantadyme", Skill.HERBLORE, 67, 13.1, CriticalItem.GRIMY_LANTADYME), - CLEAN_DWARF_WEED(ItemID.DWARF_WEED, "Clean dwarf weed", Skill.HERBLORE, 70, 13.8, CriticalItem.GRIMY_DWARF_WEED), - CLEAN_TORSTOL(ItemID.TORSTOL, "Clean torstol", Skill.HERBLORE, 75, 15, CriticalItem.GRIMY_TORSTOL), + CLEAN_GUAM(ItemID.GUAM_LEAF, "Clean guam", Skill.HERBLORE, 3, 2.5, + CriticalItem.GRIMY_GUAM_LEAF, null, new ItemStack(ItemID.GUAM_LEAF, 1)), + CLEAN_MARRENTILL(ItemID.MARRENTILL, "Clean marrentill", Skill.HERBLORE, 5, 3.8, + CriticalItem.GRIMY_MARRENTILL, null, new ItemStack(ItemID.MARRENTILL, 1)), + CLEAN_TARROMIN(ItemID.TARROMIN, "Clean tarromin", Skill.HERBLORE, 11, 5, + CriticalItem.GRIMY_TARROMIN, null, new ItemStack(ItemID.TARROMIN, 1)), + CLEAN_HARRALANDER(ItemID.HARRALANDER, "Clean harralander", Skill.HERBLORE, 20, 6.3, + CriticalItem.GRIMY_HARRALANDER, null, new ItemStack(ItemID.HARRALANDER, 1)), + CLEAN_RANARR_WEED(ItemID.RANARR_WEED, "Clean ranarr weed", Skill.HERBLORE, 25, 7.5, + CriticalItem.GRIMY_RANARR_WEED, null, new ItemStack(ItemID.RANARR_WEED, 1)), + CLEAN_TOADFLAX(ItemID.TOADFLAX, "Clean toadflax", Skill.HERBLORE, 30, 8, + CriticalItem.GRIMY_TOADFLAX, null, new ItemStack(ItemID.TOADFLAX, 1)), + CLEAN_IRIT_LEAF(ItemID.IRIT_LEAF, "Clean irit leaf", Skill.HERBLORE, 40, 8.8, + CriticalItem.GRIMY_IRIT_LEAF, null, new ItemStack(ItemID.IRIT_LEAF, 1)), + CLEAN_AVANTOE(ItemID.AVANTOE, "Clean avantoe", Skill.HERBLORE, 48, 10, + CriticalItem.GRIMY_AVANTOE, null, new ItemStack(ItemID.AVANTOE, 1)), + CLEAN_KWUARM(ItemID.KWUARM, "Clean kwuarm", Skill.HERBLORE, 54, 11.3, + CriticalItem.GRIMY_KWUARM, null, new ItemStack(ItemID.KWUARM, 1)), + CLEAN_SNAPDRAGON(ItemID.SNAPDRAGON, "Clean snapdragon", Skill.HERBLORE, 59, 11.8, + CriticalItem.GRIMY_SNAPDRAGON, null, new ItemStack(ItemID.SNAPDRAGON, 1)), + CLEAN_CADANTINE(ItemID.CADANTINE, "Clean cadantine", Skill.HERBLORE, 65, 12.5, + CriticalItem.GRIMY_CADANTINE, null, new ItemStack(ItemID.CADANTINE, 1)), + CLEAN_LANTADYME(ItemID.LANTADYME, "Clean lantadyme", Skill.HERBLORE, 67, 13.1, + CriticalItem.GRIMY_LANTADYME, null, new ItemStack(ItemID.LANTADYME, 1)), + CLEAN_DWARF_WEED(ItemID.DWARF_WEED, "Clean dwarf weed", Skill.HERBLORE, 70, 13.8, + CriticalItem.GRIMY_DWARF_WEED, null, new ItemStack(ItemID.DWARF_WEED, 1)), + CLEAN_TORSTOL(ItemID.TORSTOL, "Clean torstol", Skill.HERBLORE, 75, 15, + CriticalItem.GRIMY_TORSTOL, null, new ItemStack(ItemID.TORSTOL, 1)), /** * Construction Options */ - PLANKS(ItemID.PLANK, "Normal Plank Products", Skill.CONSTRUCTION, 1, 29, CriticalItem.PLANK), - OAK_PLANKS(ItemID.OAK_PLANK, "Normal Oak Products", Skill.CONSTRUCTION, 1, 60, CriticalItem.OAK_PLANK), - TEAK_PLANKS(ItemID.TEAK_PLANK, "Normal Teak Products", Skill.CONSTRUCTION, 1, 90, CriticalItem.TEAK_PLANK), - MYTHICAL_CAPE(ItemID.MYTHICAL_CAPE, "Mythical Cape Rakes", Skill.CONSTRUCTION, 1, 123.33, CriticalItem.TEAK_PLANK), - MAHOGANY_PLANKS(ItemID.MAHOGANY_PLANK, "Normal Mahogany Products", Skill.CONSTRUCTION, 1, 140, CriticalItem.MAHOGANY_PLANK), + PLANK(ItemID.PLANK, "Regular Plank", Skill.CONSTRUCTION, 1, 0, + CriticalItem.LOGS, Secondaries.COINS_100, new ItemStack(ItemID.PLANK, 1)), + PLANKS(ItemID.PLANK, "Regular plank products", Skill.CONSTRUCTION, 1, 29, + CriticalItem.PLANK, null, null), + + OAK_PLANK(ItemID.OAK_PLANK, "Oak Plank", Skill.CONSTRUCTION, 1, 0, + CriticalItem.OAK_LOGS, Secondaries.COINS_250, new ItemStack(ItemID.OAK_PLANK, 1)), + OAK_PLANKS(ItemID.OAK_PLANK, "Oak products", Skill.CONSTRUCTION, 1, 60, + CriticalItem.OAK_PLANK, null, null), + + TEAK_PLANK(ItemID.TEAK_PLANK, "Teak Plank", Skill.CONSTRUCTION, 1, 0, + CriticalItem.TEAK_LOGS, Secondaries.COINS_500, new ItemStack(ItemID.TEAK_PLANK, 1)), + TEAK_PLANKS(ItemID.TEAK_PLANK, "Teak products", Skill.CONSTRUCTION, 1, 90, + CriticalItem.TEAK_PLANK, null, null), + MYTHICAL_CAPE(ItemID.MYTHICAL_CAPE, "Mythical cape rakes", Skill.CONSTRUCTION, 1, 123.33, + CriticalItem.TEAK_PLANK, null, null), + + + MAHOGANY_PLANK(ItemID.MAHOGANY_PLANK, "Mahogany Plank", Skill.CONSTRUCTION, 1, 0, + CriticalItem.MAHOGANY_LOGS, Secondaries.COINS_1500, new ItemStack(ItemID.MAHOGANY_PLANK, 1)), + MAHOGANY_PLANKS(ItemID.MAHOGANY_PLANK, "Mahogany products", Skill.CONSTRUCTION, 1, 140, + CriticalItem.MAHOGANY_PLANK, null, null), /** * Prayer Options */ - BONES(ItemID.BONES, "Bones", Skill.PRAYER, 1, 4.5, CriticalItem.BONES), - WOLF_BONES(ItemID.WOLF_BONES, "Bones", Skill.PRAYER, 1, 4.5, CriticalItem.WOLF_BONES), - BURNT_BONES(ItemID.BURNT_BONES, "Bones", Skill.PRAYER, 1, 4.5, CriticalItem.BURNT_BONES), - MONKEY_BONES(ItemID.MONKEY_BONES, "Bones", Skill.PRAYER, 1, 5.0, CriticalItem.MONKEY_BONES), - BAT_BONES(ItemID.BAT_BONES, "Bones", Skill.PRAYER, 1, 5.3, CriticalItem.BAT_BONES), - JOGRE_BONES(ItemID.JOGRE_BONES, "Bones", Skill.PRAYER, 1, 15.0, CriticalItem.JOGRE_BONES), - BIG_BONES(ItemID.BIG_BONES, "Bones", Skill.PRAYER, 1, 15.0, CriticalItem.BIG_BONES), - ZOGRE_BONES(ItemID.ZOGRE_BONES, "Bones", Skill.PRAYER, 1, 22.5, CriticalItem.ZOGRE_BONES), - SHAIKAHAN_BONES(ItemID.SHAIKAHAN_BONES, "Bones", Skill.PRAYER, 1, 25.0, CriticalItem.SHAIKAHAN_BONES), - BABYDRAGON_BONES(ItemID.BABYDRAGON_BONES, "Bones", Skill.PRAYER, 1, 30.0, CriticalItem.BABYDRAGON_BONES), - WYVERN_BONES(ItemID.WYVERN_BONES, "Bones", Skill.PRAYER, 1, 72.0, CriticalItem.WYVERN_BONES), - DRAGON_BONES(ItemID.DRAGON_BONES, "Bones", Skill.PRAYER, 1, 72.0, CriticalItem.DRAGON_BONES), - FAYRG_BONES(ItemID.FAYRG_BONES, "Bones", Skill.PRAYER, 1, 84.0, CriticalItem.FAYRG_BONES), - LAVA_DRAGON_BONES(ItemID.LAVA_DRAGON_BONES, "Bones", Skill.PRAYER, 1, 85.0, CriticalItem.LAVA_DRAGON_BONES), - RAURG_BONES(ItemID.RAURG_BONES, "Bones", Skill.PRAYER, 1, 96.0, CriticalItem.RAURG_BONES), - DAGANNOTH_BONES(ItemID.DAGANNOTH_BONES, "Bones", Skill.PRAYER, 1, 125.0, CriticalItem.DAGANNOTH_BONES), - OURG_BONES(ItemID.OURG_BONES, "Bones", Skill.PRAYER, 1, 140.0, CriticalItem.OURG_BONES), - SUPERIOR_DRAGON_BONES(ItemID.SUPERIOR_DRAGON_BONES, "Bones", Skill.PRAYER, 1, 150.0, CriticalItem.SUPERIOR_DRAGON_BONES), + BONES(ItemID.BONES, "Bones", Skill.PRAYER, 1, 4.5, + CriticalItem.BONES, null, null), + WOLF_BONES(ItemID.WOLF_BONES, "Wolf bones", Skill.PRAYER, 1, 4.5, + CriticalItem.WOLF_BONES, null, null), + BURNT_BONES(ItemID.BURNT_BONES, "Burnt bones", Skill.PRAYER, 1, 4.5, + CriticalItem.BURNT_BONES, null, null), + MONKEY_BONES(ItemID.MONKEY_BONES, "Monkey bones", Skill.PRAYER, 1, 5.0, + CriticalItem.MONKEY_BONES, null, null), + BAT_BONES(ItemID.BAT_BONES, "Bat bones", Skill.PRAYER, 1, 5.3, + CriticalItem.BAT_BONES, null, null), + JOGRE_BONES(ItemID.JOGRE_BONES, "Jogre bones", Skill.PRAYER, 1, 15.0, + CriticalItem.JOGRE_BONES, null, null), + BIG_BONES(ItemID.BIG_BONES, "Big bones", Skill.PRAYER, 1, 15.0, + CriticalItem.BIG_BONES, null, null), + ZOGRE_BONES(ItemID.ZOGRE_BONES, "Zogre bones", Skill.PRAYER, 1, 22.5, + CriticalItem.ZOGRE_BONES, null, null), + SHAIKAHAN_BONES(ItemID.SHAIKAHAN_BONES, "Shaikahan bones", Skill.PRAYER, 1, 25.0, + CriticalItem.SHAIKAHAN_BONES, null, null), + BABYDRAGON_BONES(ItemID.BABYDRAGON_BONES, "Babydragon bones", Skill.PRAYER, 1, 30.0, + CriticalItem.BABYDRAGON_BONES, null, null), + WYVERN_BONES(ItemID.WYVERN_BONES, "Wyvern bones", Skill.PRAYER, 1, 72.0, + CriticalItem.WYVERN_BONES, null, null), + DRAGON_BONES(ItemID.DRAGON_BONES, "Dragon bones", Skill.PRAYER, 1, 72.0, + CriticalItem.DRAGON_BONES, null, null), + FAYRG_BONES(ItemID.FAYRG_BONES, "Fayrg bones", Skill.PRAYER, 1, 84.0, + CriticalItem.FAYRG_BONES, null, null), + LAVA_DRAGON_BONES(ItemID.LAVA_DRAGON_BONES, "Lava dragon bones", Skill.PRAYER, 1, 85.0, + CriticalItem.LAVA_DRAGON_BONES, null, null), + RAURG_BONES(ItemID.RAURG_BONES, "Raurg bones", Skill.PRAYER, 1, 96.0, + CriticalItem.RAURG_BONES, null, null), + DAGANNOTH_BONES(ItemID.DAGANNOTH_BONES, "Dagannoth bones", Skill.PRAYER, 1, 125.0, + CriticalItem.DAGANNOTH_BONES, null, null), + OURG_BONES(ItemID.OURG_BONES, "Ourg bones", Skill.PRAYER, 1, 140.0, + CriticalItem.OURG_BONES, null, null), + SUPERIOR_DRAGON_BONES(ItemID.SUPERIOR_DRAGON_BONES, "Superior dragon bones", Skill.PRAYER, 1, 150.0, + CriticalItem.SUPERIOR_DRAGON_BONES, null, null), // Shade Remains (Pyre Logs) - LOAR_REMAINS(ItemID.LOAR_REMAINS, "Shades", Skill.PRAYER, 1, 33.0, CriticalItem.LOAR_REMAINS), - PHRIN_REMAINS(ItemID.PHRIN_REMAINS, "Shades", Skill.PRAYER, 1, 46.5, CriticalItem.PHRIN_REMAINS), - RIYL_REMAINS(ItemID.RIYL_REMAINS, "Shades", Skill.PRAYER, 1, 59.5, CriticalItem.RIYL_REMAINS), - ASYN_REMAINS(ItemID.ASYN_REMAINS, "Shades", Skill.PRAYER, 1, 82.5, CriticalItem.ASYN_REMAINS), - FIYR_REMAINS(ItemID.FIYR_REMAINS, "Shades", Skill.PRAYER, 1, 84.0, CriticalItem.FIYR_REMAINS), + LOAR_REMAINS(ItemID.LOAR_REMAINS, "Loar remains", Skill.PRAYER, 1, 33.0, + CriticalItem.LOAR_REMAINS, null, null), + PHRIN_REMAINS(ItemID.PHRIN_REMAINS, "Phrin remains", Skill.PRAYER, 1, 46.5, + CriticalItem.PHRIN_REMAINS, null, null), + RIYL_REMAINS(ItemID.RIYL_REMAINS, "Riyl remains", Skill.PRAYER, 1, 59.5, + CriticalItem.RIYL_REMAINS, null, null), + ASYN_REMAINS(ItemID.ASYN_REMAINS, "Asyn remains", Skill.PRAYER, 1, 82.5, + CriticalItem.ASYN_REMAINS, null, null), + FIYR_REMAINS(ItemID.FIYR_REMAINS, "Fiyre remains", Skill.PRAYER, 1, 84.0, + CriticalItem.FIYR_REMAINS, null, null), // Ensouled Heads - ENSOULED_GOBLIN_HEAD(ItemID.ENSOULED_GOBLIN_HEAD_13448, "Ensouled Heads", Skill.PRAYER, 1, 130.0, CriticalItem.ENSOULED_GOBLIN_HEAD), - ENSOULED_MONKEY_HEAD(ItemID.ENSOULED_MONKEY_HEAD_13451, "Ensouled Heads", Skill.PRAYER, 1, 182.0, CriticalItem.ENSOULED_MONKEY_HEAD), - ENSOULED_IMP_HEAD(ItemID.ENSOULED_IMP_HEAD_13454, "Ensouled Heads", Skill.PRAYER, 1, 286.0, CriticalItem.ENSOULED_IMP_HEAD), - ENSOULED_MINOTAUR_HEAD(ItemID.ENSOULED_MINOTAUR_HEAD_13457, "Ensouled Heads", Skill.PRAYER, 1, 364.0, CriticalItem.ENSOULED_MINOTAUR_HEAD), - ENSOULED_SCORPION_HEAD(ItemID.ENSOULED_SCORPION_HEAD_13460, "Ensouled Heads", Skill.PRAYER, 1, 454.0, CriticalItem.ENSOULED_SCORPION_HEAD), - ENSOULED_BEAR_HEAD(ItemID.ENSOULED_BEAR_HEAD_13463, "Ensouled Heads", Skill.PRAYER, 1, 480.0, CriticalItem.ENSOULED_BEAR_HEAD), - ENSOULED_UNICORN_HEAD(ItemID.ENSOULED_UNICORN_HEAD_13466, "Ensouled Heads", Skill.PRAYER, 1, 494.0, CriticalItem.ENSOULED_UNICORN_HEAD), - ENSOULED_DOG_HEAD(ItemID.ENSOULED_DOG_HEAD_13469, "Ensouled Heads", Skill.PRAYER, 1, 520.0, CriticalItem.ENSOULED_DOG_HEAD), - ENSOULED_CHAOS_DRUID_HEAD(ItemID.ENSOULED_CHAOS_DRUID_HEAD_13472, "Ensouled Heads", Skill.PRAYER, 1, 584.0, CriticalItem.ENSOULED_CHAOS_DRUID_HEAD), - ENSOULED_GIANT_HEAD(ItemID.ENSOULED_GIANT_HEAD_13475, "Ensouled Heads", Skill.PRAYER, 1, 650.0, CriticalItem.ENSOULED_GIANT_HEAD), - ENSOULED_OGRE_HEAD(ItemID.ENSOULED_OGRE_HEAD_13478, "Ensouled Heads", Skill.PRAYER, 1, 716.0, CriticalItem.ENSOULED_OGRE_HEAD), - ENSOULED_ELF_HEAD(ItemID.ENSOULED_ELF_HEAD_13481, "Ensouled Heads", Skill.PRAYER, 1, 754.0, CriticalItem.ENSOULED_ELF_HEAD), - ENSOULED_TROLL_HEAD(ItemID.ENSOULED_TROLL_HEAD_13484, "Ensouled Heads", Skill.PRAYER, 1, 780.0, CriticalItem.ENSOULED_TROLL_HEAD), - ENSOULED_HORROR_HEAD(ItemID.ENSOULED_HORROR_HEAD_13487, "Ensouled Heads", Skill.PRAYER, 1, 832.0, CriticalItem.ENSOULED_HORROR_HEAD), - ENSOULED_KALPHITE_HEAD(ItemID.ENSOULED_KALPHITE_HEAD_13490, "Ensouled Heads", Skill.PRAYER, 1, 884.0, CriticalItem.ENSOULED_KALPHITE_HEAD), - ENSOULED_DAGANNOTH_HEAD(ItemID.ENSOULED_DAGANNOTH_HEAD_13493, "Ensouled Heads", Skill.PRAYER, 1, 936.0, CriticalItem.ENSOULED_DAGANNOTH_HEAD), - ENSOULED_BLOODVELD_HEAD(ItemID.ENSOULED_BLOODVELD_HEAD_13496, "Ensouled Heads", Skill.PRAYER, 1, 1040.0, CriticalItem.ENSOULED_BLOODVELD_HEAD), - ENSOULED_TZHAAR_HEAD(ItemID.ENSOULED_TZHAAR_HEAD_13499, "Ensouled Heads", Skill.PRAYER, 1, 1104.0, CriticalItem.ENSOULED_TZHAAR_HEAD), - ENSOULED_DEMON_HEAD(ItemID.ENSOULED_DEMON_HEAD_13502, "Ensouled Heads", Skill.PRAYER, 1, 1170.0, CriticalItem.ENSOULED_DEMON_HEAD), - ENSOULED_AVIANSIE_HEAD(ItemID.ENSOULED_AVIANSIE_HEAD_13505, "Ensouled Heads", Skill.PRAYER, 1, 1234.0, CriticalItem.ENSOULED_AVIANSIE_HEAD), - ENSOULED_ABYSSAL_HEAD(ItemID.ENSOULED_ABYSSAL_HEAD_13508, "Ensouled Heads", Skill.PRAYER, 1, 1300.0, CriticalItem.ENSOULED_ABYSSAL_HEAD), - ENSOULED_DRAGON_HEAD(ItemID.ENSOULED_DRAGON_HEAD_13511, "Ensouled Heads", Skill.PRAYER, 1, 1560.0, CriticalItem.ENSOULED_DRAGON_HEAD), + ENSOULED_GOBLIN_HEAD(ItemID.ENSOULED_GOBLIN_HEAD_13448, "Ensouled goblin head", Skill.PRAYER, 1, 130.0, + CriticalItem.ENSOULED_GOBLIN_HEAD, null, null), + ENSOULED_MONKEY_HEAD(ItemID.ENSOULED_MONKEY_HEAD_13451, "Ensouled monkey head", Skill.PRAYER, 1, 182.0, + CriticalItem.ENSOULED_MONKEY_HEAD, null, null), + ENSOULED_IMP_HEAD(ItemID.ENSOULED_IMP_HEAD_13454, "Ensouled imp head", Skill.PRAYER, 1, 286.0, + CriticalItem.ENSOULED_IMP_HEAD, null, null), + ENSOULED_MINOTAUR_HEAD(ItemID.ENSOULED_MINOTAUR_HEAD_13457, "Ensouled minotaur head", Skill.PRAYER, 1, 364.0, + CriticalItem.ENSOULED_MINOTAUR_HEAD, null, null), + ENSOULED_SCORPION_HEAD(ItemID.ENSOULED_SCORPION_HEAD_13460, "Ensouled scorpion head", Skill.PRAYER, 1, 454.0, + CriticalItem.ENSOULED_SCORPION_HEAD, null, null), + ENSOULED_BEAR_HEAD(ItemID.ENSOULED_BEAR_HEAD_13463, "Ensouled bear head", Skill.PRAYER, 1, 480.0, + CriticalItem.ENSOULED_BEAR_HEAD, null, null), + ENSOULED_UNICORN_HEAD(ItemID.ENSOULED_UNICORN_HEAD_13466, "Ensouled unicorn head", Skill.PRAYER, 1, 494.0, + CriticalItem.ENSOULED_UNICORN_HEAD, null, null), + ENSOULED_DOG_HEAD(ItemID.ENSOULED_DOG_HEAD_13469, "Ensouled dog head", Skill.PRAYER, 1, 520.0, + CriticalItem.ENSOULED_DOG_HEAD, null, null), + ENSOULED_CHAOS_DRUID_HEAD(ItemID.ENSOULED_CHAOS_DRUID_HEAD_13472, "Ensouled druid head", Skill.PRAYER, 1, 584.0, + CriticalItem.ENSOULED_CHAOS_DRUID_HEAD, null, null), + ENSOULED_GIANT_HEAD(ItemID.ENSOULED_GIANT_HEAD_13475, "Ensouled giant head", Skill.PRAYER, 1, 650.0, + CriticalItem.ENSOULED_GIANT_HEAD, null, null), + ENSOULED_OGRE_HEAD(ItemID.ENSOULED_OGRE_HEAD_13478, "Ensouled ogre head", Skill.PRAYER, 1, 716.0, + CriticalItem.ENSOULED_OGRE_HEAD, null, null), + ENSOULED_ELF_HEAD(ItemID.ENSOULED_ELF_HEAD_13481, "Ensouled elf head", Skill.PRAYER, 1, 754.0, + CriticalItem.ENSOULED_ELF_HEAD, null, null), + ENSOULED_TROLL_HEAD(ItemID.ENSOULED_TROLL_HEAD_13484, "Ensouled troll head", Skill.PRAYER, 1, 780.0, + CriticalItem.ENSOULED_TROLL_HEAD, null, null), + ENSOULED_HORROR_HEAD(ItemID.ENSOULED_HORROR_HEAD_13487, "Ensouled horror head", Skill.PRAYER, 1, 832.0, + CriticalItem.ENSOULED_HORROR_HEAD, null, null), + ENSOULED_KALPHITE_HEAD(ItemID.ENSOULED_KALPHITE_HEAD_13490, "Ensouled kalphite head", Skill.PRAYER, 1, 884.0, + CriticalItem.ENSOULED_KALPHITE_HEAD, null, null), + ENSOULED_DAGANNOTH_HEAD(ItemID.ENSOULED_DAGANNOTH_HEAD_13493, "Ensouled dagannoth head", Skill.PRAYER, 1, 936.0, + CriticalItem.ENSOULED_DAGANNOTH_HEAD, null, null), + ENSOULED_BLOODVELD_HEAD(ItemID.ENSOULED_BLOODVELD_HEAD_13496, "Ensouled bloodveld head", Skill.PRAYER, 1, 1040.0, + CriticalItem.ENSOULED_BLOODVELD_HEAD, null, null), + ENSOULED_TZHAAR_HEAD(ItemID.ENSOULED_TZHAAR_HEAD_13499, "Ensouled tzhaar head", Skill.PRAYER, 1, 1104.0, + CriticalItem.ENSOULED_TZHAAR_HEAD, null, null), + ENSOULED_DEMON_HEAD(ItemID.ENSOULED_DEMON_HEAD_13502, "Ensouled demon head", Skill.PRAYER, 1, 1170.0, + CriticalItem.ENSOULED_DEMON_HEAD, null, null), + ENSOULED_AVIANSIE_HEAD(ItemID.ENSOULED_AVIANSIE_HEAD_13505, "Ensouled aviansie head", Skill.PRAYER, 1, 1234.0, + CriticalItem.ENSOULED_AVIANSIE_HEAD, null, null), + ENSOULED_ABYSSAL_HEAD(ItemID.ENSOULED_ABYSSAL_HEAD_13508, "Ensouled abyssal head", Skill.PRAYER, 1, 1300.0, + CriticalItem.ENSOULED_ABYSSAL_HEAD, null, null), + ENSOULED_DRAGON_HEAD(ItemID.ENSOULED_DRAGON_HEAD_13511, "Ensouled dragon head", Skill.PRAYER, 1, 1560.0, + CriticalItem.ENSOULED_DRAGON_HEAD, null, null), /* * Cooking Items */ - RAW_HERRING(ItemID.RAW_HERRING, "Fish", Skill.COOKING, 5, 50.0, CriticalItem.RAW_HERRING), - RAW_MACKEREL(ItemID.RAW_MACKEREL, "Fish", Skill.COOKING, 10, 60.0, CriticalItem.RAW_MACKEREL), - RAW_TROUT(ItemID.RAW_TROUT, "Fish", Skill.COOKING, 15, 70.0, CriticalItem.RAW_TROUT), - RAW_COD(ItemID.RAW_COD, "Fish", Skill.COOKING, 18, 75.0, CriticalItem.RAW_COD), - RAW_PIKE(ItemID.RAW_PIKE, "Fish", Skill.COOKING, 20, 80.0, CriticalItem.RAW_PIKE), - RAW_SALMON(ItemID.RAW_SALMON, "Fish", Skill.COOKING, 25, 90.0, CriticalItem.RAW_SALMON), - RAW_TUNA(ItemID.RAW_TUNA, "Fish", Skill.COOKING, 30, 100.0, CriticalItem.RAW_TUNA), - RAW_KARAMBWAN(ItemID.RAW_KARAMBWAN, "Fish", Skill.COOKING, 30, 190.0, CriticalItem.RAW_KARAMBWAN), - RAW_LOBSTER(ItemID.RAW_LOBSTER, "Fish", Skill.COOKING, 40, 120.0, CriticalItem.RAW_LOBSTER), - RAW_BASS(ItemID.RAW_BASS, "Fish", Skill.COOKING, 43, 130.0, CriticalItem.RAW_BASS), - RAW_SWORDFISH(ItemID.RAW_SWORDFISH, "Fish", Skill.COOKING, 45, 140.0, CriticalItem.RAW_SWORDFISH), - RAW_MONKFISH(ItemID.RAW_MONKFISH, "Fish", Skill.COOKING, 62, 150.0, CriticalItem.RAW_MONKFISH), - RAW_SHARK(ItemID.RAW_SHARK, "Fish", Skill.COOKING, 80, 210.0, CriticalItem.RAW_SHARK), - RAW_SEA_TURTLE(ItemID.RAW_SEA_TURTLE, "Fish", Skill.COOKING, 82, 211.3, CriticalItem.RAW_SEA_TURTLE), - RAW_ANGLERFISH(ItemID.RAW_ANGLERFISH, "Fish", Skill.COOKING, 84, 230.0, CriticalItem.RAW_ANGLERFISH), - RAW_DARK_CRAB(ItemID.RAW_DARK_CRAB, "Fish", Skill.COOKING, 90, 215.0, CriticalItem.RAW_DARK_CRAB), - RAW_MANTA_RAY(ItemID.RAW_MANTA_RAY, "Fish", Skill.COOKING, 91, 216.2, CriticalItem.RAW_MANTA_RAY), + COOK_HERRING(ItemID.HERRING, "Herring", Skill.COOKING, 5, 50.0, + CriticalItem.RAW_HERRING, null, new ItemStack(ItemID.HERRING, 1)), + COOK_MACKEREL(ItemID.MACKEREL, "Mackerel", Skill.COOKING, 10, 60.0, + CriticalItem.RAW_MACKEREL, null, new ItemStack(ItemID.MACKEREL, 1)), + COOK_TROUT(ItemID.TROUT, "Trout", Skill.COOKING, 15, 70.0, + CriticalItem.RAW_TROUT, null, new ItemStack(ItemID.TROUT, 1)), + COOK_COD(ItemID.COD, "Cod", Skill.COOKING, 18, 75.0, + CriticalItem.RAW_COD, null, new ItemStack(ItemID.COD, 1)), + COOK_PIKE(ItemID.PIKE, "Pike", Skill.COOKING, 20, 80.0, + CriticalItem.RAW_PIKE, null, new ItemStack(ItemID.PIKE, 1)), + COOK_SALMON(ItemID.SALMON, "Salmon", Skill.COOKING, 25, 90.0, + CriticalItem.RAW_SALMON, null, new ItemStack(ItemID.SALMON, 1)), + COOK_TUNA(ItemID.TUNA, "Tuna", Skill.COOKING, 30, 100.0, + CriticalItem.RAW_TUNA, null, new ItemStack(ItemID.TUNA, 1)), + COOK_KARAMBWAN(ItemID.COOKED_KARAMBWAN, "Cooked Karambwan", Skill.COOKING, 30, 190.0, + CriticalItem.RAW_KARAMBWAN, null, new ItemStack(ItemID.COOKED_KARAMBWAN, 1)), + COOK_LOBSTER(ItemID.LOBSTER, "Lobster", Skill.COOKING, 40, 120.0, + CriticalItem.RAW_LOBSTER, null, new ItemStack(ItemID.LOBSTER, 1)), + COOK_BASS(ItemID.BASS, "Bass", Skill.COOKING, 43, 130.0, + CriticalItem.RAW_BASS, null, new ItemStack(ItemID.BASS, 1)), + COOK_SWORDFISH(ItemID.SWORDFISH, "Swordfish", Skill.COOKING, 45, 140.0, + CriticalItem.RAW_SWORDFISH, null, new ItemStack(ItemID.SWORDFISH, 1)), + COOK_MONKFISH(ItemID.MONKFISH, "Monkfish", Skill.COOKING, 62, 150.0, + CriticalItem.RAW_MONKFISH, null, new ItemStack(ItemID.MONKFISH, 1)), + COOK_SHARK(ItemID.SHARK, "Shark", Skill.COOKING, 80, 210.0, + CriticalItem.RAW_SHARK, null, new ItemStack(ItemID.SHARK, 1)), + COOK_SEA_TURTLE(ItemID.SEA_TURTLE, "Sea turtle", Skill.COOKING, 82, 211.3, + CriticalItem.RAW_SEA_TURTLE, null, new ItemStack(ItemID.SEA_TURTLE, 1)), + COOK_ANGLERFISH(ItemID.ANGLERFISH, "Anglerfish", Skill.COOKING, 84, 230.0, + CriticalItem.RAW_ANGLERFISH, null, new ItemStack(ItemID.ANGLERFISH, 1)), + COOK_DARK_CRAB(ItemID.DARK_CRAB, "Dark crab", Skill.COOKING, 90, 215.0, + CriticalItem.RAW_DARK_CRAB, null, new ItemStack(ItemID.DARK_CRAB, 1)), + COOK_MANTA_RAY(ItemID.MANTA_RAY, "Manta ray", Skill.COOKING, 91, 216.2, + CriticalItem.RAW_MANTA_RAY, null, new ItemStack(ItemID.MANTA_RAY, 1)), - WINE(ItemID.JUG_OF_WINE, "Other", Skill.COOKING, 35, 200, CriticalItem.GRAPES, ActivitySecondaries.JUG_OF_WATER), + WINE(ItemID.JUG_OF_WINE, "Jug of wine", Skill.COOKING, 35, 200, + CriticalItem.GRAPES, Secondaries.JUG_OF_WATER, new ItemStack(ItemID.JUG_OF_WINE, 1)), /* * Crafting Items */ // Spinning - BALL_OF_WOOL(ItemID.WOOL, "Misc", Skill.CRAFTING, 1, 2.5, CriticalItem.WOOL), - BOW_STRING(ItemID.BOW_STRING, "Misc", Skill.CRAFTING, 1, 15, CriticalItem.FLAX), + BALL_OF_WOOL(ItemID.BALL_OF_WOOL, "Ball of wool", Skill.CRAFTING, 1, 2.5, + CriticalItem.WOOL, null, new ItemStack(ItemID.BALL_OF_WOOL, 1)), + BOW_STRING(ItemID.BOW_STRING, "Bow string", Skill.CRAFTING, 1, 15, + CriticalItem.FLAX, null, new ItemStack(ItemID.BOW_STRING, 1)), // Glass Blowing - BEER_GLASS(ItemID.BEER_GLASS, "Beer Glass", Skill.CRAFTING, 1, 17.5, CriticalItem.MOLTEN_GLASS), - CANDLE_LANTERN(ItemID.CANDLE_LANTERN, "Candle Lantern", Skill.CRAFTING, 4, 19, CriticalItem.MOLTEN_GLASS), - OIL_LAMP(ItemID.OIL_LAMP, "Oil Lamp", Skill.CRAFTING, 12, 25, CriticalItem.MOLTEN_GLASS), - VIAL(ItemID.VIAL, "Vial", Skill.CRAFTING, 33, 35, CriticalItem.MOLTEN_GLASS), - EMPTY_FISHBOWL(ItemID.EMPTY_FISHBOWL, "Empty fishbowl", Skill.CRAFTING, 42, 42.5, CriticalItem.MOLTEN_GLASS), - UNPOWERED_ORB(ItemID.UNPOWERED_ORB, "Unpowered orb", Skill.CRAFTING, 46, 52.5, CriticalItem.MOLTEN_GLASS), - LANTERN_LENS(ItemID.LANTERN_LENS, "Lantern lens", Skill.CRAFTING, 49, 55, CriticalItem.MOLTEN_GLASS), - LIGHT_ORB(ItemID.LIGHT_ORB, "Light orb", Skill.CRAFTING, 87, 70, CriticalItem.MOLTEN_GLASS), + BEER_GLASS(ItemID.BEER_GLASS, "Beer glass", Skill.CRAFTING, 1, 17.5, + CriticalItem.MOLTEN_GLASS, null, new ItemStack(ItemID.BEER_GLASS, 1)), + CANDLE_LANTERN(ItemID.CANDLE_LANTERN, "Candle lantern", Skill.CRAFTING, 4, 19, + CriticalItem.MOLTEN_GLASS, null, new ItemStack(ItemID.CANDLE_LANTERN, 1)), + OIL_LAMP(ItemID.OIL_LAMP, "Oil lamp", Skill.CRAFTING, 12, 25, + CriticalItem.MOLTEN_GLASS, null, new ItemStack(ItemID.OIL_LAMP, 1)), + VIAL(ItemID.VIAL, "Vial", Skill.CRAFTING, 33, 35, + CriticalItem.MOLTEN_GLASS, null, new ItemStack(ItemID.VIAL, 1)), + EMPTY_FISHBOWL(ItemID.EMPTY_FISHBOWL, "Empty fishbowl", Skill.CRAFTING, 42, 42.5, + CriticalItem.MOLTEN_GLASS, null, new ItemStack(ItemID.EMPTY_FISHBOWL, 1)), + UNPOWERED_ORB(ItemID.UNPOWERED_ORB, "Unpowered orb", Skill.CRAFTING, 46, 52.5, + CriticalItem.MOLTEN_GLASS, null, new ItemStack(ItemID.UNPOWERED_ORB, 1)), + LANTERN_LENS(ItemID.LANTERN_LENS, "Lantern lens", Skill.CRAFTING, 49, 55, + CriticalItem.MOLTEN_GLASS, null, new ItemStack(ItemID.LANTERN_LENS, 1)), + LIGHT_ORB(ItemID.LIGHT_ORB, "Light orb", Skill.CRAFTING, 87, 70, + CriticalItem.MOLTEN_GLASS, null, new ItemStack(ItemID.LIGHT_ORB, 1)), + // D'hide/Dragon Leather - GREEN_DRAGON_LEATHER(ItemID.GREEN_DRAGON_LEATHER, "D'hide", Skill.CRAFTING, 57, 62.0, CriticalItem.GREEN_DRAGON_LEATHER), - BLUE_DRAGON_LEATHER(ItemID.BLUE_DRAGON_LEATHER, "D'hide", Skill.CRAFTING, 66, 70.0, CriticalItem.BLUE_DRAGON_LEATHER), - RED_DRAGON_LEATHER(ItemID.RED_DRAGON_LEATHER, "D'hide", Skill.CRAFTING, 73, 78.0, CriticalItem.RED_DRAGON_LEATHER), - BLACK_DRAGON_LEATHER(ItemID.BLACK_DRAGON_LEATHER, "D'hide", Skill.CRAFTING, 79, 86.0, CriticalItem.BLACK_DRAGON_LEATHER), + GREEN_DRAGONHIDE(ItemID.GREEN_DRAGON_LEATHER, "Tan Green D'hide", Skill.CRAFTING, 57, 0, + CriticalItem.GREEN_DRAGONHIDE, null, new ItemStack(ItemID.GREEN_DRAGON_LEATHER, 1)), + BLUE_DRAGONHIDE(ItemID.BLUE_DRAGON_LEATHER, "Tan Blue D'hide", Skill.CRAFTING, 66, 0, + CriticalItem.BLUE_DRAGONHIDE, null, new ItemStack(ItemID.BLUE_DRAGON_LEATHER, 1)), + RED_DRAGONHIDE(ItemID.RED_DRAGON_LEATHER, "Tan Red D'hide", Skill.CRAFTING, 73, 0, + CriticalItem.RED_DRAGONHIDE, null, new ItemStack(ItemID.RED_DRAGON_LEATHER, 1)), + BLACK_DRAGONHIDE(ItemID.BLACK_DRAGON_LEATHER, "Tan Black D'hide", Skill.CRAFTING, 79, 0, + CriticalItem.BLACK_DRAGONHIDE, null, new ItemStack(ItemID.BLACK_DRAGON_LEATHER, 1)), + + GREEN_DRAGON_LEATHER(ItemID.GREEN_DHIDE_VAMB, "Green D'hide product", Skill.CRAFTING, 57, 62.0, + CriticalItem.GREEN_DRAGON_LEATHER, null, null), + BLUE_DRAGON_LEATHER(ItemID.BLUE_DHIDE_VAMB, "Blue D'hide product", Skill.CRAFTING, 66, 70.0, + CriticalItem.BLUE_DRAGON_LEATHER, null, null), + RED_DRAGON_LEATHER(ItemID.RED_DHIDE_VAMB, "Red D'hide product", Skill.CRAFTING, 73, 78.0, + CriticalItem.RED_DRAGON_LEATHER, null, null), + BLACK_DRAGON_LEATHER(ItemID.BLACK_DHIDE_VAMB, "Black D'hide product", Skill.CRAFTING, 79, 86.0, + CriticalItem.BLACK_DRAGON_LEATHER, null, null), + // Uncut Gems - UNCUT_OPAL(ItemID.UNCUT_OPAL, "Gems", Skill.CRAFTING, 1, 15.0, CriticalItem.UNCUT_OPAL), - UNCUT_JADE(ItemID.UNCUT_JADE, "Gems", Skill.CRAFTING, 13, 20.0, CriticalItem.UNCUT_JADE), - UNCUT_RED_TOPAZ(ItemID.UNCUT_RED_TOPAZ, "Gems", Skill.CRAFTING, 16, 25.0, CriticalItem.UNCUT_RED_TOPAZ), - UNCUT_SAPPHIRE(ItemID.UNCUT_SAPPHIRE, "Gems", Skill.CRAFTING, 20, 50.0, CriticalItem.UNCUT_SAPPHIRE), - UNCUT_EMERALD(ItemID.UNCUT_EMERALD, "Gems", Skill.CRAFTING, 27, 67.5, CriticalItem.UNCUT_EMERALD), - UNCUT_RUBY(ItemID.UNCUT_RUBY, "Gems", Skill.CRAFTING, 34, 85, CriticalItem.UNCUT_RUBY), - UNCUT_DIAMOND(ItemID.UNCUT_DIAMOND, "Gems", Skill.CRAFTING, 43, 107.5, CriticalItem.UNCUT_DIAMOND), - UNCUT_DRAGONSTONE(ItemID.UNCUT_DRAGONSTONE, "Gems", Skill.CRAFTING, 55, 137.5, CriticalItem.UNCUT_DRAGONSTONE), - UNCUT_ONYX(ItemID.UNCUT_ONYX, "Gems", Skill.CRAFTING, 67, 167.5, CriticalItem.UNCUT_ONYX), - UNCUT_ZENYTE(ItemID.UNCUT_ZENYTE, "Gems", Skill.CRAFTING, 89, 200.0, CriticalItem.UNCUT_ZENYTE), + UNCUT_OPAL(ItemID.OPAL, "Cut opal", Skill.CRAFTING, 1, 15.0, + CriticalItem.UNCUT_OPAL, null, new ItemStack(ItemID.OPAL, 1)), + UNCUT_JADE(ItemID.JADE, "Cut jade", Skill.CRAFTING, 13, 20.0, + CriticalItem.UNCUT_JADE, null, new ItemStack(ItemID.JADE, 1)), + UNCUT_RED_TOPAZ(ItemID.RED_TOPAZ, "Cut red topaz", Skill.CRAFTING, 16, 25.0, + CriticalItem.UNCUT_RED_TOPAZ, null, new ItemStack(ItemID.RED_TOPAZ, 1)), + UNCUT_SAPPHIRE(ItemID.SAPPHIRE, "Cut sapphire", Skill.CRAFTING, 20, 50.0, + CriticalItem.UNCUT_SAPPHIRE, null, new ItemStack(ItemID.SAPPHIRE, 1)), + UNCUT_EMERALD(ItemID.EMERALD, "Cut emerald", Skill.CRAFTING, 27, 67.5, + CriticalItem.UNCUT_EMERALD, null, new ItemStack(ItemID.EMERALD, 1)), + UNCUT_RUBY(ItemID.RUBY, "Cut ruby", Skill.CRAFTING, 34, 85, + CriticalItem.UNCUT_RUBY, null, new ItemStack(ItemID.RUBY, 1)), + UNCUT_DIAMOND(ItemID.DIAMOND, "Cut diamond", Skill.CRAFTING, 43, 107.5, + CriticalItem.UNCUT_DIAMOND, null, new ItemStack(ItemID.DIAMOND, 1)), + UNCUT_DRAGONSTONE(ItemID.DRAGONSTONE, "Cut dragonstone", Skill.CRAFTING, 55, 137.5, + CriticalItem.UNCUT_DRAGONSTONE, null, new ItemStack(ItemID.DRAGONSTONE, 1)), + UNCUT_ONYX(ItemID.ONYX, "Cut onyx", Skill.CRAFTING, 67, 167.5, + CriticalItem.UNCUT_ONYX, null, new ItemStack(ItemID.ONYX, 1)), + UNCUT_ZENYTE(ItemID.ZENYTE, "Cut zenyte", Skill.CRAFTING, 89, 200.0, + CriticalItem.UNCUT_ZENYTE, null, new ItemStack(ItemID.ZENYTE, 1)), // Silver Jewelery - OPAL_RING(ItemID.OPAL_RING, "Opal ring", Skill.CRAFTING, 1, 10, CriticalItem.OPAL, ActivitySecondaries.SILVER_BAR), - OPAL_NECKLACE(ItemID.OPAL_NECKLACE, "Opal necklace", Skill.CRAFTING, 16, 35, CriticalItem.OPAL, ActivitySecondaries.SILVER_BAR), - OPAL_BRACELET(ItemID.OPAL_BRACELET, "Opal bracelet", Skill.CRAFTING, 22, 45, CriticalItem.OPAL, ActivitySecondaries.SILVER_BAR), - OPAL_AMULET(ItemID.OPAL_AMULET, "Opal amulet", Skill.CRAFTING, 27, 55, CriticalItem.OPAL, ActivitySecondaries.SILVER_BAR), - JADE_RING(ItemID.JADE_RING, "Jade ring", Skill.CRAFTING, 13, 32, CriticalItem.JADE, ActivitySecondaries.SILVER_BAR), - JADE_NECKLACE(ItemID.JADE_NECKLACE, "Jade necklace", Skill.CRAFTING, 25, 54, CriticalItem.JADE, ActivitySecondaries.SILVER_BAR), - JADE_BRACELET(ItemID.JADE_BRACELET, "Jade bracelet", Skill.CRAFTING, 29, 60, CriticalItem.JADE, ActivitySecondaries.SILVER_BAR), - JADE_AMULET(ItemID.JADE_AMULET, "Jade amulet", Skill.CRAFTING, 34, 70, CriticalItem.JADE, ActivitySecondaries.SILVER_BAR), - TOPAZ_RING(ItemID.TOPAZ_RING, "Topaz ring", Skill.CRAFTING, 16, 35, CriticalItem.RED_TOPAZ, ActivitySecondaries.SILVER_BAR), - TOPAZ_NECKLACE(ItemID.TOPAZ_NECKLACE, "Topaz necklace", Skill.CRAFTING, 32, 70, CriticalItem.RED_TOPAZ, ActivitySecondaries.SILVER_BAR), - TOPAZ_BRACELET(ItemID.TOPAZ_BRACELET, "Topaz bracelet", Skill.CRAFTING, 38, 75, CriticalItem.RED_TOPAZ, ActivitySecondaries.SILVER_BAR), - TOPAZ_AMULET(ItemID.TOPAZ_AMULET, "Topaz amulet", Skill.CRAFTING, 45, 80, CriticalItem.RED_TOPAZ, ActivitySecondaries.SILVER_BAR), + OPAL_RING(ItemID.OPAL_RING, "Opal ring", Skill.CRAFTING, 1, 10, + CriticalItem.OPAL, Secondaries.SILVER_BAR, new ItemStack(ItemID.OPAL_RING, 1)), + OPAL_NECKLACE(ItemID.OPAL_NECKLACE, "Opal necklace", Skill.CRAFTING, 16, 35, + CriticalItem.OPAL, Secondaries.SILVER_BAR, new ItemStack(ItemID.OPAL_NECKLACE, 1)), + OPAL_BRACELET(ItemID.OPAL_BRACELET, "Opal bracelet", Skill.CRAFTING, 22, 45, + CriticalItem.OPAL, Secondaries.SILVER_BAR, new ItemStack(ItemID.OPAL_BRACELET, 1)), + OPAL_AMULET(ItemID.OPAL_AMULET, "Opal amulet", Skill.CRAFTING, 27, 55, + CriticalItem.OPAL, Secondaries.SILVER_BAR, new ItemStack(ItemID.OPAL_AMULET, 1)), + JADE_RING(ItemID.JADE_RING, "Jade ring", Skill.CRAFTING, 13, 32, + CriticalItem.JADE, Secondaries.SILVER_BAR, new ItemStack(ItemID.JADE_RING, 1)), + JADE_NECKLACE(ItemID.JADE_NECKLACE, "Jade necklace", Skill.CRAFTING, 25, 54, + CriticalItem.JADE, Secondaries.SILVER_BAR, new ItemStack(ItemID.JADE_NECKLACE, 1)), + JADE_BRACELET(ItemID.JADE_BRACELET, "Jade bracelet", Skill.CRAFTING, 29, 60, + CriticalItem.JADE, Secondaries.SILVER_BAR, new ItemStack(ItemID.JADE_BRACELET, 1)), + JADE_AMULET(ItemID.JADE_AMULET, "Jade amulet", Skill.CRAFTING, 34, 70, + CriticalItem.JADE, Secondaries.SILVER_BAR, new ItemStack(ItemID.JADE_AMULET, 1)), + TOPAZ_RING(ItemID.TOPAZ_RING, "Topaz ring", Skill.CRAFTING, 16, 35, + CriticalItem.RED_TOPAZ, Secondaries.SILVER_BAR, new ItemStack(ItemID.TOPAZ_RING, 1)), + TOPAZ_NECKLACE(ItemID.TOPAZ_NECKLACE, "Topaz necklace", Skill.CRAFTING, 32, 70, + CriticalItem.RED_TOPAZ, Secondaries.SILVER_BAR, new ItemStack(ItemID.TOPAZ_NECKLACE, 1)), + TOPAZ_BRACELET(ItemID.TOPAZ_BRACELET, "Topaz bracelet", Skill.CRAFTING, 38, 75, + CriticalItem.RED_TOPAZ, Secondaries.SILVER_BAR, new ItemStack(ItemID.TOPAZ_BRACELET, 1)), + TOPAZ_AMULET(ItemID.TOPAZ_AMULET, "Topaz amulet", Skill.CRAFTING, 45, 80, + CriticalItem.RED_TOPAZ, Secondaries.SILVER_BAR, new ItemStack(ItemID.TOPAZ_AMULET, 1)), // Gold Jewelery - SAPPHIRE_RING(ItemID.SAPPHIRE_RING, "Sapphire ring", Skill.CRAFTING, 20, 40, CriticalItem.SAPPHIRE, ActivitySecondaries.GOLD_BAR), - SAPPHIRE_NECKLACE(ItemID.SAPPHIRE_NECKLACE, "Sapphire necklace", Skill.CRAFTING, 22, 55, CriticalItem.SAPPHIRE, ActivitySecondaries.GOLD_BAR), - SAPPHIRE_BRACELET(ItemID.SAPPHIRE_BRACELET, "Sapphire bracelet", Skill.CRAFTING, 23, 60, CriticalItem.SAPPHIRE, ActivitySecondaries.GOLD_BAR), - SAPPHIRE_AMULET(ItemID.SAPPHIRE_AMULET, "Sapphire amulet", Skill.CRAFTING, 24, 65, CriticalItem.SAPPHIRE, ActivitySecondaries.GOLD_BAR), - EMERALD_RING(ItemID.EMERALD_RING, "Emerald ring", Skill.CRAFTING, 27, 55, CriticalItem.EMERALD, ActivitySecondaries.GOLD_BAR), - EMERALD_NECKLACE(ItemID.EMERALD_NECKLACE, "Emerald necklace", Skill.CRAFTING, 29, 60, CriticalItem.EMERALD, ActivitySecondaries.GOLD_BAR), - EMERALD_BRACELET(ItemID.EMERALD_BRACELET, "Emerald bracelet", Skill.CRAFTING, 30, 65, CriticalItem.EMERALD, ActivitySecondaries.GOLD_BAR), - EMERALD_AMULET(ItemID.EMERALD_AMULET, "Emerald amulet", Skill.CRAFTING, 31, 70, CriticalItem.EMERALD, ActivitySecondaries.GOLD_BAR), - RUBY_RING(ItemID.RUBY_RING, "Ruby ring", Skill.CRAFTING, 34, 70, CriticalItem.RUBY, ActivitySecondaries.GOLD_BAR), - RUBY_NECKLACE(ItemID.RUBY_NECKLACE, "Ruby necklace", Skill.CRAFTING, 40, 75, CriticalItem.RUBY, ActivitySecondaries.GOLD_BAR), - RUBY_BRACELET(ItemID.RUBY_BRACELET, "Ruby bracelet", Skill.CRAFTING, 42, 80, CriticalItem.RUBY, ActivitySecondaries.GOLD_BAR), - RUBY_AMULET(ItemID.RUBY_AMULET, "Ruby amulet", Skill.CRAFTING, 50, 85, CriticalItem.RUBY, ActivitySecondaries.GOLD_BAR), - DIAMOND_RING(ItemID.DIAMOND_RING, "Diamond ring", Skill.CRAFTING, 43, 85, CriticalItem.DIAMOND, ActivitySecondaries.GOLD_BAR), - DIAMOND_NECKLACE(ItemID.DIAMOND_NECKLACE, "Diamond necklace", Skill.CRAFTING, 56, 90, CriticalItem.DIAMOND, ActivitySecondaries.GOLD_BAR), - DIAMOND_BRACELET(ItemID.DIAMOND_BRACELET, "Diamond bracelet", Skill.CRAFTING, 58, 95, CriticalItem.DIAMOND, ActivitySecondaries.GOLD_BAR), - DIAMOND_AMULET(ItemID.DIAMOND_AMULET, "Diamond amulet", Skill.CRAFTING, 70, 100, CriticalItem.DIAMOND, ActivitySecondaries.GOLD_BAR), - DRAGONSTONE_RING(ItemID.DRAGONSTONE_RING, "Dragonstone ring", Skill.CRAFTING, 55, 100, CriticalItem.DRAGONSTONE, ActivitySecondaries.GOLD_BAR), - DRAGON_NECKLACE(ItemID.DRAGON_NECKLACE, "Dragon necklace", Skill.CRAFTING, 72, 105, CriticalItem.DRAGONSTONE, ActivitySecondaries.GOLD_BAR), - DRAGONSTONE_BRACELET(ItemID.DRAGONSTONE_BRACELET, "Dragonstone bracelet", Skill.CRAFTING, 74, 110, CriticalItem.DRAGONSTONE, ActivitySecondaries.GOLD_BAR), - DRAGONSTONE_AMULET(ItemID.DRAGONSTONE_AMULET, "Dragonstone amulet", Skill.CRAFTING, 80, 150, CriticalItem.DRAGONSTONE, ActivitySecondaries.GOLD_BAR), - ONYX_RING(ItemID.ONYX_RING, "Onyx ring", Skill.CRAFTING, 67, 115, CriticalItem.ONYX, ActivitySecondaries.GOLD_BAR), - ONYX_NECKLACE(ItemID.ONYX_NECKLACE, "Onyx necklace", Skill.CRAFTING, 82, 120, CriticalItem.ONYX, ActivitySecondaries.GOLD_BAR), - REGEN_BRACELET(ItemID.REGEN_BRACELET, "Regen bracelet", Skill.CRAFTING, 84, 125, CriticalItem.ONYX, ActivitySecondaries.GOLD_BAR), - ONYX_AMULET(ItemID.ONYX_AMULET, "Onyx amulet", Skill.CRAFTING, 90, 165, CriticalItem.ONYX, ActivitySecondaries.GOLD_BAR), - ZENYTE_RING(ItemID.ZENYTE_RING, "Zenyte ring", Skill.CRAFTING, 89, 150, CriticalItem.ZENYTE, ActivitySecondaries.GOLD_BAR), - ZENYTE_NECKLACE(ItemID.ZENYTE_NECKLACE, "Zenyte necklace", Skill.CRAFTING, 92, 165, CriticalItem.ZENYTE, ActivitySecondaries.GOLD_BAR), - ZENYTE_BRACELET(ItemID.ZENYTE_BRACELET, "Zenyte bracelet", Skill.CRAFTING, 95, 180, CriticalItem.ZENYTE, ActivitySecondaries.GOLD_BAR), - ZENYTE_AMULET(ItemID.ZENYTE_AMULET, "Zenyte amulet", Skill.CRAFTING, 98, 200, CriticalItem.ZENYTE, ActivitySecondaries.GOLD_BAR), + SAPPHIRE_RING(ItemID.SAPPHIRE_RING, "Sapphire ring", Skill.CRAFTING, 20, 40, + CriticalItem.SAPPHIRE, Secondaries.GOLD_BAR, new ItemStack(ItemID.SAPPHIRE_RING, 1)), + SAPPHIRE_NECKLACE(ItemID.SAPPHIRE_NECKLACE, "Sapphire necklace", Skill.CRAFTING, 22, 55, + CriticalItem.SAPPHIRE, Secondaries.GOLD_BAR, new ItemStack(ItemID.SAPPHIRE_NECKLACE, 1)), + SAPPHIRE_BRACELET(ItemID.SAPPHIRE_BRACELET, "Sapphire bracelet", Skill.CRAFTING, 23, 60, + CriticalItem.SAPPHIRE, Secondaries.GOLD_BAR, new ItemStack(ItemID.SAPPHIRE_BRACELET, 1)), + SAPPHIRE_AMULET(ItemID.SAPPHIRE_AMULET, "Sapphire amulet", Skill.CRAFTING, 24, 65, + CriticalItem.SAPPHIRE, Secondaries.GOLD_BAR, new ItemStack(ItemID.SAPPHIRE_AMULET, 1)), + EMERALD_RING(ItemID.EMERALD_RING, "Emerald ring", Skill.CRAFTING, 27, 55, + CriticalItem.EMERALD, Secondaries.GOLD_BAR, new ItemStack(ItemID.EMERALD_RING, 1)), + EMERALD_NECKLACE(ItemID.EMERALD_NECKLACE, "Emerald necklace", Skill.CRAFTING, 29, 60, + CriticalItem.EMERALD, Secondaries.GOLD_BAR, new ItemStack(ItemID.EMERALD_NECKLACE, 1)), + EMERALD_BRACELET(ItemID.EMERALD_BRACELET, "Emerald bracelet", Skill.CRAFTING, 30, 65, + CriticalItem.EMERALD, Secondaries.GOLD_BAR, new ItemStack(ItemID.EMERALD_BRACELET, 1)), + EMERALD_AMULET(ItemID.EMERALD_AMULET, "Emerald amulet", Skill.CRAFTING, 31, 70, + CriticalItem.EMERALD, Secondaries.GOLD_BAR, new ItemStack(ItemID.EMERALD_AMULET, 1)), + RUBY_RING(ItemID.RUBY_RING, "Ruby ring", Skill.CRAFTING, 34, 70, + CriticalItem.RUBY, Secondaries.GOLD_BAR, new ItemStack(ItemID.RUBY_RING, 1)), + RUBY_NECKLACE(ItemID.RUBY_NECKLACE, "Ruby necklace", Skill.CRAFTING, 40, 75, + CriticalItem.RUBY, Secondaries.GOLD_BAR, new ItemStack(ItemID.RUBY_NECKLACE, 1)), + RUBY_BRACELET(ItemID.RUBY_BRACELET, "Ruby bracelet", Skill.CRAFTING, 42, 80, + CriticalItem.RUBY, Secondaries.GOLD_BAR, new ItemStack(ItemID.RUBY_BRACELET, 1)), + RUBY_AMULET(ItemID.RUBY_AMULET, "Ruby amulet", Skill.CRAFTING, 50, 85, + CriticalItem.RUBY, Secondaries.GOLD_BAR, new ItemStack(ItemID.RUBY_AMULET, 1)), + DIAMOND_RING(ItemID.DIAMOND_RING, "Diamond ring", Skill.CRAFTING, 43, 85, + CriticalItem.DIAMOND, Secondaries.GOLD_BAR, new ItemStack(ItemID.DIAMOND_RING, 1)), + DIAMOND_NECKLACE(ItemID.DIAMOND_NECKLACE, "Diamond necklace", Skill.CRAFTING, 56, 90, + CriticalItem.DIAMOND, Secondaries.GOLD_BAR, new ItemStack(ItemID.DIAMOND_NECKLACE, 1)), + DIAMOND_BRACELET(ItemID.DIAMOND_BRACELET, "Diamond bracelet", Skill.CRAFTING, 58, 95, + CriticalItem.DIAMOND, Secondaries.GOLD_BAR, new ItemStack(ItemID.DIAMOND_BRACELET, 1)), + DIAMOND_AMULET(ItemID.DIAMOND_AMULET, "Diamond amulet", Skill.CRAFTING, 70, 100, + CriticalItem.DIAMOND, Secondaries.GOLD_BAR, new ItemStack(ItemID.DIAMOND_AMULET, 1)), + DRAGONSTONE_RING(ItemID.DRAGONSTONE_RING, "Dragonstone ring", Skill.CRAFTING, 55, 100, + CriticalItem.DRAGONSTONE, Secondaries.GOLD_BAR, new ItemStack(ItemID.DRAGONSTONE_RING, 1)), + DRAGON_NECKLACE(ItemID.DRAGON_NECKLACE, "Dragon necklace", Skill.CRAFTING, 72, 105, + CriticalItem.DRAGONSTONE, Secondaries.GOLD_BAR, new ItemStack(ItemID.DRAGON_NECKLACE, 1)), + DRAGONSTONE_BRACELET(ItemID.DRAGONSTONE_BRACELET, "Dragonstone bracelet", Skill.CRAFTING, 74, 110, + CriticalItem.DRAGONSTONE, Secondaries.GOLD_BAR, new ItemStack(ItemID.DRAGONSTONE_BRACELET, 1)), + DRAGONSTONE_AMULET(ItemID.DRAGONSTONE_AMULET, "Dragonstone amulet", Skill.CRAFTING, 80, 150, + CriticalItem.DRAGONSTONE, Secondaries.GOLD_BAR, new ItemStack(ItemID.DRAGONSTONE_AMULET, 1)), + ONYX_RING(ItemID.ONYX_RING, "Onyx ring", Skill.CRAFTING, 67, 115, + CriticalItem.ONYX, Secondaries.GOLD_BAR, new ItemStack(ItemID.ONYX_RING, 1)), + ONYX_NECKLACE(ItemID.ONYX_NECKLACE, "Onyx necklace", Skill.CRAFTING, 82, 120, + CriticalItem.ONYX, Secondaries.GOLD_BAR, new ItemStack(ItemID.ONYX_NECKLACE, 1)), + REGEN_BRACELET(ItemID.REGEN_BRACELET, "Regen bracelet", Skill.CRAFTING, 84, 125, + CriticalItem.ONYX, Secondaries.GOLD_BAR, new ItemStack(ItemID.REGEN_BRACELET, 1)), + ONYX_AMULET(ItemID.ONYX_AMULET, "Onyx amulet", Skill.CRAFTING, 90, 165, + CriticalItem.ONYX, Secondaries.GOLD_BAR, new ItemStack(ItemID.ONYX_AMULET, 1)), + ZENYTE_RING(ItemID.ZENYTE_RING, "Zenyte ring", Skill.CRAFTING, 89, 150, + CriticalItem.ZENYTE, Secondaries.GOLD_BAR, new ItemStack(ItemID.ZENYTE_RING, 1)), + ZENYTE_NECKLACE(ItemID.ZENYTE_NECKLACE, "Zenyte necklace", Skill.CRAFTING, 92, 165, + CriticalItem.ZENYTE, Secondaries.GOLD_BAR, new ItemStack(ItemID.ZENYTE_NECKLACE, 1)), + ZENYTE_BRACELET(ItemID.ZENYTE_BRACELET, "Zenyte bracelet", Skill.CRAFTING, 95, 180, + CriticalItem.ZENYTE, Secondaries.GOLD_BAR, new ItemStack(ItemID.ZENYTE_BRACELET, 1)), + ZENYTE_AMULET(ItemID.ZENYTE_AMULET, "Zenyte amulet", Skill.CRAFTING, 98, 200, + CriticalItem.ZENYTE, Secondaries.GOLD_BAR, new ItemStack(ItemID.ZENYTE_AMULET, 1)), // Battle Staves - WATER_BATTLESTAFF(ItemID.WATER_BATTLESTAFF, "Water battlestaff", Skill.CRAFTING, 54, 100, CriticalItem.BATTLESTAFF, ActivitySecondaries.WATER_ORB), - EARTH_BATTLESTAFF(ItemID.EARTH_BATTLESTAFF, "Earth battlestaff", Skill.CRAFTING, 58, 112.5, CriticalItem.BATTLESTAFF, ActivitySecondaries.EARTH_ORB), - FIRE_BATTLESTAFF(ItemID.FIRE_BATTLESTAFF, "Fire battlestaff", Skill.CRAFTING, 62, 125, CriticalItem.BATTLESTAFF, ActivitySecondaries.FIRE_ORB), - AIR_BATTLESTAFF(ItemID.AIR_BATTLESTAFF, "Air battlestaff", Skill.CRAFTING, 66, 137.5, CriticalItem.BATTLESTAFF, ActivitySecondaries.AIR_ORB), + WATER_BATTLESTAFF(ItemID.WATER_BATTLESTAFF, "Water battlestaff", Skill.CRAFTING, 54, 100, + CriticalItem.BATTLESTAFF, Secondaries.WATER_ORB, new ItemStack(ItemID.WATER_BATTLESTAFF, 1)), + EARTH_BATTLESTAFF(ItemID.EARTH_BATTLESTAFF, "Earth battlestaff", Skill.CRAFTING, 58, 112.5, + CriticalItem.BATTLESTAFF, Secondaries.EARTH_ORB, new ItemStack(ItemID.EARTH_BATTLESTAFF, 1)), + FIRE_BATTLESTAFF(ItemID.FIRE_BATTLESTAFF, "Fire battlestaff", Skill.CRAFTING, 62, 125, + CriticalItem.BATTLESTAFF, Secondaries.FIRE_ORB, new ItemStack(ItemID.FIRE_BATTLESTAFF, 1)), + AIR_BATTLESTAFF(ItemID.AIR_BATTLESTAFF, "Air battlestaff", Skill.CRAFTING, 66, 137.5, + CriticalItem.BATTLESTAFF, Secondaries.AIR_ORB, new ItemStack(ItemID.AIR_BATTLESTAFF, 1)), /* * Smithing Items */ // Smelting ores (Furnace) - IRON_ORE(ItemID.IRON_BAR, "Iron Bars", Skill.SMITHING, 15, 12.5, CriticalItem.IRON_ORE, ActivitySecondaries.COAL_ORE), - STEEL_ORE(ItemID.STEEL_BAR, "Steel Bars", Skill.SMITHING, 30, 17.5, CriticalItem.IRON_ORE, ActivitySecondaries.COAL_ORE_2), - SILVER_ORE(ItemID.SILVER_ORE, "Bar", Skill.SMITHING, 20, 13.67, CriticalItem.SILVER_ORE), - GOLD_ORE(ItemID.GOLD_BAR, "Regular exp", Skill.SMITHING, 40, 22.5, CriticalItem.GOLD_ORE), - GOLD_ORE_GAUNTLETS(ItemID.GOLDSMITH_GAUNTLETS, "Goldsmith Gauntlets", Skill.SMITHING, 40, 56.2, CriticalItem.GOLD_ORE), - MITHRIL_ORE(ItemID.MITHRIL_ORE, "Bar", Skill.SMITHING, 50, 30, CriticalItem.MITHRIL_ORE, ActivitySecondaries.COAL_ORE_4), - ADAMANTITE_ORE(ItemID.ADAMANTITE_ORE, "Bar", Skill.SMITHING, 70, 37.5, CriticalItem.ADAMANTITE_ORE, ActivitySecondaries.COAL_ORE_6), - RUNITE_ORE(ItemID.RUNITE_ORE, "Bar", Skill.SMITHING, 85, 50, CriticalItem.RUNITE_ORE, ActivitySecondaries.COAL_ORE_8), + IRON_ORE(ItemID.IRON_BAR, "Iron bar", Skill.SMITHING, 15, 12.5, + CriticalItem.IRON_ORE, Secondaries.COAL_ORE, new ItemStack(ItemID.IRON_BAR, 1)), + STEEL_ORE(ItemID.STEEL_BAR, "Steel bar", Skill.SMITHING, 30, 17.5, + CriticalItem.IRON_ORE, Secondaries.COAL_ORE_2, new ItemStack(ItemID.STEEL_BAR, 1)), + SILVER_ORE(ItemID.SILVER_BAR, "Silver Bar", Skill.SMITHING, 20, 13.67, + CriticalItem.SILVER_ORE, null, new ItemStack(ItemID.SILVER_BAR, 1)), + GOLD_ORE(ItemID.GOLD_BAR, "Gold bar", Skill.SMITHING, 40, 22.5, + CriticalItem.GOLD_ORE, null, new ItemStack(ItemID.GOLD_BAR, 1)), + GOLD_ORE_GAUNTLETS(ItemID.GOLDSMITH_GAUNTLETS, "Goldsmith gauntlets", Skill.SMITHING, 40, 56.2, + CriticalItem.GOLD_ORE, null, new ItemStack(ItemID.GOLD_BAR, 1)), + MITHRIL_ORE(ItemID.MITHRIL_BAR, "Mithril bar", Skill.SMITHING, 50, 30, + CriticalItem.MITHRIL_ORE, Secondaries.COAL_ORE_4, new ItemStack(ItemID.MITHRIL_BAR, 1)), + ADAMANTITE_ORE(ItemID.ADAMANTITE_BAR, "Adamantite bar", Skill.SMITHING, 70, 37.5, + CriticalItem.ADAMANTITE_ORE, Secondaries.COAL_ORE_6, new ItemStack(ItemID.ADAMANTITE_BAR, 1)), + RUNITE_ORE(ItemID.RUNITE_BAR, "Runite bar", Skill.SMITHING, 85, 50, + CriticalItem.RUNITE_ORE, Secondaries.COAL_ORE_8, new ItemStack(ItemID.RUNITE_BAR, 1)), // Smelting bars (Anvil) - BRONZE_BAR(ItemID.BRONZE_BAR, "Bars", Skill.SMITHING, 1, 12.5, CriticalItem.BRONZE_BAR), - IRON_BAR(ItemID.IRON_BAR, "Bars", Skill.SMITHING, 15, 25.0, CriticalItem.IRON_BAR), - STEEL_BAR(ItemID.STEEL_BAR, "Steel Products", Skill.SMITHING, 30, 37.5, CriticalItem.STEEL_BAR), - CANNONBALLS(ItemID.CANNONBALL, "Cannonballs", Skill.SMITHING, 35, 25.5, CriticalItem.STEEL_BAR), - MITHRIL_BAR(ItemID.MITHRIL_BAR, "Bars", Skill.SMITHING, 50, 50.0, CriticalItem.MITHRIL_BAR), - ADAMANTITE_BAR(ItemID.ADAMANTITE_BAR, "Bars", Skill.SMITHING, 70, 62.5, CriticalItem.ADAMANTITE_BAR), - RUNITE_BAR(ItemID.RUNITE_BAR, "Bars", Skill.SMITHING, 85, 75.0, CriticalItem.RUNITE_BAR), + BRONZE_BAR(ItemID.BRONZE_BAR, "Bronze products", Skill.SMITHING, 1, 12.5, + CriticalItem.BRONZE_BAR, null, null), + IRON_BAR(ItemID.IRON_BAR, "Iron products", Skill.SMITHING, 15, 25.0, + CriticalItem.IRON_BAR, null, null), + STEEL_BAR(ItemID.STEEL_BAR, "Steel products", Skill.SMITHING, 30, 37.5, + CriticalItem.STEEL_BAR, null, null), + CANNONBALLS(ItemID.CANNONBALL, "Cannonballs", Skill.SMITHING, 35, 25.5, + CriticalItem.STEEL_BAR, null, new ItemStack(ItemID.CANNONBALL, 4)), + MITHRIL_BAR(ItemID.MITHRIL_BAR, "Mithril products", Skill.SMITHING, 50, 50.0, + CriticalItem.MITHRIL_BAR, null, null), + ADAMANTITE_BAR(ItemID.ADAMANTITE_BAR, "Adamantite products", Skill.SMITHING, 70, 62.5, + CriticalItem.ADAMANTITE_BAR, null, null), + RUNITE_BAR(ItemID.RUNITE_BAR, "Runite products", Skill.SMITHING, 85, 75.0, + CriticalItem.RUNITE_BAR, null, null), /** * Farming Items */ - ACORN(ItemID.ACORN, "Seeds", Skill.FARMING, 15, 481.3, CriticalItem.ACORN), - WILLOW_SEED(ItemID.WILLOW_SEED, "Seeds", Skill.FARMING, 30, 1481.5, CriticalItem.WILLOW_SEED), - MAPLE_SEED(ItemID.MAPLE_SEED, "Seeds", Skill.FARMING, 45, 3448.4, CriticalItem.MAPLE_SEED), - YEW_SEED(ItemID.YEW_SEED, "Seeds", Skill.FARMING, 60, 7150.9, CriticalItem.YEW_SEED), - MAGIC_SEED(ItemID.MAGIC_SEED, "Seeds", Skill.FARMING, 75, 13913.8, CriticalItem.MAGIC_SEED), - APPLE_TREE_SEED(ItemID.APPLE_TREE_SEED, "Seeds", Skill.FARMING, 27, 1272.5, CriticalItem.APPLE_TREE_SEED), - BANANA_TREE_SEED(ItemID.BANANA_TREE_SEED, "Seeds", Skill.FARMING, 33, 1841.5, CriticalItem.BANANA_TREE_SEED), - ORANGE_TREE_SEED(ItemID.ORANGE_TREE_SEED, "Seeds", Skill.FARMING, 39, 2586.7, CriticalItem.ORANGE_TREE_SEED), - CURRY_TREE_SEED(ItemID.CURRY_TREE_SEED, "Seeds", Skill.FARMING, 42, 3036.9, CriticalItem.CURRY_TREE_SEED), - PINEAPPLE_SEED(ItemID.PINEAPPLE_SEED, "Seeds", Skill.FARMING, 51, 4791.7, CriticalItem.PINEAPPLE_SEED), - PAPAYA_TREE_SEED(ItemID.PAPAYA_TREE_SEED, "Seeds", Skill.FARMING, 57, 6380.4, CriticalItem.PAPAYA_TREE_SEED), - PALM_TREE_SEED(ItemID.PALM_TREE_SEED, "Seeds", Skill.FARMING, 68, 10509.6, CriticalItem.PALM_TREE_SEED), - CALQUAT_TREE_SEED(ItemID.CALQUAT_TREE_SEED, "Seeds", Skill.FARMING, 72, 12516.5, CriticalItem.CALQUAT_TREE_SEED), - TEAK_SEED(ItemID.TEAK_SEED, "Seeds", Skill.FARMING, 35, 7325, CriticalItem.TEAK_SEED), - MAHOGANY_SEED(ItemID.MAHOGANY_SEED, "Seeds", Skill.FARMING, 55, 15783, CriticalItem.MAHOGANY_SEED), - SPIRIT_SEED(ItemID.SPIRIT_SEED, "Seeds", Skill.FARMING, 83, 19500, CriticalItem.SPIRIT_SEED), + ACORN(ItemID.OAK_SAPLING, "Oak sapling", Skill.FARMING, 15, 0, + CriticalItem.ACORN, null, new ItemStack(ItemID.OAK_SAPLING, 1)), + WILLOW_SEED(ItemID.WILLOW_SAPLING, "Willow sapling", Skill.FARMING, 30, 0, + CriticalItem.WILLOW_SEED, null, new ItemStack(ItemID.WILLOW_SAPLING, 1)), + MAPLE_SEED(ItemID.MAPLE_SAPLING, "Maple sapling", Skill.FARMING, 45, 0, + CriticalItem.MAPLE_SEED, null, new ItemStack(ItemID.MAPLE_SAPLING, 1)), + YEW_SEED(ItemID.YEW_SAPLING, "Yew sapling", Skill.FARMING, 60, 0, + CriticalItem.YEW_SEED, null, new ItemStack(ItemID.YEW_SAPLING, 1)), + MAGIC_SEED(ItemID.MAGIC_SAPLING, "Magic sapling", Skill.FARMING, 75, 0, + CriticalItem.MAGIC_SEED, null, new ItemStack(ItemID.MAGIC_SAPLING, 1)), + APPLE_TREE_SEED(ItemID.APPLE_SAPLING, "Apple sapling", Skill.FARMING, 27, 0, + CriticalItem.APPLE_TREE_SEED, null, new ItemStack(ItemID.APPLE_SAPLING, 1)), + BANANA_TREE_SEED(ItemID.BANANA_SAPLING, "Banana sapling", Skill.FARMING, 33, 0, + CriticalItem.BANANA_TREE_SEED, null, new ItemStack(ItemID.BANANA_SAPLING, 1)), + ORANGE_TREE_SEED(ItemID.ORANGE_SAPLING, "Orange sapling", Skill.FARMING, 39, 0, + CriticalItem.ORANGE_TREE_SEED, null, new ItemStack(ItemID.ORANGE_SAPLING, 1)), + CURRY_TREE_SEED(ItemID.CURRY_SAPLING, "Curry sapling", Skill.FARMING, 42, 0, + CriticalItem.CURRY_TREE_SEED, null, new ItemStack(ItemID.CURRY_SAPLING, 1)), + PINEAPPLE_SEED(ItemID.PINEAPPLE_SAPLING, "Pineapple sapling", Skill.FARMING, 51, 0, + CriticalItem.PINEAPPLE_SEED, null, new ItemStack(ItemID.PINEAPPLE_SAPLING, 1)), + PAPAYA_TREE_SEED(ItemID.PAPAYA_SAPLING, "Papaya sapling", Skill.FARMING, 57, 0, + CriticalItem.PAPAYA_TREE_SEED, null, new ItemStack(ItemID.PAPAYA_SAPLING, 1)), + PALM_TREE_SEED(ItemID.PALM_SAPLING, "Palm sapling", Skill.FARMING, 68, 0, + CriticalItem.PALM_TREE_SEED, null, new ItemStack(ItemID.PALM_SAPLING, 1)), + CALQUAT_TREE_SEED(ItemID.CALQUAT_SAPLING, "Calquat sapling", Skill.FARMING, 72, 0, + CriticalItem.CALQUAT_TREE_SEED, null, new ItemStack(ItemID.CALQUAT_SAPLING, 1)), + TEAK_SEED(ItemID.TEAK_SAPLING, "Teak sapling", Skill.FARMING, 35, 0, + CriticalItem.TEAK_SEED, null, new ItemStack(ItemID.TEAK_SAPLING, 1)), + MAHOGANY_SEED(ItemID.MAHOGANY_SAPLING, "Mahogany sapling", Skill.FARMING, 55, 0, + CriticalItem.MAHOGANY_SEED, null, new ItemStack(ItemID.MAHOGANY_SAPLING, 1)), + SPIRIT_SEED(ItemID.SPIRIT_SAPLING, "Spirit sapling", Skill.FARMING, 83, 0, + CriticalItem.SPIRIT_SEED, null, new ItemStack(ItemID.SPIRIT_SAPLING, 1)), + + OAK_SAPPLING(ItemID.OAK_SAPLING, "Oak tree", Skill.FARMING, 15, 481.3, + CriticalItem.OAK_SAPLING, null, null), + WILLOW_SAPLING(ItemID.WILLOW_SAPLING, "Willow tree", Skill.FARMING, 30, 1481.5, + CriticalItem.WILLOW_SAPLING, null, null), + MAPLE_SAPLING(ItemID.MAPLE_SAPLING, "Maple tree", Skill.FARMING, 45, 3448.4, + CriticalItem.MAPLE_SAPLING, null, null), + YEW_SAPLING(ItemID.YEW_SAPLING, "Yew tree", Skill.FARMING, 60, 7150.9, + CriticalItem.YEW_SAPLING, null, null), + MAGIC_SAPLING(ItemID.MAGIC_SAPLING, "Magic tree", Skill.FARMING, 75, 13913.8, + CriticalItem.MAGIC_SAPLING, null, null), + APPLE_TREE_SAPLING(ItemID.APPLE_SAPLING, "Apple tree", Skill.FARMING, 27, 1272.5, + CriticalItem.APPLE_TREE_SAPLING, null, null), + BANANA_TREE_SAPLING(ItemID.BANANA_SAPLING, "Banana tree", Skill.FARMING, 33, 1841.5, + CriticalItem.BANANA_TREE_SAPLING, null, null), + ORANGE_TREE_SAPLING(ItemID.ORANGE_SAPLING, "Orange tree", Skill.FARMING, 39, 2586.7, + CriticalItem.ORANGE_TREE_SAPLING, null, null), + CURRY_TREE_SAPLING(ItemID.CURRY_SAPLING, "Curry tree", Skill.FARMING, 42, 3036.9, + CriticalItem.CURRY_TREE_SAPLING, null, null), + PINEAPPLE_SAPLING(ItemID.PINEAPPLE_SAPLING, "Pineapple tree", Skill.FARMING, 51, 4791.7, + CriticalItem.PINEAPPLE_SAPLING, null, null), + PAPAYA_TREE_SAPLING(ItemID.PAPAYA_SAPLING, "Papaya tree", Skill.FARMING, 57, 6380.4, + CriticalItem.PAPAYA_TREE_SAPLING, null, null), + PALM_TREE_SAPLING(ItemID.PALM_SAPLING, "Palm tree", Skill.FARMING, 68, 10509.6, + CriticalItem.PALM_TREE_SAPLING, null, null), + CALQUAT_TREE_SAPLING(ItemID.CALQUAT_SAPLING, "Calquat tree", Skill.FARMING, 72, 12516.5, + CriticalItem.CALQUAT_TREE_SAPLING, null, null), + TEAK_SAPLING(ItemID.TEAK_SAPLING, "Teak tree", Skill.FARMING, 35, 7325, + CriticalItem.TEAK_SAPLING, null, null), + MAHOGANY_SAPLING(ItemID.MAHOGANY_SAPLING, "Mahogany tree", Skill.FARMING, 55, 15783, + CriticalItem.MAHOGANY_SAPLING, null, null), + SPIRIT_SAPLING(ItemID.SPIRIT_SAPLING, "Spirit tree", Skill.FARMING, 83, 19500, + CriticalItem.SPIRIT_SAPLING, null, null), ; private final int icon; private final String name; + private final CriticalItem criticalItem; private final Skill skill; private final int level; private final double xp; - private final SecondaryItem[] secondaries; - private final CriticalItem criticalItem; - private final boolean preventLinked; + private final ItemStack[] secondaries; + @Nullable + private final ItemStack output; + private ItemInfo outputItemInfo = null; + @Nullable + private final CriticalItem linkedItem; - Activity(int Icon, String name, Skill skill, int level, double xp, CriticalItem criticalItem) - { - this.icon = Icon; - this.name = name; - this.skill = skill; - this.level = level; - this.xp = xp; - this.criticalItem = criticalItem; - this.secondaries = new SecondaryItem[0]; - this.preventLinked = false; - } + // Store activity by CriticalItem + private static final ImmutableMultimap CRITICAL_MAP; - Activity(int Icon, String name, Skill skill, int level, double xp, CriticalItem criticalItem, ActivitySecondaries secondaries) + static { - this.icon = Icon; - this.name = name; - this.skill = skill; - this.level = level; - this.xp = xp; - this.criticalItem = criticalItem; - this.secondaries = secondaries == null ? new SecondaryItem[0] : secondaries.getItems(); - this.preventLinked = false; - } - - Activity(final int Icon, final String name, final Skill skill, final int level, final double xp, final CriticalItem criticalItem, final ActivitySecondaries secondaries, final boolean preventLinked) - { - this.icon = Icon; - this.name = name; - this.skill = skill; - this.level = level; - this.xp = xp; - this.criticalItem = criticalItem; - this.secondaries = secondaries == null ? new SecondaryItem[0] : secondaries.getItems(); - this.preventLinked = preventLinked; - } - - // Builds a Map to reduce looping frequency - private static Map> buildItemMap() - { - Map> map = new HashMap<>(); - for (Activity item : values()) + final ImmutableMultimap.Builder map = ImmutableMultimap.builder(); + for (final Activity item : values()) { - map.computeIfAbsent(item.getCriticalItem(), e -> new ArrayList<>()).add(item); + map.put(item.getCriticalItem(), item); } - - return map; + CRITICAL_MAP = map.build(); } - private static final Map> byCriticalItem = buildItemMap(); - - public static List getByCriticalItem(CriticalItem item) + Activity( + final int icon, + final String name, + final Skill skill, + final int level, + final double xp, + final CriticalItem criticalItem, + @Nullable final Secondaries secondaries, + @Nullable final ItemStack output) { - - return byCriticalItem.getOrDefault(item, new ArrayList<>()); + this.icon = icon; + this.name = name; + this.skill = skill; + this.level = level; + this.xp = xp; + this.criticalItem = criticalItem; + this.secondaries = secondaries == null ? new ItemStack[0] : secondaries.getItems(); + this.output = output; + this.linkedItem = output == null ? null : CriticalItem.getByItemId(output.getId()); } /** * Get all Activities for this CriticalItem * - * @param item CriticalItem to check for - * @param limitLevel Level to check Activitiy requirements against. -1 or 0 value disables limits - * @return an empty list if no activities + * @param item CriticalItem to check for + * @return an empty Collection if no activities */ - public static List getByCriticalItem(CriticalItem item, int limitLevel) + public static List getByCriticalItem(CriticalItem item) { - List activities = getByCriticalItem(item); - List l = new ArrayList<>(); + final Collection activities = CRITICAL_MAP.get(item); + if (activities == null) + { + return new ArrayList<>(); + } + + return new ArrayList<>(activities); + } + + /** + * Get all Activities for this CriticalItem limited to level + * + * @param item CriticalItem to check for + * @param limitLevel Level to check Activitiy requirements against. -1/0 value disables limits + * @return an empty Collection if no activities + */ + public static List getByCriticalItem(final CriticalItem item, final int limitLevel) + { + // Return as list to allow getting by index + final List l = getByCriticalItem(item); if (limitLevel <= 0) { return l; } - for (Activity a : activities) - { - if (!(a.getLevel() > limitLevel)) - { - l.add(a); - } - } - - return l; + return l.stream().filter(a -> a.getLevel() <= limitLevel).collect(Collectors.toList()); } -} + + /** + * Attaches the Item Composition to each CriticalItem on client initial load + * + * @param m ItemManager + */ + public static void prepareItemDefinitions(ItemManager m) + { + for (Activity a : values()) + { + final ItemStack output = a.getOutput(); + if (output == null) + { + continue; + } + + if (a.getOutputItemInfo() != null) + { + return; + } + + final ItemDefinition c = m.getItemDefinition(output.getId()); + a.outputItemInfo = new ItemInfo(c.getName(), c.isStackable()); + } + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/ActivitySecondaries.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/ActivitySecondaries.java deleted file mode 100644 index 0afb3cf033..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/ActivitySecondaries.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2018, TheStonedTurtle - * 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.skillcalculator.banked.beans; - -import lombok.AccessLevel; -import lombok.Getter; -import net.runelite.api.ItemID; - -@Getter(AccessLevel.PUBLIC) -public enum ActivitySecondaries -{ - /** - * Herblore - */ - UNFINISHED_POTION(new SecondaryItem(ItemID.VIAL_OF_WATER, 1)), - SWAMP_TAR(new SecondaryItem(ItemID.SWAMP_TAR, 15)), - - // Guam - ATTACK_POTION(new SecondaryItem(ItemID.EYE_OF_NEWT)), - // Marrentil - ANTIPOISON(new SecondaryItem(ItemID.UNICORN_HORN_DUST)), - // Tarromin - STRENGTH_POTION(new SecondaryItem(ItemID.LIMPWURT_ROOT)), - SERUM_207(new SecondaryItem(ItemID.ASHES)), - // Harralander - COMPOST_POTION(new SecondaryItem(ItemID.VOLCANIC_ASH)), - RESTORE_POTION(new SecondaryItem(ItemID.RED_SPIDERS_EGGS)), - ENERGY_POTION(new SecondaryItem(ItemID.CHOCOLATE_DUST)), - COMBAT_POTION(new SecondaryItem(ItemID.GOAT_HORN_DUST)), - // Ranarr Weed - DEFENCE_POTION(new SecondaryItem(ItemID.WHITE_BERRIES)), - PRAYER_POTION(new SecondaryItem(ItemID.SNAPE_GRASS)), - // Toadflax - AGILITY_POTION(new SecondaryItem(ItemID.TOADS_LEGS)), - SARADOMIN_BREW(new SecondaryItem(ItemID.CRUSHED_NEST)), - // Irit - SUPER_ATTACK(new SecondaryItem(ItemID.EYE_OF_NEWT)), - SUPERANTIPOISON(new SecondaryItem(ItemID.UNICORN_HORN_DUST)), - // Avantoe - FISHING_POTION(new SecondaryItem(ItemID.SNAPE_GRASS)), - SUPER_ENERGY_POTION(new SecondaryItem(ItemID.MORT_MYRE_FUNGUS)), - HUNTER_POTION(new SecondaryItem(ItemID.KEBBIT_TEETH_DUST)), - // Kwuarm - SUPER_STRENGTH(new SecondaryItem(ItemID.LIMPWURT_ROOT)), - // Snapdragon - SUPER_RESTORE(new SecondaryItem(ItemID.RED_SPIDERS_EGGS)), - SANFEW_SERUM(new SecondaryItem(ItemID.SNAKE_WEED), new SecondaryItem(ItemID.UNICORN_HORN_DUST), new SecondaryItem(ItemID.SUPER_RESTORE4), new SecondaryItem(ItemID.NAIL_BEAST_NAILS)), - // Cadantine - SUPER_DEFENCE_POTION(new SecondaryItem(ItemID.WHITE_BERRIES)), - // Lantadyme - ANTIFIRE_POTION(new SecondaryItem(ItemID.DRAGON_SCALE_DUST)), - MAGIC_POTION(new SecondaryItem(ItemID.POTATO_CACTUS)), - // Dwarf Weed - RANGING_POTION(new SecondaryItem(ItemID.WINE_OF_ZAMORAK)), - // Torstol - ZAMORAK_BREW(new SecondaryItem(ItemID.JANGERBERRIES)), - SUPER_COMBAT_POTION(new SecondaryItem(ItemID.SUPER_ATTACK3), new SecondaryItem(ItemID.SUPER_STRENGTH3), new SecondaryItem(ItemID.SUPER_DEFENCE3)), - ANTIVENOM_PLUS(new SecondaryItem(ItemID.ANTIVENOM4)), - - /** - * Smithing - */ - COAL_ORE(new SecondaryItem(ItemID.COAL)), - COAL_ORE_2(new SecondaryItem(ItemID.COAL, 2)), - COAL_ORE_4(new SecondaryItem(ItemID.COAL, 4)), - COAL_ORE_6(new SecondaryItem(ItemID.COAL, 6)), - COAL_ORE_8(new SecondaryItem(ItemID.COAL, 8)), - - /** - * Crafting - */ - GOLD_BAR(new SecondaryItem(ItemID.GOLD_BAR)), - SILVER_BAR(new SecondaryItem(ItemID.SILVER_BAR)), - WATER_ORB(new SecondaryItem(ItemID.WATER_ORB)), - EARTH_ORB(new SecondaryItem(ItemID.EARTH_ORB)), - FIRE_ORB(new SecondaryItem(ItemID.FIRE_ORB)), - AIR_ORB(new SecondaryItem(ItemID.AIR_ORB)), - - /** - * Cooking - */ - JUG_OF_WATER(new SecondaryItem(ItemID.JUG_OF_WATER)), - ; - private final SecondaryItem[] items; - - ActivitySecondaries(final SecondaryItem... items) - { - this.items = items; - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/BankedItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/BankedItem.java new file mode 100644 index 0000000000..5256d26351 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/BankedItem.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018, TheStonedTurtle + * 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.skillcalculator.banked.beans; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public class BankedItem +{ + private final CriticalItem item; + private final int qty; + + public double getXpRate() + { + final Activity selectedActivity = item.getSelectedActivity(); + if (selectedActivity == null) + { + return 0; + } + + return selectedActivity.getXp(); + } + + @Override + public String toString() + { + return item.name() + " x " + qty; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/CriticalItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/CriticalItem.java new file mode 100644 index 0000000000..d6f3752742 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/CriticalItem.java @@ -0,0 +1,355 @@ +/* + * Copyright (c) 2018, TheStonedTurtle + * 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.skillcalculator.banked.beans; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import lombok.Getter; +import lombok.Setter; +import net.runelite.api.ItemDefinition; +import net.runelite.api.ItemID; +import net.runelite.api.Skill; +import net.runelite.client.game.ItemManager; + +@Getter +public enum CriticalItem +{ + /** + * Construction Items + */ + // Logs + LOGS(ItemID.LOGS, Skill.CONSTRUCTION, "Logs"), + OAK_LOGS(ItemID.OAK_LOGS, Skill.CONSTRUCTION, "Logs"), + TEAK_LOGS(ItemID.TEAK_LOGS, Skill.CONSTRUCTION, "Logs"), + MAHOGANY_LOGS(ItemID.MAHOGANY_LOGS, Skill.CONSTRUCTION, "Logs"), + // Planks + PLANK(ItemID.PLANK, Skill.CONSTRUCTION, "Planks"), + OAK_PLANK(ItemID.OAK_PLANK, Skill.CONSTRUCTION, "Planks"), + TEAK_PLANK(ItemID.TEAK_PLANK, Skill.CONSTRUCTION, "Planks"), + MAHOGANY_PLANK(ItemID.MAHOGANY_PLANK, Skill.CONSTRUCTION, "Planks"), + + /** + * Herblore Items + */ + // Grimy Herbs + GRIMY_GUAM_LEAF(ItemID.GRIMY_GUAM_LEAF, Skill.HERBLORE, "Grimy Herbs"), + GRIMY_MARRENTILL(ItemID.GRIMY_MARRENTILL, Skill.HERBLORE, "Grimy Herbs"), + GRIMY_TARROMIN(ItemID.GRIMY_TARROMIN, Skill.HERBLORE, "Grimy Herbs"), + GRIMY_HARRALANDER(ItemID.GRIMY_HARRALANDER, Skill.HERBLORE, "Grimy Herbs"), + GRIMY_RANARR_WEED(ItemID.GRIMY_RANARR_WEED, Skill.HERBLORE, "Grimy Herbs"), + GRIMY_TOADFLAX(ItemID.GRIMY_TOADFLAX, Skill.HERBLORE, "Grimy Herbs"), + GRIMY_IRIT_LEAF(ItemID.GRIMY_IRIT_LEAF, Skill.HERBLORE, "Grimy Herbs"), + GRIMY_AVANTOE(ItemID.GRIMY_AVANTOE, Skill.HERBLORE, "Grimy Herbs"), + GRIMY_KWUARM(ItemID.GRIMY_KWUARM, Skill.HERBLORE, "Grimy Herbs"), + GRIMY_SNAPDRAGON(ItemID.GRIMY_SNAPDRAGON, Skill.HERBLORE, "Grimy Herbs"), + GRIMY_CADANTINE(ItemID.GRIMY_CADANTINE, Skill.HERBLORE, "Grimy Herbs"), + GRIMY_LANTADYME(ItemID.GRIMY_LANTADYME, Skill.HERBLORE, "Grimy Herbs"), + GRIMY_DWARF_WEED(ItemID.GRIMY_DWARF_WEED, Skill.HERBLORE, "Grimy Herbs"), + GRIMY_TORSTOL(ItemID.GRIMY_TORSTOL, Skill.HERBLORE, "Grimy Herbs"), + // Clean Herbs + GUAM_LEAF(ItemID.GUAM_LEAF, Skill.HERBLORE, "Cleaned Herbs"), + MARRENTILL(ItemID.MARRENTILL, Skill.HERBLORE, "Cleaned Herbs"), + TARROMIN(ItemID.TARROMIN, Skill.HERBLORE, "Cleaned Herbs"), + HARRALANDER(ItemID.HARRALANDER, Skill.HERBLORE, "Cleaned Herbs"), + RANARR_WEED(ItemID.RANARR_WEED, Skill.HERBLORE, "Cleaned Herbs"), + TOADFLAX(ItemID.TOADFLAX, Skill.HERBLORE, "Cleaned Herbs"), + IRIT_LEAF(ItemID.IRIT_LEAF, Skill.HERBLORE, "Cleaned Herbs"), + AVANTOE(ItemID.AVANTOE, Skill.HERBLORE, "Cleaned Herbs"), + KWUARM(ItemID.KWUARM, Skill.HERBLORE, "Cleaned Herbs"), + SNAPDRAGON(ItemID.SNAPDRAGON, Skill.HERBLORE, "Cleaned Herbs"), + CADANTINE(ItemID.CADANTINE, Skill.HERBLORE, "Cleaned Herbs"), + LANTADYME(ItemID.LANTADYME, Skill.HERBLORE, "Cleaned Herbs"), + DWARF_WEED(ItemID.DWARF_WEED, Skill.HERBLORE, "Cleaned Herbs"), + TORSTOL(ItemID.TORSTOL, Skill.HERBLORE, "Cleaned Herbs"), + // Unfinished Potions + GUAM_LEAF_POTION_UNF(ItemID.GUAM_POTION_UNF, Skill.HERBLORE, "Unfinished Potions"), + MARRENTILL_POTION_UNF(ItemID.MARRENTILL_POTION_UNF, Skill.HERBLORE, "Unfinished Potions"), + TARROMIN_POTION_UNF(ItemID.TARROMIN_POTION_UNF, Skill.HERBLORE, "Unfinished Potions"), + HARRALANDER_POTION_UNF(ItemID.HARRALANDER_POTION_UNF, Skill.HERBLORE, "Unfinished Potions"), + RANARR_POTION_UNF(ItemID.RANARR_POTION_UNF, Skill.HERBLORE, "Unfinished Potions"), + TOADFLAX_POTION_UNF(ItemID.TOADFLAX_POTION_UNF, Skill.HERBLORE, "Unfinished Potions"), + IRIT_POTION_UNF(ItemID.IRIT_POTION_UNF, Skill.HERBLORE, "Unfinished Potions"), + AVANTOE_POTION_UNF(ItemID.AVANTOE_POTION_UNF, Skill.HERBLORE, "Unfinished Potions"), + KWUARM_POTION_UNF(ItemID.KWUARM_POTION_UNF, Skill.HERBLORE, "Unfinished Potions"), + SNAPDRAGON_POTION_UNF(ItemID.SNAPDRAGON_POTION_UNF, Skill.HERBLORE, "Unfinished Potions"), + CADANTINE_POTION_UNF(ItemID.CADANTINE_POTION_UNF, Skill.HERBLORE, "Unfinished Potions"), + LANTADYME_POTION_UNF(ItemID.LANTADYME_POTION_UNF, Skill.HERBLORE, "Unfinished Potions"), + DWARF_WEED_POTION_UNF(ItemID.DWARF_WEED_POTION_UNF, Skill.HERBLORE, "Unfinished Potions"), + TORSTOL_POTION_UNF(ItemID.TORSTOL_POTION_UNF, Skill.HERBLORE, "Unfinished Potions"), + + /** + * Prayer Items + */ + // Bones + BONES(ItemID.BONES, Skill.PRAYER, "Bones"), + WOLF_BONES(ItemID.WOLF_BONES, Skill.PRAYER, "Bones"), + BURNT_BONES(ItemID.BURNT_BONES, Skill.PRAYER, "Bones"), + MONKEY_BONES(ItemID.MONKEY_BONES, Skill.PRAYER, "Bones"), + BAT_BONES(ItemID.BAT_BONES, Skill.PRAYER, "Bones"), + JOGRE_BONES(ItemID.JOGRE_BONES, Skill.PRAYER, "Bones"), + BIG_BONES(ItemID.BIG_BONES, Skill.PRAYER, "Bones"), + ZOGRE_BONES(ItemID.ZOGRE_BONES, Skill.PRAYER, "Bones"), + SHAIKAHAN_BONES(ItemID.SHAIKAHAN_BONES, Skill.PRAYER, "Bones"), + BABYDRAGON_BONES(ItemID.BABYDRAGON_BONES, Skill.PRAYER, "Bones"), + WYVERN_BONES(ItemID.WYVERN_BONES, Skill.PRAYER, "Bones"), + DRAGON_BONES(ItemID.DRAGON_BONES, Skill.PRAYER, "Bones"), + FAYRG_BONES(ItemID.FAYRG_BONES, Skill.PRAYER, "Bones"), + LAVA_DRAGON_BONES(ItemID.LAVA_DRAGON_BONES, Skill.PRAYER, "Bones"), + RAURG_BONES(ItemID.RAURG_BONES, Skill.PRAYER, "Bones"), + DAGANNOTH_BONES(ItemID.DAGANNOTH_BONES, Skill.PRAYER, "Bones"), + OURG_BONES(ItemID.OURG_BONES, Skill.PRAYER, "Bones"), + SUPERIOR_DRAGON_BONES(ItemID.SUPERIOR_DRAGON_BONES, Skill.PRAYER, "Bones"), + // Shade Remains (Pyre Logs) + LOAR_REMAINS(ItemID.LOAR_REMAINS, Skill.PRAYER, "Shades", true), + PHRIN_REMAINS(ItemID.PHRIN_REMAINS, Skill.PRAYER, "Shades", true), + RIYL_REMAINS(ItemID.RIYL_REMAINS, Skill.PRAYER, "Shades", true), + ASYN_REMAINS(ItemID.ASYN_REMAINS, Skill.PRAYER, "Shades", true), + FIYR_REMAINS(ItemID.FIYR_REMAINS, Skill.PRAYER, "Shades", true), + // Ensouled Heads + ENSOULED_GOBLIN_HEAD(ItemID.ENSOULED_GOBLIN_HEAD_13448, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_MONKEY_HEAD(ItemID.ENSOULED_MONKEY_HEAD_13451, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_IMP_HEAD(ItemID.ENSOULED_IMP_HEAD_13454, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_MINOTAUR_HEAD(ItemID.ENSOULED_MINOTAUR_HEAD_13457, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_SCORPION_HEAD(ItemID.ENSOULED_SCORPION_HEAD_13460, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_BEAR_HEAD(ItemID.ENSOULED_BEAR_HEAD_13463, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_UNICORN_HEAD(ItemID.ENSOULED_UNICORN_HEAD_13466, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_DOG_HEAD(ItemID.ENSOULED_DOG_HEAD_13469, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_CHAOS_DRUID_HEAD(ItemID.ENSOULED_CHAOS_DRUID_HEAD_13472, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_GIANT_HEAD(ItemID.ENSOULED_GIANT_HEAD_13475, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_OGRE_HEAD(ItemID.ENSOULED_OGRE_HEAD_13478, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_ELF_HEAD(ItemID.ENSOULED_ELF_HEAD_13481, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_TROLL_HEAD(ItemID.ENSOULED_TROLL_HEAD_13484, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_HORROR_HEAD(ItemID.ENSOULED_HORROR_HEAD_13487, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_KALPHITE_HEAD(ItemID.ENSOULED_KALPHITE_HEAD_13490, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_DAGANNOTH_HEAD(ItemID.ENSOULED_DAGANNOTH_HEAD_13493, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_BLOODVELD_HEAD(ItemID.ENSOULED_BLOODVELD_HEAD_13496, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_TZHAAR_HEAD(ItemID.ENSOULED_TZHAAR_HEAD_13499, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_DEMON_HEAD(ItemID.ENSOULED_DEMON_HEAD_13502, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_AVIANSIE_HEAD(ItemID.ENSOULED_AVIANSIE_HEAD_13505, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_ABYSSAL_HEAD(ItemID.ENSOULED_ABYSSAL_HEAD_13508, Skill.PRAYER, "Ensouled Heads", true), + ENSOULED_DRAGON_HEAD(ItemID.ENSOULED_DRAGON_HEAD_13511, Skill.PRAYER, "Ensouled Heads", true), + + /** + * Cooking Items + */ + RAW_HERRING(ItemID.RAW_HERRING, Skill.COOKING, "Fish"), + RAW_MACKEREL(ItemID.RAW_MACKEREL, Skill.COOKING, "Fish"), + RAW_TROUT(ItemID.RAW_TROUT, Skill.COOKING, "Fish"), + RAW_COD(ItemID.RAW_COD, Skill.COOKING, "Fish"), + RAW_PIKE(ItemID.RAW_PIKE, Skill.COOKING, "Fish"), + RAW_SALMON(ItemID.RAW_SALMON, Skill.COOKING, "Fish"), + RAW_TUNA(ItemID.RAW_TUNA, Skill.COOKING, "Fish"), + RAW_KARAMBWAN(ItemID.RAW_KARAMBWAN, Skill.COOKING, "Fish"), + RAW_LOBSTER(ItemID.RAW_LOBSTER, Skill.COOKING, "Fish"), + RAW_BASS(ItemID.RAW_BASS, Skill.COOKING, "Fish"), + RAW_SWORDFISH(ItemID.RAW_SWORDFISH, Skill.COOKING, "Fish"), + RAW_MONKFISH(ItemID.RAW_MONKFISH, Skill.COOKING, "Fish"), + RAW_SHARK(ItemID.RAW_SHARK, Skill.COOKING, "Fish"), + RAW_SEA_TURTLE(ItemID.RAW_SEA_TURTLE, Skill.COOKING, "Fish"), + RAW_ANGLERFISH(ItemID.RAW_ANGLERFISH, Skill.COOKING, "Fish"), + RAW_DARK_CRAB(ItemID.RAW_DARK_CRAB, Skill.COOKING, "Fish"), + RAW_MANTA_RAY(ItemID.RAW_MANTA_RAY, Skill.COOKING, "Fish"), + + GRAPES(ItemID.GRAPES, Skill.COOKING, "Other"), + + /** + * Crafting Items + */ + WOOL(ItemID.WOOL, Skill.CRAFTING, "Misc"), + FLAX(ItemID.FLAX, Skill.CRAFTING, "Misc"), + MOLTEN_GLASS(ItemID.MOLTEN_GLASS, Skill.CRAFTING, "Misc"), + BATTLESTAFF(ItemID.BATTLESTAFF, Skill.CRAFTING, "Misc"), + + // D'hide/Dragon Leather + GREEN_DRAGONHIDE(ItemID.GREEN_DRAGONHIDE, Skill.CRAFTING, "D'hide"), + GREEN_DRAGON_LEATHER(ItemID.GREEN_DRAGON_LEATHER, Skill.CRAFTING, "D'hide"), + BLUE_DRAGONHIDE(ItemID.BLUE_DRAGONHIDE, Skill.CRAFTING, "D'hide"), + BLUE_DRAGON_LEATHER(ItemID.BLUE_DRAGON_LEATHER, Skill.CRAFTING, "D'hide"), + RED_DRAGONHIDE(ItemID.RED_DRAGONHIDE, Skill.CRAFTING, "D'hide"), + RED_DRAGON_LEATHER(ItemID.RED_DRAGON_LEATHER, Skill.CRAFTING, "D'hide"), + BLACK_DRAGONHIDE(ItemID.BLACK_DRAGONHIDE, Skill.CRAFTING, "D'hide"), + BLACK_DRAGON_LEATHER(ItemID.BLACK_DRAGON_LEATHER, Skill.CRAFTING, "D'hide"), + + // Uncut Gems + UNCUT_OPAL(ItemID.UNCUT_OPAL, Skill.CRAFTING, "Gems"), + UNCUT_JADE(ItemID.UNCUT_JADE, Skill.CRAFTING, "Gems"), + UNCUT_RED_TOPAZ(ItemID.UNCUT_RED_TOPAZ, Skill.CRAFTING, "Gems"), + UNCUT_SAPPHIRE(ItemID.UNCUT_SAPPHIRE, Skill.CRAFTING, "Gems"), + UNCUT_EMERALD(ItemID.UNCUT_EMERALD, Skill.CRAFTING, "Gems"), + UNCUT_RUBY(ItemID.UNCUT_RUBY, Skill.CRAFTING, "Gems"), + UNCUT_DIAMOND(ItemID.UNCUT_DIAMOND, Skill.CRAFTING, "Gems"), + UNCUT_DRAGONSTONE(ItemID.UNCUT_DRAGONSTONE, Skill.CRAFTING, "Gems"), + UNCUT_ONYX(ItemID.UNCUT_ONYX, Skill.CRAFTING, "Gems"), + UNCUT_ZENYTE(ItemID.UNCUT_ZENYTE, Skill.CRAFTING, "Gems"), + + // Cut Gems + OPAL(ItemID.OPAL, Skill.CRAFTING, "Gems"), + JADE(ItemID.JADE, Skill.CRAFTING, "Gems"), + RED_TOPAZ(ItemID.RED_TOPAZ, Skill.CRAFTING, "Gems"), + SAPPHIRE(ItemID.SAPPHIRE, Skill.CRAFTING, "Gems"), + EMERALD(ItemID.EMERALD, Skill.CRAFTING, "Gems"), + RUBY(ItemID.RUBY, Skill.CRAFTING, "Gems"), + DIAMOND(ItemID.DIAMOND, Skill.CRAFTING, "Gems"), + DRAGONSTONE(ItemID.DRAGONSTONE, Skill.CRAFTING, "Gems"), + ONYX(ItemID.ONYX, Skill.CRAFTING, "Gems"), + ZENYTE(ItemID.ZENYTE, Skill.CRAFTING, "Gems"), + + /** + * Smithing Items + */ + + // Ores + IRON_ORE(ItemID.IRON_ORE, Skill.SMITHING, "Ore"), + SILVER_ORE(ItemID.SILVER_ORE, Skill.SMITHING, "Ore"), + GOLD_ORE(ItemID.GOLD_ORE, Skill.SMITHING, "Ore"), + MITHRIL_ORE(ItemID.MITHRIL_ORE, Skill.SMITHING, "Ore"), + ADAMANTITE_ORE(ItemID.ADAMANTITE_ORE, Skill.SMITHING, "Ore"), + RUNITE_ORE(ItemID.RUNITE_ORE, Skill.SMITHING, "Ore"), + + // Bars + BRONZE_BAR(ItemID.BRONZE_BAR, Skill.SMITHING, "Bars"), + IRON_BAR(ItemID.IRON_BAR, Skill.SMITHING, "Bars"), + STEEL_BAR(ItemID.STEEL_BAR, Skill.SMITHING, "Bars"), + MITHRIL_BAR(ItemID.MITHRIL_BAR, Skill.SMITHING, "Bars"), + ADAMANTITE_BAR(ItemID.ADAMANTITE_BAR, Skill.SMITHING, "Bars"), + RUNITE_BAR(ItemID.RUNITE_BAR, Skill.SMITHING, "Bars"), + + /** + * Farming Items + */ + // Seeds + ACORN(ItemID.ACORN, Skill.FARMING, "Seeds"), + WILLOW_SEED(ItemID.WILLOW_SEED, Skill.FARMING, "Seeds"), + MAPLE_SEED(ItemID.MAPLE_SEED, Skill.FARMING, "Seeds"), + YEW_SEED(ItemID.YEW_SEED, Skill.FARMING, "Seeds"), + MAGIC_SEED(ItemID.MAGIC_SEED, Skill.FARMING, "Seeds"), + APPLE_TREE_SEED(ItemID.APPLE_TREE_SEED, Skill.FARMING, "Seeds"), + BANANA_TREE_SEED(ItemID.BANANA_TREE_SEED, Skill.FARMING, "Seeds"), + ORANGE_TREE_SEED(ItemID.ORANGE_TREE_SEED, Skill.FARMING, "Seeds"), + CURRY_TREE_SEED(ItemID.CURRY_TREE_SEED, Skill.FARMING, "Seeds"), + PINEAPPLE_SEED(ItemID.PINEAPPLE_SEED, Skill.FARMING, "Seeds"), + PAPAYA_TREE_SEED(ItemID.PAPAYA_TREE_SEED, Skill.FARMING, "Seeds"), + PALM_TREE_SEED(ItemID.PALM_TREE_SEED, Skill.FARMING, "Seeds"), + CALQUAT_TREE_SEED(ItemID.CALQUAT_TREE_SEED, Skill.FARMING, "Seeds"), + TEAK_SEED(ItemID.TEAK_SEED, Skill.FARMING, "Seeds"), + MAHOGANY_SEED(ItemID.MAHOGANY_SEED, Skill.FARMING, "Seeds"), + SPIRIT_SEED(ItemID.SPIRIT_SEED, Skill.FARMING, "Seeds"), + + // Saplings + OAK_SAPLING(ItemID.OAK_SAPLING, Skill.FARMING, "Saplings"), + WILLOW_SAPLING(ItemID.WILLOW_SAPLING, Skill.FARMING, "Saplings"), + MAPLE_SAPLING(ItemID.MAPLE_SAPLING, Skill.FARMING, "Saplings"), + YEW_SAPLING(ItemID.YEW_SAPLING, Skill.FARMING, "Saplings"), + MAGIC_SAPLING(ItemID.MAGIC_SAPLING, Skill.FARMING, "Saplings"), + APPLE_TREE_SAPLING(ItemID.APPLE_SAPLING, Skill.FARMING, "Saplings"), + BANANA_TREE_SAPLING(ItemID.BANANA_SAPLING, Skill.FARMING, "Saplings"), + ORANGE_TREE_SAPLING(ItemID.ORANGE_SAPLING, Skill.FARMING, "Saplings"), + CURRY_TREE_SAPLING(ItemID.CURRY_SAPLING, Skill.FARMING, "Saplings"), + PINEAPPLE_SAPLING(ItemID.PINEAPPLE_SAPLING, Skill.FARMING, "Saplings"), + PAPAYA_TREE_SAPLING(ItemID.PAPAYA_SAPLING, Skill.FARMING, "Saplings"), + PALM_TREE_SAPLING(ItemID.PALM_SAPLING, Skill.FARMING, "Saplings"), + CALQUAT_TREE_SAPLING(ItemID.CALQUAT_SAPLING, Skill.FARMING, "Saplings"), + TEAK_SAPLING(ItemID.TEAK_SAPLING, Skill.FARMING, "Saplings"), + MAHOGANY_SAPLING(ItemID.MAHOGANY_SAPLING, Skill.FARMING, "Saplings"), + SPIRIT_SAPLING(ItemID.SPIRIT_SAPLING, Skill.FARMING, "Saplings"), + ; + + private final int itemID; + private final Skill skill; + private final String category; + private boolean ignoreBonus; + + @Setter + // Stores the item composition info we use since we don't operate on the game thread + private ItemInfo itemInfo = null; + + @Setter + private Activity selectedActivity; + + private static final Multimap SKILL_MAP = ArrayListMultimap.create(); + private static final Map ITEM_ID_MAP = new HashMap<>(); + + static + { + for (CriticalItem i : values()) + { + Skill s = i.getSkill(); + SKILL_MAP.put(s, i); + ITEM_ID_MAP.put(i.getItemID(), i); + } + } + + CriticalItem(int itemID, Skill skill, String category, boolean ignoreBonus) + { + this.itemID = itemID; + this.category = category; + this.skill = skill; + this.ignoreBonus = ignoreBonus; + } + + CriticalItem(int itemID, Skill skill, String category) + { + this(itemID, skill, category, false); + } + + public static Collection getBySkill(Skill skill) + { + Collection items = SKILL_MAP.get(skill); + if (items == null) + { + items = new ArrayList<>(); + } + + return items; + } + + public static CriticalItem getByItemId(int id) + { + return ITEM_ID_MAP.get(id); + } + + /** + * Attaches the Item Composition to each CriticalItem on client initial load + * + * @param m ItemManager + */ + public static void prepareItemDefinitions(ItemManager m) + { + for (CriticalItem i : values()) + { + if (i.itemInfo != null) + { + return; + } + + final ItemDefinition c = m.getItemDefinition(i.getItemID()); + i.itemInfo = new ItemInfo(c.getName(), c.isStackable()); + } + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/SecondaryItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/ItemInfo.java similarity index 80% rename from runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/SecondaryItem.java rename to runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/ItemInfo.java index 0cc728d0f9..8bc3651ed9 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/SecondaryItem.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/ItemInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, TheStonedTurtle + * Copyright (c) 2019, TheStonedTurtle * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,23 +24,13 @@ */ package net.runelite.client.plugins.skillcalculator.banked.beans; -import lombok.AccessLevel; -import lombok.Getter; +import lombok.AllArgsConstructor; +import lombok.Data; -@Getter(AccessLevel.PUBLIC) -public class SecondaryItem +@Data +@AllArgsConstructor +public class ItemInfo { - private final int id; - private final int qty; - - SecondaryItem(int id, int qty) - { - this.id = id; - this.qty = qty; - } - - SecondaryItem(int id) - { - this(id, 1); - } -} + private String name; + private boolean stackable; +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/ItemStack.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/ItemStack.java new file mode 100644 index 0000000000..3b785f4020 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/ItemStack.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2019, TheStonedTurtle + * 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.skillcalculator.banked.beans; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +class ItemStack +{ + private int id; + private int qty; +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/Secondaries.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/Secondaries.java new file mode 100644 index 0000000000..cab6b7474f --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/Secondaries.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2018, TheStonedTurtle + * 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.skillcalculator.banked.beans; + +import lombok.Getter; +import net.runelite.api.ItemID; + +@Getter +public enum Secondaries +{ + /** + * Herblore + */ + UNFINISHED_POTION(new ItemStack(ItemID.VIAL_OF_WATER, 1)), + SWAMP_TAR(new ItemStack(ItemID.SWAMP_TAR, 15)), + // Guam + ATTACK_POTION(new ItemStack(ItemID.EYE_OF_NEWT, 1)), + // Marrentil + ANTIPOISON(new ItemStack(ItemID.UNICORN_HORN_DUST, 1)), + // Tarromin + STRENGTH_POTION(new ItemStack(ItemID.LIMPWURT_ROOT, 1)), + SERUM_207(new ItemStack(ItemID.ASHES, 1)), + // Harralander + COMPOST_POTION(new ItemStack(ItemID.VOLCANIC_ASH, 1)), + RESTORE_POTION(new ItemStack(ItemID.RED_SPIDERS_EGGS, 1)), + ENERGY_POTION(new ItemStack(ItemID.CHOCOLATE_DUST, 1)), + COMBAT_POTION(new ItemStack(ItemID.GOAT_HORN_DUST, 1)), + // Ranarr Weed + DEFENCE_POTION(new ItemStack(ItemID.WHITE_BERRIES, 1)), + PRAYER_POTION(new ItemStack(ItemID.SNAPE_GRASS, 1)), + // Toadflax + AGILITY_POTION(new ItemStack(ItemID.TOADS_LEGS, 1)), + SARADOMIN_BREW(new ItemStack(ItemID.CRUSHED_NEST, 1)), + // Irit + SUPER_ATTACK(new ItemStack(ItemID.EYE_OF_NEWT, 1)), + SUPERANTIPOISON(new ItemStack(ItemID.UNICORN_HORN_DUST, 1)), + // Avantoe + FISHING_POTION(new ItemStack(ItemID.SNAPE_GRASS, 1)), + SUPER_ENERGY_POTION(new ItemStack(ItemID.MORT_MYRE_FUNGUS, 1)), + HUNTER_POTION(new ItemStack(ItemID.KEBBIT_TEETH_DUST, 1)), + // Kwuarm + SUPER_STRENGTH(new ItemStack(ItemID.LIMPWURT_ROOT, 1)), + // Snapdragon + SUPER_RESTORE(new ItemStack(ItemID.RED_SPIDERS_EGGS, 1)), + SANFEW_SERUM(new ItemStack(ItemID.SNAKE_WEED, 1), new ItemStack(ItemID.UNICORN_HORN_DUST, 1), new ItemStack(ItemID.SUPER_RESTORE4, 1), new ItemStack(ItemID.NAIL_BEAST_NAILS, 1)), + // Cadantine + SUPER_DEFENCE_POTION(new ItemStack(ItemID.WHITE_BERRIES, 1)), + // Lantadyme + ANTIFIRE_POTION(new ItemStack(ItemID.DRAGON_SCALE_DUST, 1)), + MAGIC_POTION(new ItemStack(ItemID.POTATO_CACTUS, 1)), + // Dwarf Weed + RANGING_POTION(new ItemStack(ItemID.WINE_OF_ZAMORAK, 1)), + // Torstol + ZAMORAK_BREW(new ItemStack(ItemID.JANGERBERRIES, 1)), + SUPER_COMBAT_POTION(new ItemStack(ItemID.SUPER_ATTACK4, 1), new ItemStack(ItemID.SUPER_STRENGTH4, 1), new ItemStack(ItemID.SUPER_DEFENCE4, 1)), + ANTIVENOM_PLUS(new ItemStack(ItemID.ANTIVENOM4, 1)), + + /** + * Smithing + */ + COAL_ORE(new ItemStack(ItemID.COAL, 1)), + COAL_ORE_2(new ItemStack(ItemID.COAL, 2)), + COAL_ORE_4(new ItemStack(ItemID.COAL, 4)), + COAL_ORE_6(new ItemStack(ItemID.COAL, 6)), + COAL_ORE_8(new ItemStack(ItemID.COAL, 8)), + + /** + * Crafting + */ + GOLD_BAR(new ItemStack(ItemID.GOLD_BAR, 1)), + SILVER_BAR(new ItemStack(ItemID.SILVER_BAR, 1)), + WATER_ORB(new ItemStack(ItemID.WATER_ORB, 1)), + EARTH_ORB(new ItemStack(ItemID.EARTH_ORB, 1)), + FIRE_ORB(new ItemStack(ItemID.FIRE_ORB, 1)), + AIR_ORB(new ItemStack(ItemID.AIR_ORB, 1)), + + /** + * Construction + */ + COINS_100(new ItemStack(ItemID.COINS_995, 100)), + COINS_250(new ItemStack(ItemID.COINS_995, 250)), + COINS_500(new ItemStack(ItemID.COINS_995, 500)), + COINS_1500(new ItemStack(ItemID.COINS_995, 1500)), + + /** + * Cooking + */ + JUG_OF_WATER(new ItemStack(ItemID.JUG_OF_WATER, 1)), + ; + private final ItemStack[] items; + + Secondaries(ItemStack... items) + { + this.items = items; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/XpModifiers.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/XpModifiers.java new file mode 100644 index 0000000000..b9fcae99c6 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/XpModifiers.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2019, TheStonedTurtle + * 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.skillcalculator.banked.beans; + +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.Multimap; +import java.util.Collection; +import lombok.AllArgsConstructor; +import lombok.Getter; +import net.runelite.api.Skill; + +@AllArgsConstructor +@Getter +public enum XpModifiers +{ + LIT_GILDER_ALTAR(Skill.PRAYER, "Lit Gilded Altar (350%)", 3.5f), + ECTOFUNTUS(Skill.PRAYER, "Ectofuntus (400%)", 4), + WILDY_ALTAR(Skill.PRAYER, "Wildy Altar (700%)", 7), + + FARMERS_OUTFIT(Skill.FARMING, "Farmer's Outfit (+2.5%)", 1.025f); + + private final Skill skill; + private final String name; + private final float modifier; + + private final static Multimap MODIFIERS_MAP; + + static + { + final ImmutableMultimap.Builder map = ImmutableMultimap.builder(); + for (final XpModifiers m : values()) + { + map.put(m.skill, m); + } + MODIFIERS_MAP = map.build(); + } + + public static Collection getModifiersBySkill(final Skill skill) + { + return MODIFIERS_MAP.get(skill); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/components/GridItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/components/GridItem.java new file mode 100644 index 0000000000..9cb03ff36b --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/components/GridItem.java @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2019, TheStonedTurtle + * 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.skillcalculator.banked.components; + +import java.awt.Color; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.function.BooleanSupplier; +import javax.swing.BorderFactory; +import javax.swing.JLabel; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.SwingConstants; +import javax.swing.border.EmptyBorder; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; +import net.runelite.client.game.AsyncBufferedImage; +import net.runelite.client.plugins.skillcalculator.banked.BankedCalculator; +import net.runelite.client.plugins.skillcalculator.banked.beans.Activity; +import net.runelite.client.plugins.skillcalculator.banked.beans.BankedItem; +import net.runelite.client.ui.ColorScheme; + +@Getter(AccessLevel.PUBLIC) +public class GridItem extends JLabel +{ + private final static String IGNORE = "Ignore Item"; + private final static String INCLUDE = "Include Item"; + + private static final Color UNSELECTED_BACKGROUND = ColorScheme.DARKER_GRAY_COLOR; + private static final Color UNSELECTED_HOVER_BACKGROUND = ColorScheme.DARKER_GRAY_HOVER_COLOR; + + private static final Color SELECTED_BACKGROUND = new Color(0, 70, 0); + private static final Color SELECTED_HOVER_BACKGROUND = new Color(0, 100, 0); + + private static final Color IGNORED_BACKGROUND = new Color(90, 0, 0); + private static final Color IGNORED_HOVER_BACKGROUND = new Color(120, 0, 0); + + /* To be executed when this element is clicked */ + @Setter(AccessLevel.PUBLIC) + private BooleanSupplier onSelectEvent; + + /* To be executed when this element is ignored */ + @Setter(AccessLevel.PUBLIC) + private BooleanSupplier onIgnoreEvent; + + private final SelectionGrid parent; + private final BankedItem bankedItem; + private int amount; + + private boolean selected = false; + private boolean ignored = false; + + private final JMenuItem IGNORE_OPTION = new JMenuItem(IGNORE); + + GridItem(final SelectionGrid parent, final BankedItem item, final AsyncBufferedImage icon, final int amount) + { + super(""); + + this.parent = parent; + this.bankedItem = item; + + this.setOpaque(true); + this.setBackground(ColorScheme.DARKER_GRAY_COLOR); + this.setBorder(BorderFactory.createEmptyBorder(5, 0, 2, 0)); + + this.setVerticalAlignment(SwingConstants.CENTER); + this.setHorizontalAlignment(SwingConstants.CENTER); + + updateIcon(icon, amount); + updateToolTip(); + + this.addMouseListener(new MouseAdapter() + { + @Override + public void mousePressed(MouseEvent mouseEvent) + { + if (mouseEvent.getButton() == MouseEvent.BUTTON1) + { + select(); + } + } + + @Override + public void mouseEntered(MouseEvent e) + { + final GridItem item = (GridItem) e.getSource(); + item.setBackground(getHoverBackgroundColor()); + } + + @Override + public void mouseExited(MouseEvent e) + { + final GridItem item = (GridItem) e.getSource(); + item.setBackground(getBackgroundColor()); + } + }); + + IGNORE_OPTION.addActionListener(e -> + { + // Update ignored flag now so event knows new state + this.ignored = !this.ignored; + + if (onIgnoreEvent != null && !onIgnoreEvent.getAsBoolean()) + { + // Reset state + this.ignored = !this.ignored; + return; + } + + IGNORE_OPTION.setText(this.ignored ? INCLUDE : IGNORE); + this.setBackground(getBackgroundColor()); + }); + + final JPopupMenu popupMenu = new JPopupMenu(); + popupMenu.setBorder(new EmptyBorder(5, 5, 5, 5)); + popupMenu.add(IGNORE_OPTION); + + this.setComponentPopupMenu(popupMenu); + } + + private Color getBackgroundColor() + { + return ignored ? IGNORED_BACKGROUND : (selected ? SELECTED_BACKGROUND : UNSELECTED_BACKGROUND); + } + + private Color getHoverBackgroundColor() + { + return ignored ? IGNORED_HOVER_BACKGROUND : (selected ? SELECTED_HOVER_BACKGROUND : UNSELECTED_HOVER_BACKGROUND); + } + + public void select() + { + if (onSelectEvent != null && !onSelectEvent.getAsBoolean()) + { + return; + } + + selected = true; + setBackground(getBackgroundColor()); + } + + void unselect() + { + selected = false; + setBackground(getBackgroundColor()); + } + + public void updateIcon(final AsyncBufferedImage icon, final int amount) + { + icon.addTo(this); + this.amount = amount; + } + + public void updateToolTip() + { + this.setToolTipText(buildToolTip()); + } + + private String buildToolTip() + { + String tip = "" + bankedItem.getItem().getItemInfo().getName(); + + final Activity a = bankedItem.getItem().getSelectedActivity(); + if (a != null) + { + final double xp = parent.getCalc().getItemXpRate(bankedItem); + tip += "
Activity: " + a.getName(); + tip += "
Xp/Action: " + BankedCalculator.XP_FORMAT_COMMA.format(xp); + tip += "
Total Xp: " + BankedCalculator.XP_FORMAT_COMMA.format(xp * amount); + } + else + { + tip += "
Outputs: " + bankedItem.getItem().getItemInfo().getName(); + } + + return tip + ""; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/components/ModifyPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/components/ModifyPanel.java new file mode 100644 index 0000000000..c111789377 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/components/ModifyPanel.java @@ -0,0 +1,354 @@ +/* + * Copyright (c) 2018, TheStonedTurtle + * 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.skillcalculator.banked.components; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.GridLayout; +import java.awt.Image; +import java.awt.event.ItemEvent; +import java.text.DecimalFormat; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.swing.ImageIcon; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; +import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; +import lombok.AccessLevel; +import lombok.Getter; +import net.runelite.api.Constants; +import net.runelite.client.game.AsyncBufferedImage; +import net.runelite.client.game.ItemManager; +import net.runelite.client.plugins.skillcalculator.banked.BankedCalculator; +import net.runelite.client.plugins.skillcalculator.banked.beans.Activity; +import net.runelite.client.plugins.skillcalculator.banked.beans.BankedItem; +import net.runelite.client.plugins.skillcalculator.banked.beans.CriticalItem; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.FontManager; +import net.runelite.client.ui.components.ComboBoxIconEntry; +import net.runelite.client.ui.components.ComboBoxListRenderer; +import net.runelite.client.ui.components.shadowlabel.JShadowedLabel; + +public class ModifyPanel extends JPanel +{ + private static final Dimension ICON_SIZE = new Dimension(Constants.ITEM_SPRITE_WIDTH, Constants.ITEM_SPRITE_HEIGHT); + private static final DecimalFormat FORMAT_COMMA = new DecimalFormat("#,###.#"); + + private static final Border PANEL_BORDER = new EmptyBorder(3, 0, 3, 0); + private static final Color BACKGROUND_COLOR = ColorScheme.DARKER_GRAY_COLOR; + + private final BankedCalculator calc; + private final ItemManager itemManager; + + @Getter(AccessLevel.PUBLIC) + private BankedItem bankedItem; + private Map linkedMap; + @Getter(AccessLevel.PUBLIC) + private int amount = 0; + @Getter(AccessLevel.PUBLIC) + private double total = 0; + + // Banked item information display + private final JPanel labelContainer; + private final JLabel image; + private final JShadowedLabel labelName; + private final JShadowedLabel labelValue; + + // Elements used to adjust banked item + private final JPanel adjustContainer; + + public ModifyPanel(final BankedCalculator calc, final ItemManager itemManager) + { + this.calc = calc; + this.itemManager = itemManager; + + this.setLayout(new GridBagLayout()); + this.setBorder(PANEL_BORDER); + this.setBackground(ColorScheme.DARK_GRAY_COLOR); + + // Banked item information display + labelContainer = new JPanel(); + labelContainer.setLayout(new BorderLayout()); + labelContainer.setBackground(BACKGROUND_COLOR); + labelContainer.setBorder(new EmptyBorder(5, 0, 5, 0)); + + // Icon + image = new JLabel(); + image.setMinimumSize(ICON_SIZE); + image.setMaximumSize(ICON_SIZE); + image.setPreferredSize(ICON_SIZE); + image.setHorizontalAlignment(SwingConstants.CENTER); + image.setBorder(new EmptyBorder(0, 8, 0, 0)); + + // Wrapper panel for the shadowed labels + final JPanel uiInfo = new JPanel(new GridLayout(2, 1)); + uiInfo.setBorder(new EmptyBorder(0, 5, 0, 0)); + uiInfo.setBackground(BACKGROUND_COLOR); + + labelName = new JShadowedLabel(); + labelName.setForeground(Color.WHITE); + labelName.setVerticalAlignment(SwingUtilities.BOTTOM); + + labelValue = new JShadowedLabel(); + labelValue.setFont(FontManager.getRunescapeSmallFont()); + labelValue.setVerticalAlignment(SwingUtilities.TOP); + + uiInfo.add(labelName); + uiInfo.add(labelValue); + + // Append elements to item info panel + labelContainer.add(image, BorderLayout.LINE_START); + labelContainer.add(uiInfo, BorderLayout.CENTER); + + // Container for tools to adjust banked calculation for this item + adjustContainer = new JPanel(); + adjustContainer.setLayout(new GridBagLayout()); + adjustContainer.setBackground(BACKGROUND_COLOR); + + GridBagConstraints c = new GridBagConstraints(); + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + c.gridx = 0; + c.gridy = 0; + c.ipady = 0; + + this.add(labelContainer, c); + c.gridy++; + this.add(adjustContainer, c); + } + + // Updates the UI for the selected item + public void setBankedItem(final BankedItem bankedItem) + { + if (bankedItem == null) + { + return; + } + + this.bankedItem = bankedItem; + if (this.calc.getConfig().cascadeBankedXp()) + { + this.linkedMap = this.calc.createLinksMap(bankedItem); + + this.amount = bankedItem.getQty(); + for (int i : linkedMap.values()) + { + this.amount += i; + } + } + else + { + this.linkedMap = new HashMap<>(); + this.amount = this.calc.getItemQty(bankedItem); + } + + updateImageTooltip(); + updateLabelContainer(); + updateAdjustContainer(); + } + + private void updateImageTooltip() + { + final StringBuilder b = new StringBuilder(""); + b.append(bankedItem.getQty()).append(" x ").append(bankedItem.getItem().getItemInfo().getName()); + + for (final Map.Entry e : this.linkedMap.entrySet()) + { + b.append("
").append(e.getValue()).append(" x ").append(e.getKey().getItemInfo().getName()); + } + + b.append(""); + this.image.setToolTipText(b.toString()); + } + + private void updateLabelContainer() + { + final CriticalItem item = bankedItem.getItem(); + + // Update image icon + final boolean stackable = item.getItemInfo().isStackable() || amount > 1; + final AsyncBufferedImage icon = itemManager.getImage(item.getItemID(), amount, stackable); + final Runnable resize = () -> image.setIcon(new ImageIcon(icon.getScaledInstance(ICON_SIZE.width, ICON_SIZE.height, Image.SCALE_SMOOTH))); + icon.onChanged(resize); + resize.run(); + + final String itemName = item.getItemInfo().getName(); + labelName.setText(itemName); + + double xp = calc.getItemXpRate(bankedItem); + total = amount * xp; + + final String value = FORMAT_COMMA.format(total) + "xp"; + labelValue.setText(value); + + labelContainer.setToolTipText("" + itemName + + "
xp: " + xp + + "
Total: " + total + " activities = Activity.getByCriticalItem(bankedItem.getItem(), calc.getSkillLevel()); + if (activities == null || activities.size() == 0) + { + adjustContainer.add(new JLabel("Unknown")); + } + else if (activities.size() == 1) + { + final Activity a = activities.get(0); + + final AsyncBufferedImage img = itemManager.getImage(a.getIcon()); + final ImageIcon icon = new ImageIcon(img); + final double xp = a.getXp() * xpFactor; + final JPanel container = createShadowedLabel(icon, a.getName(), FORMAT_COMMA.format(xp) + "xp"); + + img.onChanged(() -> + { + icon.setImage(img); + container.repaint(); + }); + + adjustContainer.add(container, c); + } + else + { + final JComboBox dropdown = new JComboBox<>(); + final ComboBoxListRenderer renderer = new ComboBoxListRenderer(); + dropdown.setRenderer(renderer); + + for (final Activity option : activities) + { + final double xp = option.getXp() * xpFactor; + String name = option.getName(); + if (xp > 0) + { + name += " (" + FORMAT_COMMA.format(xp) + "xp)"; + } + + final AsyncBufferedImage img = itemManager.getImage(option.getIcon()); + final ImageIcon icon = new ImageIcon(img); + final ComboBoxIconEntry entry = new ComboBoxIconEntry(icon, name, option); + dropdown.addItem(entry); + + img.onChanged(() -> + { + icon.setImage(img); + dropdown.revalidate(); + dropdown.repaint(); + }); + + final Activity selected = bankedItem.getItem().getSelectedActivity(); + if (option.equals(selected)) + { + dropdown.setSelectedItem(entry); + } + } + + // Add click event handler now to prevent above code from triggering it. + dropdown.addItemListener(e -> + { + if (e.getStateChange() == ItemEvent.SELECTED && e.getItem() instanceof ComboBoxIconEntry) + { + final ComboBoxIconEntry source = (ComboBoxIconEntry) e.getItem(); + if (source.getData() instanceof Activity) + { + final Activity selectedActivity = ((Activity) source.getData()); + calc.activitySelected(bankedItem, selectedActivity); + updateLabelContainer(); + } + } + }); + + adjustContainer.add(dropdown, c); + } + } + + private JPanel createShadowedLabel(final ImageIcon icon, final String name, final String value) + { + // Wrapper panel for the shadowed labels + final JPanel wrapper = new JPanel(new GridLayout(2, 1)); + wrapper.setBorder(new EmptyBorder(0, 5, 0, 0)); + wrapper.setBackground(BACKGROUND_COLOR); + + final JShadowedLabel nameLabel = new JShadowedLabel(name); + nameLabel.setForeground(Color.WHITE); + nameLabel.setVerticalAlignment(SwingUtilities.BOTTOM); + + final JShadowedLabel valueLabel = new JShadowedLabel(value); + valueLabel.setFont(FontManager.getRunescapeSmallFont()); + valueLabel.setVerticalAlignment(SwingUtilities.TOP); + + wrapper.add(nameLabel); + wrapper.add(valueLabel); + + final JPanel container = new JPanel(); + container.setLayout(new BorderLayout()); + container.setBackground(BACKGROUND_COLOR); + container.setBorder(new EmptyBorder(5, 0, 5, 0)); + + final JLabel image = new JLabel(); + image.setMinimumSize(ICON_SIZE); + image.setMaximumSize(ICON_SIZE); + image.setPreferredSize(ICON_SIZE); + image.setHorizontalAlignment(SwingConstants.CENTER); + image.setBorder(new EmptyBorder(0, 8, 0, 0)); + + image.setIcon(icon); + + container.add(image, BorderLayout.LINE_START); + container.add(wrapper, BorderLayout.CENTER); + + return container; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/components/SelectionGrid.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/components/SelectionGrid.java new file mode 100644 index 0000000000..2449857920 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/components/SelectionGrid.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2018, TheStonedTurtle + * 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.skillcalculator.banked.components; + +import java.awt.GridLayout; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BooleanSupplier; +import java.util.stream.Collectors; +import javax.swing.JPanel; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; +import net.runelite.client.game.AsyncBufferedImage; +import net.runelite.client.game.ItemManager; +import net.runelite.client.plugins.skillcalculator.banked.BankedCalculator; +import net.runelite.client.plugins.skillcalculator.banked.beans.BankedItem; + +/** + * A grid that supports mouse events + */ +public class SelectionGrid extends JPanel +{ + private static final int ITEMS_PER_ROW = 5; + + @Getter(AccessLevel.PUBLIC) + private final Map panelMap = new LinkedHashMap<>(); + + @Getter(AccessLevel.PUBLIC) + private BankedItem selectedItem; + + @Getter(AccessLevel.PUBLIC) + private BankedItem lastIgnoredItem; + + /* To be executed when this element is clicked */ + @Setter(AccessLevel.PUBLIC) + private BooleanSupplier onSelectEvent; + + /* To be executed when this element is ignored */ + @Setter(AccessLevel.PUBLIC) + private BooleanSupplier onIgnoreEvent; + + @Getter(AccessLevel.PUBLIC) + private final BankedCalculator calc; + + public SelectionGrid(final BankedCalculator calc, final Collection items, final ItemManager itemManager) + { + this.calc = calc; + // Create a panel for every item + for (final BankedItem item : items) + { + final int qty = calc.getItemQty(item); + final boolean stackable = item.getItem().getItemInfo().isStackable() || qty > 1; + final AsyncBufferedImage img = itemManager.getImage(item.getItem().getItemID(), qty, stackable); + + final GridItem gridItem = new GridItem(this, item, img, qty); + + gridItem.setOnSelectEvent(() -> selected(item)); + gridItem.setOnIgnoreEvent(() -> ignore(item)); + panelMap.put(item, gridItem); + } + + refreshGridDisplay(); + } + + public void refreshGridDisplay() + { + this.removeAll(); + + final List items = panelMap.values().stream().filter(gi -> gi.getAmount() > 0).collect(Collectors.toList()); + + // Calculates how many rows need to be display to fit all items + final int rowSize = ((items.size() % ITEMS_PER_ROW == 0) ? 0 : 1) + items.size() / ITEMS_PER_ROW; + setLayout(new GridLayout(rowSize, ITEMS_PER_ROW, 1, 1)); + + for (final GridItem gridItem : items) + { + // Select the first option + if (selectedItem == null) + { + gridItem.select(); + } + + this.add(gridItem); + } + } + + private boolean selected(final BankedItem item) + { + final BankedItem old = this.selectedItem; + if (item.equals(old)) + { + return false; + } + + // Set selected item now so the boolean can see what was just clicked + this.selectedItem = item; + if (onSelectEvent != null && !onSelectEvent.getAsBoolean()) + { + this.selectedItem = old; + return false; + } + + final GridItem gridItem = panelMap.get(old); + if (gridItem != null) + { + gridItem.unselect(); + } + + return true; + } + + private boolean ignore(final BankedItem item) + { + this.lastIgnoredItem = item; + return onIgnoreEvent.getAsBoolean(); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/ui/CriticalItemPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/ui/CriticalItemPanel.java deleted file mode 100644 index 1b12419ec0..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/ui/CriticalItemPanel.java +++ /dev/null @@ -1,402 +0,0 @@ -/* - * Copyright (c) 2018, TheStonedTurtle - * 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.skillcalculator.banked.ui; - -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Dimension; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.GridLayout; -import java.awt.Image; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.image.BufferedImage; -import java.io.IOException; -import java.text.DecimalFormat; -import java.util.List; -import java.util.Map; -import javax.imageio.ImageIO; -import javax.inject.Singleton; -import javax.swing.ImageIcon; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.SwingConstants; -import javax.swing.SwingUtilities; -import javax.swing.border.Border; -import javax.swing.border.EmptyBorder; -import javax.swing.border.MatteBorder; -import lombok.AccessLevel; -import lombok.Getter; -import net.runelite.client.game.AsyncBufferedImage; -import net.runelite.client.game.ItemManager; -import net.runelite.client.plugins.skillcalculator.BankedCalculator; -import net.runelite.client.plugins.skillcalculator.banked.CriticalItem; -import net.runelite.client.plugins.skillcalculator.banked.beans.Activity; -import net.runelite.client.ui.ColorScheme; -import net.runelite.client.ui.FontManager; -import net.runelite.client.ui.components.materialtabs.MaterialTab; -import net.runelite.client.ui.components.materialtabs.MaterialTabGroup; -import net.runelite.client.ui.components.shadowlabel.JShadowedLabel; -import net.runelite.client.util.StackFormatter; - -@Singleton -public class CriticalItemPanel extends JPanel -{ - private static final Dimension ICON_SIZE = new Dimension(36, 36); - private static final DecimalFormat FORMAT_COMMA = new DecimalFormat("#,###.#"); - - private static final BufferedImage ICON_SETTINGS; - - private static final Border PANEL_BORDER = new EmptyBorder(3, 0, 3, 0); - private final static Color BACKGROUND_COLOR = ColorScheme.DARKER_GRAY_COLOR; - private final static Color BUTTON_HOVER_COLOR = ColorScheme.DARKER_GRAY_HOVER_COLOR; - - static - { - BufferedImage i1; - try - { - synchronized (ImageIO.class) - { - i1 = ImageIO.read(BankedCalculator.class.getResourceAsStream("view-more-white.png")); - } - } - catch (IOException e) - { - throw new RuntimeException(e); - } - ICON_SETTINGS = i1; - } - - private final BankedCalculator bankedCalculator; - private final CriticalItem item; - private final ItemManager itemManager; - private double xp; - @Getter(AccessLevel.PUBLIC) - private int amount; - @Getter(AccessLevel.PUBLIC) - private double total; - private Map linkedMap; - private JShadowedLabel labelValue; - - private final JPanel infoContainer; - private final JLabel image; - private boolean infoVisibility = false; - - public CriticalItemPanel(BankedCalculator bankedCalculator, ItemManager itemManager, CriticalItem item, double xp, int amount, Map linkedMap) - { - this.bankedCalculator = bankedCalculator; - this.item = item; - this.xp = xp; - this.amount = amount; - this.total = xp * amount; - this.itemManager = itemManager; - this.linkedMap = linkedMap; - - this.setLayout(new GridBagLayout()); - this.setBorder(PANEL_BORDER); - this.setBackground(ColorScheme.DARK_GRAY_COLOR); - this.setVisible(this.amount > 0); - - infoContainer = new JPanel(); - infoContainer.setLayout(new GridBagLayout()); - infoContainer.setVisible(false); - infoContainer.setBackground(BACKGROUND_COLOR); - infoContainer.setBorder(new MatteBorder(1, 0, 0, 0, Color.GRAY)); - - // Icon - AsyncBufferedImage icon = itemManager.getImage(item.getItemID(), amount, item.getDefinition().isStackable() || amount > 1); - image = new JLabel(); - image.setMinimumSize(ICON_SIZE); - image.setMaximumSize(ICON_SIZE); - image.setPreferredSize(ICON_SIZE); - image.setHorizontalAlignment(SwingConstants.CENTER); - image.setBorder(new EmptyBorder(0, 8, 0, 0)); - - Runnable resize = () -> - image.setIcon(new ImageIcon(icon.getScaledInstance((int) ICON_SIZE.getWidth(), (int) ICON_SIZE.getHeight(), Image.SCALE_SMOOTH))); - icon.onChanged(resize); - resize.run(); - - // Container for Info - JPanel uiInfo = new JPanel(new GridLayout(2, 1)); - uiInfo.setBorder(new EmptyBorder(0, 5, 0, 0)); - uiInfo.setBackground(BACKGROUND_COLOR); - - JShadowedLabel labelName = new JShadowedLabel(item.getDefinition().getName()); - labelName.setForeground(Color.WHITE); - labelName.setVerticalAlignment(SwingUtilities.BOTTOM); - - labelValue = new JShadowedLabel(); - labelValue.setFont(FontManager.getRunescapeSmallFont()); - labelValue.setVerticalAlignment(SwingUtilities.TOP); - updateXp(xp); - - uiInfo.add(labelName); - uiInfo.add(labelValue); - - // Settings Button - JLabel settingsButton = new JLabel(); - settingsButton.setBorder(new EmptyBorder(0, 5, 0, 5)); - settingsButton.setIcon(new ImageIcon(ICON_SETTINGS)); - settingsButton.setOpaque(true); - settingsButton.setBackground(BACKGROUND_COLOR); - - settingsButton.addMouseListener(new MouseAdapter() - { - @Override - public void mouseEntered(MouseEvent e) - { - settingsButton.setBackground(BUTTON_HOVER_COLOR); - } - - @Override - public void mouseExited(MouseEvent e) - { - settingsButton.setBackground(BACKGROUND_COLOR); - } - - @Override - public void mouseClicked(MouseEvent e) - { - toggleInfo(); - } - }); - - // Create and append elements to container panel - JPanel panel = new JPanel(); - panel.setLayout(new BorderLayout()); - panel.setBackground(BACKGROUND_COLOR); - - panel.add(image, BorderLayout.LINE_START); - panel.add(uiInfo, BorderLayout.CENTER); - - // Only add button if has activity selection options or linked items - List activities = Activity.getByCriticalItem(item); - // If linked map has 1 item and it isn't this item still show breakdown (cleaned herbs into unfinished) - if ((linkedMap.size() > 1 || (linkedMap.size() == 1 && linkedMap.get(item) == null)) - || activities.size() > 1) - { - panel.add(settingsButton, BorderLayout.LINE_END); - } - - panel.setToolTipText("" + item.getDefinition().getName() - + "
xp: " + xp - + "
Total: " + StackFormatter.quantityToStackSize((long) total) + " 1 || (linkedMap.size() == 1 && linkedMap.get(item) == null)) - { - JLabel l = new JLabel("Item Breakdown"); - l.setBorder(new EmptyBorder(3, 0, 3, 0)); - l.setHorizontalAlignment(JLabel.CENTER); - infoContainer.add(l, c); - c.gridy++; - - JPanel con = new JPanel(); - con.setLayout(new GridBagLayout()); - con.setBackground(BACKGROUND_COLOR); - for (Map.Entry e : linkedMap.entrySet()) - { - // Icon - AsyncBufferedImage icon = itemManager.getImage(e.getKey().getItemID(), e.getValue(), e.getKey().getDefinition().isStackable() || e.getValue() > 1); - JLabel image = new JLabel(); - image.setMinimumSize(ICON_SIZE); - image.setMaximumSize(ICON_SIZE); - image.setPreferredSize(ICON_SIZE); - image.setHorizontalAlignment(SwingConstants.CENTER); - image.setBorder(new EmptyBorder(0, 8, 0, 0)); - - Runnable resize = () -> - image.setIcon(new ImageIcon(icon.getScaledInstance((int) ICON_SIZE.getWidth(), (int) ICON_SIZE.getHeight(), Image.SCALE_SMOOTH))); - icon.onChanged(resize); - resize.run(); - - image.setToolTipText(e.getKey().getDefinition().getName()); - - con.add(image, c); - c.gridx++; - } - c.gridx = 0; - infoContainer.add(con, c); - } - - } - - private JPanel createActivitiesPanel() - { - List activities = Activity.getByCriticalItem(item); - if (activities == null || activities.size() == 1) - { - return null; - } - - JPanel p = new JPanel(); - p.setBackground(BACKGROUND_COLOR); - p.setLayout(new BorderLayout()); - - JLabel label = new JLabel("Possible training methods"); - - MaterialTabGroup group = new MaterialTabGroup(); - group.setLayout(new GridLayout(0, 6, 0, 2)); - group.setBorder(new MatteBorder(1, 1, 1, 1, Color.BLACK)); - - Activity selected = this.bankedCalculator.getSelectedActivity(this.item); - boolean s = false; - - for (Activity option : activities) - { - AsyncBufferedImage icon = itemManager.getImage(option.getIcon()); - MaterialTab matTab = new MaterialTab("", group, null); - matTab.setHorizontalAlignment(SwingUtilities.RIGHT); - matTab.setToolTipText(option.getName()); - - Runnable resize = () -> - matTab.setIcon(new ImageIcon(icon.getScaledInstance(24, 24, Image.SCALE_SMOOTH))); - icon.onChanged(resize); - resize.run(); - - group.addTab(matTab); - - // Select first option by default - if (!s) - { - s = true; - group.select(matTab); - } - - // Select the option if its their selected activity - if (option.equals(selected)) - { - group.select(matTab); - } - - // Add click event handler now to prevent above code from triggering it. - matTab.setOnSelectEvent(() -> - { - bankedCalculator.activitySelected(item, option); - return true; - }); - } - - p.add(label, BorderLayout.NORTH); - p.add(group, BorderLayout.SOUTH); - - return p; - } - - public void updateXp(double newXpRate) - { - xp = newXpRate; - total = xp * amount; - labelValue.setText(FORMAT_COMMA.format(total) + "xp"); - } - - public void updateAmount(int newAmount, boolean forceVisible) - { - this.setVisible(newAmount > 0 || forceVisible); - this.amount = newAmount; - AsyncBufferedImage icon = itemManager.getImage(item.getItemID(), amount, item.getDefinition().isStackable() || amount > 1); - Runnable resize = () -> - image.setIcon(new ImageIcon(icon.getScaledInstance((int) ICON_SIZE.getWidth(), (int) ICON_SIZE.getHeight(), Image.SCALE_SMOOTH))); - icon.onChanged(resize); - resize.run(); - } - - public void updateLinkedMap(Map newLinkedMap) - { - this.linkedMap = newLinkedMap; - - int sum = 0; - for (Integer v : newLinkedMap.values()) - { - sum += v; - } - this.updateAmount(sum, false); - - this.updateXp(xp); - - // Refresh info panel if visible - if (infoVisibility) - { - createInfoPanel(); - } - } - - public void recalculate() - { - updateXp(xp); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/beans/SkillData.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/beans/SkillData.java index 0d5b1cad40..4c2d36aa68 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/beans/SkillData.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/beans/SkillData.java @@ -24,10 +24,9 @@ */ package net.runelite.client.plugins.skillcalculator.beans; -import lombok.AccessLevel; import lombok.Getter; -@Getter(AccessLevel.PUBLIC) +@Getter public class SkillData { private SkillDataEntry[] actions; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/beans/SkillDataBonus.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/beans/SkillDataBonus.java index 6cce75a62e..675fcb51db 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/beans/SkillDataBonus.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/beans/SkillDataBonus.java @@ -24,10 +24,9 @@ */ package net.runelite.client.plugins.skillcalculator.beans; -import lombok.AccessLevel; import lombok.Getter; -@Getter(AccessLevel.PUBLIC) +@Getter public class SkillDataBonus { private String name; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/beans/SkillDataEntry.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/beans/SkillDataEntry.java index 05ec9c878a..b21188d913 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/beans/SkillDataEntry.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/beans/SkillDataEntry.java @@ -24,10 +24,9 @@ */ package net.runelite.client.plugins.skillcalculator.beans; -import lombok.AccessLevel; import lombok.Getter; -@Getter(AccessLevel.PUBLIC) +@Getter public class SkillDataEntry { private String name; diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/ComboBoxIconEntry.java b/runelite-client/src/main/java/net/runelite/client/ui/components/ComboBoxIconEntry.java new file mode 100644 index 0000000000..7c94851bc9 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/ComboBoxIconEntry.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2019, TheStonedTurtle + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.ui.components; + +import javax.annotation.Nullable; +import javax.swing.Icon; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * Used with ComboBoxListRenderer to render an icon next to the text of the list entry. + * Also supports adding a data object to be used for more complex selection logic + */ +@AllArgsConstructor +@Getter +public class ComboBoxIconEntry +{ + private Icon icon; + private String text; + @Nullable + private Object data; +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/ComboBoxListRenderer.java b/runelite-client/src/main/java/net/runelite/client/ui/components/ComboBoxListRenderer.java index 5aeb710115..907170f412 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/components/ComboBoxListRenderer.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/ComboBoxListRenderer.java @@ -30,6 +30,7 @@ import javax.swing.JLabel; import javax.swing.JList; import javax.swing.ListCellRenderer; import javax.swing.border.EmptyBorder; +import lombok.Setter; import net.runelite.client.ui.ColorScheme; import net.runelite.client.util.Text; @@ -41,6 +42,8 @@ import net.runelite.client.util.Text; */ public final class ComboBoxListRenderer extends JLabel implements ListCellRenderer { + @Setter + private String defaultText = "Select an option..."; @Override public Component getListCellRendererComponent(JList list, Object o, int index, boolean isSelected, boolean cellHasFocus) @@ -57,12 +60,24 @@ public final class ComboBoxListRenderer extends JLabel implements ListCellRender } setBorder(new EmptyBorder(5, 5, 5, 0)); + setIcon(null); String text; - if (o instanceof Enum) + // If using setSelectedItem(null) or setSelectedIndex(-1) show default text until a selection is made + if (index == -1 && o == null) + { + text = defaultText; + } + else if (o instanceof Enum) { text = Text.titleCase((Enum) o); } + else if (o instanceof ComboBoxIconEntry) + { + ComboBoxIconEntry e = (ComboBoxIconEntry) o; + text = e.getText(); + setIcon(e.getIcon()); + } else { text = o.toString(); From a026eb05211aa484c47168d83405914dbeb561df Mon Sep 17 00:00:00 2001 From: Owain van Brakel Date: Wed, 10 Jul 2019 21:06:36 +0200 Subject: [PATCH 13/18] statusorbsplugin: Merge regenmeter and runenergy plugins Add dynamic heart to status orbs plugin based on poison / disease vars Add run enery regen Add smoothing over the regen --- .../plugins/regenmeter/RegenMeterOverlay.java | 126 ----- .../plugins/regenmeter/RegenMeterPlugin.java | 201 ------- .../plugins/runenergy/RunEnergyConfig.java | 43 -- .../plugins/runenergy/RunEnergyOverlay.java | 109 ---- .../plugins/runenergy/RunEnergyPlugin.java | 340 ------------ .../StatusOrbsConfig.java} | 110 +++- .../plugins/statusorbs/StatusOrbsOverlay.java | 228 ++++++++ .../plugins/statusorbs/StatusOrbsPlugin.java | 490 ++++++++++++++++++ .../net/runelite/client/util/Graceful.java | 102 ++++ .../client/plugins/skillcalculator/banked.png | Bin 0 -> 2246 bytes .../plugins/statusorbs/1067-DISEASE.png | Bin 0 -> 2991 bytes .../client/plugins/statusorbs/1067-POISON.png | Bin 0 -> 2992 bytes .../client/plugins/statusorbs/1067-VENOM.png | Bin 0 -> 2986 bytes 13 files changed, 916 insertions(+), 833 deletions(-) delete mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterOverlay.java delete mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterPlugin.java delete mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/runenergy/RunEnergyConfig.java delete mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/runenergy/RunEnergyOverlay.java delete mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/runenergy/RunEnergyPlugin.java rename runelite-client/src/main/java/net/runelite/client/plugins/{regenmeter/RegenMeterConfig.java => statusorbs/StatusOrbsConfig.java} (56%) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/statusorbs/StatusOrbsOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/statusorbs/StatusOrbsPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/util/Graceful.java create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/skillcalculator/banked.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/statusorbs/1067-DISEASE.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/statusorbs/1067-POISON.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/statusorbs/1067-VENOM.png diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterOverlay.java deleted file mode 100644 index f95edccb1b..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterOverlay.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (c) 2018 Abex - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.runelite.client.plugins.regenmeter; - -import java.awt.BasicStroke; -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Graphics2D; -import java.awt.Rectangle; -import java.awt.RenderingHints; -import java.awt.Stroke; -import java.awt.geom.Arc2D; -import javax.inject.Inject; -import javax.inject.Singleton; -import net.runelite.api.Client; -import net.runelite.api.VarPlayer; -import net.runelite.api.widgets.Widget; -import net.runelite.api.widgets.WidgetInfo; -import net.runelite.client.ui.overlay.Overlay; -import net.runelite.client.ui.overlay.OverlayLayer; -import net.runelite.client.ui.overlay.OverlayPosition; - -@Singleton -public class RegenMeterOverlay extends Overlay -{ - private static final Color HITPOINTS_COLOR = brighter(0x9B0703); - private static final Color SPECIAL_COLOR = brighter(0x1E95B0); - private static final Color OVERLAY_COLOR = new Color(255, 255, 255, 60); - private static final double DIAMETER = 26D; - private static final int OFFSET = 27; - private static final Stroke STROKE = new BasicStroke(2f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER); - - private final Client client; - private final RegenMeterPlugin plugin; - - private Rectangle getBounds(WidgetInfo widgetInfo) - { - Widget widget = client.getWidget(widgetInfo); - if (widget == null || widget.isHidden()) - { - return null; - } - return widget.getBounds(); - } - - private static Color brighter(int color) - { - float[] hsv = new float[3]; - Color.RGBtoHSB(color >>> 16, (color >> 8) & 0xFF, color & 0xFF, hsv); - return Color.getHSBColor(hsv[0], 1.f, 1.f); - } - - @Inject - public RegenMeterOverlay(final Client client, final RegenMeterPlugin plugin) - { - setPosition(OverlayPosition.DYNAMIC); - setLayer(OverlayLayer.ABOVE_WIDGETS); - this.client = client; - this.plugin = plugin; - } - - @Override - public Dimension render(Graphics2D g) - { - g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); - - if (plugin.isShowHitpoints()) - { - renderRegen(g, WidgetInfo.MINIMAP_HEALTH_ORB, plugin.getHitpointsPercentage(), HITPOINTS_COLOR); - } - - if (plugin.isShowSpecial()) - { - if (client.getVar(VarPlayer.SPECIAL_ATTACK_ENABLED) == 1) - { - final Rectangle bounds = getBounds(WidgetInfo.MINIMAP_SPEC_ORB); - if (bounds != null) - { - g.setColor(RegenMeterOverlay.OVERLAY_COLOR); - g.fillOval( - bounds.x + OFFSET, - bounds.y + (int) (bounds.height / 2D - (DIAMETER) / 2D), - (int) DIAMETER, (int) DIAMETER); - } - } - - renderRegen(g, WidgetInfo.MINIMAP_SPEC_ORB, plugin.getSpecialPercentage(), SPECIAL_COLOR); - } - - return null; - } - - private void renderRegen(Graphics2D g, WidgetInfo widgetInfo, double percent, Color color) - { - final Rectangle bounds = getBounds(widgetInfo); - if (bounds != null) - { - Arc2D.Double arc = new Arc2D.Double(bounds.x + OFFSET, bounds.y + (bounds.height / 2 - DIAMETER / 2), DIAMETER, DIAMETER, 90.d, -360.d * percent, Arc2D.OPEN); - g.setStroke(STROKE); - g.setColor(color); - g.draw(arc); - } - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterPlugin.java deleted file mode 100644 index cc5b57e8ed..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterPlugin.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (c) 2019, Sean Dewar - * Copyright (c) 2018, Abex - * Copyright (c) 2018, Zimaya - * Copyright (c) 2017, Adam - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.runelite.client.plugins.regenmeter; - -import com.google.inject.Provides; -import javax.inject.Inject; -import javax.inject.Singleton; -import lombok.AccessLevel; -import lombok.Getter; -import net.runelite.api.Client; -import net.runelite.api.Constants; -import net.runelite.api.GameState; -import net.runelite.api.Prayer; -import net.runelite.api.Skill; -import net.runelite.api.VarPlayer; -import net.runelite.api.events.ConfigChanged; -import net.runelite.api.events.GameStateChanged; -import net.runelite.api.events.GameTick; -import net.runelite.api.events.VarbitChanged; -import net.runelite.client.Notifier; -import net.runelite.client.config.ConfigManager; -import net.runelite.client.eventbus.Subscribe; -import net.runelite.client.plugins.Plugin; -import net.runelite.client.plugins.PluginDescriptor; -import net.runelite.client.ui.overlay.OverlayManager; - -@PluginDescriptor( - name = "Regeneration Meter", - description = "Track and show the hitpoints and special attack regeneration timers", - tags = {"combat", "health", "hitpoints", "special", "attack", "overlay", "notifications"} -) -@Singleton -public class RegenMeterPlugin extends Plugin -{ - private static final int SPEC_REGEN_TICKS = 50; - private static final int NORMAL_HP_REGEN_TICKS = 100; - - @Inject - private Client client; - - @Inject - private OverlayManager overlayManager; - - @Inject - private Notifier notifier; - - @Inject - private RegenMeterOverlay overlay; - - @Inject - private RegenMeterConfig config; - - @Getter(AccessLevel.PACKAGE) - private double hitpointsPercentage; - - @Getter(AccessLevel.PACKAGE) - private double specialPercentage; - - private int ticksSinceSpecRegen; - private int ticksSinceHPRegen; - private boolean wasRapidHeal; - - @Getter(AccessLevel.PACKAGE) - private boolean showHitpoints; - @Getter(AccessLevel.PACKAGE) - private boolean showSpecial; - private boolean showWhenNoChange; - private int getNotifyBeforeHpRegenSeconds; - - @Provides - RegenMeterConfig provideConfig(ConfigManager configManager) - { - return configManager.getConfig(RegenMeterConfig.class); - } - - @Override - protected void startUp() throws Exception - { - updateConfig(); - overlayManager.add(overlay); - } - - @Override - protected void shutDown() throws Exception - { - overlayManager.remove(overlay); - } - - @Subscribe - private void onGameStateChanged(GameStateChanged ev) - { - if (ev.getGameState() == GameState.HOPPING || ev.getGameState() == GameState.LOGIN_SCREEN) - { - ticksSinceHPRegen = -2; // For some reason this makes this accurate - ticksSinceSpecRegen = 0; - } - } - - @Subscribe - private void onVarbitChanged(VarbitChanged ev) - { - boolean isRapidHeal = client.isPrayerActive(Prayer.RAPID_HEAL); - if (wasRapidHeal != isRapidHeal) - { - ticksSinceHPRegen = 0; - } - wasRapidHeal = isRapidHeal; - } - - @Subscribe - public void onGameTick(GameTick event) - { - if (client.getVar(VarPlayer.SPECIAL_ATTACK_PERCENT) == 1000) - { - // The recharge doesn't tick when at 100% - ticksSinceSpecRegen = 0; - } - else - { - ticksSinceSpecRegen = (ticksSinceSpecRegen + 1) % SPEC_REGEN_TICKS; - } - specialPercentage = ticksSinceSpecRegen / (double) SPEC_REGEN_TICKS; - - - int ticksPerHPRegen = NORMAL_HP_REGEN_TICKS; - if (client.isPrayerActive(Prayer.RAPID_HEAL)) - { - ticksPerHPRegen /= 2; - } - - ticksSinceHPRegen = (ticksSinceHPRegen + 1) % ticksPerHPRegen; - hitpointsPercentage = ticksSinceHPRegen / (double) ticksPerHPRegen; - - int currentHP = client.getBoostedSkillLevel(Skill.HITPOINTS); - int maxHP = client.getRealSkillLevel(Skill.HITPOINTS); - if (currentHP == maxHP && !this.showWhenNoChange) - { - hitpointsPercentage = 0; - } - else if (currentHP > maxHP) - { - // Show it going down - hitpointsPercentage = 1 - hitpointsPercentage; - } - - if (this.getNotifyBeforeHpRegenSeconds > 0 && currentHP < maxHP && shouldNotifyHpRegenThisTick(ticksPerHPRegen)) - { - notifier.notify("[" + client.getLocalPlayer().getName() + "] regenerates their next hitpoint soon!"); - } - } - - private boolean shouldNotifyHpRegenThisTick(int ticksPerHPRegen) - { - // if the configured duration lies between two ticks, choose the earlier tick - final int ticksBeforeHPRegen = ticksPerHPRegen - ticksSinceHPRegen; - final int notifyTick = (int) Math.ceil(this.getNotifyBeforeHpRegenSeconds * 1000d / Constants.GAME_TICK_LENGTH); - return ticksBeforeHPRegen == notifyTick; - } - - @Subscribe - public void onConfigChanged(ConfigChanged event) - { - if (event.getGroup().equals("regenmeter")) - { - updateConfig(); - } - } - - private void updateConfig() - { - this.showHitpoints = config.showHitpoints(); - this.showSpecial = config.showSpecial(); - this.showWhenNoChange = config.showWhenNoChange(); - this.getNotifyBeforeHpRegenSeconds = config.getNotifyBeforeHpRegenSeconds(); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/runenergy/RunEnergyConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/runenergy/RunEnergyConfig.java deleted file mode 100644 index 3c1418411a..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/runenergy/RunEnergyConfig.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2018, Sean Dewar - * 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.runenergy; - -import net.runelite.client.config.Config; -import net.runelite.client.config.ConfigGroup; -import net.runelite.client.config.ConfigItem; - -@ConfigGroup("runenergy") -public interface RunEnergyConfig extends Config -{ - @ConfigItem( - keyName = "replaceOrbText", - name = "Replace orb text with run time left", - description = "Show the remaining run time (in seconds) next in the energy orb." - ) - default boolean replaceOrbText() - { - return false; - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/runenergy/RunEnergyOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/runenergy/RunEnergyOverlay.java deleted file mode 100644 index 258fc98211..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/runenergy/RunEnergyOverlay.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2018, Sean Dewar - * 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.runenergy; - -import java.awt.Dimension; -import java.awt.Graphics2D; -import java.awt.Rectangle; -import javax.inject.Inject; -import javax.inject.Singleton; -import net.runelite.api.Client; -import net.runelite.api.Point; -import net.runelite.api.widgets.Widget; -import net.runelite.api.widgets.WidgetInfo; -import net.runelite.client.ui.overlay.Overlay; -import net.runelite.client.ui.overlay.OverlayLayer; -import net.runelite.client.ui.overlay.OverlayPosition; -import net.runelite.client.ui.overlay.tooltip.Tooltip; -import net.runelite.client.ui.overlay.tooltip.TooltipManager; -import org.apache.commons.lang3.StringUtils; - -@Singleton -class RunEnergyOverlay extends Overlay -{ - private final RunEnergyPlugin plugin; - private final Client client; - private final RunEnergyConfig config; - private final TooltipManager tooltipManager; - - @Inject - private RunEnergyOverlay(final RunEnergyPlugin plugin, final Client client, final RunEnergyConfig config, final TooltipManager tooltipManager) - { - this.plugin = plugin; - this.client = client; - this.config = config; - this.tooltipManager = tooltipManager; - setPosition(OverlayPosition.DYNAMIC); - setLayer(OverlayLayer.ABOVE_WIDGETS); - } - - @Override - public Dimension render(Graphics2D graphics) - { - final Widget runOrb = client.getWidget(WidgetInfo.MINIMAP_TOGGLE_RUN_ORB); - - if (runOrb == null || runOrb.isHidden()) - { - return null; - } - - final Rectangle bounds = runOrb.getBounds(); - - if (bounds.getX() <= 0) - { - return null; - } - - final Point mousePosition = client.getMouseCanvasPosition(); - - if (bounds.contains(mousePosition.getX(), mousePosition.getY())) - { - StringBuilder sb = new StringBuilder(); - sb.append("Weight: ").append(client.getWeight()).append(" kg
"); - - if (config.replaceOrbText()) - { - sb.append("Run Energy: ").append(client.getEnergy()).append("%"); - } - else - { - sb.append("Run Time Remaining: ").append(plugin.getEstimatedRunTimeRemaining(false)); - } - - int secondsUntil100 = plugin.getEstimatedRecoverTimeRemaining(); - if (secondsUntil100 > 0) - { - final int minutes = (int) Math.floor(secondsUntil100 / 60.0); - final int seconds = (int) Math.floor(secondsUntil100 - (minutes * 60.0)); - - sb.append("
").append("100% Energy In: ").append(minutes).append(':').append(StringUtils.leftPad(Integer.toString(seconds), 2, "0")); - } - - tooltipManager.add(new Tooltip(sb.toString())); - } - - return null; - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/runenergy/RunEnergyPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/runenergy/RunEnergyPlugin.java deleted file mode 100644 index 8ef9debda1..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/runenergy/RunEnergyPlugin.java +++ /dev/null @@ -1,340 +0,0 @@ -/* - * Copyright (c) 2018, Sean Dewar - * 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.runenergy; - -import com.google.common.collect.ImmutableSet; -import com.google.inject.Provides; -import javax.inject.Inject; -import javax.inject.Singleton; -import net.runelite.api.Client; -import net.runelite.api.Constants; -import net.runelite.api.EquipmentInventorySlot; -import net.runelite.api.InventoryID; -import net.runelite.api.Item; -import net.runelite.api.ItemContainer; -import static net.runelite.api.ItemID.AGILITY_CAPE; -import static net.runelite.api.ItemID.AGILITY_CAPET; -import static net.runelite.api.ItemID.GRACEFUL_BOOTS_11861; -import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13589; -import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13590; -import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13601; -import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13602; -import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13613; -import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13614; -import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13625; -import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13626; -import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13637; -import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13638; -import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13677; -import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13678; -import static net.runelite.api.ItemID.GRACEFUL_BOOTS_21076; -import static net.runelite.api.ItemID.GRACEFUL_BOOTS_21078; -import static net.runelite.api.ItemID.GRACEFUL_CAPE_11853; -import static net.runelite.api.ItemID.GRACEFUL_CAPE_13581; -import static net.runelite.api.ItemID.GRACEFUL_CAPE_13582; -import static net.runelite.api.ItemID.GRACEFUL_CAPE_13593; -import static net.runelite.api.ItemID.GRACEFUL_CAPE_13594; -import static net.runelite.api.ItemID.GRACEFUL_CAPE_13605; -import static net.runelite.api.ItemID.GRACEFUL_CAPE_13606; -import static net.runelite.api.ItemID.GRACEFUL_CAPE_13617; -import static net.runelite.api.ItemID.GRACEFUL_CAPE_13618; -import static net.runelite.api.ItemID.GRACEFUL_CAPE_13629; -import static net.runelite.api.ItemID.GRACEFUL_CAPE_13630; -import static net.runelite.api.ItemID.GRACEFUL_CAPE_13669; -import static net.runelite.api.ItemID.GRACEFUL_CAPE_13670; -import static net.runelite.api.ItemID.GRACEFUL_CAPE_21064; -import static net.runelite.api.ItemID.GRACEFUL_CAPE_21066; -import static net.runelite.api.ItemID.GRACEFUL_GLOVES_11859; -import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13587; -import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13588; -import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13599; -import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13600; -import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13611; -import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13612; -import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13623; -import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13624; -import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13635; -import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13636; -import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13675; -import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13676; -import static net.runelite.api.ItemID.GRACEFUL_GLOVES_21073; -import static net.runelite.api.ItemID.GRACEFUL_GLOVES_21075; -import static net.runelite.api.ItemID.GRACEFUL_HOOD_11851; -import static net.runelite.api.ItemID.GRACEFUL_HOOD_13579; -import static net.runelite.api.ItemID.GRACEFUL_HOOD_13580; -import static net.runelite.api.ItemID.GRACEFUL_HOOD_13591; -import static net.runelite.api.ItemID.GRACEFUL_HOOD_13592; -import static net.runelite.api.ItemID.GRACEFUL_HOOD_13603; -import static net.runelite.api.ItemID.GRACEFUL_HOOD_13604; -import static net.runelite.api.ItemID.GRACEFUL_HOOD_13615; -import static net.runelite.api.ItemID.GRACEFUL_HOOD_13616; -import static net.runelite.api.ItemID.GRACEFUL_HOOD_13627; -import static net.runelite.api.ItemID.GRACEFUL_HOOD_13628; -import static net.runelite.api.ItemID.GRACEFUL_HOOD_13667; -import static net.runelite.api.ItemID.GRACEFUL_HOOD_13668; -import static net.runelite.api.ItemID.GRACEFUL_HOOD_21061; -import static net.runelite.api.ItemID.GRACEFUL_HOOD_21063; -import static net.runelite.api.ItemID.GRACEFUL_LEGS_11857; -import static net.runelite.api.ItemID.GRACEFUL_LEGS_13585; -import static net.runelite.api.ItemID.GRACEFUL_LEGS_13586; -import static net.runelite.api.ItemID.GRACEFUL_LEGS_13597; -import static net.runelite.api.ItemID.GRACEFUL_LEGS_13598; -import static net.runelite.api.ItemID.GRACEFUL_LEGS_13609; -import static net.runelite.api.ItemID.GRACEFUL_LEGS_13610; -import static net.runelite.api.ItemID.GRACEFUL_LEGS_13621; -import static net.runelite.api.ItemID.GRACEFUL_LEGS_13622; -import static net.runelite.api.ItemID.GRACEFUL_LEGS_13633; -import static net.runelite.api.ItemID.GRACEFUL_LEGS_13634; -import static net.runelite.api.ItemID.GRACEFUL_LEGS_13673; -import static net.runelite.api.ItemID.GRACEFUL_LEGS_13674; -import static net.runelite.api.ItemID.GRACEFUL_LEGS_21070; -import static net.runelite.api.ItemID.GRACEFUL_LEGS_21072; -import static net.runelite.api.ItemID.GRACEFUL_TOP_11855; -import static net.runelite.api.ItemID.GRACEFUL_TOP_13583; -import static net.runelite.api.ItemID.GRACEFUL_TOP_13584; -import static net.runelite.api.ItemID.GRACEFUL_TOP_13595; -import static net.runelite.api.ItemID.GRACEFUL_TOP_13596; -import static net.runelite.api.ItemID.GRACEFUL_TOP_13607; -import static net.runelite.api.ItemID.GRACEFUL_TOP_13608; -import static net.runelite.api.ItemID.GRACEFUL_TOP_13619; -import static net.runelite.api.ItemID.GRACEFUL_TOP_13620; -import static net.runelite.api.ItemID.GRACEFUL_TOP_13631; -import static net.runelite.api.ItemID.GRACEFUL_TOP_13632; -import static net.runelite.api.ItemID.GRACEFUL_TOP_13671; -import static net.runelite.api.ItemID.GRACEFUL_TOP_13672; -import static net.runelite.api.ItemID.GRACEFUL_TOP_21067; -import static net.runelite.api.ItemID.GRACEFUL_TOP_21069; -import static net.runelite.api.ItemID.MAX_CAPE; -import net.runelite.api.Skill; -import net.runelite.api.Varbits; -import net.runelite.api.coords.WorldPoint; -import net.runelite.api.events.ConfigChanged; -import net.runelite.api.events.GameTick; -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.plugins.Plugin; -import net.runelite.client.plugins.PluginDescriptor; -import net.runelite.client.ui.overlay.OverlayManager; -import org.apache.commons.lang3.StringUtils; - -@PluginDescriptor( - name = "Run Energy", - description = "Show various information related to run energy", - tags = {"overlay", "stamina"} -) -@Singleton -public class RunEnergyPlugin extends Plugin -{ - // TODO It would be nice if we have the IDs for just the equipped variants of the Graceful set items. - private static final ImmutableSet ALL_GRACEFUL_HOODS = ImmutableSet.of( - GRACEFUL_HOOD_11851, GRACEFUL_HOOD_13579, GRACEFUL_HOOD_13580, GRACEFUL_HOOD_13591, GRACEFUL_HOOD_13592, - GRACEFUL_HOOD_13603, GRACEFUL_HOOD_13604, GRACEFUL_HOOD_13615, GRACEFUL_HOOD_13616, GRACEFUL_HOOD_13627, - GRACEFUL_HOOD_13628, GRACEFUL_HOOD_13667, GRACEFUL_HOOD_13668, GRACEFUL_HOOD_21061, GRACEFUL_HOOD_21063 - ); - - private static final ImmutableSet ALL_GRACEFUL_TOPS = ImmutableSet.of( - GRACEFUL_TOP_11855, GRACEFUL_TOP_13583, GRACEFUL_TOP_13584, GRACEFUL_TOP_13595, GRACEFUL_TOP_13596, - GRACEFUL_TOP_13607, GRACEFUL_TOP_13608, GRACEFUL_TOP_13619, GRACEFUL_TOP_13620, GRACEFUL_TOP_13631, - GRACEFUL_TOP_13632, GRACEFUL_TOP_13671, GRACEFUL_TOP_13672, GRACEFUL_TOP_21067, GRACEFUL_TOP_21069 - ); - - private static final ImmutableSet ALL_GRACEFUL_LEGS = ImmutableSet.of( - GRACEFUL_LEGS_11857, GRACEFUL_LEGS_13585, GRACEFUL_LEGS_13586, GRACEFUL_LEGS_13597, GRACEFUL_LEGS_13598, - GRACEFUL_LEGS_13609, GRACEFUL_LEGS_13610, GRACEFUL_LEGS_13621, GRACEFUL_LEGS_13622, GRACEFUL_LEGS_13633, - GRACEFUL_LEGS_13634, GRACEFUL_LEGS_13673, GRACEFUL_LEGS_13674, GRACEFUL_LEGS_21070, GRACEFUL_LEGS_21072 - ); - - private static final ImmutableSet ALL_GRACEFUL_GLOVES = ImmutableSet.of( - GRACEFUL_GLOVES_11859, GRACEFUL_GLOVES_13587, GRACEFUL_GLOVES_13588, GRACEFUL_GLOVES_13599, GRACEFUL_GLOVES_13600, - GRACEFUL_GLOVES_13611, GRACEFUL_GLOVES_13612, GRACEFUL_GLOVES_13623, GRACEFUL_GLOVES_13624, GRACEFUL_GLOVES_13635, - GRACEFUL_GLOVES_13636, GRACEFUL_GLOVES_13675, GRACEFUL_GLOVES_13676, GRACEFUL_GLOVES_21073, GRACEFUL_GLOVES_21075 - ); - - private static final ImmutableSet ALL_GRACEFUL_BOOTS = ImmutableSet.of( - GRACEFUL_BOOTS_11861, GRACEFUL_BOOTS_13589, GRACEFUL_BOOTS_13590, GRACEFUL_BOOTS_13601, GRACEFUL_BOOTS_13602, - GRACEFUL_BOOTS_13613, GRACEFUL_BOOTS_13614, GRACEFUL_BOOTS_13625, GRACEFUL_BOOTS_13626, GRACEFUL_BOOTS_13637, - GRACEFUL_BOOTS_13638, GRACEFUL_BOOTS_13677, GRACEFUL_BOOTS_13678, GRACEFUL_BOOTS_21076, GRACEFUL_BOOTS_21078 - ); - - // Agility skill capes and the non-cosmetic Max capes also count for the Graceful set effect - private static final ImmutableSet ALL_GRACEFUL_CAPES = ImmutableSet.of( - GRACEFUL_CAPE_11853, GRACEFUL_CAPE_13581, GRACEFUL_CAPE_13582, GRACEFUL_CAPE_13593, GRACEFUL_CAPE_13594, - GRACEFUL_CAPE_13605, GRACEFUL_CAPE_13606, GRACEFUL_CAPE_13617, GRACEFUL_CAPE_13618, GRACEFUL_CAPE_13629, - GRACEFUL_CAPE_13630, GRACEFUL_CAPE_13669, GRACEFUL_CAPE_13670, GRACEFUL_CAPE_21064, GRACEFUL_CAPE_21066, - AGILITY_CAPE, AGILITY_CAPET, MAX_CAPE - ); - - @Inject - private Client client; - - @Inject - private OverlayManager overlayManager; - - @Inject - private RunEnergyOverlay energyOverlay; - - @Inject - private RunEnergyConfig energyConfig; - - private boolean localPlayerRunningToDestination; - private WorldPoint prevLocalPlayerLocation; - - @Provides - RunEnergyConfig getConfig(ConfigManager configManager) - { - return configManager.getConfig(RunEnergyConfig.class); - } - - @Override - protected void startUp() throws Exception - { - overlayManager.add(energyOverlay); - } - - @Override - protected void shutDown() throws Exception - { - overlayManager.remove(energyOverlay); - localPlayerRunningToDestination = false; - prevLocalPlayerLocation = null; - resetRunOrbText(); - } - - @Subscribe - public void onGameTick(GameTick event) - { - localPlayerRunningToDestination = - prevLocalPlayerLocation != null && - client.getLocalDestinationLocation() != null && - prevLocalPlayerLocation.distanceTo(client.getLocalPlayer().getWorldLocation()) > 1; - - prevLocalPlayerLocation = client.getLocalPlayer().getWorldLocation(); - - if (energyConfig.replaceOrbText()) - { - setRunOrbText(getEstimatedRunTimeRemaining(true)); - } - } - - @Subscribe - public void onConfigChanged(ConfigChanged event) - { - if (event.getGroup().equals("runenergy") && !energyConfig.replaceOrbText()) - { - resetRunOrbText(); - } - } - - private void setRunOrbText(String text) - { - Widget runOrbText = client.getWidget(WidgetInfo.MINIMAP_RUN_ORB_TEXT); - - if (runOrbText != null) - { - runOrbText.setText(text); - } - } - - private void resetRunOrbText() - { - setRunOrbText(Integer.toString(client.getEnergy())); - } - - String getEstimatedRunTimeRemaining(boolean inSeconds) - { - // Calculate the amount of energy lost every tick. - // Negative weight has the same depletion effect as 0 kg. - final int effectiveWeight = Math.max(client.getWeight(), 0); - double lossRate = (Math.min(effectiveWeight, 64) / 100.0) + 0.64; - - if (client.getVar(Varbits.RUN_SLOWED_DEPLETION_ACTIVE) != 0) - { - lossRate *= 0.3; // Stamina effect reduces energy depletion to 30% - } - - // Calculate the number of seconds left - final double secondsLeft = (client.getEnergy() * Constants.GAME_TICK_LENGTH) / (lossRate * 1000.0); - - // Return the text - if (inSeconds) - { - return (int) Math.floor(secondsLeft) + "s"; - } - else - { - final int minutes = (int) Math.floor(secondsLeft / 60.0); - final int seconds = (int) Math.floor(secondsLeft - (minutes * 60.0)); - - return minutes + ":" + StringUtils.leftPad(Integer.toString(seconds), 2, "0"); - } - } - - private boolean isLocalPlayerWearingFullGraceful() - { - final ItemContainer equipment = client.getItemContainer(InventoryID.EQUIPMENT); - - if (equipment == null) - { - return false; - } - - final Item[] items = equipment.getItems(); - - // Check that the local player is wearing enough items to be using full Graceful - // (the Graceful boots will have the highest slot index in the worn set). - if (items == null || items.length <= EquipmentInventorySlot.BOOTS.getSlotIdx()) - { - return false; - } - - return (ALL_GRACEFUL_HOODS.contains(items[EquipmentInventorySlot.HEAD.getSlotIdx()].getId()) && - ALL_GRACEFUL_TOPS.contains(items[EquipmentInventorySlot.BODY.getSlotIdx()].getId()) && - ALL_GRACEFUL_LEGS.contains(items[EquipmentInventorySlot.LEGS.getSlotIdx()].getId()) && - ALL_GRACEFUL_GLOVES.contains(items[EquipmentInventorySlot.GLOVES.getSlotIdx()].getId()) && - ALL_GRACEFUL_BOOTS.contains(items[EquipmentInventorySlot.BOOTS.getSlotIdx()].getId()) && - ALL_GRACEFUL_CAPES.contains(items[EquipmentInventorySlot.CAPE.getSlotIdx()].getId())); - } - - int getEstimatedRecoverTimeRemaining() - { - if (localPlayerRunningToDestination) - { - return -1; - } - - // Calculate the amount of energy recovered every second - double recoverRate = (48 + client.getBoostedSkillLevel(Skill.AGILITY)) / 360.0; - - if (isLocalPlayerWearingFullGraceful()) - { - recoverRate *= 1.3; // 30% recover rate increase from Graceful set effect - } - - // Calculate the number of seconds left - return (int) ((100 - client.getEnergy()) / recoverRate); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/statusorbs/StatusOrbsConfig.java similarity index 56% rename from runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterConfig.java rename to runelite-client/src/main/java/net/runelite/client/plugins/statusorbs/StatusOrbsConfig.java index 2e8b577381..c05c47645d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/statusorbs/StatusOrbsConfig.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2018 Abex + * Copyright (c) 2018, Sean Dewar * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -22,29 +23,47 @@ * (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.regenmeter; +package net.runelite.client.plugins.statusorbs; import net.runelite.client.config.Config; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; +import net.runelite.client.config.Stub; -@ConfigGroup("regenmeter") -public interface RegenMeterConfig extends Config +@ConfigGroup("statusorbs") +public interface StatusOrbsConfig extends Config { @ConfigItem( - keyName = "showHitpoints", - name = "Show hitpoints regen", - description = "Show a ring around the hitpoints orb") - default boolean showHitpoints() + keyName = "hp", + name = "Hitpoints", + description = "", + position = 0 + ) + default Stub hp() + { + return new Stub(); + } + + @ConfigItem( + keyName = "dynamicHpHeart", + name = "Dynamic hitpoints heart", + description = "Changes the HP heart color to match players current affliction", + parent = "hp", + position = 1 + ) + default boolean dynamicHpHeart() { return true; } @ConfigItem( - keyName = "showSpecial", - name = "Show Spec. Attack regen", - description = "Show a ring around the Special Attack orb") - default boolean showSpecial() + keyName = "showHitpoints", + name = "Show hitpoints regen", + description = "Show a ring around the hitpoints orb", + parent = "hp", + position = 2 + ) + default boolean showHitpoints() { return true; } @@ -52,7 +71,10 @@ public interface RegenMeterConfig extends Config @ConfigItem( keyName = "showWhenNoChange", name = "Show hitpoints regen at full hitpoints", - description = "Always show the hitpoints regen orb, even if there will be no stat change") + description = "Always show the hitpoints regen orb, even if there will be no stat change", + parent = "hp", + position = 3 + ) default boolean showWhenNoChange() { return false; @@ -61,10 +83,70 @@ public interface RegenMeterConfig extends Config @ConfigItem( keyName = "notifyBeforeHpRegenDuration", name = "Hitpoint Regen Notification (seconds)", - description = "Notify approximately when your next hitpoint is about to regen. A value of 0 will disable notification." + description = "Notify approximately when your next hitpoint is about to regen. A value of 0 will disable notification.", + parent = "hp", + position = 4 ) default int getNotifyBeforeHpRegenSeconds() { return 0; } -} + + @ConfigItem( + keyName = "spec", + name = "Special attack", + description = "", + position = 5 + ) + default Stub spec() + { + return new Stub(); + } + + @ConfigItem( + keyName = "showSpecial", + name = "Show Spec. Attack regen", + description = "Show a ring around the Special Attack orb", + parent = "spec", + position = 6 + ) + default boolean showSpecial() + { + return true; + } + + @ConfigItem( + keyName = "run", + name = "Run energy", + description = "", + position = 7 + ) + default Stub run() + { + return new Stub(); + } + + @ConfigItem( + keyName = "showRun", + name = "Show run energy regen", + description = "Show a ring around the run regen orb", + position = 8, + parent = "run" + ) + default boolean showRun() + { + return true; + } + + @ConfigItem( + keyName = "replaceOrbText", + name = "Replace run orb text with run time left", + description = "Show the remaining run time (in seconds) next in the energy orb", + position = 9, + parent = "run" + ) + default boolean replaceOrbText() + { + return false; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/statusorbs/StatusOrbsOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/statusorbs/StatusOrbsOverlay.java new file mode 100644 index 0000000000..901b2aa771 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/statusorbs/StatusOrbsOverlay.java @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2018 Abex + * Copyright (c) 2018, Sean Dewar + * 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.statusorbs; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.Stroke; +import java.awt.geom.Arc2D; +import javax.inject.Inject; +import net.runelite.api.Client; +import net.runelite.api.InventoryID; +import net.runelite.api.Point; +import net.runelite.api.Skill; +import net.runelite.api.VarPlayer; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayLayer; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.tooltip.Tooltip; +import net.runelite.client.ui.overlay.tooltip.TooltipManager; +import net.runelite.client.util.Graceful; +import org.apache.commons.lang3.StringUtils; + +public class StatusOrbsOverlay extends Overlay +{ + private static final Color HITPOINTS_COLOR = brighter(0x9B0703); + private static final Color SPECIAL_COLOR = brighter(0x1E95B0); + private static final Color RUN_COLOR = new Color(255, 215, 0); + private static final Color OVERLAY_COLOR = new Color(255, 255, 255, 60); + private static final double DIAMETER = 26D; + private static final int OFFSET = 27; + + private final Client client; + private final StatusOrbsPlugin plugin; + private final TooltipManager tooltipManager; + + private long last = System.nanoTime(); + private double percentHp; + private double lastHp; + private double percentSpec; + private double lastSpec; + private double percentRun; + private double lastRun; + + private static Color brighter(int color) + { + float[] hsv = new float[3]; + Color.RGBtoHSB(color >>> 16, (color >> 8) & 0xFF, color & 0xFF, hsv); + return Color.getHSBColor(hsv[0], 1.f, 1.f); + } + + @Inject + public StatusOrbsOverlay(Client client, StatusOrbsPlugin plugin, TooltipManager tooltipManager) + { + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ABOVE_WIDGETS); + this.client = client; + this.plugin = plugin; + this.tooltipManager = tooltipManager; + } + + @Override + public Dimension render(Graphics2D g) + { + g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); + + long current = System.nanoTime(); + double ms = (current - last) / (double) 1000000; + + if (plugin.isShowHitpoints()) + { + if (lastHp == plugin.getHitpointsPercentage() && plugin.getHitpointsPercentage() != 0) + { + percentHp += ms * plugin.getHpPerMs(); + } + else + { + percentHp = plugin.getHitpointsPercentage(); + lastHp = plugin.getHitpointsPercentage(); + } + renderRegen(g, WidgetInfo.MINIMAP_HEALTH_ORB, percentHp, HITPOINTS_COLOR); + } + + if (plugin.isShowSpecial()) + { + if (client.getVar(VarPlayer.SPECIAL_ATTACK_ENABLED) == 1) + { + final Widget widget = client.getWidget(WidgetInfo.MINIMAP_SPEC_ORB); + + if (widget != null && !widget.isHidden()) + { + final Rectangle bounds = widget.getBounds(); + g.setColor(OVERLAY_COLOR); + g.fillOval( + bounds.x + OFFSET, + bounds.y + (int) (bounds.height / 2 - (DIAMETER) / 2), + (int) DIAMETER, (int) DIAMETER); + } + } + + if (lastSpec == plugin.getSpecialPercentage() && plugin.getSpecialPercentage() != 0) + { + percentSpec += ms * plugin.getSpecPerMs(); + } + else + { + percentSpec = plugin.getSpecialPercentage(); + lastSpec = plugin.getSpecialPercentage(); + } + + renderRegen(g, WidgetInfo.MINIMAP_SPEC_ORB, percentSpec, SPECIAL_COLOR); + } + + if (plugin.isReplaceOrbText()) + { + final Widget runOrb = client.getWidget(WidgetInfo.MINIMAP_TOGGLE_RUN_ORB); + + if (runOrb == null || runOrb.isHidden()) + { + return null; + } + + final Rectangle bounds = runOrb.getBounds(); + + if (bounds.getX() <= 0) + { + return null; + } + + final Point mousePosition = client.getMouseCanvasPosition(); + + if (bounds.contains(mousePosition.getX(), mousePosition.getY())) + { + StringBuilder sb = new StringBuilder(); + sb.append("Weight: ").append(client.getWeight()).append(" kg
"); + + if (plugin.isReplaceOrbText()) + { + sb.append("Run Energy: ").append(client.getEnergy()).append("%"); + } + else + { + sb.append("Run Time Remaining: ").append(plugin.getEstimatedRunTimeRemaining(false)); + } + + int secondsUntil100 = plugin.getEstimatedRecoverTimeRemaining(); + if (secondsUntil100 > 0) + { + final int minutes = (int) Math.floor(secondsUntil100 / 60.0); + final int seconds = (int) Math.floor(secondsUntil100 - (minutes * 60.0)); + + sb.append("
").append("100% Energy In: ").append(minutes).append(':').append(StringUtils.leftPad(Integer.toString(seconds), 2, "0")); + } + + tooltipManager.add(new Tooltip(sb.toString())); + } + } + + if (plugin.isShowRun()) + { + if (lastRun == plugin.getRunPercentage() && plugin.getRunPercentage() != 0) + { + double recoverRate = (48 + client.getBoostedSkillLevel(Skill.AGILITY)) / 360000.0; + + if (Graceful.hasFullSet(client.getItemContainer(InventoryID.EQUIPMENT))) + { + recoverRate *= 1.3; // 30% recover rate increase from Graceful set effect + } + + percentRun += ms * recoverRate; + } + else + { + percentRun = plugin.getRunPercentage(); + lastRun = plugin.getRunPercentage(); + } + renderRegen(g, WidgetInfo.MINIMAP_RUN_ORB, percentRun, RUN_COLOR); + } + + last = current; + + return null; + } + + private void renderRegen(Graphics2D g, WidgetInfo widgetInfo, double percent, Color color) + { + Widget widget = client.getWidget(widgetInfo); + if (widget == null || widget.isHidden()) + { + return; + } + Rectangle bounds = widget.getBounds(); + + Arc2D.Double arc = new Arc2D.Double(bounds.x + OFFSET, bounds.y + (bounds.height / 2 - DIAMETER / 2), DIAMETER, DIAMETER, 90.d, -360.d * percent, Arc2D.OPEN); + final Stroke STROKE = new BasicStroke(2f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER); + g.setStroke(STROKE); + g.setColor(color); + g.draw(arc); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/statusorbs/StatusOrbsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/statusorbs/StatusOrbsPlugin.java new file mode 100644 index 0000000000..3e3e671f4a --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/statusorbs/StatusOrbsPlugin.java @@ -0,0 +1,490 @@ +/* + * Copyright (c) 2019, Owain van Brakel + * Copyright (c) 2018, TheStonedTurtle + * Copyright (c) 2018 Abex + * Copyright (c) 2018, Zimaya + * Copyright (c) 2017, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.statusorbs; + +import com.google.inject.Provides; +import java.awt.image.BufferedImage; +import javax.inject.Inject; +import lombok.AccessLevel; +import lombok.Getter; +import net.runelite.api.Client; +import net.runelite.api.Constants; +import net.runelite.api.GameState; +import net.runelite.api.InventoryID; +import net.runelite.api.Prayer; +import net.runelite.api.Skill; +import net.runelite.api.SpriteID; +import net.runelite.api.VarPlayer; +import net.runelite.api.Varbits; +import net.runelite.api.coords.WorldPoint; +import net.runelite.api.events.ConfigChanged; +import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.GameTick; +import net.runelite.api.events.VarbitChanged; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.Notifier; +import net.runelite.client.callback.ClientThread; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.overlay.OverlayManager; +import net.runelite.client.util.Graceful; +import net.runelite.client.util.ImageUtil; +import org.apache.commons.lang3.StringUtils; + +@PluginDescriptor( + name = "Status Orbs", + description = "Configure settings for the Minimap orbs", + tags = {"minimap", "orb", "regen", "energy", "special"} +) +public class StatusOrbsPlugin extends Plugin +{ + private static final BufferedImage HEART_DISEASE; + private static final BufferedImage HEART_POISON; + private static final BufferedImage HEART_VENOM; + + static + { + HEART_DISEASE = ImageUtil.resizeCanvas(ImageUtil.getResourceStreamFromClass(StatusOrbsPlugin.class, "1067-DISEASE.png"), 26, 26); + HEART_POISON = ImageUtil.resizeCanvas(ImageUtil.getResourceStreamFromClass(StatusOrbsPlugin.class, "1067-POISON.png"), 26, 26); + HEART_VENOM = ImageUtil.resizeCanvas(ImageUtil.getResourceStreamFromClass(StatusOrbsPlugin.class, "1067-VENOM.png"), 26, 26); + } + + private static final int SPEC_REGEN_TICKS = 50; + private static final int NORMAL_HP_REGEN_TICKS = 100; + + @Inject + private Client client; + + @Inject + private ClientThread clientThread; + + @Inject + private ConfigManager configManager; + + @Inject + private StatusOrbsConfig config; + + @Inject + private StatusOrbsOverlay overlay; + + @Inject + private OverlayManager overlayManager; + + @Inject + private Notifier notifier; + + @Getter + private double hitpointsPercentage; + + @Getter + private double specialPercentage; + + @Getter + private double runPercentage; + + @Getter + private double hpPerMs; + + @Getter + private double specPerMs = (double) 1 / (SPEC_REGEN_TICKS * 600); + + // RegenMeter + private int ticksSinceSpecRegen; + private int ticksSinceHPRegen; + private boolean wasRapidHeal; + private double ticksSinceRunRegen; + + // Run Energy + private int lastEnergy = 0; + private boolean localPlayerRunningToDestination; + private WorldPoint currPoint; + private WorldPoint prevLocalPlayerLocation; + + private BufferedImage heart; + + private boolean dynamicHpHeart; + @Getter(AccessLevel.PACKAGE) + private boolean showHitpoints; + private boolean showWhenNoChange; + private int getNotifyBeforeHpRegenSeconds; + @Getter(AccessLevel.PACKAGE) + private boolean showSpecial; + @Getter(AccessLevel.PACKAGE) + private boolean showRun; + @Getter(AccessLevel.PACKAGE) + private boolean replaceOrbText; + + @Provides + StatusOrbsConfig provideConfig(ConfigManager configManager) + { + return configManager.getConfig(StatusOrbsConfig.class); + } + + @Override + protected void startUp() throws Exception + { + migrateConfigs(); + updateConfig(); + overlayManager.add(overlay); + if (this.dynamicHpHeart && client.getGameState().equals(GameState.LOGGED_IN)) + { + clientThread.invoke(this::checkHealthIcon); + } + } + + @Override + protected void shutDown() throws Exception + { + overlayManager.remove(overlay); + localPlayerRunningToDestination = false; + prevLocalPlayerLocation = null; + resetRunOrbText(); + if (this.dynamicHpHeart) + { + clientThread.invoke(this::resetHealthIcon); + } + } + + @Subscribe + public void onConfigChanged(ConfigChanged event) + { + if (event.getGroup().equals("statusorbs")) + { + updateConfig(); + switch (event.getKey()) + { + case "replaceOrbText": + if (!this.replaceOrbText) + { + resetRunOrbText(); + } + break; + case "dynamicHpHeart": + if (this.dynamicHpHeart) + { + checkHealthIcon(); + } + else + { + resetHealthIcon(); + } + break; + } + } + } + + @Subscribe + private void onVarbitChanged(VarbitChanged e) + { + if (this.dynamicHpHeart) + { + checkHealthIcon(); + } + + boolean isRapidHeal = client.isPrayerActive(Prayer.RAPID_HEAL); + if (wasRapidHeal != isRapidHeal) + { + ticksSinceHPRegen = 0; + } + wasRapidHeal = isRapidHeal; + } + + @Subscribe + private void onGameStateChanged(GameStateChanged ev) + { + if (ev.getGameState() == GameState.HOPPING || ev.getGameState() == GameState.LOGIN_SCREEN) + { + ticksSinceHPRegen = -2; // For some reason this makes this accurate + ticksSinceSpecRegen = 0; + ticksSinceRunRegen = -1; + } + } + + @Subscribe + public void onGameTick(GameTick event) + { + if (client.getVar(VarPlayer.SPECIAL_ATTACK_PERCENT) == 1000) + { + // The recharge doesn't tick when at 100% + ticksSinceSpecRegen = 0; + } + else + { + ticksSinceSpecRegen = (ticksSinceSpecRegen + 1) % SPEC_REGEN_TICKS; + } + specialPercentage = ticksSinceSpecRegen / (double) SPEC_REGEN_TICKS; + + int ticksPerHPRegen = NORMAL_HP_REGEN_TICKS; + hpPerMs = ticksPerHPRegen / (double) 6000000; + if (client.isPrayerActive(Prayer.RAPID_HEAL)) + { + ticksPerHPRegen /= 2; + hpPerMs *= 2; + } + + ticksSinceHPRegen = (ticksSinceHPRegen + 1) % ticksPerHPRegen; + hitpointsPercentage = ticksSinceHPRegen / (double) ticksPerHPRegen; + + int currentHP = client.getBoostedSkillLevel(Skill.HITPOINTS); + int maxHP = client.getRealSkillLevel(Skill.HITPOINTS); + if (currentHP == maxHP && !this.showWhenNoChange) + { + hitpointsPercentage = 0; + } + else if (currentHP > maxHP) + { + // Show it going down + hitpointsPercentage = 1 - hitpointsPercentage; + } + + // Run Energy + localPlayerRunningToDestination = + prevLocalPlayerLocation != null && + client.getLocalDestinationLocation() != null && + prevLocalPlayerLocation.distanceTo(client.getLocalPlayer().getWorldLocation()) > 1; + + if (this.getNotifyBeforeHpRegenSeconds > 0 && currentHP < maxHP && shouldNotifyHpRegenThisTick(ticksPerHPRegen)) + { + notifier.notify("[" + client.getLocalPlayer().getName() + "] regenerates their next hitpoint soon!"); + } + + localPlayerRunningToDestination = + prevLocalPlayerLocation != null && + client.getLocalDestinationLocation() != null && + prevLocalPlayerLocation.distanceTo(client.getLocalPlayer().getWorldLocation()) > 1; + + prevLocalPlayerLocation = client.getLocalPlayer().getWorldLocation(); + + if (this.replaceOrbText) + { + setRunOrbText(getEstimatedRunTimeRemaining(true)); + } + + int currEnergy = client.getEnergy(); + currPoint = client.getLocalPlayer().getWorldLocation(); + if (currEnergy == 100 || (prevLocalPlayerLocation != null && currPoint.distanceTo(prevLocalPlayerLocation) > 1) || currEnergy < lastEnergy) + { + ticksSinceRunRegen = 0; + } + else if (currEnergy > lastEnergy) + { + if (runPercentage < 1) + { + ticksSinceRunRegen = (1 - runPercentage) / runRegenPerTick(); + ticksSinceRunRegen = ticksSinceRunRegen > 1 ? 1 : ticksSinceRunRegen; + } + else + { + ticksSinceRunRegen = (runPercentage - 1) / runRegenPerTick(); + } + } + else + { + ticksSinceRunRegen += 1; + } + runPercentage = ticksSinceRunRegen * runRegenPerTick(); + prevLocalPlayerLocation = currPoint; + lastEnergy = currEnergy; + } + + private boolean shouldNotifyHpRegenThisTick(int ticksPerHPRegen) + { + // if the configured duration lies between two ticks, choose the earlier tick + final int ticksBeforeHPRegen = ticksPerHPRegen - ticksSinceHPRegen; + final int notifyTick = (int) Math.ceil(this.getNotifyBeforeHpRegenSeconds * 1000d / Constants.GAME_TICK_LENGTH); + return ticksBeforeHPRegen == notifyTick; + } + + private void setRunOrbText(String text) + { + Widget runOrbText = client.getWidget(WidgetInfo.MINIMAP_RUN_ORB_TEXT); + + if (runOrbText != null) + { + runOrbText.setText(text); + } + } + + private void resetRunOrbText() + { + setRunOrbText(Integer.toString(client.getEnergy())); + } + + String getEstimatedRunTimeRemaining(boolean inSeconds) + { + // Calculate the amount of energy lost every 2 ticks (0.6 seconds). + // Negative weight has the same depletion effect as 0 kg. + final int effectiveWeight = Math.max(client.getWeight(), 0); + double lossRate = (Math.min(effectiveWeight, 64) / 100.0) + 0.64; + + if (client.getVar(Varbits.RUN_SLOWED_DEPLETION_ACTIVE) != 0) + { + lossRate *= 0.3; // Stamina effect reduces energy depletion to 30% + } + + // Calculate the number of seconds left + final double secondsLeft = (client.getEnergy() * 0.6) / lossRate; + + // Return the text + if (inSeconds) + { + return (int) Math.floor(secondsLeft) + "s"; + } + else + { + final int minutes = (int) Math.floor(secondsLeft / 60.0); + final int seconds = (int) Math.floor(secondsLeft - (minutes * 60.0)); + + return minutes + ":" + StringUtils.leftPad(Integer.toString(seconds), 2, "0"); + } + } + + int getEstimatedRecoverTimeRemaining() + { + if (localPlayerRunningToDestination) + { + return -1; + } + + // Calculate the amount of energy recovered every second + double recoverRate = (48 + client.getBoostedSkillLevel(Skill.AGILITY)) / 360.0; + + if (Graceful.hasFullSet(client.getItemContainer(InventoryID.EQUIPMENT))) + { + recoverRate *= 1.3; // 30% recover rate increase from Graceful set effect + } + + // Calculate the number of seconds left + final double secondsLeft = (100 - client.getEnergy()) / recoverRate; + return (int) secondsLeft; + } + + /** + * Check player afflictions to determine health icon + */ + private void checkHealthIcon() + { + BufferedImage newHeart; + + int poison = client.getVar(VarPlayer.IS_POISONED); + if (poison >= 1000000) + { + newHeart = HEART_VENOM; + } + else if (poison > 0) + { + newHeart = HEART_POISON; + } + else if (client.getVar(VarPlayer.DISEASE_VALUE) > 0) + { + newHeart = HEART_DISEASE; + } + else + { + heart = null; + resetHealthIcon(); + return; + } + + // Only update sprites when the heart icon actually changes + if (newHeart != heart) + { + heart = newHeart; + client.getWidgetSpriteCache().reset(); + client.getSpriteOverrides().put(SpriteID.MINIMAP_ORB_HITPOINTS_ICON, ImageUtil.getImageSprite(heart, client)); + } + } + + private double runRegenPerTick() + { + double recoverRate = (client.getBoostedSkillLevel(Skill.AGILITY) / 6d + 8) / 100; + + if (Graceful.hasFullSet(client.getItemContainer(InventoryID.EQUIPMENT))) + { + return recoverRate * 1.3; + } + return recoverRate; + } + + /** + * Ensure the HP Heart is the default Sprite + */ + private void resetHealthIcon() + { + client.getWidgetSpriteCache().reset(); + client.getSpriteOverrides().remove(SpriteID.MINIMAP_ORB_HITPOINTS_ICON); + } + + /** + * Migrates configs from runenergy and regenmeter to this plugin and deletes the old config values. + * This method should be removed after a reasonable amount of time. + */ + @Deprecated + private void migrateConfigs() + { + migrateConfig("regenmeter", "showHitpoints"); + migrateConfig("regenmeter", "showSpecial"); + migrateConfig("regenmeter", "showWhenNoChange"); + migrateConfig("regenmeter", "notifyBeforeHpRegenDuration"); + + migrateConfig("runenergy", "replaceOrbText"); + } + + /** + * Wrapper for migrating individual config options + * This method should be removed after a reasonable amount of time. + * + * @param group old group name + * @param key key name to migrate + */ + @Deprecated + private void migrateConfig(String group, String key) + { + String value = configManager.getConfiguration(group, key); + if (value != null) + { + configManager.setConfiguration("statusorbs", key, value); + configManager.unsetConfiguration(group, key); + } + } + + private void updateConfig() + { + this.dynamicHpHeart = config.dynamicHpHeart(); + this.showHitpoints = config.showHitpoints(); + this.showWhenNoChange = config.showWhenNoChange(); + this.getNotifyBeforeHpRegenSeconds = config.getNotifyBeforeHpRegenSeconds(); + this.showSpecial = config.showSpecial(); + this.showRun = config.showRun(); + this.replaceOrbText = config.replaceOrbText(); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/util/Graceful.java b/runelite-client/src/main/java/net/runelite/client/util/Graceful.java new file mode 100644 index 0000000000..4cd05ec128 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/util/Graceful.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2018 raiyni + * 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.util; + +import com.google.common.collect.ImmutableSet; +import net.runelite.api.EquipmentInventorySlot; +import net.runelite.api.Item; +import net.runelite.api.ItemContainer; +import static net.runelite.api.ItemID.*; + +public enum Graceful +{ + // TODO: It would be nice if we have the IDs for just the equipped variants of the Graceful set items. + HOOD( + GRACEFUL_HOOD_11851, GRACEFUL_HOOD_13579, GRACEFUL_HOOD_13580, GRACEFUL_HOOD_13591, GRACEFUL_HOOD_13592, + GRACEFUL_HOOD_13603, GRACEFUL_HOOD_13604, GRACEFUL_HOOD_13615, GRACEFUL_HOOD_13616, GRACEFUL_HOOD_13627, + GRACEFUL_HOOD_13628, GRACEFUL_HOOD_13667, GRACEFUL_HOOD_13668, GRACEFUL_HOOD_21061, GRACEFUL_HOOD_21063 + ), + + TOP( + GRACEFUL_TOP_11855, GRACEFUL_TOP_13583, GRACEFUL_TOP_13584, GRACEFUL_TOP_13595, GRACEFUL_TOP_13596, + GRACEFUL_TOP_13607, GRACEFUL_TOP_13608, GRACEFUL_TOP_13619, GRACEFUL_TOP_13620, GRACEFUL_TOP_13631, + GRACEFUL_TOP_13632, GRACEFUL_TOP_13671, GRACEFUL_TOP_13672, GRACEFUL_TOP_21067, GRACEFUL_TOP_21069 + ), + + LEGS( + GRACEFUL_LEGS_11857, GRACEFUL_LEGS_13585, GRACEFUL_LEGS_13586, GRACEFUL_LEGS_13597, GRACEFUL_LEGS_13598, + GRACEFUL_LEGS_13609, GRACEFUL_LEGS_13610, GRACEFUL_LEGS_13621, GRACEFUL_LEGS_13622, GRACEFUL_LEGS_13633, + GRACEFUL_LEGS_13634, GRACEFUL_LEGS_13673, GRACEFUL_LEGS_13674, GRACEFUL_LEGS_21070, GRACEFUL_LEGS_21072 + ), + + GLOVES( + GRACEFUL_GLOVES_11859, GRACEFUL_GLOVES_13587, GRACEFUL_GLOVES_13588, GRACEFUL_GLOVES_13599, GRACEFUL_GLOVES_13600, + GRACEFUL_GLOVES_13611, GRACEFUL_GLOVES_13612, GRACEFUL_GLOVES_13623, GRACEFUL_GLOVES_13624, GRACEFUL_GLOVES_13635, + GRACEFUL_GLOVES_13636, GRACEFUL_GLOVES_13675, GRACEFUL_GLOVES_13676, GRACEFUL_GLOVES_21073, GRACEFUL_GLOVES_21075 + ), + + BOOTS( + GRACEFUL_BOOTS_11861, GRACEFUL_BOOTS_13589, GRACEFUL_BOOTS_13590, GRACEFUL_BOOTS_13601, GRACEFUL_BOOTS_13602, + GRACEFUL_BOOTS_13613, GRACEFUL_BOOTS_13614, GRACEFUL_BOOTS_13625, GRACEFUL_BOOTS_13626, GRACEFUL_BOOTS_13637, + GRACEFUL_BOOTS_13638, GRACEFUL_BOOTS_13677, GRACEFUL_BOOTS_13678, GRACEFUL_BOOTS_21076, GRACEFUL_BOOTS_21078 + ), + + // Agility skill capes and the non-cosmetic Max capes also count for the Graceful set effect + CAPE( + GRACEFUL_CAPE_11853, GRACEFUL_CAPE_13581, GRACEFUL_CAPE_13582, GRACEFUL_CAPE_13593, GRACEFUL_CAPE_13594, + GRACEFUL_CAPE_13605, GRACEFUL_CAPE_13606, GRACEFUL_CAPE_13617, GRACEFUL_CAPE_13618, GRACEFUL_CAPE_13629, + GRACEFUL_CAPE_13630, GRACEFUL_CAPE_13669, GRACEFUL_CAPE_13670, GRACEFUL_CAPE_21064, GRACEFUL_CAPE_21066, + AGILITY_CAPE, AGILITY_CAPET, MAX_CAPE + ); + + private final ImmutableSet ids; + + Graceful(Integer... ids) + { + this.ids = ImmutableSet.copyOf(ids); + } + + public static boolean hasFullSet(final ItemContainer equipment) + { + if (equipment == null) + { + return false; + } + + final Item[] items = equipment.getItems(); + + if (equipment == null || items.length <= EquipmentInventorySlot.BOOTS.getSlotIdx()) + { + return false; + } + + return HOOD.ids.contains(items[EquipmentInventorySlot.HEAD.getSlotIdx()].getId()) + && TOP.ids.contains(items[EquipmentInventorySlot.BODY.getSlotIdx()].getId()) + && LEGS.ids.contains(items[EquipmentInventorySlot.LEGS.getSlotIdx()].getId()) + && GLOVES.ids.contains(items[EquipmentInventorySlot.GLOVES.getSlotIdx()].getId()) + && BOOTS.ids.contains(items[EquipmentInventorySlot.BOOTS.getSlotIdx()].getId()) + && CAPE.ids.contains(items[EquipmentInventorySlot.CAPE.getSlotIdx()].getId()); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/skillcalculator/banked.png b/runelite-client/src/main/resources/net/runelite/client/plugins/skillcalculator/banked.png new file mode 100644 index 0000000000000000000000000000000000000000..5256088e5f33293f6dcf07b2804cf1ba252d8a2a GIT binary patch literal 2246 zcmV;%2s!tOP)N2bZe?^J zG%hhNHvLWs=l}o+L`g(JR9Hu)SZi#P*BN$hrp-N$<4b&r?axkpO>BS0#5WSh2Ao@2 z8!83_ws9HlTsn<`G!j}ZjH-pv(Pmk@uGuCks*R*ZEmhkktBO=LnpUbwV;UVlR#g=m zKPDk0q=`S(yYG3^V;nG)X+NHPKIgpWeV_Mv&-uP{q9UUE!b6#WI@8;kP0IGkSjax# z8**G+6LehbcALIba834GE+%Bh{BYVonOp14=29MYIX~r4wzCu)v)%ektTUgGrA431 zNPTO2Y46@Wbn5hJS~!2678V!j4z%qu(Yr)>E2wLrBTkS=Hcy8LVm0mwH$As2zSVuh`(~LsI zaC^)Zjr1C+PFZx*sbsGr?OQA{GGnKWt>lpt6i$y(IJ=GFL)$4fvYUb50QxZwa)NZb z$;=9Z%s01gA!QLyuS>b>O5?5|(c5F4SQ8C%y`P$zVkZEy>`9JrkZ5j4F?cx$BCy9i zC;Lmw1xF4M{SWrDr_B(#$r4eiT1K_t6e60C`+!mQe(xTfFN@> z2m*j-?G~PI^ebz`;-d=1uU(QTzwb%P<~C3&Y8@|<=SW4#WAP4~F&@dUC5%!A&hek$O5tqv}$0QQLcG=@~-@w8NM9D;_U>s z#mNfVTyZ+~qhb2j`N!x#pFByI&V7eh{qSJC*oX0nec58W)b`+a6YN8jM58k@k7UKGvlxOrXUYWb)|8I7r~-ChW&XWG)B%!Vcx|g=Hpz_^M8~`wEm` z63T10lmvj^=)+{2!%QEa*+`e)d4!*^2T70s$s_wBJhp9A;w^b@Cc}0nDNytU(jbBL z2NF(~bVb!6NjFgn$YXX$caRWjU^24%!45b5wJ=DhkM|WzCrCV<5AfKr{bBm$(>^AF zG75iq3MD`SF*}^nO_jxBW)gtY?creMNMJJ7!s072T6!(bfb1YG%5`vOC-*}M1Zr%* zMF0?fFoyRLCvLOfROwBoD`gy++5?`e^=$0T2)&&3~Q4&C~-;gwNP^@}&oraD);o>%) zC=>9+HXHZB7Q{3a>rfr&qJXj>yyVxNp34#O260)Cq#>4Qq z+nFfHoIUAdpbTR<8?OVjL2NMN=j=;W;F2ou2@pUm91}E}rdgF)SDSBbYhx04Vc{v% zKsI=SBxDzL2H+5V9rk5tjV6Ag4|3z9h2T&_r~tnREMg(yriKUdsvc*v+}PA`10})t z1MhS!KxVB>6p8?)CAt@2!vquvd-y}ipaSBc6m<>tH_V0xnLl9d+S)k;#`}Edo;}Oo z5M_dR5sBc!>DS?Q0%ChZ1rR_CD7Q*ov-|IZz=D}N`V5~7K?jKK5y2rpl*B>l)l;uA3G`J|R4Qd?D1XoeQXxZ~+txCzFy#!O2<{9ZgQFiZf@8>w zKo+Bhwmmw^!9g4!G>ijK5NhI91_iJXLBMQinO1IfcR%t2$)LgDm0D+Ns;iXI3f@x4 z1lPN{LVKEgMY8}^Kn4zxF$f`Br~q8}(}%pZQ9h^vJD|F%iVST{3tp%3f4>ly zA|A5nySwyxhoyD#!BuLiQXHeU2!c$-`al~;ddRM>sV04E)q3^DF6Tf07*qoM6N<$g7|ABTmS$7 literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/statusorbs/1067-DISEASE.png b/runelite-client/src/main/resources/net/runelite/client/plugins/statusorbs/1067-DISEASE.png new file mode 100644 index 0000000000000000000000000000000000000000..f2ff5ac911aafdd5efdebda94d62098d226a1eb2 GIT binary patch literal 2991 zcmV;g3sCflP)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0002mNklIq7* zIMl_&wkB482)yv}y`Q|72ws{^`Xv>GG8b(*S0#6wF@+!CMF-Mb$(A>;UfeZ!zI1?o z8OmG;1rFv*0J75&CzGVNnpp#eYtqF5DEvStRXiFBy=-y;yy!rd#1wu&FNyh8t_qw? z;zg2w-OQS+^h5aHX`*%YICp^Fo7SiselbM!m7a3 lf9Smlw8G8LU;+W20|4(PYp5iOkT(DT002ovPDHLkV1fjkjR^n% literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/statusorbs/1067-POISON.png b/runelite-client/src/main/resources/net/runelite/client/plugins/statusorbs/1067-POISON.png new file mode 100644 index 0000000000000000000000000000000000000000..a4b05c1ca1c413f92f9e3f421500aafcfdc15016 GIT binary patch literal 2992 zcmV;h3s3ZkP)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0002nNkl-L=rrxSK(vZZ69}!OjcWOsxsZD=9!LtLl<%;5{VEF```MCnct^>F# zi*g|~I2$hjNDm>&D25lHuD*W-Tx9`xc2F6*?u|rsOp*ZGaz%Ow@$7)~5`sl5NnIpX zOj5?JL1=NcI4Ywj^t6YY;wN~YG@SNbvM3kw-?Wcpo$h>DPd)Vw{??!g{D7Mpn7~F@ mH@LqdjGz&&e}*Oy;5Go2@ME3bOWqIw0000KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0002hNkl+)kPyLB(JD5)s3~eO=1FTV_G?m^an>iyttC3*fOb1|U~st;5u2u{g|y&i zz5}3AIc1{WfWGuHBqBBeKq@n5*jXeKw}vDE&iX{9a#ER5bvX!rlC(o96s1-_g8;$U z0ds2yJz;OVJIqGG;Bp5bylR@F7V_V=k7nEc?RomGZ}7JOANT?L0){{@Yy Date: Wed, 10 Jul 2019 23:32:24 +0200 Subject: [PATCH 14/18] client: Add Widget Animation Smoothing --- .../main/java/net/runelite/api/Client.java | 14 + .../AnimationSmoothingConfig.java | 10 + .../AnimationSmoothingPlugin.java | 2 + .../net/runelite/mixins/RSClientMixin.java | 17 + .../mixins/RSSequenceDefinitionMixin.java | 384 +++++++++++------- .../net/runelite/mixins/RSWidgetMixin.java | 18 + .../runelite/rs/api/RSSequenceDefinition.java | 3 + .../java/net/runelite/rs/api/RSWidget.java | 6 + .../src/main/java/NPCDefinition.java | 6 +- .../src/main/java/ObjectDefinition.java | 2 +- .../src/main/java/PlayerAppearance.java | 6 +- .../src/main/java/SequenceDefinition.java | 36 +- .../main/java/SpotAnimationDefinition.java | 2 +- runescape-client/src/main/java/Widget.java | 6 +- 14 files changed, 325 insertions(+), 187 deletions(-) diff --git a/runelite-api/src/main/java/net/runelite/api/Client.java b/runelite-api/src/main/java/net/runelite/api/Client.java index ab4ee7f15b..3eaadba176 100644 --- a/runelite-api/src/main/java/net/runelite/api/Client.java +++ b/runelite-api/src/main/java/net/runelite/api/Client.java @@ -1353,6 +1353,20 @@ public interface Client extends GameShell */ void setInterpolateObjectAnimations(boolean interpolate); + /** + * Checks whether animation smoothing is enabled for widgets. + * + * @return true if widget animation smoothing is enabled, false otherwise + */ + boolean isInterpolateWidgetAnimations(); + + /** + * Sets the animation smoothing state for widgets. + * + * @param interpolate the new smoothing state + */ + void setInterpolateWidgetAnimations(boolean interpolate); + /** * Checks whether the logged in player is in an instanced region. * diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/animsmoothing/AnimationSmoothingConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/animsmoothing/AnimationSmoothingConfig.java index 9260e9ef56..05dadb450a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/animsmoothing/AnimationSmoothingConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/animsmoothing/AnimationSmoothingConfig.java @@ -65,4 +65,14 @@ public interface AnimationSmoothingConfig extends Config return true; } + @ConfigItem( + keyName = "smoothWidgetAnimations", + name = "Smooth Widget Animations", + description = "Configures whether the widget animations are smooth or not", + position = 4 + ) + default boolean smoothWidgetAnimations() + { + return true; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/animsmoothing/AnimationSmoothingPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/animsmoothing/AnimationSmoothingPlugin.java index 6a23856217..7c77990a26 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/animsmoothing/AnimationSmoothingPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/animsmoothing/AnimationSmoothingPlugin.java @@ -69,6 +69,7 @@ public class AnimationSmoothingPlugin extends Plugin client.setInterpolatePlayerAnimations(false); client.setInterpolateNpcAnimations(false); client.setInterpolateObjectAnimations(false); + client.setInterpolateWidgetAnimations(false); } @Subscribe @@ -85,5 +86,6 @@ public class AnimationSmoothingPlugin extends Plugin client.setInterpolatePlayerAnimations(config.smoothPlayerAnimations()); client.setInterpolateNpcAnimations(config.smoothNpcAnimations()); client.setInterpolateObjectAnimations(config.smoothObjectAnimations()); + client.setInterpolateWidgetAnimations(config.smoothWidgetAnimations()); } } diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java index 43d8582787..cca4cae90d 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java @@ -163,6 +163,9 @@ public abstract class RSClientMixin implements RSClient @Inject private static boolean interpolateObjectAnimations; + @Inject + private static boolean interpolateWidgetAnimations; + @Inject private static RSPlayer[] oldPlayers = new RSPlayer[2048]; @@ -307,6 +310,20 @@ public abstract class RSClientMixin implements RSClient interpolateObjectAnimations = interpolate; } + @Inject + @Override + public boolean isInterpolateWidgetAnimations() + { + return interpolateWidgetAnimations; + } + + @Inject + @Override + public void setInterpolateWidgetAnimations(boolean interpolate) + { + interpolateWidgetAnimations = interpolate; + } + @Inject @Override public void setInventoryDragDelay(int delay) diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSSequenceDefinitionMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSSequenceDefinitionMixin.java index 2433ed1d2b..4bf1ab4538 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSSequenceDefinitionMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSSequenceDefinitionMixin.java @@ -15,10 +15,10 @@ public abstract class RSSequenceDefinitionMixin implements RSSequenceDefinition @Shadow("client") private static RSClient client; - @Copy("animateSequence2") + @Copy("applyTransformations") public abstract RSModel rs$applyTransformations(RSModel model, int actionFrame, RSSequenceDefinition poseSeq, int poseFrame); - @Replace("animateSequence2") + @Replace("applyTransformations") public RSModel rl$applyTransformations(RSModel model, int actionFrame, RSSequenceDefinition poseSeq, int poseFrame) { // reset frame ids because we're not interpolating this @@ -35,185 +35,253 @@ public abstract class RSSequenceDefinitionMixin implements RSSequenceDefinition return rs$applyTransformations(model, actionFrame, poseSeq, poseFrame); } - @Copy("animateSequence") + @Copy("transformActorModel") public abstract RSModel rs$transformActorModel(RSModel model, int frameIdx); - @Replace("animateSequence") + @Replace("transformActorModel") public RSModel rl$transformActorModel(RSModel model, int frame) { - // check if the frame has been modified - if (frame < 0) - { - // remove flag to check if the frame has been modified - int packed = frame ^ Integer.MIN_VALUE; - int interval = packed >> 16; - frame = packed & 0xFFFF; - int nextFrame = frame + 1; - if (nextFrame >= getFrameIDs().length) - { - // dont interpolate last frame - nextFrame = -1; - } - int[] frameIds = getFrameIDs(); - int frameId = frameIds[frame]; - RSFrames frames = client.getFrames(frameId >> 16); - int frameIdx = frameId & 0xFFFF; - - int nextFrameIdx = -1; - RSFrames nextFrames = null; - if (nextFrame != -1) - { - int nextFrameId = frameIds[nextFrame]; - nextFrames = client.getFrames(nextFrameId >> 16); - nextFrameIdx = nextFrameId & 0xFFFF; - } - - if (frames == null) - { - // not sure what toSharedModel does but it is needed - return model.toSharedModel(true); - } - else - { - RSModel animatedModel = model.toSharedModel(!frames.getFrames()[frameIdx].isShowing()); - animatedModel.interpolateFrames(frames, frameIdx, nextFrames, nextFrameIdx, interval, - getFrameLenths()[frame]); - return animatedModel; - } - } - else + // check if the frame has not been modified + if (frame >= 0) { return rs$transformActorModel(model, frame); } + + // remove flag to check if the frame has been modified + int packed = frame ^ Integer.MIN_VALUE; + int interval = packed >> 16; + frame = packed & 0xFFFF; + int nextFrame = frame + 1; + if (nextFrame >= getFrameIDs().length) + { + // dont interpolate last frame + nextFrame = -1; + } + int[] frameIds = getFrameIDs(); + int frameId = frameIds[frame]; + RSFrames frames = client.getFrames(frameId >> 16); + int frameIdx = frameId & 0xFFFF; + + int nextFrameIdx = -1; + RSFrames nextFrames = null; + if (nextFrame != -1) + { + int nextFrameId = frameIds[nextFrame]; + nextFrames = client.getFrames(nextFrameId >> 16); + nextFrameIdx = nextFrameId & 0xFFFF; + } + + if (frames == null) + { + // not sure what toSharedModel does but it is needed + return model.toSharedModel(true); + } + + RSModel animatedModel = model.toSharedModel(!frames.getFrames()[frameIdx].isShowing()); + animatedModel.interpolateFrames(frames, frameIdx, nextFrames, nextFrameIdx, interval, + getFrameLenths()[frame]); + return animatedModel; } - @Copy("animateObject") + @Copy("transformObjectModel") public abstract RSModel rs$transformObjectModel(RSModel model, int frame, int rotation); - @Replace("animateObject") + @Replace("transformObjectModel") public RSModel rl$transformObjectModel(RSModel model, int frame, int rotation) { - // check if the frame has been modified - if (frame < 0) - { - // remove flag to check if the frame has been modified - int packed = frame ^ Integer.MIN_VALUE; - int interval = packed >> 16; - frame = packed & 0xFFFF; - - int nextFrame = frame + 1; - if (nextFrame >= getFrameIDs().length) - { - // dont interpolate last frame - nextFrame = -1; - } - int[] frameIds = getFrameIDs(); - int frameId = frameIds[frame]; - RSFrames frames = client.getFrames(frameId >> 16); - int frameIdx = frameId & 0xFFFF; - - int nextFrameIdx = -1; - RSFrames nextFrames = null; - if (nextFrame != -1) - { - int nextFrameId = frameIds[nextFrame]; - nextFrames = client.getFrames(nextFrameId >> 16); - nextFrameIdx = nextFrameId & 0xFFFF; - } - - if (frames == null) - { - return model.toSharedModel(true); - } - else - { - RSModel animatedModel = model.toSharedModel(!frames.getFrames()[frameIdx].isShowing()); - // reset rotation before animating - rotation &= 3; - if (rotation == 1) - { - animatedModel.rotateY270Ccw(); - } - else if (rotation == 2) - { - animatedModel.rotateY180Ccw(); - } - else if (rotation == 3) - { - animatedModel.rotateY90Ccw(); - } - animatedModel.interpolateFrames(frames, frameIdx, nextFrames, nextFrameIdx, interval, - getFrameLenths()[frame]); - // reapply rotation after animating - if (rotation == 1) - { - animatedModel.rotateY90Ccw(); - } - else if (rotation == 2) - { - animatedModel.rotateY180Ccw(); - } - else if (rotation == 3) - { - animatedModel.rotateY270Ccw(); - } - return animatedModel; - } - } - else + // check if the frame has not been modified + if (frame >= 0) { return rs$transformObjectModel(model, frame, rotation); } + + // remove flag to check if the frame has been modified + int packed = frame ^ Integer.MIN_VALUE; + int interval = packed >> 16; + frame = packed & 0xFFFF; + + int nextFrame = frame + 1; + if (nextFrame >= getFrameIDs().length) + { + // dont interpolate last frame + nextFrame = -1; + } + int[] frameIds = getFrameIDs(); + int frameId = frameIds[frame]; + RSFrames frames = client.getFrames(frameId >> 16); + int frameIdx = frameId & 0xFFFF; + + int nextFrameIdx = -1; + RSFrames nextFrames = null; + if (nextFrame != -1) + { + int nextFrameId = frameIds[nextFrame]; + nextFrames = client.getFrames(nextFrameId >> 16); + nextFrameIdx = nextFrameId & 0xFFFF; + } + + if (frames == null) + { + return model.toSharedModel(true); + } + + RSModel animatedModel = model.toSharedModel(!frames.getFrames()[frameIdx].isShowing()); + // reset rotation before animating + rotation &= 3; + if (rotation == 1) + { + animatedModel.rotateY270Ccw(); + } + else if (rotation == 2) + { + animatedModel.rotateY180Ccw(); + } + else if (rotation == 3) + { + animatedModel.rotateY90Ccw(); + } + animatedModel.interpolateFrames(frames, frameIdx, nextFrames, nextFrameIdx, interval, + getFrameLenths()[frame]); + // reapply rotation after animating + if (rotation == 1) + { + animatedModel.rotateY90Ccw(); + } + else if (rotation == 2) + { + animatedModel.rotateY180Ccw(); + } + else if (rotation == 3) + { + animatedModel.rotateY270Ccw(); + } + return animatedModel; } - @Copy("animateSpotAnimation") - public abstract RSModel rs$transformSpotAnimModel(RSModel model, int frame); + @Copy("transformSpotAnimationModel") + public abstract RSModel rs$transformSpotAnimationModel(RSModel model, int frame); - @Replace("animateSpotAnimation") - public RSModel rl$transformSpotAnimModel(RSModel model, int frame) + @Replace("transformSpotAnimationModel") + public RSModel rl$transformSpotAnimationModel(RSModel model, int frame) { - // check if the frame has been modified - if (frame < 0) + // check if the frame has not been modified + if (frame >= 0) { - // remove flag to check if the frame has been modified - int packed = frame ^ Integer.MIN_VALUE; - int interval = packed >> 16; - frame = packed & 0xFFFF; - int nextFrame = frame + 1; - if (nextFrame >= getFrameIDs().length) - { - // dont interpolate last frame - nextFrame = -1; - } - int[] frameIds = getFrameIDs(); - int frameId = frameIds[frame]; - RSFrames frames = client.getFrames(frameId >> 16); - int frameIdx = frameId & 0xFFFF; - - int nextFrameIdx = -1; - RSFrames nextFrames = null; - if (nextFrame != -1) - { - int nextFrameId = frameIds[nextFrame]; - nextFrames = client.getFrames(nextFrameId >> 16); - nextFrameIdx = nextFrameId & 0xFFFF; - } - - if (frames == null) - { - return model.toSharedSpotAnimModel(true); - } - else - { - RSModel animatedModel = model.toSharedSpotAnimModel(!frames.getFrames()[frameIdx].isShowing()); - animatedModel.interpolateFrames(frames, frameIdx, nextFrames, nextFrameIdx, interval, - getFrameLenths()[frame]); - return animatedModel; - } + return rs$transformSpotAnimationModel(model, frame); } - else + + // remove flag to check if the frame has been modified + int packed = frame ^ Integer.MIN_VALUE; + int interval = packed >> 16; + frame = packed & 0xFFFF; + int nextFrame = frame + 1; + if (nextFrame >= getFrameIDs().length) { - return rs$transformSpotAnimModel(model, frame); + // dont interpolate last frame + nextFrame = -1; } + int[] frameIds = getFrameIDs(); + int frameId = frameIds[frame]; + RSFrames frames = client.getFrames(frameId >> 16); + int frameIdx = frameId & 0xFFFF; + + int nextFrameIdx = -1; + RSFrames nextFrames = null; + if (nextFrame != -1) + { + int nextFrameId = frameIds[nextFrame]; + nextFrames = client.getFrames(nextFrameId >> 16); + nextFrameIdx = nextFrameId & 0xFFFF; + } + + if (frames == null) + { + return model.toSharedSpotAnimModel(true); + } + + RSModel animatedModel = model.toSharedSpotAnimModel(!frames.getFrames()[frameIdx].isShowing()); + animatedModel.interpolateFrames(frames, frameIdx, nextFrames, nextFrameIdx, interval, + getFrameLenths()[frame]); + return animatedModel; + } + + @Copy("transformWidgetModel") + public abstract RSModel rs$transformWidgetModel(RSModel model, int frame); + + @Replace("transformWidgetModel") + public RSModel rl$transformWidgetModel(RSModel model, int frame) + { + // check if the frame has not been modified + if (frame >= 0) + { + return rs$transformWidgetModel(model, frame); + } + + // remove flag to check if the frame has been modified + int packed = frame ^ Integer.MIN_VALUE; + int interval = packed >> 16; + frame = packed & 0xFFFF; + + int nextFrame = frame + 1; + if (nextFrame >= getFrameIDs().length) + { + // dont interpolate last frame + nextFrame = -1; + } + int[] frameIds = getFrameIDs(); + int frameId = frameIds[frame]; + RSFrames frames = client.getFrames(frameId >> 16); + int frameIdx = frameId & 0xFFFF; + + int nextFrameIdx = -1; + RSFrames nextFrames = null; + if (nextFrame != -1) + { + int nextFrameId = frameIds[nextFrame]; + nextFrames = client.getFrames(nextFrameId >> 16); + nextFrameIdx = nextFrameId & 0xFFFF; + } + + if (frames == null) + { + return model.toSharedModel(true); + } + + RSFrames chatFrames = null; + int chatFrameIdx = 0; + if (getChatFrameIds() != null && frame < getChatFrameIds().length) + { + int chatFrameId = getChatFrameIds()[frame]; + chatFrames = client.getFrames(chatFrameId >> 16); + chatFrameIdx = chatFrameId & 0xFFFF; + } + if (chatFrames != null && chatFrameIdx != 0xFFFF) + { + RSFrames nextChatFrames = null; + int nextChatFrameIdx = -1; + if (nextFrame != -1 && nextFrame < getChatFrameIds().length) + { + int chatFrameId = getChatFrameIds()[nextFrame]; + nextChatFrames = client.getFrames(chatFrameId >> 16); + nextChatFrameIdx = chatFrameId & 0xFFFF; + } + // not sure if this can even happen but the client checks for this so to be safe + if (nextChatFrameIdx == 0xFFFF) + { + nextChatFrames = null; + } + RSModel animatedModel = model.toSharedModel(!frames.getFrames()[frameIdx].isShowing() + & !chatFrames.getFrames()[chatFrameIdx].isShowing()); + animatedModel.interpolateFrames(frames, frameIdx, nextFrames, nextFrameIdx, interval, + getFrameLenths()[frame]); + animatedModel.interpolateFrames(chatFrames, chatFrameIdx, nextChatFrames, nextChatFrameIdx, + interval, getFrameLenths()[frame]); + return animatedModel; + } + + RSModel animatedModel = model.toSharedModel(!frames.getFrames()[frameIdx].isShowing()); + animatedModel.interpolateFrames(frames, frameIdx, nextFrames, nextFrameIdx, interval, + getFrameLenths()[frame]); + return animatedModel; } } diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSWidgetMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSWidgetMixin.java index 208c813667..55db558d24 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSWidgetMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSWidgetMixin.java @@ -30,6 +30,8 @@ import net.runelite.api.Point; import net.runelite.api.WidgetNode; import net.runelite.api.events.WidgetHiddenChanged; import net.runelite.api.events.WidgetPositioned; +import net.runelite.api.mixins.Copy; +import net.runelite.api.mixins.Replace; import net.runelite.api.widgets.Widget; import static net.runelite.api.widgets.WidgetInfo.TO_CHILD; import static net.runelite.api.widgets.WidgetInfo.TO_GROUP; @@ -44,8 +46,11 @@ import net.runelite.api.mixins.Inject; import net.runelite.api.mixins.Mixin; import net.runelite.api.mixins.Shadow; import net.runelite.rs.api.RSClient; +import net.runelite.rs.api.RSModel; import net.runelite.rs.api.RSNode; import net.runelite.rs.api.RSNodeHashTable; +import net.runelite.rs.api.RSPlayerAppearance; +import net.runelite.rs.api.RSSequenceDefinition; import net.runelite.rs.api.RSWidget; @Mixin(RSWidget.class) @@ -581,4 +586,17 @@ public abstract class RSWidgetMixin implements RSWidget Arrays.fill(getChildren(), null); } } + + @Copy("getModel") + public abstract RSModel rs$getModel(RSSequenceDefinition sequence, int frame, boolean alternate, RSPlayerAppearance playerComposition); + + @Replace("getModel") + public RSModel rl$getModel(RSSequenceDefinition sequence, int frame, boolean alternate, RSPlayerAppearance playerComposition) + { + if (frame != -1 && client.isInterpolateWidgetAnimations()) + { + frame = frame | getModelFrameCycle() << 16 | Integer.MIN_VALUE; + } + return rs$getModel(sequence, frame, alternate, playerComposition); + } } diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSSequenceDefinition.java b/runescape-api/src/main/java/net/runelite/rs/api/RSSequenceDefinition.java index 97c8d9a91a..ce8d1bae49 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSSequenceDefinition.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSSequenceDefinition.java @@ -24,4 +24,7 @@ public interface RSSequenceDefinition @Import("frameLengths") int[] getFrameLenths(); + + @Import("chatFrameIds") + int[] getChatFrameIds(); } diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSWidget.java b/runescape-api/src/main/java/net/runelite/rs/api/RSWidget.java index 85c45da695..286eae5ce9 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSWidget.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSWidget.java @@ -475,4 +475,10 @@ public interface RSWidget extends Widget @Import("noScrollThrough") @Override void setNoScrollThrough(boolean noScrollThrough); + + @Import("modelFrame") + int getModelFrame(); + + @Import("modelFrameCycle") + int getModelFrameCycle(); } diff --git a/runescape-client/src/main/java/NPCDefinition.java b/runescape-client/src/main/java/NPCDefinition.java index f12aa595fa..e26103531a 100644 --- a/runescape-client/src/main/java/NPCDefinition.java +++ b/runescape-client/src/main/java/NPCDefinition.java @@ -424,11 +424,11 @@ public class NPCDefinition extends DualNode { Model var10; if (var1 != null && var3 != null) { - var10 = var1.animateSequence2(var5, var2, var3, var4); + var10 = var1.applyTransformations(var5, var2, var3, var4); } else if (var1 != null) { - var10 = var1.animateSequence(var5, var2); + var10 = var1.transformActorModel(var5, var2); } else if (var3 != null) { - var10 = var3.animateSequence(var5, var4); + var10 = var3.transformActorModel(var5, var4); } else { var10 = var5.toSharedSequenceModel(true); } diff --git a/runescape-client/src/main/java/ObjectDefinition.java b/runescape-client/src/main/java/ObjectDefinition.java index 6a6a68db6f..e8d3c9e0cf 100644 --- a/runescape-client/src/main/java/ObjectDefinition.java +++ b/runescape-client/src/main/java/ObjectDefinition.java @@ -662,7 +662,7 @@ public class ObjectDefinition extends DualNode { return var11; } else { if (var7 != null) { - var11 = var7.animateObject(var11, var8, var2); + var11 = var7.transformObjectModel(var11, var8, var2); } else { var11 = var11.toSharedSequenceModel(true); } diff --git a/runescape-client/src/main/java/PlayerAppearance.java b/runescape-client/src/main/java/PlayerAppearance.java index ad5de3e57f..d308ce0f27 100644 --- a/runescape-client/src/main/java/PlayerAppearance.java +++ b/runescape-client/src/main/java/PlayerAppearance.java @@ -321,11 +321,11 @@ public class PlayerAppearance { } else { Model var16; if (var1 != null && var3 != null) { - var16 = var1.animateSequence2(var15, var2, var3, var4); + var16 = var1.applyTransformations(var15, var2, var3, var4); } else if (var1 != null) { - var16 = var1.animateSequence(var15, var2); + var16 = var1.transformActorModel(var15, var2); } else { - var16 = var3.animateSequence(var15, var4); + var16 = var3.transformActorModel(var15, var4); } return var16; diff --git a/runescape-client/src/main/java/SequenceDefinition.java b/runescape-client/src/main/java/SequenceDefinition.java index 3db6636931..323bb88dce 100644 --- a/runescape-client/src/main/java/SequenceDefinition.java +++ b/runescape-client/src/main/java/SequenceDefinition.java @@ -38,8 +38,8 @@ public class SequenceDefinition extends DualNode { @Export("frameIds") public int[] frameIds; @ObfuscatedName("g") - @Export("frameIds2") - int[] frameIds2; + @Export("chatFrameIds") + int[] chatFrameIds; @ObfuscatedName("l") @Export("frameLengths") public int[] frameLengths; @@ -179,14 +179,14 @@ public class SequenceDefinition extends DualNode { this.field783 = var1.readUnsignedByte(); } else if (var2 == 12) { var4 = var1.readUnsignedByte(); - this.frameIds2 = new int[var4]; + this.chatFrameIds = new int[var4]; for (var5 = 0; var5 < var4; ++var5) { - this.frameIds2[var5] = var1.readUnsignedShort(); + this.chatFrameIds[var5] = var1.readUnsignedShort(); } for (var5 = 0; var5 < var4; ++var5) { - var3 = this.frameIds2; + var3 = this.chatFrameIds; var3[var5] += var1.readUnsignedShort() << 16; } } else if (var2 == 13) { @@ -230,8 +230,8 @@ public class SequenceDefinition extends DualNode { signature = "(Ldu;II)Ldu;", garbageValue = "128527714" ) - @Export("animateSequence") - public Model animateSequence(Model model, int frame) { + @Export("transformActorModel") + public Model transformActorModel(Model model, int frame) { frame = this.frameIds[frame]; Frames var3 = ItemContainer.getFrames(frame >> 16); frame &= 65535; @@ -249,8 +249,8 @@ public class SequenceDefinition extends DualNode { signature = "(Ldu;IIB)Ldu;", garbageValue = "-65" ) - @Export("animateObject") - Model animateObject(Model model, int frame, int orientation) { + @Export("transformObjectModel") + Model transformObjectModel(Model model, int frame, int orientation) { frame = this.frameIds[frame]; Frames var4 = ItemContainer.getFrames(frame >> 16); frame &= 65535; @@ -285,8 +285,8 @@ public class SequenceDefinition extends DualNode { signature = "(Ldu;II)Ldu;", garbageValue = "-1692496767" ) - @Export("animateSpotAnimation") - Model animateSpotAnimation(Model model, int frame) { + @Export("transformSpotAnimationModel") + Model transformSpotAnimationModel(Model model, int frame) { frame = this.frameIds[frame]; Frames var3 = ItemContainer.getFrames(frame >> 16); frame &= 65535; @@ -304,13 +304,13 @@ public class SequenceDefinition extends DualNode { signature = "(Ldu;ILjh;II)Ldu;", garbageValue = "-386360993" ) - @Export("animateSequence2") - public Model animateSequence2(Model model, int frame, SequenceDefinition sequence, int sequenceFrame) { + @Export("applyTransformations") + public Model applyTransformations(Model model, int frame, SequenceDefinition sequence, int sequenceFrame) { frame = this.frameIds[frame]; Frames var5 = ItemContainer.getFrames(frame >> 16); frame &= 65535; if (var5 == null) { - return sequence.animateSequence(model, sequenceFrame); + return sequence.transformActorModel(model, sequenceFrame); } else { sequenceFrame = sequence.frameIds[sequenceFrame]; Frames var6 = ItemContainer.getFrames(sequenceFrame >> 16); @@ -333,8 +333,8 @@ public class SequenceDefinition extends DualNode { signature = "(Ldu;II)Ldu;", garbageValue = "-15433768" ) - @Export("animateWidget") - public Model animateWidget(Model model, int frame) { + @Export("transformWidgetModel") + public Model transformWidgetModel(Model model, int frame) { int var3 = this.frameIds[frame]; Frames var4 = ItemContainer.getFrames(var3 >> 16); var3 &= 65535; @@ -343,8 +343,8 @@ public class SequenceDefinition extends DualNode { } else { Frames var5 = null; int var6 = 0; - if (this.frameIds2 != null && frame < this.frameIds2.length) { - var6 = this.frameIds2[frame]; + if (this.chatFrameIds != null && frame < this.chatFrameIds.length) { + var6 = this.chatFrameIds[frame]; var5 = ItemContainer.getFrames(var6 >> 16); var6 &= 65535; } diff --git a/runescape-client/src/main/java/SpotAnimationDefinition.java b/runescape-client/src/main/java/SpotAnimationDefinition.java index 3d7668daf8..d7d530aba5 100644 --- a/runescape-client/src/main/java/SpotAnimationDefinition.java +++ b/runescape-client/src/main/java/SpotAnimationDefinition.java @@ -196,7 +196,7 @@ public class SpotAnimationDefinition extends DualNode { Model var5; if (this.sequence != -1 && var1 != -1) { - var5 = WorldMapAreaData.getSequenceDefinition(this.sequence).animateSpotAnimation(var2, var1); + var5 = WorldMapAreaData.getSequenceDefinition(this.sequence).transformSpotAnimationModel(var2, var1); } else { var5 = var2.toSharedSpotAnimationModel(true); } diff --git a/runescape-client/src/main/java/Widget.java b/runescape-client/src/main/java/Widget.java index 69b5dca3b4..7b91706a4a 100644 --- a/runescape-client/src/main/java/Widget.java +++ b/runescape-client/src/main/java/Widget.java @@ -1376,11 +1376,11 @@ public class Widget extends Node { garbageValue = "1082545676" ) @Export("getModel") - public Model getModel(SequenceDefinition sequence, int frame, boolean var3, PlayerAppearance appearance) { + public Model getModel(SequenceDefinition sequence, int frame, boolean alternate, PlayerAppearance appearance) { field957 = false; int var5; int var6; - if (var3) { + if (alternate) { var5 = this.modelType2; var6 = this.modelId2; } else { @@ -1445,7 +1445,7 @@ public class Widget extends Node { } if (sequence != null) { - var7 = sequence.animateWidget(var7, frame); + var7 = sequence.transformWidgetModel(var7, frame); } return var7; From ccb172598114bc8bf718cb4c13ef0cd6bc433080 Mon Sep 17 00:00:00 2001 From: Journey Date: Wed, 10 Jul 2019 20:57:12 -0500 Subject: [PATCH 15/18] Inventory Grid Plugin: Make it so the grid/background actually works The way it was being done before was the correct way, this commit https://github.com/runelite-extended/runelite/commit/d6285235cb836d90c09dae22e686a113bef88c4d actually broke it. So this reverts it back to the original method. --- .../client/plugins/inventorygrid/InventoryGridOverlay.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inventorygrid/InventoryGridOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/inventorygrid/InventoryGridOverlay.java index 9309861a98..4e86d634ff 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/inventorygrid/InventoryGridOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/inventorygrid/InventoryGridOverlay.java @@ -69,7 +69,7 @@ class InventoryGridOverlay extends Overlay final Widget if1DraggingWidget = client.getIf1DraggedWidget(); final Widget inventoryWidget = client.getWidget(WidgetInfo.INVENTORY); - if (if1DraggingWidget == null || if1DraggingWidget.equals(inventoryWidget) + if (if1DraggingWidget == null || if1DraggingWidget != inventoryWidget || client.getItemPressedDuration() < plugin.getDragDelay() / Constants.CLIENT_TICK_LENGTH) { return null; From 7ba7bb64dd92d8ba57852cee5960a3c7c26ab61b Mon Sep 17 00:00:00 2001 From: Zeruth Date: Wed, 10 Jul 2019 23:25:39 -0400 Subject: [PATCH 16/18] finish bringup --- .../java/net/runelite/api/events/ItemContainerChanged.java | 2 +- .../plugins/itemskeptondeath/ItemsKeptOnDeathPlugin.java | 2 +- .../client/plugins/keyremapping/KeyRemappingListener.java | 3 +++ .../itemskeptondeath/ItemsKeptOnDeathPluginTest.java | 6 +++--- .../main/java/net/runelite/mixins/RSItemContainerMixin.java | 4 ++-- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/runelite-api/src/main/java/net/runelite/api/events/ItemContainerChanged.java b/runelite-api/src/main/java/net/runelite/api/events/ItemContainerChanged.java index a46692f416..1b0a80fa4e 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/ItemContainerChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/ItemContainerChanged.java @@ -28,7 +28,7 @@ import net.runelite.api.ItemContainer; import lombok.Value; /** - * An event called whenever the stack size of an {@link api.Item} + * An event called whenever the stack size of an {@link net.runelite.api.Item} * in an {@link ItemContainer} is modified. *

* Examples of when this event may trigger include: diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/ItemsKeptOnDeathPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/ItemsKeptOnDeathPlugin.java index 176eb708db..89115daa29 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/ItemsKeptOnDeathPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemskeptondeath/ItemsKeptOnDeathPlugin.java @@ -706,7 +706,7 @@ public class ItemsKeptOnDeathPlugin extends Plugin { final int id = item.getId(); final int qty = item.getQty(); - final ItemDefinition c = itemManager.getItemComposition(id); + final ItemDefinition c = itemManager.getItemDefinition(id); final Widget itemWidget = parent.createChild(-1, WidgetType.GRAPHIC); itemWidget.setOriginalWidth(Constants.ITEM_SPRITE_WIDTH); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingListener.java b/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingListener.java index 1bc2141c06..185545ba27 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingListener.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingListener.java @@ -45,6 +45,9 @@ class KeyRemappingListener extends MouseAdapter implements KeyListener @Inject private KeyRemappingPlugin plugin; + @Inject + private KeyRemappingConfig config; + @Inject private Client client; diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/itemskeptondeath/ItemsKeptOnDeathPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/itemskeptondeath/ItemsKeptOnDeathPluginTest.java index 37e8a7d99d..4025ccf1b7 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/itemskeptondeath/ItemsKeptOnDeathPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/itemskeptondeath/ItemsKeptOnDeathPluginTest.java @@ -33,7 +33,7 @@ import java.util.Collections; import java.util.List; import net.runelite.api.Client; import net.runelite.api.Item; -import net.runelite.api.ItemComposition; +import net.runelite.api.ItemDefinition; import net.runelite.api.ItemID; import net.runelite.client.game.ItemManager; import static net.runelite.client.plugins.itemskeptondeath.ItemsKeptOnDeathPlugin.DeathItems; @@ -80,7 +80,7 @@ public class ItemsKeptOnDeathPluginTest private Item mItem(final int id, final int qty, final String name, final boolean tradeable, final int price) { // Mock Item Composition and necessary ItemManager methods for this item - ItemComposition c = mock(ItemComposition.class); + ItemDefinition c = mock(ItemDefinition.class); when(c.getId()) .thenReturn(id); when(c.getName()) @@ -96,7 +96,7 @@ public class ItemsKeptOnDeathPluginTest when(c.getLinkedNoteId()).thenReturn(-1); } - when(itemManager.getItemComposition(id)).thenReturn(c); + when(itemManager.getItemDefinition(id)).thenReturn(c); when(itemManager.canonicalize(id)).thenReturn(id); when(itemManager.getItemPrice(id, true)).thenReturn(price); diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSItemContainerMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSItemContainerMixin.java index 74c1bf29a3..f3505f1b80 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSItemContainerMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSItemContainerMixin.java @@ -64,7 +64,7 @@ public abstract class RSItemContainerMixin implements RSItemContainer @FieldHook("quantities") @Inject - public void stackSizesChanged(int idx) + public void stackSizesChanged(int containerID) { int cycle = client.getGameCycle(); if (rl$lastCycle == cycle) @@ -75,7 +75,7 @@ public abstract class RSItemContainerMixin implements RSItemContainer rl$lastCycle = cycle; - ItemContainerChanged event = new ItemContainerChanged(this); + ItemContainerChanged event = new ItemContainerChanged(containerID, this); client.getCallbacks().postDeferred(event); } From a017961e99978e01d41c7e177bd24a44a6338ff2 Mon Sep 17 00:00:00 2001 From: Zeruth Date: Thu, 11 Jul 2019 00:28:53 -0400 Subject: [PATCH 17/18] fixes ItemContainerChanged event --- .../java/net/runelite/api/InventoryID.java | 12 ++++++++++ .../runelite/mixins/RSItemContainerMixin.java | 24 ++++++++++++------- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/runelite-api/src/main/java/net/runelite/api/InventoryID.java b/runelite-api/src/main/java/net/runelite/api/InventoryID.java index df3f995df9..ec170bbc2e 100644 --- a/runelite-api/src/main/java/net/runelite/api/InventoryID.java +++ b/runelite-api/src/main/java/net/runelite/api/InventoryID.java @@ -82,4 +82,16 @@ public enum InventoryID { return id; } + + public static InventoryID getValue(int value) + { + for (InventoryID e: InventoryID.values()) + { + if (e.id == value) + { + return e; + } + } + return null; + } } \ No newline at end of file diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSItemContainerMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSItemContainerMixin.java index f3505f1b80..25ddd7e681 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSItemContainerMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSItemContainerMixin.java @@ -24,11 +24,13 @@ */ package net.runelite.mixins; +import net.runelite.api.InventoryID; import net.runelite.api.Item; import net.runelite.api.events.ItemContainerChanged; -import net.runelite.api.mixins.FieldHook; +import net.runelite.api.mixins.Copy; import net.runelite.api.mixins.Inject; import net.runelite.api.mixins.Mixin; +import net.runelite.api.mixins.Replace; import net.runelite.api.mixins.Shadow; import net.runelite.rs.api.RSClient; import net.runelite.rs.api.RSGroundItem; @@ -41,7 +43,7 @@ public abstract class RSItemContainerMixin implements RSItemContainer private static RSClient client; @Inject - private int rl$lastCycle; + static private int rl$lastCycle; @Inject @Override @@ -62,21 +64,27 @@ public abstract class RSItemContainerMixin implements RSItemContainer return items; } - @FieldHook("quantities") - @Inject - public void stackSizesChanged(int containerID) + @Copy("itemContainerSetItem") + static void rs$itemContainerSetItem(int itemContainerId, int index, int itemId, int itemQuantity) { + + } + + @Replace("itemContainerSetItem") + static void rl$itemContainerSetItem(int itemContainerId, int index, int itemId, int itemQuantity) + { + rs$itemContainerSetItem(itemContainerId, index, itemId, itemQuantity); + int cycle = client.getGameCycle(); if (rl$lastCycle == cycle) { // Limit item container updates to one per cycle + // No need to repeatedly update. The game just needs to know that containers changed once per cycle return; } rl$lastCycle = cycle; - - ItemContainerChanged event = new ItemContainerChanged(containerID, this); + ItemContainerChanged event = new ItemContainerChanged(itemContainerId, client.getItemContainer(InventoryID.getValue(itemContainerId))); client.getCallbacks().postDeferred(event); } - } From eaeedbaadb513cc3b2cf05430cdc0aca02a3ee34 Mon Sep 17 00:00:00 2001 From: Ian William O'Neill Date: Thu, 11 Jul 2019 05:43:51 +0100 Subject: [PATCH 18/18] Slayer Points command and some Renaming. (#966) * Renaming and Tests for Sound Effects in cache. * SlayerPlugin: added !points command. --- .../sound/AudioEnvelopeLoader.java} | 59 +-- .../loaders/sound/InstrumentLoader.java | 93 +++++ .../loaders/sound/SoundEffect1Loader.java | 93 ----- .../loaders/sound/SoundEffect3Loader.java | 95 ----- .../loaders/sound/SoundEffectLoader.java | 67 +++- ...oader.java => SoundEffectTrackLoader.java} | 46 +-- ...tion.java => AudioEnvelopeDefinition.java} | 60 ++- .../sound/InstrumentDefinition.java | 356 ++++++++++++++++++ .../sound/SoundEffectDefinition.java | 103 ++++- ...n.java => SoundEffectTrackDefinition.java} | 72 +++- .../cache/SoundEffectsDumperTest.java | 78 +++- runelite-client/pom.xml | 24 +- .../client/plugins/PluginWatcher.java | 2 +- .../client/plugins/config/ConfigPlugin.java | 22 +- .../client/plugins/slayer/SlayerConfig.java | 11 + .../client/plugins/slayer/SlayerPlugin.java | 45 +++ 16 files changed, 927 insertions(+), 299 deletions(-) rename cache/src/main/java/net/runelite/cache/definitions/{sound/SoundEffect1Definition.java => loaders/sound/AudioEnvelopeLoader.java} (57%) create mode 100644 cache/src/main/java/net/runelite/cache/definitions/loaders/sound/InstrumentLoader.java delete mode 100644 cache/src/main/java/net/runelite/cache/definitions/loaders/sound/SoundEffect1Loader.java delete mode 100644 cache/src/main/java/net/runelite/cache/definitions/loaders/sound/SoundEffect3Loader.java rename cache/src/main/java/net/runelite/cache/definitions/loaders/sound/{SoundEffect2Loader.java => SoundEffectTrackLoader.java} (63%) rename cache/src/main/java/net/runelite/cache/definitions/sound/{SoundEffect3Definition.java => AudioEnvelopeDefinition.java} (56%) create mode 100644 cache/src/main/java/net/runelite/cache/definitions/sound/InstrumentDefinition.java rename cache/src/main/java/net/runelite/cache/definitions/sound/{SoundEffect2Definition.java => SoundEffectTrackDefinition.java} (56%) diff --git a/cache/src/main/java/net/runelite/cache/definitions/sound/SoundEffect1Definition.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/AudioEnvelopeLoader.java similarity index 57% rename from cache/src/main/java/net/runelite/cache/definitions/sound/SoundEffect1Definition.java rename to cache/src/main/java/net/runelite/cache/definitions/loaders/sound/AudioEnvelopeLoader.java index ecd9854892..084a8a9a86 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/sound/SoundEffect1Definition.java +++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/AudioEnvelopeLoader.java @@ -22,34 +22,41 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package net.runelite.cache.definitions.sound; +package net.runelite.cache.definitions.loaders.sound; -public class SoundEffect1Definition +import net.runelite.cache.definitions.sound.AudioEnvelopeDefinition; +import net.runelite.cache.io.InputStream; + +public class AudioEnvelopeLoader { - public SoundEffect2Definition field1173; - public SoundEffect2Definition field1174; - public SoundEffect2Definition field1175; - public int field1176 = 500; - public int[] field1177 = new int[] + public AudioEnvelopeDefinition load(InputStream in) { - 0, 0, 0, 0, 0 - }; - public SoundEffect2Definition field1178; - public int[] field1179 = new int[] + AudioEnvelopeDefinition audioEnvelope = new AudioEnvelopeDefinition(); + + load(audioEnvelope, in); + + return audioEnvelope; + } + + private void load(AudioEnvelopeDefinition audioEnvelope, InputStream in) { - 0, 0, 0, 0, 0 - }; - public int[] field1180 = new int[] + audioEnvelope.form = in.readUnsignedByte(); + audioEnvelope.start = in.readInt(); + audioEnvelope.end = in.readInt(); + this.loadSegments(audioEnvelope, in); + } + + final void loadSegments(AudioEnvelopeDefinition audioEnvelope, InputStream in) { - 0, 0, 0, 0, 0 - }; - public SoundEffect2Definition field1181; - public SoundEffect3Definition field1182; - public SoundEffect2Definition field1183; - public int field1184 = 100; - public SoundEffect2Definition field1186; - public int field1187 = 0; - public int field1188 = 0; - public SoundEffect2Definition field1192; - public SoundEffect2Definition field1193; -} + audioEnvelope.segments = in.readUnsignedByte(); + audioEnvelope.durations = new int[audioEnvelope.segments]; + audioEnvelope.phases = new int[audioEnvelope.segments]; + + for (int i = 0; i < audioEnvelope.segments; ++i) + { + audioEnvelope.durations[i] = in.readUnsignedShort(); + audioEnvelope.phases[i] = in.readUnsignedShort(); + } + + } +} \ No newline at end of file diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/InstrumentLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/InstrumentLoader.java new file mode 100644 index 0000000000..f3d2326760 --- /dev/null +++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/InstrumentLoader.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2017, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.cache.definitions.loaders.sound; + +import net.runelite.cache.definitions.sound.InstrumentDefinition; +import net.runelite.cache.definitions.sound.AudioEnvelopeDefinition; +import net.runelite.cache.io.InputStream; + +public class InstrumentLoader +{ + private final AudioEnvelopeLoader aeLoader = new AudioEnvelopeLoader(); + private final SoundEffectLoader seLoader = new SoundEffectLoader(); + + public InstrumentDefinition load(InputStream in) + { + InstrumentDefinition instrument = new InstrumentDefinition(); + + load(instrument, in); + + return instrument; + } + + private void load(InstrumentDefinition instrument, InputStream in) + { + instrument.pitch = aeLoader.load(in); + instrument.volume = aeLoader.load(in); + int volume = in.readUnsignedByte(); + if (volume != 0) + { + in.setOffset(in.getOffset() - 1); + instrument.pitchModifier = aeLoader.load(in); + instrument.pitchModifierAmplitude = aeLoader.load(in); + } + + volume = in.readUnsignedByte(); + if (volume != 0) + { + in.setOffset(in.getOffset() - 1); + instrument.volumeMultiplier = aeLoader.load(in); + instrument.volumeMultiplierAmplitude = aeLoader.load(in); + } + + volume = in.readUnsignedByte(); + if (volume != 0) + { + in.setOffset(in.getOffset() - 1); + instrument.release = aeLoader.load(in); + instrument.field1175 = aeLoader.load(in); + } + + for (int i = 0; i < 10; ++i) + { + int vol = in.readUnsignedShortSmart(); + if (vol == 0) + { + break; + } + + instrument.oscillatorVolume[i] = vol; + instrument.oscillatorPitch[i] = in.readShortSmart(); + instrument.oscillatorDelays[i] = in.readUnsignedShortSmart(); + } + + instrument.delayTime = in.readUnsignedShortSmart(); + instrument.delayDecay = in.readUnsignedShortSmart(); + instrument.duration = in.readUnsignedShort(); + instrument.offset = in.readUnsignedShort(); + instrument.filterEnvelope = new AudioEnvelopeDefinition(); + instrument.filter = seLoader.load(in, instrument.filterEnvelope); + } +} \ No newline at end of file diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/SoundEffect1Loader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/SoundEffect1Loader.java deleted file mode 100644 index a5c8f16f78..0000000000 --- a/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/SoundEffect1Loader.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2017, Adam - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.runelite.cache.definitions.loaders.sound; - -import net.runelite.cache.definitions.sound.SoundEffect1Definition; -import net.runelite.cache.definitions.sound.SoundEffect2Definition; -import net.runelite.cache.io.InputStream; - -public class SoundEffect1Loader -{ - private final SoundEffect2Loader se2Loader = new SoundEffect2Loader(); - private final SoundEffect3Loader se3Loader = new SoundEffect3Loader(); - - public SoundEffect1Definition load(InputStream in) - { - SoundEffect1Definition se = new SoundEffect1Definition(); - - load(se, in); - - return se; - } - - private void load(SoundEffect1Definition se, InputStream var1) - { - se.field1181 = se2Loader.load(var1); - se.field1173 = se2Loader.load(var1); - int var2 = var1.readUnsignedByte(); - if (var2 != 0) - { - var1.setOffset(var1.getOffset() - 1); - se.field1174 = se2Loader.load(var1); - se.field1193 = se2Loader.load(var1); - } - - var2 = var1.readUnsignedByte(); - if (var2 != 0) - { - var1.setOffset(var1.getOffset() - 1); - se.field1183 = se2Loader.load(var1); - se.field1192 = se2Loader.load(var1); - } - - var2 = var1.readUnsignedByte(); - if (var2 != 0) - { - var1.setOffset(var1.getOffset() - 1); - se.field1178 = se2Loader.load(var1); - se.field1175 = se2Loader.load(var1); - } - - for (int var3 = 0; var3 < 10; ++var3) - { - int var4 = var1.readUnsignedShortSmart(); - if (var4 == 0) - { - break; - } - - se.field1180[var3] = var4; - se.field1179[var3] = var1.readShortSmart(); - se.field1177[var3] = var1.readUnsignedShortSmart(); - } - - se.field1187 = var1.readUnsignedShortSmart(); - se.field1184 = var1.readUnsignedShortSmart(); - se.field1176 = var1.readUnsignedShort(); - se.field1188 = var1.readUnsignedShort(); - se.field1186 = new SoundEffect2Definition(); - se.field1182 = se3Loader.load(var1, se.field1186); - } -} diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/SoundEffect3Loader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/SoundEffect3Loader.java deleted file mode 100644 index ad7f733efb..0000000000 --- a/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/SoundEffect3Loader.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2017, Adam - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.runelite.cache.definitions.loaders.sound; - -import net.runelite.cache.definitions.sound.SoundEffect2Definition; -import net.runelite.cache.definitions.sound.SoundEffect3Definition; -import net.runelite.cache.io.InputStream; - -public class SoundEffect3Loader -{ - private final SoundEffect2Loader se2Loader = new SoundEffect2Loader(); - - public SoundEffect3Definition load(InputStream in, SoundEffect2Definition var2) - { - SoundEffect3Definition se = new SoundEffect3Definition(); - - load(se, in, var2); - - return se; - } - - private void load(SoundEffect3Definition se, InputStream var1, SoundEffect2Definition var2) - { - int var3 = var1.readUnsignedByte(); - se.field1155[0] = var3 >> 4; - se.field1155[1] = var3 & 15; - if (var3 != 0) - { - se.field1156[0] = var1.readUnsignedShort(); - se.field1156[1] = var1.readUnsignedShort(); - int var4 = var1.readUnsignedByte(); - - int var5; - int var6; - for (var5 = 0; var5 < 2; ++var5) - { - for (var6 = 0; var6 < se.field1155[var5]; ++var6) - { - se.field1154[var5][0][var6] = var1.readUnsignedShort(); - se.field1159[var5][0][var6] = var1.readUnsignedShort(); - } - } - - for (var5 = 0; var5 < 2; ++var5) - { - for (var6 = 0; var6 < se.field1155[var5]; ++var6) - { - if ((var4 & 1 << var5 * 4 << var6) != 0) - { - se.field1154[var5][1][var6] = var1.readUnsignedShort(); - se.field1159[var5][1][var6] = var1.readUnsignedShort(); - } - else - { - se.field1154[var5][1][var6] = se.field1154[var5][0][var6]; - se.field1159[var5][1][var6] = se.field1159[var5][0][var6]; - } - } - } - - if (var4 != 0 || se.field1156[1] != se.field1156[0]) - { - se2Loader.method1144(var2, var1); - } - } - else - { - int[] var7 = se.field1156; - se.field1156[1] = 0; - var7[0] = 0; - } - } -} diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/SoundEffectLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/SoundEffectLoader.java index ddb6a4d9fc..2ee518d4ee 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/SoundEffectLoader.java +++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/SoundEffectLoader.java @@ -24,39 +24,70 @@ */ package net.runelite.cache.definitions.loaders.sound; +import net.runelite.cache.definitions.sound.AudioEnvelopeDefinition; import net.runelite.cache.definitions.sound.SoundEffectDefinition; -import net.runelite.cache.definitions.sound.SoundEffect1Definition; import net.runelite.cache.io.InputStream; public class SoundEffectLoader { - public SoundEffectDefinition load(byte[] b) + private final AudioEnvelopeLoader audioEnvelopeLoader = new AudioEnvelopeLoader(); + + public SoundEffectDefinition load(InputStream in, AudioEnvelopeDefinition audioEnvelope) { - SoundEffectDefinition se = new SoundEffectDefinition(); - InputStream in = new InputStream(b); + SoundEffectDefinition soundEffect = new SoundEffectDefinition(); - load(se, in); + load(soundEffect, audioEnvelope, in); - return se; + return soundEffect; } - private void load(SoundEffectDefinition se, InputStream var1) + private void load(SoundEffectDefinition soundEffect, AudioEnvelopeDefinition audioEnvelope, InputStream in) { - for (int var2 = 0; var2 < 10; ++var2) + int id = in.readUnsignedByte(); + soundEffect.pairs[0] = id >> 4; + soundEffect.pairs[1] = id & 15; + if (id != 0) { - int var3 = var1.readUnsignedByte(); - if (var3 != 0) + soundEffect.unity[0] = in.readUnsignedShort(); + soundEffect.unity[1] = in.readUnsignedShort(); + int track = in.readUnsignedByte(); + + for (int i = 0; i < 2; ++i) { - var1.setOffset(var1.getOffset() - 1); + for (int j = 0; j < soundEffect.pairs[i]; ++j) + { + soundEffect.phases[i][0][j] = in.readUnsignedShort(); + soundEffect.magnitudes[i][0][j] = in.readUnsignedShort(); + } + } - SoundEffect1Loader se1Loader = new SoundEffect1Loader(); - SoundEffect1Definition se1 = se1Loader.load(var1); + for (int i = 0; i < 2; ++i) + { + for (int j = 0; j < soundEffect.pairs[i]; ++j) + { + if ((track & 1 << i * 4 << j) != 0) + { + soundEffect.phases[i][1][j] = in.readUnsignedShort(); + soundEffect.magnitudes[i][1][j] = in.readUnsignedShort(); + } + else + { + soundEffect.phases[i][1][j] = soundEffect.phases[i][0][j]; + soundEffect.magnitudes[i][1][j] = soundEffect.magnitudes[i][0][j]; + } + } + } - se.field1008[var2] = se1; + if (track != 0 || soundEffect.unity[1] != soundEffect.unity[0]) + { + audioEnvelopeLoader.loadSegments(audioEnvelope, in); } } - - se.field1006 = var1.readUnsignedShort(); - se.field1009 = var1.readUnsignedShort(); + else + { + int[] clean = soundEffect.unity; + soundEffect.unity[1] = 0; + clean[0] = 0; + } } -} +} \ No newline at end of file diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/SoundEffect2Loader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/SoundEffectTrackLoader.java similarity index 63% rename from cache/src/main/java/net/runelite/cache/definitions/loaders/sound/SoundEffect2Loader.java rename to cache/src/main/java/net/runelite/cache/definitions/loaders/sound/SoundEffectTrackLoader.java index d9ae3d3abb..3b991fdc70 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/SoundEffect2Loader.java +++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/sound/SoundEffectTrackLoader.java @@ -24,39 +24,39 @@ */ package net.runelite.cache.definitions.loaders.sound; -import net.runelite.cache.definitions.sound.SoundEffect2Definition; +import net.runelite.cache.definitions.sound.SoundEffectTrackDefinition; +import net.runelite.cache.definitions.sound.InstrumentDefinition; import net.runelite.cache.io.InputStream; -public class SoundEffect2Loader +public class SoundEffectTrackLoader { - public SoundEffect2Definition load(InputStream in) + public SoundEffectTrackDefinition load(byte[] b) { - SoundEffect2Definition se = new SoundEffect2Definition(); + SoundEffectTrackDefinition soundEffect = new SoundEffectTrackDefinition(); + InputStream in = new InputStream(b); - load(se, in); + load(soundEffect, in); - return se; + return soundEffect; } - private void load(SoundEffect2Definition se, InputStream var1) + private void load(SoundEffectTrackDefinition soundEffect, InputStream in) { - se.field1087 = var1.readUnsignedByte(); - se.field1088 = var1.readInt(); - se.field1089 = var1.readInt(); - this.method1144(se, var1); - } - - final void method1144(SoundEffect2Definition se, InputStream var1) - { - se.field1092 = var1.readUnsignedByte(); - se.field1086 = new int[se.field1092]; - se.field1090 = new int[se.field1092]; - - for (int var2 = 0; var2 < se.field1092; ++var2) + for (int i = 0; i < 10; ++i) { - se.field1086[var2] = var1.readUnsignedShort(); - se.field1090[var2] = var1.readUnsignedShort(); + int volume = in.readUnsignedByte(); + if (volume != 0) + { + in.setOffset(in.getOffset() - 1); + + InstrumentLoader instrumentLoader = new InstrumentLoader(); + InstrumentDefinition instrument = instrumentLoader.load(in); + + soundEffect.instruments[i] = instrument; + } } + soundEffect.start = in.readUnsignedShort(); + soundEffect.end = in.readUnsignedShort(); } -} +} \ No newline at end of file diff --git a/cache/src/main/java/net/runelite/cache/definitions/sound/SoundEffect3Definition.java b/cache/src/main/java/net/runelite/cache/definitions/sound/AudioEnvelopeDefinition.java similarity index 56% rename from cache/src/main/java/net/runelite/cache/definitions/sound/SoundEffect3Definition.java rename to cache/src/main/java/net/runelite/cache/definitions/sound/AudioEnvelopeDefinition.java index a6580c046f..46d709f6a4 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/sound/SoundEffect3Definition.java +++ b/cache/src/main/java/net/runelite/cache/definitions/sound/AudioEnvelopeDefinition.java @@ -24,10 +24,58 @@ */ package net.runelite.cache.definitions.sound; -public class SoundEffect3Definition +public class AudioEnvelopeDefinition { - public int[][][] field1154 = new int[2][2][4]; - public int[] field1155 = new int[2]; - public int[] field1156 = new int[2]; - public int[][][] field1159 = new int[2][2][4]; -} + public int segments = 2; + public int[] durations = new int[2]; + public int[] phases = new int[2]; + public int start; + public int end; + public int form; + public int ticks; + public int phaseIndex; + public int step; + public int amplitude; + public int max; + + public AudioEnvelopeDefinition() + { + this.durations[0] = 0; + this.durations[1] = '\uffff'; + this.phases[0] = 0; + this.phases[1] = '\uffff'; + } + + public final int step(int var1) + { + if (this.max >= this.ticks) + { + this.amplitude = this.phases[this.phaseIndex++] << 15; + + if (this.phaseIndex >= this.segments) + { + this.phaseIndex = this.segments - 1; + } + + this.ticks = (int) ((double) this.durations[this.phaseIndex] / 65536.0 * (double) var1); + + if (this.ticks > this.max) + { + this.step = ((this.phases[this.phaseIndex] << 15) - this.amplitude) / (this.ticks - this.max); + } + } + this.amplitude += this.step; + ++this.max; + + return this.amplitude - this.step >> 15; + } + + public final void reset() + { + this.ticks = 0; + this.phaseIndex = 0; + this.step = 0; + this.amplitude = 0; + this.max = 0; + } +} \ No newline at end of file diff --git a/cache/src/main/java/net/runelite/cache/definitions/sound/InstrumentDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/sound/InstrumentDefinition.java new file mode 100644 index 0000000000..fafaa7b8f8 --- /dev/null +++ b/cache/src/main/java/net/runelite/cache/definitions/sound/InstrumentDefinition.java @@ -0,0 +1,356 @@ +/* + * Copyright (c) 2017, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.cache.definitions.sound; + +import java.util.Random; + +public class InstrumentDefinition +{ + public AudioEnvelopeDefinition volume; + public AudioEnvelopeDefinition pitchModifier; + public AudioEnvelopeDefinition field1175; + public AudioEnvelopeDefinition release; + public AudioEnvelopeDefinition volumeMultiplier; + public AudioEnvelopeDefinition volumeMultiplierAmplitude; + public AudioEnvelopeDefinition pitchModifierAmplitude; + public AudioEnvelopeDefinition pitch; + + public int[] oscillatorDelays = new int[] + { + 0, 0, 0, 0, 0 + }; + public int[] oscillatorPitch = new int[] + { + 0, 0, 0, 0, 0 + }; + public int[] oscillatorVolume = new int[] + { + 0, 0, 0, 0, 0 + }; + + public SoundEffectDefinition filter; + public AudioEnvelopeDefinition filterEnvelope; + + static int[] samples = new int[220500]; + + static int[] NOISE = new int[32768]; + static int[] AUDIO_SINE = new int[32768]; + + static int[] phases = new int[5]; + static int[] delays = new int[5]; + + static int[] volumeSteps = new int[5]; + static int[] pitchSteps = new int[5]; + static int[] pitchBaseSteps = new int[5]; + + public int duration = 500; + public int delayDecay = 100; + public int delayTime = 0; + public int offset = 0; + + static + { + Random random = new Random(0); + + for (int i = 0; i < 32768; ++i) + { + InstrumentDefinition.NOISE[i] = (random.nextInt() & 2) - 1; + InstrumentDefinition.AUDIO_SINE[i] = (int) (Math.sin((double) i / 5215.1903) * 16384.0); + } + } + + public final int[] synthesize(int var1, int var2) + { + int var16; + int var15; + int var14; + int var11; + int var12; + int var13; + + InstrumentDefinition.method3854(samples, 0, var1); + + if (var2 < 10) + { + return samples; + } + + double var3 = (double) var1 / ((double) var2 + 0.0); + + this.pitch.reset(); + this.volume.reset(); + + int var5 = 0; + int var6 = 0; + int var7 = 0; + + if (this.pitchModifier != null) + { + this.pitchModifier.reset(); + this.pitchModifierAmplitude.reset(); + + var5 = (int) ((double) (this.pitchModifier.end - this.pitchModifier.start) * 32.768 / var3); + var6 = (int) ((double) this.pitchModifier.start * 32.768 / var3); + } + + int var8 = 0; + int var9 = 0; + int var10 = 0; + + if (this.volumeMultiplier != null) + { + this.volumeMultiplier.reset(); + this.volumeMultiplierAmplitude.reset(); + + var8 = (int) ((double) (this.volumeMultiplier.end - this.volumeMultiplier.start) * 32.768 / var3); + var9 = (int) ((double) this.volumeMultiplier.start * 32.768 / var3); + } + + for (var11 = 0; var11 < 5; ++var11) + { + if (this.oscillatorVolume[var11] == 0) + { + continue; + } + + InstrumentDefinition.phases[var11] = 0; + InstrumentDefinition.delays[var11] = (int) ((double) this.oscillatorDelays[var11] * var3); + InstrumentDefinition.volumeSteps[var11] = (this.oscillatorVolume[var11] << 14) / 100; + InstrumentDefinition.pitchSteps[var11] = (int) ((double) (this.pitch.end - this.pitch.start) * 32.768 * Math.pow(1.0057929410678534, this.oscillatorPitch[var11]) / var3); + InstrumentDefinition.pitchBaseSteps[var11] = (int) ((double) this.pitch.start * 32.768 / var3); + } + + for (var11 = 0; var11 < var1; ++var11) + { + var12 = this.pitch.step(var1); + var13 = this.volume.step(var1); + + if (this.pitchModifier != null) + { + var14 = this.pitchModifier.step(var1); + var15 = this.pitchModifierAmplitude.step(var1); + var12 += this.evaluateWave(var7, var15, this.pitchModifier.form) >> 1; + var7 = var7 + var6 + (var14 * var5 >> 16); + } + + if (this.volumeMultiplier != null) + { + var14 = this.volumeMultiplier.step(var1); + var15 = this.volumeMultiplierAmplitude.step(var1); + var13 = var13 * ((this.evaluateWave(var10, var15, this.volumeMultiplier.form) >> 1) + 32768) >> 15; + var10 = var10 + var9 + (var14 * var8 >> 16); + } + + for (var14 = 0; var14 < 5; ++var14) + { + if (this.oscillatorVolume[var14] == 0 || (var15 = delays[var14] + var11) >= var1) + { + continue; + } + + int[] arrn = samples; + int n = var15; + arrn[n] = arrn[n] + this.evaluateWave(phases[var14], var13 * volumeSteps[var14] >> 15, this.pitch.form); + int[] arrn2 = phases; + int n2 = var14; + arrn2[n2] = arrn2[n2] + ((var12 * pitchSteps[var14] >> 16) + pitchBaseSteps[var14]); + } + } + + if (this.release != null) + { + this.release.reset(); + this.field1175.reset(); + + var11 = 0; + boolean var20 = true; + + for (var14 = 0; var14 < var1; ++var14) + { + var15 = this.release.step(var1); + var16 = this.field1175.step(var1); + var12 = var20 ? (var15 * (this.release.end - this.release.start) >> 8) + this.release.start : (var16 * (this.release.end - this.release.start) >> 8) + this.release.start; + if ((var11 += 256) >= var12) + { + var11 = 0; + } + if (!var20) + { + continue; + } + InstrumentDefinition.samples[var14] = 0; + } + } + if (this.delayTime > 0 && this.delayDecay > 0) + { + for (var12 = var11 = (int) ((double) this.delayTime * var3); var12 < var1; ++var12) + { + int[] arrn = samples; + int n = var12; + + arrn[n] = arrn[n] + samples[var12 - var11] * this.delayDecay / 100; + } + } + if (this.filter.pairs[0] > 0 || this.filter.pairs[1] > 0) + { + this.filterEnvelope.reset(); + + var11 = this.filterEnvelope.step(var1 + 1); + var12 = this.filter.compute(0, (float) var11 / 65536.0f); + var13 = this.filter.compute(1, (float) var11 / 65536.0f); + + if (var1 >= var12 + var13) + { + int var17; + + var14 = 0; + var15 = var13; + + if (var13 > var1 - var12) + { + var15 = var1 - var12; + } + + while (var14 < var15) + { + var16 = (int) ((long) samples[var14 + var12] * (long) SoundEffectDefinition.fowardMultiplier >> 16); + + for (var17 = 0; var17 < var12; ++var17) + { + var16 += (int) ((long) samples[var14 + var12 - 1 - var17] * (long) SoundEffectDefinition.coefficients[0][var17] >> 16); + } + + for (var17 = 0; var17 < var14; ++var17) + { + var16 -= (int) ((long) samples[var14 - 1 - var17] * (long) SoundEffectDefinition.coefficients[1][var17] >> 16); + } + + InstrumentDefinition.samples[var14] = var16; + var11 = this.filterEnvelope.step(var1 + 1); + ++var14; + } + var15 = 128; + + do + { + int var18; + + if (var15 > var1 - var12) + { + var15 = var1 - var12; + } + + while (var14 < var15) + { + var17 = (int) ((long) samples[var14 + var12] * (long) SoundEffectDefinition.fowardMultiplier >> 16); + + for (var18 = 0; var18 < var12; ++var18) + { + var17 += (int) ((long) samples[var14 + var12 - 1 - var18] * (long) SoundEffectDefinition.coefficients[0][var18] >> 16); + } + + for (var18 = 0; var18 < var13; ++var18) + { + var17 -= (int) ((long) samples[var14 - 1 - var18] * (long) SoundEffectDefinition.coefficients[1][var18] >> 16); + } + + InstrumentDefinition.samples[var14] = var17; + var11 = this.filterEnvelope.step(var1 + 1); + ++var14; + } + if (var14 >= var1 - var12) + { + while (var14 < var1) + { + var17 = 0; + for (var18 = var14 + var12 - var1; var18 < var12; ++var18) + { + var17 += (int) ((long) samples[var14 + var12 - 1 - var18] + * (long) SoundEffectDefinition.coefficients[0][var18] >> 16); + } + for (var18 = 0; var18 < var13; ++var18) + { + var17 -= (int) ((long) samples[var14 - 1 - var18] + * (long) SoundEffectDefinition.coefficients[1][var18] >> 16); + } + InstrumentDefinition.samples[var14] = var17; + this.filterEnvelope.step(var1 + 1); + ++var14; + } + break; + } + var12 = this.filter.compute(0, (float) var11 / 65536.0f); + var13 = this.filter.compute(1, (float) var11 / 65536.0f); + var15 += 128; + } + while (true); + } + } + for (var11 = 0; var11 < var1; ++var11) + { + if (samples[var11] < -32768) + { + InstrumentDefinition.samples[var11] = -32768; + } + + if (samples[var11] <= 32767) + { + continue; + } + + InstrumentDefinition.samples[var11] = 32767; + } + + return samples; + } + + private static void method3854(int[] var1, int var2, int var3) + { + var3 = var3 + var2 - 7; + + while (var2 < var3) + { + var1[var2++] = 0; + var1[var2++] = 0; + var1[var2++] = 0; + var1[var2++] = 0; + var1[var2++] = 0; + var1[var2++] = 0; + var1[var2++] = 0; + var1[var2++] = 0; + } + + while (var2 < (var3 += 7)) + { + var1[var2++] = 0; + } + } + + public final int evaluateWave(int var1, int var2, int var3) + { + return var3 == 1 ? ((var1 & 32767) < 16384 ? var2 : -var2) : (var3 == 2 ? AUDIO_SINE[var1 & 32767] * var2 >> 14 : (var3 == 3 ? (var2 * (var1 & 32767) >> 14) - var2 : (var3 == 4 ? var2 * NOISE[var1 / 2607 & 32767] : 0))); + } +} + diff --git a/cache/src/main/java/net/runelite/cache/definitions/sound/SoundEffectDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/sound/SoundEffectDefinition.java index 0eda10254e..7f6d71dc17 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/sound/SoundEffectDefinition.java +++ b/cache/src/main/java/net/runelite/cache/definitions/sound/SoundEffectDefinition.java @@ -26,7 +26,102 @@ package net.runelite.cache.definitions.sound; public class SoundEffectDefinition { - public int field1006; - public SoundEffect1Definition[] field1008 = new SoundEffect1Definition[10]; - public int field1009; -} + public int[][][] phases = new int[2][2][4]; + public int[] pairs = new int[2]; + public int[] unity = new int[2]; + public int[][][] magnitudes = new int[2][2][4]; + + public static float[][] minCoefficients = new float[2][8]; + public static int[][] coefficients = new int[2][8]; + public static float fowardMinCoefficientMultiplier; + public static int fowardMultiplier; + + public int compute(int var1, float var2) + { + float var3; + int var4; + + if (var1 == 0) + { + var3 = (float) this.unity[0] + (float) (this.unity[1] - this.unity[0]) * var2; + + fowardMinCoefficientMultiplier = (float) Math.pow(0.1, (var3 *= 0.0030517578f) / 20.0f); + fowardMultiplier = (int) (fowardMinCoefficientMultiplier * 65536.0f); + } + + if (this.pairs[var1] == 0) + { + return 0; + } + + var3 = this.interpolateMagniture(var1, 0, var2); + + minCoefficients[var1][0] = -2.0f * var3 * (float) Math.cos(this.interpolatePhase(var1, 0, var2)); + minCoefficients[var1][1] = var3 * var3; + + for (var4 = 1; var4 < this.pairs[var1]; ++var4) + { + var3 = this.interpolateMagniture(var1, var4, var2); + + float var5 = -2.0f * var3 * (float) Math.cos(this.interpolatePhase(var1, var4, var2)); + float var6 = var3 * var3; + + minCoefficients[var1][var4 * 2 + 1] = minCoefficients[var1][var4 * 2 - 1] * var6; + minCoefficients[var1][var4 * 2] = minCoefficients[var1][var4 * 2 - 1] * var5 + minCoefficients[var1][var4 * 2 - 2] * var6; + + for (int var7 = var4 * 2 - 1; var7 >= 2; --var7) + { + float[] arrf = minCoefficients[var1]; + int n = var7; + + arrf[n] = arrf[n] + (minCoefficients[var1][var7 - 1] * var5 + minCoefficients[var1][var7 - 2] * var6); + } + + float[] arrf = minCoefficients[var1]; + arrf[1] = arrf[1] + (minCoefficients[var1][0] * var5 + var6); + + float[] arrf2 = minCoefficients[var1]; + arrf2[0] = arrf2[0] + var5; + } + + if (var1 == 0) + { + var4 = 0; + while (var4 < this.pairs[0] * 2) + { + float[] arrf = minCoefficients[0]; + int n = var4++; + + arrf[n] = arrf[n] * fowardMinCoefficientMultiplier; + } + } + + for (var4 = 0; var4 < this.pairs[var1] * 2; ++var4) + { + coefficients[var1][var4] = (int) (minCoefficients[var1][var4] * 65536.0f); + } + + return this.pairs[var1] * 2; + } + + public float interpolateMagniture(int var1, int var2, float var3) + { + float var4 = (float) this.magnitudes[var1][0][var2] + var3 * (float) (this.magnitudes[var1][1][var2] - this.magnitudes[var1][0][var2]); + + return 1.0f - (float) Math.pow(10.0, (-(var4 *= 0.0015258789f)) / 20.0f); + } + + public float interpolatePhase(int var1, int var2, float var3) + { + float var4 = (float) this.phases[var1][0][var2] + var3 * (float) (this.phases[var1][1][var2] - this.phases[var1][0][var2]); + + return normalise(var4 *= 1.2207031E-4f); + } + + public static float normalise(float var1) + { + float var2 = 32.703197f * (float) Math.pow(2.0, var1); + + return var2 * 3.1415927f / 11025.0f; + } +} \ No newline at end of file diff --git a/cache/src/main/java/net/runelite/cache/definitions/sound/SoundEffect2Definition.java b/cache/src/main/java/net/runelite/cache/definitions/sound/SoundEffectTrackDefinition.java similarity index 56% rename from cache/src/main/java/net/runelite/cache/definitions/sound/SoundEffect2Definition.java rename to cache/src/main/java/net/runelite/cache/definitions/sound/SoundEffectTrackDefinition.java index 358d462f50..6cbbbcc2cf 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/sound/SoundEffect2Definition.java +++ b/cache/src/main/java/net/runelite/cache/definitions/sound/SoundEffectTrackDefinition.java @@ -24,25 +24,61 @@ */ package net.runelite.cache.definitions.sound; -public class SoundEffect2Definition +public class SoundEffectTrackDefinition { - public int field1085; - public int[] field1086 = new int[2]; - public int field1087; - public int field1088; - public int field1089; - public int[] field1090 = new int[2]; - public int field1091; - public int field1092 = 2; - public int field1093; - public int field1094; - public int field1095; + public int start; + public InstrumentDefinition[] instruments = new InstrumentDefinition[10]; + public int end; - public SoundEffect2Definition() + public final byte[] mix() { - this.field1086[0] = 0; - this.field1086[1] = '\uffff'; - this.field1090[0] = 0; - this.field1090[1] = '\uffff'; + int var2; + int var1 = 0; + + for (var2 = 0; var2 < 10; ++var2) + { + if (this.instruments[var2] == null || this.instruments[var2].duration + this.instruments[var2].offset <= var1) + { + continue; + } + + var1 = this.instruments[var2].duration + this.instruments[var2].offset; + } + + if (var1 == 0) + { + return new byte[0]; + } + + var2 = var1 * 22050 / 1000; + + byte[] var3 = new byte[var2]; + + for (int i = 0; i < 10; ++i) + { + if (this.instruments[i] == null) + { + continue; + } + + int var5 = this.instruments[i].duration * 22050 / 1000; + int var6 = this.instruments[i].offset * 22050 / 1000; + + int[] var7 = this.instruments[i].synthesize(var5, this.instruments[i].duration); + + for (int j = 0; j < var5; ++j) + { + int var9 = (var7[j] >> 8) + var3[j + var6]; + + if ((var9 + 128 & -256) != 0) + { + var9 = var9 >> 31 ^ 127; + } + + var3[j + var6] = (byte) var9; + } + } + + return var3; } -} +} \ No newline at end of file diff --git a/cache/src/test/java/net/runelite/cache/SoundEffectsDumperTest.java b/cache/src/test/java/net/runelite/cache/SoundEffectsDumperTest.java index 5ac0c9e1c5..ec332656b2 100644 --- a/cache/src/test/java/net/runelite/cache/SoundEffectsDumperTest.java +++ b/cache/src/test/java/net/runelite/cache/SoundEffectsDumperTest.java @@ -27,11 +27,16 @@ package net.runelite.cache; import com.google.common.io.Files; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import java.io.File; + + import java.io.IOException; +import java.io.File; +import java.io.FileOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.ByteArrayInputStream; import java.nio.charset.Charset; -import net.runelite.cache.definitions.loaders.sound.SoundEffectLoader; -import net.runelite.cache.definitions.sound.SoundEffectDefinition; +import net.runelite.cache.definitions.loaders.sound.SoundEffectTrackLoader; +import net.runelite.cache.definitions.sound.SoundEffectTrackDefinition; import net.runelite.cache.fs.Archive; import net.runelite.cache.fs.Index; import net.runelite.cache.fs.Storage; @@ -42,6 +47,11 @@ import org.junit.rules.TemporaryFolder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.sound.sampled.AudioFileFormat; +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; + public class SoundEffectsDumperTest { private static final Logger logger = LoggerFactory.getLogger(SoundEffectsDumperTest.class); @@ -68,14 +78,68 @@ public class SoundEffectsDumperTest { byte[] contents = archive.decompress(storage.loadArchive(archive)); - SoundEffectLoader soundEffectLoader = new SoundEffectLoader(); - SoundEffectDefinition soundEffect = soundEffectLoader.load(contents); + SoundEffectTrackLoader setLoader = new SoundEffectTrackLoader(); + SoundEffectTrackDefinition soundEffect = setLoader.load(contents); - Files.asCharSink(new File(dumpDir, archive.getArchiveId() + ".json"), Charset.defaultCharset()).write(gson.toJson(soundEffect)); + Files.write(gson.toJson(soundEffect), new File(dumpDir, archive.getArchiveId() + ".json"), Charset.defaultCharset()); ++count; } } logger.info("Dumped {} sound effects to {}", count, dumpDir); } -} + + @Test + public void extractWavTest() throws IOException + { + File dumpDir = folder.newFolder(); + int count = 0; + + try (Store store = new Store(StoreLocation.LOCATION)) + { + store.load(); + + Storage storage = store.getStorage(); + Index index = store.getIndex(IndexType.SOUNDEFFECTS); + + for (Archive archive : index.getArchives()) + { + byte[] contents = archive.decompress(storage.loadArchive(archive)); + + SoundEffectTrackLoader setLoader = new SoundEffectTrackLoader(); + SoundEffectTrackDefinition soundEffect = setLoader.load(contents); + try + { + Object audioStream; + byte[] data = soundEffect.mix(); + + AudioFormat audioFormat = new AudioFormat(22050.0f, 8, 1, true, false); + audioStream = new AudioInputStream(new ByteArrayInputStream(data), audioFormat, data.length); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + AudioSystem.write((AudioInputStream) audioStream, AudioFileFormat.Type.WAVE, bos); + data = bos.toByteArray(); + + FileOutputStream fos = new FileOutputStream(new File(dumpDir, archive.getArchiveId() + ".wav")); + + try + { + fos.write(data); + } + finally + { + fos.close(); + } + + ++count; + } + catch (Exception e) + { + continue; + } + } + } + + logger.info("Dumped {} sound effects to {}", count, dumpDir); + } +} \ No newline at end of file diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml index bf9d9ef83d..8175174cea 100644 --- a/runelite-client/pom.xml +++ b/runelite-client/pom.xml @@ -284,13 +284,23 @@ asm-all 6.0_BETA - - org.codehaus.plexus - plexus-utils - 3.0.21 - compile - - + + org.codehaus.plexus + plexus-utils + 3.0.21 + compile + + + org.apache.httpcomponents + httpcore + 4.4.11 + + + org.apache.httpcomponents + httpmime + 4.5.9 + + diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/PluginWatcher.java b/runelite-client/src/main/java/net/runelite/client/plugins/PluginWatcher.java index 870ccaaaf7..59a89d612d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/PluginWatcher.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/PluginWatcher.java @@ -41,6 +41,7 @@ import java.nio.file.WatchService; import java.util.List; import javax.inject.Inject; import javax.inject.Singleton; + import lombok.extern.slf4j.Slf4j; import net.runelite.client.RuneLite; import net.runelite.client.config.Config; @@ -154,7 +155,6 @@ public class PluginWatcher extends Thread { continue; } - log.info("Loading plugin from {}", file); load(file); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPlugin.java index 0996eed283..7df1fbd6cb 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPlugin.java @@ -25,11 +25,14 @@ package net.runelite.client.plugins.config; import java.awt.image.BufferedImage; +import java.lang.reflect.Method; import java.util.concurrent.ScheduledExecutorService; import javax.inject.Inject; import javax.inject.Singleton; import javax.swing.SwingUtilities; import net.runelite.api.MenuAction; +import net.runelite.client.RuneLite; +import net.runelite.client.callback.ClientThread; import net.runelite.client.config.ChatColorConfig; import net.runelite.client.config.ConfigManager; import net.runelite.client.config.RuneLiteConfig; @@ -41,6 +44,7 @@ import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.plugins.PluginManager; import net.runelite.client.ui.ClientToolbar; +import net.runelite.client.ui.ClientUI; import net.runelite.client.ui.NavigationButton; import net.runelite.client.ui.overlay.Overlay; import net.runelite.client.ui.overlay.OverlayMenuEntry; @@ -97,9 +101,25 @@ public class ConfigPlugin extends Plugin } @Override - protected void shutDown() throws Exception + public void shutDown() throws Exception { clientToolbar.removeNavigation(navButton); + RuneLite.getInjector().getInstance(ClientThread.class).invokeLater(() -> + { + try + { + ConfigPanel.pluginList.clear(); + pluginManager.setPluginEnabled(this, true); + pluginManager.startPlugin(this); + Method expand = ClientUI.class.getDeclaredMethod("expand", NavigationButton.class); + expand.setAccessible(true); + expand.invoke(RuneLite.getInjector().getInstance(ClientUI.class), navButton); + } + catch (Exception e) + { + System.out.println(e.getMessage()); + } + }); } @Subscribe diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerConfig.java index 07e68ee198..bbb246f495 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerConfig.java @@ -166,6 +166,17 @@ public interface SlayerConfig extends Config return true; } + @ConfigItem( + position = 14, + keyName = "pointsCommand", + name = "Points Command", + description = "Configures whether the slayer points command is enabled
!points" + ) + default boolean pointsCommand() + { + return true; + } + // Stored data @ConfigItem( keyName = "taskName", diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java index 2c9e181905..eee2b5beeb 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java @@ -139,6 +139,7 @@ public class SlayerPlugin extends Plugin private static final String TASK_COMMAND_STRING = "!task"; private static final Pattern TASK_STRING_VALIDATION = Pattern.compile("[^a-zA-Z0-9' -]"); private static final int TASK_STRING_MAX_LENGTH = 50; + private static final String POINTS_COMMAND_STRING = "!points"; // Superiors @VisibleForTesting @@ -280,6 +281,8 @@ public class SlayerPlugin extends Plugin private boolean taskCommand; private String taskName; private String taskLocation; + @Setter(AccessLevel.PACKAGE) + private boolean pointsCommand; private int amount; private int initialAmount; private int lastCertainAmount; @@ -316,6 +319,8 @@ public class SlayerPlugin extends Plugin clientToolbar.addNavigation(navButton); chatCommandManager.registerCommandAsync(TASK_COMMAND_STRING, this::taskLookup, this::taskSubmit); + + chatCommandManager.registerCommandAsync(POINTS_COMMAND_STRING, this::pointsLookup); //here } @Override @@ -329,6 +334,7 @@ public class SlayerPlugin extends Plugin clearTrackedNPCs(); chatCommandManager.unregisterCommand(TASK_COMMAND_STRING); + chatCommandManager.unregisterCommand(POINTS_COMMAND_STRING); clientToolbar.removeNavigation(navButton); } @@ -1121,6 +1127,44 @@ public class SlayerPlugin extends Plugin client.refreshChat(); } + void pointsLookup(ChatMessage chatMessage, String message) + { + if (!this.pointsCommand) + { + return; + } + + ChatMessageType type = chatMessage.getType(); + + final String player; + if (type.equals(ChatMessageType.PRIVATECHATOUT)) + { + player = client.getLocalPlayer().getName(); + } + else + { + player = Text.removeTags(chatMessage.getName()) + .replace('\u00A0', ' '); + } + + if (Integer.toString(getPoints()) == null) + { + return; + } + + String response = new ChatMessageBuilder() + .append(ChatColorType.NORMAL) + .append("Slayer Points: ") + .append(ChatColorType.HIGHLIGHT) + .append(Integer.toString(getPoints())) + .build(); + + final MessageNode messageNode = chatMessage.getMessageNode(); + messageNode.setRuneLiteFormatMessage(response); + chatMessageManager.update(messageNode); + client.refreshChat(); + } + /* package access method for changing the pause state of the time tracker for the current task */ void setPaused(boolean paused) { @@ -1208,6 +1252,7 @@ public class SlayerPlugin extends Plugin this.drawMinimapNames = config.drawMinimapNames(); this.weaknessPrompt = config.weaknessPrompt(); this.taskCommand = config.taskCommand(); + this.pointsCommand = config.pointsCommand(); this.taskName = config.taskName(); this.amount = config.amount(); this.initialAmount = config.initialAmount();