From a6628564f8ddf3180c2e5a97141da471c4219948 Mon Sep 17 00:00:00 2001 From: Jordan Atwood Date: Mon, 7 Sep 2020 00:40:24 -0700 Subject: [PATCH 01/75] banktags: Don't lose scroll position when hiding separators --- .../runelite/client/plugins/banktags/BankTagsPlugin.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/banktags/BankTagsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/banktags/BankTagsPlugin.java index 1d20d5338c..e00c3732e3 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/banktags/BankTagsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/banktags/BankTagsPlugin.java @@ -555,16 +555,18 @@ public class BankTagsPlugin extends Plugin implements MouseWheelListener } } - int itemContainerHeight = client.getWidget(WidgetInfo.BANK_ITEM_CONTAINER).getHeight(); + final Widget bankItemContainer = client.getWidget(WidgetInfo.BANK_ITEM_CONTAINER); + int itemContainerHeight = bankItemContainer.getHeight(); // add a second row of height here to allow users to scroll down when the last row is partially visible int adjustedScrollHeight = (items / ITEMS_PER_ROW) * ITEM_VERTICAL_SPACING + ITEM_VERTICAL_SPACING; itemContainer.setScrollHeight(Math.max(adjustedScrollHeight, itemContainerHeight)); + final int itemContainerScroll = bankItemContainer.getScrollY(); clientThread.invokeLater(() -> client.runScript(ScriptID.UPDATE_SCROLLBAR, WidgetInfo.BANK_SCROLLBAR.getId(), WidgetInfo.BANK_ITEM_CONTAINER.getId(), - 0)); + itemContainerScroll)); } From a0acbb0f7b3a44837da6f311fe919db406c0fada Mon Sep 17 00:00:00 2001 From: Jordan Atwood Date: Tue, 8 Sep 2020 17:27:23 -0700 Subject: [PATCH 02/75] Move Experience tests to proper test class --- .../java/net/runelite/api/ExperienceTest.java | 389 ++++++++++++++++- .../combatlevel/CombatLevelPluginTest.java | 396 ------------------ 2 files changed, 377 insertions(+), 408 deletions(-) delete mode 100644 runelite-client/src/test/java/net/runelite/client/plugins/combatlevel/CombatLevelPluginTest.java diff --git a/runelite-api/src/test/java/net/runelite/api/ExperienceTest.java b/runelite-api/src/test/java/net/runelite/api/ExperienceTest.java index c4e615bdbb..683a5e04ed 100644 --- a/runelite-api/src/test/java/net/runelite/api/ExperienceTest.java +++ b/runelite-api/src/test/java/net/runelite/api/ExperienceTest.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, Adam + * Copyright (c) 2018, Brett Middle * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,7 +25,7 @@ */ package net.runelite.api; -import org.junit.Assert; +import static org.junit.Assert.assertEquals; import org.junit.Test; public class ExperienceTest @@ -36,13 +37,13 @@ public class ExperienceTest public void testGetXpForLevel() { int xp = Experience.getXpForLevel(99); - Assert.assertEquals(XP_FOR_99, xp); + assertEquals(XP_FOR_99, xp); xp = Experience.getXpForLevel(126); - Assert.assertEquals(XP_FOR_126, xp); + assertEquals(XP_FOR_126, xp); xp = Experience.getXpForLevel(1); - Assert.assertEquals(0, xp); + assertEquals(0, xp); } @Test(expected = IllegalArgumentException.class) @@ -61,22 +62,22 @@ public class ExperienceTest public void testGetLevelForXp() { int level = Experience.getLevelForXp(XP_FOR_99); - Assert.assertEquals(99, level); + assertEquals(99, level); level = Experience.getLevelForXp(XP_FOR_99 - 1); - Assert.assertEquals(98, level); + assertEquals(98, level); level = Experience.getLevelForXp(XP_FOR_126); - Assert.assertEquals(126, level); + assertEquals(126, level); level = Experience.getLevelForXp(XP_FOR_126 - 1); - Assert.assertEquals(125, level); + assertEquals(125, level); level = Experience.getLevelForXp(Integer.MAX_VALUE); - Assert.assertEquals(126, level); + assertEquals(126, level); level = Experience.getLevelForXp(0); - Assert.assertEquals(1, level); + assertEquals(1, level); } @Test(expected = IllegalArgumentException.class) @@ -88,7 +89,371 @@ public class ExperienceTest @Test public void testGetCombatLevel() { - Assert.assertEquals(126, Experience.getCombatLevel(99, 99, 99, 99, 70, 42, 98)); - Assert.assertEquals(40, Experience.getCombatLevel(27, 22, 1, 36, 64, 45, 1)); + assertEquals(126, Experience.getCombatLevel(99, 99, 99, 99, 70, 42, 98)); + assertEquals(40, Experience.getCombatLevel(27, 22, 1, 36, 64, 45, 1)); + } + + @Test + public void testNewPlayerNextCombatLevel() + { + int attackLevel = 1; + int strengthLevel = 1; + int defenceLevel = 1; + int hitpointsLevel = 10; + int magicLevel = 1; + int rangeLevel = 1; + int prayerLevel = 1; + + int combatLevel = Experience.getCombatLevel(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int meleeNeed = Experience.getNextCombatLevelMelee(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int hpDefNeed = Experience.getNextCombatLevelHpDef(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int rangeNeed = Experience.getNextCombatLevelRange(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int magicNeed = Experience.getNextCombatLevelMagic(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int prayerNeed = Experience.getNextCombatLevelPrayer(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + + // test combat level + assertEquals(3, combatLevel); + + // test attack/strength + assertEquals(2, meleeNeed); + + // test defence/hitpoints + assertEquals(3, hpDefNeed); + + // test ranged + assertEquals(2, rangeNeed); + + // test magic + assertEquals(2, magicNeed); + + // test prayer + assertEquals(5, prayerNeed); + } + + @Test + public void testAll10NextCombatLevel() + { + int attackLevel = 10; + int strengthLevel = 10; + int defenceLevel = 10; + int hitpointsLevel = 10; + int magicLevel = 10; + int rangeLevel = 10; + int prayerLevel = 10; + + int combatLevel = Experience.getCombatLevel(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int meleeNeed = Experience.getNextCombatLevelMelee(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int hpDefNeed = Experience.getNextCombatLevelHpDef(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int rangeNeed = Experience.getNextCombatLevelRange(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int magicNeed = Experience.getNextCombatLevelMagic(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int prayerNeed = Experience.getNextCombatLevelPrayer(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + + // test combat level + assertEquals(12, combatLevel); + + // test attack/strength + assertEquals(1, meleeNeed); + + // test defence/hitpoints + assertEquals(1, hpDefNeed); + + // test ranged + assertEquals(4, rangeNeed); + + // test magic + assertEquals(4, magicNeed); + + // test prayer + assertEquals(2, prayerNeed); + } + + @Test + public void testPlayerBmidNextCombatLevel() + { + // snapshot of current stats 2018-10-2 + int attackLevel = 65; + int strengthLevel = 70; + int defenceLevel = 60; + int hitpointsLevel = 71; + int magicLevel = 73; + int rangeLevel = 75; + int prayerLevel = 56; + + int combatLevel = Experience.getCombatLevel(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int meleeNeed = Experience.getNextCombatLevelMelee(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int hpDefNeed = Experience.getNextCombatLevelHpDef(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int rangeNeed = Experience.getNextCombatLevelRange(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int magicNeed = Experience.getNextCombatLevelMagic(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int prayerNeed = Experience.getNextCombatLevelPrayer(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + + // test combat level + assertEquals(83, combatLevel); + + // test attack/strength + assertEquals(2, meleeNeed); + + // test defence/hitpoints + assertEquals(2, hpDefNeed); + + // test ranged + assertEquals(17, rangeNeed); + + // test magic + assertEquals(19, magicNeed); + + // test prayer + assertEquals(4, prayerNeed); + } + + @Test + public void testPlayerRuneliteNextCombatLevel() + { + // snapshot of current stats 2018-10-2 + int attackLevel = 43; + int strengthLevel = 36; + int defenceLevel = 1; + int hitpointsLevel = 42; + int magicLevel = 64; + int rangeLevel = 51; + int prayerLevel = 15; + + int combatLevel = Experience.getCombatLevel(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int meleeNeed = Experience.getNextCombatLevelMelee(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int hpDefNeed = Experience.getNextCombatLevelHpDef(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int rangeNeed = Experience.getNextCombatLevelRange(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int magicNeed = Experience.getNextCombatLevelMagic(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int prayerNeed = Experience.getNextCombatLevelPrayer(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + + // test combat level + assertEquals(43, combatLevel); + + // test attack/strength + assertEquals(18, meleeNeed); + + // test defence/hitpoints + assertEquals(2, hpDefNeed); + + // test ranged + assertEquals(14, rangeNeed); + + // test magic + assertEquals(1, magicNeed); + + // test prayer + assertEquals(3, prayerNeed); + } + + @Test + public void testPlayerZezimaNextCombatLevel() + { + // snapshot of current stats 2018-10-3 + // Zezima cannot earn a combat level from ranged/magic anymore, so it won't show as the result is too high + int attackLevel = 74; + int strengthLevel = 74; + int defenceLevel = 72; + int hitpointsLevel = 72; + int magicLevel = 60; + int rangeLevel = 44; + int prayerLevel = 52; + + int combatLevel = Experience.getCombatLevel(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int meleeNeed = Experience.getNextCombatLevelMelee(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int hpDefNeed = Experience.getNextCombatLevelHpDef(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int prayerNeed = Experience.getNextCombatLevelPrayer(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + + // test combat level + assertEquals(90, combatLevel); + + // test attack/strength + assertEquals(2, meleeNeed); + + // test defence/hitpoints + assertEquals(2, hpDefNeed); + + // test prayer + assertEquals(4, prayerNeed); + } + + @Test + public void testPrayerLevelsNeeded() + { + int attackLevel = 99; + int strengthLevel = 99; + int defenceLevel = 99; + int hitpointsLevel = 99; + int magicLevel = 99; + int rangeLevel = 99; + int prayerLevel = 89; + + int combatLevel = Experience.getCombatLevel(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int prayerNeed = Experience.getNextCombatLevelPrayer(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + + // test combat level + assertEquals(124, combatLevel); + + // test prayer + assertEquals(1, prayerNeed); + } + + @Test + public void testEvenPrayerLevelsNeededWhenNearNextCombatLevel() + { + int attackLevel = 74; + int strengthLevel = 75; + int defenceLevel = 72; + int hitpointsLevel = 72; + int magicLevel = 60; + int rangeLevel = 44; + int prayerLevel = 52; + + int combatLevel = Experience.getCombatLevel(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int prayerNeed = Experience.getNextCombatLevelPrayer(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + + // test combat level + assertEquals(90, combatLevel); + + // test prayer + assertEquals(2, prayerNeed); + } + + @Test + public void testOddPrayerLevelsNeededWhenNearNextCombatLevel() + { + int attackLevel = 74; + int strengthLevel = 75; + int defenceLevel = 72; + int hitpointsLevel = 72; + int magicLevel = 60; + int rangeLevel = 44; + int prayerLevel = 53; + + int combatLevel = Experience.getCombatLevel(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int prayerNeed = Experience.getNextCombatLevelPrayer(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + + // test combat level + assertEquals(90, combatLevel); + + // test prayer + assertEquals(1, prayerNeed); + } + + @Test + public void testNextMagicLevelBarelyReachesNextCombatLevel() + { + int attackLevel = 40; + int strengthLevel = 44; + int defenceLevel = 46; + int hitpointsLevel = 39; + int magicLevel = 57; + int rangeLevel = 40; + int prayerLevel = 29; + + int combatLevel = Experience.getCombatLevel(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int meleeNeed = Experience.getNextCombatLevelMelee(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int hpDefNeed = Experience.getNextCombatLevelHpDef(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int rangeNeed = Experience.getNextCombatLevelRange(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int magicNeed = Experience.getNextCombatLevelMagic(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int prayerNeed = Experience.getNextCombatLevelPrayer(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + + // test combat level + assertEquals(52, combatLevel); + + // test attack/strength + assertEquals(3, meleeNeed); + + // test defence/hitpoints + assertEquals(3, hpDefNeed); + + // test ranged + assertEquals(18, rangeNeed); + + // test magic + assertEquals(1, magicNeed); + + // test prayer + assertEquals(5, prayerNeed); + } + + @Test + public void testRangeMagicLevelsNeeded() + { + int attackLevel = 60; + int strengthLevel = 69; + int defenceLevel = 1; + int hitpointsLevel = 78; + int magicLevel = 85; + int rangeLevel = 85; + int prayerLevel = 52; + + int combatLevel = Experience.getCombatLevel(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int meleeNeed = Experience.getNextCombatLevelMelee(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int hpDefNeed = Experience.getNextCombatLevelHpDef(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int rangeNeed = Experience.getNextCombatLevelRange(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int magicNeed = Experience.getNextCombatLevelMagic(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + int prayerNeed = Experience.getNextCombatLevelPrayer(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, + magicLevel, rangeLevel, prayerLevel); + + // test combat level + assertEquals(68, combatLevel); + + // test attack/strength + assertEquals(3, meleeNeed); + + // test defence/hitpoints + assertEquals(4, hpDefNeed); + + // test ranged + assertEquals(3, rangeNeed); + + // test magic + assertEquals(3, magicNeed); + + // test prayer + assertEquals(8, prayerNeed); } } diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/combatlevel/CombatLevelPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/combatlevel/CombatLevelPluginTest.java deleted file mode 100644 index e372ef763f..0000000000 --- a/runelite-client/src/test/java/net/runelite/client/plugins/combatlevel/CombatLevelPluginTest.java +++ /dev/null @@ -1,396 +0,0 @@ -/* - * Copyright (c) 2018, Brett Middle - * 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.combatlevel; - -import net.runelite.api.Experience; -import static org.junit.Assert.assertEquals; -import org.junit.Test; - -public class CombatLevelPluginTest -{ - @Test - public void testNewPlayer() - { - int attackLevel = 1; - int strengthLevel = 1; - int defenceLevel = 1; - int hitpointsLevel = 10; - int magicLevel = 1; - int rangeLevel = 1; - int prayerLevel = 1; - - int combatLevel = Experience.getCombatLevel(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int meleeNeed = Experience.getNextCombatLevelMelee(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int hpDefNeed = Experience.getNextCombatLevelHpDef(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int rangeNeed = Experience.getNextCombatLevelRange(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int magicNeed = Experience.getNextCombatLevelMagic(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int prayerNeed = Experience.getNextCombatLevelPrayer(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - - // test combat level - assertEquals(3, combatLevel); - - // test attack/strength - assertEquals(2, meleeNeed); - - // test defence/hitpoints - assertEquals(3, hpDefNeed); - - // test ranged - assertEquals(2, rangeNeed); - - // test magic - assertEquals(2, magicNeed); - - // test prayer - assertEquals(5, prayerNeed); - } - - @Test - public void testAll10() - { - int attackLevel = 10; - int strengthLevel = 10; - int defenceLevel = 10; - int hitpointsLevel = 10; - int magicLevel = 10; - int rangeLevel = 10; - int prayerLevel = 10; - - int combatLevel = Experience.getCombatLevel(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int meleeNeed = Experience.getNextCombatLevelMelee(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int hpDefNeed = Experience.getNextCombatLevelHpDef(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int rangeNeed = Experience.getNextCombatLevelRange(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int magicNeed = Experience.getNextCombatLevelMagic(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int prayerNeed = Experience.getNextCombatLevelPrayer(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - - // test combat level - assertEquals(12, combatLevel); - - // test attack/strength - assertEquals(1, meleeNeed); - - // test defence/hitpoints - assertEquals(1, hpDefNeed); - - // test ranged - assertEquals(4, rangeNeed); - - // test magic - assertEquals(4, magicNeed); - - // test prayer - assertEquals(2, prayerNeed); - } - - @Test - public void testPlayerBmid() - { - // snapshot of current stats 2018-10-2 - int attackLevel = 65; - int strengthLevel = 70; - int defenceLevel = 60; - int hitpointsLevel = 71; - int magicLevel = 73; - int rangeLevel = 75; - int prayerLevel = 56; - - int combatLevel = Experience.getCombatLevel(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int meleeNeed = Experience.getNextCombatLevelMelee(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int hpDefNeed = Experience.getNextCombatLevelHpDef(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int rangeNeed = Experience.getNextCombatLevelRange(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int magicNeed = Experience.getNextCombatLevelMagic(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int prayerNeed = Experience.getNextCombatLevelPrayer(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - - // test combat level - assertEquals(83, combatLevel); - - // test attack/strength - assertEquals(2, meleeNeed); - - // test defence/hitpoints - assertEquals(2, hpDefNeed); - - // test ranged - assertEquals(17, rangeNeed); - - // test magic - assertEquals(19, magicNeed); - - // test prayer - assertEquals(4, prayerNeed); - } - - @Test - public void testPlayerRunelite() - { - // snapshot of current stats 2018-10-2 - int attackLevel = 43; - int strengthLevel = 36; - int defenceLevel = 1; - int hitpointsLevel = 42; - int magicLevel = 64; - int rangeLevel = 51; - int prayerLevel = 15; - - int combatLevel = Experience.getCombatLevel(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int meleeNeed = Experience.getNextCombatLevelMelee(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int hpDefNeed = Experience.getNextCombatLevelHpDef(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int rangeNeed = Experience.getNextCombatLevelRange(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int magicNeed = Experience.getNextCombatLevelMagic(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int prayerNeed = Experience.getNextCombatLevelPrayer(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - - // test combat level - assertEquals(43, combatLevel); - - // test attack/strength - assertEquals(18, meleeNeed); - - // test defence/hitpoints - assertEquals(2, hpDefNeed); - - // test ranged - assertEquals(14, rangeNeed); - - // test magic - assertEquals(1, magicNeed); - - // test prayer - assertEquals(3, prayerNeed); - } - - @Test - public void testPlayerZezima() - { - // snapshot of current stats 2018-10-3 - // Zezima cannot earn a combat level from ranged/magic anymore, so it won't show as the result is too high - int attackLevel = 74; - int strengthLevel = 74; - int defenceLevel = 72; - int hitpointsLevel = 72; - int magicLevel = 60; - int rangeLevel = 44; - int prayerLevel = 52; - - int combatLevel = Experience.getCombatLevel(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int meleeNeed = Experience.getNextCombatLevelMelee(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int hpDefNeed = Experience.getNextCombatLevelHpDef(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int prayerNeed = Experience.getNextCombatLevelPrayer(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - - // test combat level - assertEquals(90, combatLevel); - - // test attack/strength - assertEquals(2, meleeNeed); - - // test defence/hitpoints - assertEquals(2, hpDefNeed); - - // test prayer - assertEquals(4, prayerNeed); - } - - @Test - public void testPrayerLevelsNeeded() - { - int attackLevel = 99; - int strengthLevel = 99; - int defenceLevel = 99; - int hitpointsLevel = 99; - int magicLevel = 99; - int rangeLevel = 99; - int prayerLevel = 89; - - int combatLevel = Experience.getCombatLevel(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int prayerNeed = Experience.getNextCombatLevelPrayer(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - - // test combat level - assertEquals(124, combatLevel); - - // test prayer - assertEquals(1, prayerNeed); - } - - @Test - public void testEvenPrayerLevelsNeededWhenNearNextCombatLevel() - { - int attackLevel = 74; - int strengthLevel = 75; - int defenceLevel = 72; - int hitpointsLevel = 72; - int magicLevel = 60; - int rangeLevel = 44; - int prayerLevel = 52; - - int combatLevel = Experience.getCombatLevel(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int prayerNeed = Experience.getNextCombatLevelPrayer(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - - // test combat level - assertEquals(90, combatLevel); - - // test prayer - assertEquals(2, prayerNeed); - } - - @Test - public void testOddPrayerLevelsNeededWhenNearNextCombatLevel() - { - int attackLevel = 74; - int strengthLevel = 75; - int defenceLevel = 72; - int hitpointsLevel = 72; - int magicLevel = 60; - int rangeLevel = 44; - int prayerLevel = 53; - - int combatLevel = Experience.getCombatLevel(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int prayerNeed = Experience.getNextCombatLevelPrayer(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - - // test combat level - assertEquals(90, combatLevel); - - // test prayer - assertEquals(1, prayerNeed); - } - - @Test - public void testNextMagicLevelBarelyReachesNextCombatLevel() - { - int attackLevel = 40; - int strengthLevel = 44; - int defenceLevel = 46; - int hitpointsLevel = 39; - int magicLevel = 57; - int rangeLevel = 40; - int prayerLevel = 29; - - int combatLevel = Experience.getCombatLevel(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int meleeNeed = Experience.getNextCombatLevelMelee(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int hpDefNeed = Experience.getNextCombatLevelHpDef(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int rangeNeed = Experience.getNextCombatLevelRange(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int magicNeed = Experience.getNextCombatLevelMagic(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int prayerNeed = Experience.getNextCombatLevelPrayer(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - - // test combat level - assertEquals(52, combatLevel); - - // test attack/strength - assertEquals(3, meleeNeed); - - // test defence/hitpoints - assertEquals(3, hpDefNeed); - - // test ranged - assertEquals(18, rangeNeed); - - // test magic - assertEquals(1, magicNeed); - - // test prayer - assertEquals(5, prayerNeed); - } - - @Test - public void testRangeMagicLevelsNeeded() - { - int attackLevel = 60; - int strengthLevel = 69; - int defenceLevel = 1; - int hitpointsLevel = 78; - int magicLevel = 85; - int rangeLevel = 85; - int prayerLevel = 52; - - int combatLevel = Experience.getCombatLevel(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int meleeNeed = Experience.getNextCombatLevelMelee(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int hpDefNeed = Experience.getNextCombatLevelHpDef(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int rangeNeed = Experience.getNextCombatLevelRange(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int magicNeed = Experience.getNextCombatLevelMagic(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - int prayerNeed = Experience.getNextCombatLevelPrayer(attackLevel, strengthLevel, defenceLevel, hitpointsLevel, - magicLevel, rangeLevel, prayerLevel); - - // test combat level - assertEquals(68, combatLevel); - - // test attack/strength - assertEquals(3, meleeNeed); - - // test defence/hitpoints - assertEquals(4, hpDefNeed); - - // test ranged - assertEquals(3, rangeNeed); - - // test magic - assertEquals(3, magicNeed); - - // test prayer - assertEquals(8, prayerNeed); - } -} From 43c5e1f916e065e1b7b11a462997545edbd85192 Mon Sep 17 00:00:00 2001 From: jcwhisman Date: Tue, 1 Sep 2020 11:16:59 -0500 Subject: [PATCH 03/75] ElapsedTimer: Display time in mm:ss format --- .../client/plugins/timers/ElapsedTimer.java | 2 +- .../client/plugins/timers/ElapsedTimerTest.java | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timers/ElapsedTimer.java b/runelite-client/src/main/java/net/runelite/client/plugins/timers/ElapsedTimer.java index 0b4ec17517..55a06025d4 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timers/ElapsedTimer.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timers/ElapsedTimer.java @@ -55,7 +55,7 @@ class ElapsedTimer extends InfoBox } Duration time = Duration.between(startTime, lastTime == null ? Instant.now() : lastTime); - final String formatString = time.toHours() > 0 ? "HH:mm" : "mm:ss"; + final String formatString = "mm:ss"; return DurationFormatUtils.formatDuration(time.toMillis(), formatString, true); } diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/timers/ElapsedTimerTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/timers/ElapsedTimerTest.java index fddb31beb6..2b3c1e4613 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/timers/ElapsedTimerTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/timers/ElapsedTimerTest.java @@ -48,13 +48,13 @@ public class ElapsedTimerTest assertEquals("05:00", timerText(fiveMinutesAgo, null)); assertEquals("55:00", timerText(oneHourAgo, fiveMinutesAgo)); assertEquals("59:55", timerText(oneHourAgo, fiveSecondsAgo)); - assertEquals("01:00", timerText(oneHourAgo, now)); - assertEquals("01:00", timerText(oneHourAgo, null)); - assertEquals("04:00", timerText(fiveHoursAgo, oneHourAgo)); - assertEquals("04:55", timerText(fiveHoursAgo, fiveMinutesAgo)); - assertEquals("04:59", timerText(fiveHoursAgo, fiveSecondsAgo)); - assertEquals("05:00", timerText(fiveHoursAgo, now)); - assertEquals("05:00", timerText(fiveHoursAgo, null)); + assertEquals("60:00", timerText(oneHourAgo, now)); + assertEquals("60:00", timerText(oneHourAgo, null)); + assertEquals("240:00", timerText(fiveHoursAgo, oneHourAgo)); + assertEquals("295:00", timerText(fiveHoursAgo, fiveMinutesAgo)); + assertEquals("299:55", timerText(fiveHoursAgo, fiveSecondsAgo)); + assertEquals("300:00", timerText(fiveHoursAgo, now)); + assertEquals("300:00", timerText(fiveHoursAgo, null)); } private static String timerText(final Instant startTime, final Instant lastTime) From a0e4080b8696fa6ceb7b77af7599c49e423da81a Mon Sep 17 00:00:00 2001 From: Max Weber Date: Sun, 13 Sep 2020 00:50:29 -0600 Subject: [PATCH 04/75] skybox: include twisted league and hosidius POH themes --- .../runelite/client/plugins/skybox/skybox.txt | 34 ++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/skybox/skybox.txt b/runelite-client/src/main/resources/net/runelite/client/plugins/skybox/skybox.txt index de37f0f479..fb8bd463ce 100644 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/skybox/skybox.txt +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/skybox/skybox.txt @@ -931,12 +931,38 @@ p 1 2 #8AD2DF C 0 0 7 15 -p 2 +p 0 1 2 +#2C2C29 +c 3 0 +c 0 3 +c 2 3 +c 4 3 +c 6 3 +c 7 4 + +// 30 89 p3 Twisted League +p 3 +#161414 +C 0 0 7 15 +// dungeon +p 3 +#2C2C29 +c 3 0 +c 0 3 +c 2 3 +c 4 3 +c 6 3 +c 7 4 + + +m31 89 + +// 31 89 p0 Hosidius +p 0 #8AD2DF C 0 0 7 15 - -p all -#2C2C29 +// dungeon +#3e2c0a c 3 0 c 0 3 c 2 3 From 2c2c013cbaa452c5a8c433cbbe49d9942c735ee3 Mon Sep 17 00:00:00 2001 From: jcwhisman Date: Tue, 1 Sep 2020 11:15:14 -0500 Subject: [PATCH 05/75] timers: Fix inferno timer starting time --- .../client/plugins/timers/TimersPlugin.java | 15 ++++++-- .../plugins/timers/TimersPluginTest.java | 34 +++++++++++++++++-- 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java index dcf01d3daf..ad0173b5fe 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java @@ -119,8 +119,8 @@ public class TimersPlugin extends Plugin private static final int VENOM_VALUE_CUTOFF = -40; // Antivenom < -40 <= Antipoison < 0 private static final int POISON_TICK_LENGTH = 30; - private static final int FIGHT_CAVES_REGION_ID = 9551; - private static final int INFERNO_REGION_ID = 9043; + static final int FIGHT_CAVES_REGION_ID = 9551; + static final int INFERNO_REGION_ID = 9043; private static final Pattern TZHAAR_WAVE_MESSAGE = Pattern.compile("Wave: (\\d+)"); private static final String TZHAAR_DEFEATED_MESSAGE = "You have been defeated!"; private static final Pattern TZHAAR_COMPLETE_MESSAGE = Pattern.compile("Your (TzTok-Jad|TzKal-Zuk) kill count is:"); @@ -679,7 +679,16 @@ public class TimersPlugin extends Plugin int wave = Integer.parseInt(matcher.group(1)); if (wave == 1) { - config.tzhaarStartTime(now); + if (isInInferno()) + { + // The first wave message of the inferno comes six seconds after the ingame timer starts counting + config.tzhaarStartTime(now.minus(Duration.ofSeconds(6))); + } + else + { + config.tzhaarStartTime(now); + } + createTzhaarTimer(); } } diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/timers/TimersPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/timers/TimersPluginTest.java index 494db7bd9d..20a1df10d1 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/timers/TimersPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/timers/TimersPluginTest.java @@ -63,8 +63,6 @@ import org.mockito.stubbing.Answer; @RunWith(MockitoJUnitRunner.class) public class TimersPluginTest { - private static final int FIGHT_CAVES_REGION_ID = 9551; - @Inject private TimersPlugin timersPlugin; @@ -270,7 +268,7 @@ public class TimersPluginTest public void testTzhaarTimer() { when(timersConfig.showTzhaarTimers()).thenReturn(true); - when(client.getMapRegions()).thenReturn(new int[]{FIGHT_CAVES_REGION_ID}); + when(client.getMapRegions()).thenReturn(new int[]{TimersPlugin.FIGHT_CAVES_REGION_ID}); class InstantRef { @@ -327,4 +325,34 @@ public class TimersPluginTest verify(infoBoxManager, times(3)).removeInfoBox(captor.capture()); verify(infoBoxManager, times(3)).addInfoBox(captor.capture()); } + + @Test + public void testInfernoTimerStartOffset() + { + when(timersConfig.showTzhaarTimers()).thenReturn(true); + when(client.getMapRegions()).thenReturn(new int[]{TimersPlugin.INFERNO_REGION_ID}); + + class InstantRef + { + Instant i; + } + + InstantRef startTime = new InstantRef(); + when(timersConfig.tzhaarStartTime()).then(a -> startTime.i); + doAnswer((Answer) invocationOnMock -> + { + Object argument = invocationOnMock.getArguments()[0]; + startTime.i = (Instant) argument; + return null; + }).when(timersConfig).tzhaarStartTime(nullable(Instant.class)); + + ChatMessage chatMessage = new ChatMessage(null, ChatMessageType.GAMEMESSAGE, "", "Wave: 1", "", 0); + timersPlugin.onChatMessage(chatMessage); + + ArgumentCaptor captor = ArgumentCaptor.forClass(InfoBox.class); + verify(infoBoxManager, times(1)).addInfoBox(captor.capture()); + assertTrue(captor.getValue() instanceof ElapsedTimer); + ElapsedTimer timer = (ElapsedTimer) captor.getValue(); + assertEquals("00:06", timer.getText()); + } } \ No newline at end of file From 620e42ba80dcc9e335d959cef01cc51d8220e4ad Mon Sep 17 00:00:00 2001 From: Hydrox6 Date: Fri, 18 Sep 2020 11:30:53 +0100 Subject: [PATCH 06/75] motherlode: add 3a pickaxe --- .../runelite/client/plugins/motherlode/MotherlodeOverlay.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/motherlode/MotherlodeOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/motherlode/MotherlodeOverlay.java index 1abe210ef8..0ee91ab522 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/motherlode/MotherlodeOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/motherlode/MotherlodeOverlay.java @@ -48,7 +48,7 @@ class MotherlodeOverlay extends OverlayPanel MINING_MOTHERLODE_BLACK, MINING_MOTHERLODE_MITHRIL, MINING_MOTHERLODE_ADAMANT, MINING_MOTHERLODE_RUNE, MINING_MOTHERLODE_GILDED, MINING_MOTHERLODE_DRAGON, MINING_MOTHERLODE_DRAGON_UPGRADED, MINING_MOTHERLODE_DRAGON_OR, MINING_MOTHERLODE_INFERNAL, - MINING_MOTHERLODE_CRYSTAL + MINING_MOTHERLODE_3A, MINING_MOTHERLODE_CRYSTAL ); static final String MINING_RESET = "Reset"; From 8718a83f104ecbce3c7ffaf2a483a6b8a5c43581 Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 18 Sep 2020 22:18:27 -0400 Subject: [PATCH 07/75] disassembler: escape string operands --- .../runelite/cache/script/disassembler/Disassembler.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cache/src/main/java/net/runelite/cache/script/disassembler/Disassembler.java b/cache/src/main/java/net/runelite/cache/script/disassembler/Disassembler.java index 0a6382fec8..523796c49f 100644 --- a/cache/src/main/java/net/runelite/cache/script/disassembler/Disassembler.java +++ b/cache/src/main/java/net/runelite/cache/script/disassembler/Disassembler.java @@ -24,6 +24,8 @@ */ package net.runelite.cache.script.disassembler; +import com.google.common.escape.Escaper; +import com.google.common.escape.Escapers; import java.io.IOException; import java.util.Map; import java.util.Map.Entry; @@ -37,6 +39,10 @@ import org.slf4j.LoggerFactory; public class Disassembler { private static final Logger logger = LoggerFactory.getLogger(Disassembler.class); + private static final Escaper ESCAPER = Escapers.builder() + .addEscape('"', "\\\"") + .addEscape('\\', "\\\\") + .build(); private final Instructions instructions = new Instructions(); @@ -165,7 +171,7 @@ public class Disassembler if (sop != null) { - writer.append(" \"").append(sop).append("\""); + writer.append(" \"").append(ESCAPER.escape(sop)).append("\""); } if (opcode == Opcodes.SWITCH) From 62a69b94c5548e5d733025b284c9b8c32befb2e6 Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 20 Sep 2020 16:46:08 -0400 Subject: [PATCH 08/75] containableframe: fix parsing Oracle Java 8 version string --- .../runelite/client/ui/ContainableFrame.java | 19 +++++++-- .../client/ui/ContainableFrameTest.java | 40 +++++++++++++++++++ 2 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 runelite-client/src/test/java/net/runelite/client/ui/ContainableFrameTest.java diff --git a/runelite-client/src/main/java/net/runelite/client/ui/ContainableFrame.java b/runelite-client/src/main/java/net/runelite/client/ui/ContainableFrame.java index b03c863dda..0a69897461 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/ContainableFrame.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/ContainableFrame.java @@ -24,6 +24,7 @@ */ package net.runelite.client.ui; +import com.google.common.annotations.VisibleForTesting; import java.awt.Frame; import java.awt.GraphicsConfiguration; import java.awt.GraphicsDevice; @@ -49,6 +50,7 @@ public class ContainableFrame extends JFrame NEVER; } + private static final int SCREEN_EDGE_CLOSE_DISTANCE = 40; private static boolean jdk8231564; static @@ -56,9 +58,7 @@ public class ContainableFrame extends JFrame try { String javaVersion = System.getProperty("java.version"); - String[] s = javaVersion.split("\\."); - int major = Integer.parseInt(s[0]), minor = Integer.parseInt(s[1]), patch = Integer.parseInt(s[2]); - jdk8231564 = major > 11 || (major == 11 && minor > 0) || (major == 11 && minor == 0 && patch >= 8); + jdk8231564 = jdk8231564(javaVersion); } catch (Exception ex) { @@ -66,7 +66,18 @@ public class ContainableFrame extends JFrame } } - private static final int SCREEN_EDGE_CLOSE_DISTANCE = 40; + @VisibleForTesting + static boolean jdk8231564(String javaVersion) + { + int idx = javaVersion.indexOf('_'); + if (idx != -1) + { + javaVersion = javaVersion.substring(0, idx); + } + String[] s = javaVersion.split("\\."); + int major = Integer.parseInt(s[0]), minor = Integer.parseInt(s[1]), patch = Integer.parseInt(s[2]); + return major > 11 || (major == 11 && minor > 0) || (major == 11 && minor == 0 && patch >= 8); + } @Setter private ExpandResizeType expandResizeType; diff --git a/runelite-client/src/test/java/net/runelite/client/ui/ContainableFrameTest.java b/runelite-client/src/test/java/net/runelite/client/ui/ContainableFrameTest.java new file mode 100644 index 0000000000..d0b432ce39 --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/ui/ContainableFrameTest.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2020, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.ui; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.Test; + +public class ContainableFrameTest +{ + @Test + public void testJdk8231564() + { + assertTrue(ContainableFrame.jdk8231564("11.0.8")); + assertFalse(ContainableFrame.jdk8231564("11.0.7")); + assertFalse(ContainableFrame.jdk8231564("1.8.0_261")); + } +} \ No newline at end of file From 2a15cbde8ace7e4efd984eb50ee503944adfbf75 Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 30 Aug 2020 18:01:01 -0400 Subject: [PATCH 09/75] scene uploder: remove unnecessary reset Instead we can just check the model scene id is correct and not have to be concerned about reused Models from previous scenes having positive buffer offsets Co-authored-by: Jonathon Reesor --- .../client/plugins/gpu/SceneUploader.java | 84 +------------------ 1 file changed, 2 insertions(+), 82 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/SceneUploader.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/SceneUploader.java index dc11fbc846..b6818d8991 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/SceneUploader.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/SceneUploader.java @@ -59,21 +59,6 @@ class SceneUploader vertexbuffer.clear(); uvBuffer.clear(); - for (int z = 0; z < Constants.MAX_Z; ++z) - { - for (int x = 0; x < Constants.SCENE_SIZE; ++x) - { - for (int y = 0; y < Constants.SCENE_SIZE; ++y) - { - Tile tile = scene.getTiles()[z][x][y]; - if (tile != null) - { - reset(tile); - } - } - } - } - for (int z = 0; z < Constants.MAX_Z; ++z) { for (int x = 0; x < Constants.SCENE_SIZE; ++x) @@ -90,71 +75,6 @@ class SceneUploader } } - private void reset(Tile tile) - { - Tile bridge = tile.getBridge(); - if (bridge != null) - { - reset(bridge); - } - - SceneTilePaint sceneTilePaint = tile.getSceneTilePaint(); - if (sceneTilePaint != null) - { - sceneTilePaint.setBufferOffset(-1); - } - - SceneTileModel sceneTileModel = tile.getSceneTileModel(); - if (sceneTileModel != null) - { - sceneTileModel.setBufferOffset(-1); - } - - WallObject wallObject = tile.getWallObject(); - if (wallObject != null) - { - if (wallObject.getRenderable1() instanceof Model) - { - ((Model) wallObject.getRenderable1()).setBufferOffset(-1); - } - if (wallObject.getRenderable2() instanceof Model) - { - ((Model) wallObject.getRenderable2()).setBufferOffset(-1); - } - } - - GroundObject groundObject = tile.getGroundObject(); - if (groundObject != null) - { - if (groundObject.getRenderable() instanceof Model) - { - ((Model) groundObject.getRenderable()).setBufferOffset(-1); - } - } - - DecorativeObject decorativeObject = tile.getDecorativeObject(); - if (decorativeObject != null) - { - if (decorativeObject.getRenderable() instanceof Model) - { - ((Model) decorativeObject.getRenderable()).setBufferOffset(-1); - } - } - - GameObject[] gameObjects = tile.getGameObjects(); - for (GameObject gameObject : gameObjects) - { - if (gameObject == null) - { - continue; - } - if (gameObject.getRenderable() instanceof Model) - { - ((Model) gameObject.getRenderable()).setBufferOffset(-1); - } - } - } - private void upload(Tile tile, GpuIntBuffer vertexBuffer, GpuFloatBuffer uvBuffer) { Tile bridge = tile.getBridge(); @@ -424,9 +344,9 @@ class SceneUploader private void uploadModel(Model model, GpuIntBuffer vertexBuffer, GpuFloatBuffer uvBuffer) { - if (model.getBufferOffset() > 0) + if (model.getSceneId() == sceneId) { - return; + return; // model has already been uploaded } model.setBufferOffset(offset); From f2498d445fc0155d9704deb370ea2f7c6f98cfaf Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 30 Aug 2020 18:06:26 -0400 Subject: [PATCH 10/75] gpu: optimize ensureCapacity() Prevent unnecessary allocations by updating the ensureCapacity() methods to not create intermediate buffers, just create a single new buffer with the final size. Co-authored-by: Jonathon Reesor --- .../runelite/client/plugins/gpu/GpuFloatBuffer.java | 12 ++++++++++-- .../runelite/client/plugins/gpu/GpuIntBuffer.java | 12 ++++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuFloatBuffer.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuFloatBuffer.java index a90d85a16d..5e5bbb529e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuFloatBuffer.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuFloatBuffer.java @@ -49,9 +49,17 @@ class GpuFloatBuffer void ensureCapacity(int size) { - while (buffer.remaining() < size) + int capacity = buffer.capacity(); + final int position = buffer.position(); + if ((capacity - position) < size) { - FloatBuffer newB = allocateDirect(buffer.capacity() * 2); + do + { + capacity *= 2; + } + while ((capacity - position) < size); + + FloatBuffer newB = allocateDirect(capacity); buffer.flip(); newB.put(buffer); buffer = newB; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuIntBuffer.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuIntBuffer.java index 15ea5461ad..1e85111c19 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuIntBuffer.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuIntBuffer.java @@ -54,9 +54,17 @@ class GpuIntBuffer void ensureCapacity(int size) { - while (buffer.remaining() < size) + int capacity = buffer.capacity(); + final int position = buffer.position(); + if ((capacity - position) < size) { - IntBuffer newB = allocateDirect(buffer.capacity() * 2); + do + { + capacity *= 2; + } + while ((capacity - position) < size); + + IntBuffer newB = allocateDirect(capacity); buffer.flip(); newB.put(buffer); buffer = newB; From af352c54c5857969535a9cc5fb14af3388f9efad Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 30 Aug 2020 18:09:03 -0400 Subject: [PATCH 11/75] scene uploader: inline pushFace() into uploadModel() Inline the body of pushFace() into uploadModel() and perform the loop hoisting of the locals that this enables. This cuts off 10-20ms of scene load time on my machine. Co-authored-by: Jonathon Reesor --- .../client/plugins/gpu/SceneUploader.java | 135 +++++++++++++----- 1 file changed, 103 insertions(+), 32 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/SceneUploader.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/SceneUploader.java index b6818d8991..20b0b59f84 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/SceneUploader.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/SceneUploader.java @@ -360,14 +360,74 @@ class SceneUploader } model.setSceneId(sceneId); - vertexBuffer.ensureCapacity(model.getTrianglesCount() * 12); - uvBuffer.ensureCapacity(model.getTrianglesCount() * 12); - final int triangleCount = model.getTrianglesCount(); + + vertexBuffer.ensureCapacity(triangleCount * 12); + uvBuffer.ensureCapacity(triangleCount * 12); + + final int[] vertexX = model.getVerticesX(); + final int[] vertexY = model.getVerticesY(); + final int[] vertexZ = model.getVerticesZ(); + + final int[] trianglesX = model.getTrianglesX(); + final int[] trianglesY = model.getTrianglesY(); + final int[] trianglesZ = model.getTrianglesZ(); + + final int[] color1s = model.getFaceColors1(); + final int[] color2s = model.getFaceColors2(); + final int[] color3s = model.getFaceColors3(); + + final byte[] transparencies = model.getTriangleTransparencies(); + final short[] faceTextures = model.getFaceTextures(); + final byte[] facePriorities = model.getFaceRenderPriorities(); + + float[][] u = model.getFaceTextureUCoordinates(); + float[][] v = model.getFaceTextureVCoordinates(); + int len = 0; - for (int i = 0; i < triangleCount; ++i) + for (int face = 0; face < triangleCount; ++face) { - len += pushFace(model, i, false, vertexBuffer, uvBuffer, 0, 0, 0, 0); + int color1 = color1s[face]; + int color2 = color2s[face]; + int color3 = color3s[face]; + + if (color3 == -1) + { + color2 = color3 = color1; + } + else if (color3 == -2) + { + vertexBuffer.put(0, 0, 0, 0); + vertexBuffer.put(0, 0, 0, 0); + vertexBuffer.put(0, 0, 0, 0); + + if (faceTextures != null) + { + uvBuffer.put(0, 0, 0, 0); + uvBuffer.put(0, 0, 0, 0); + uvBuffer.put(0, 0, 0, 0); + } + + len += 3; + continue; + } + + int packAlphaPriority = packAlphaPriority(faceTextures, transparencies, facePriorities, face); + + int triangleA = trianglesX[face]; + int triangleB = trianglesY[face]; + int triangleC = trianglesZ[face]; + + vertexBuffer.put(vertexX[triangleA], vertexY[triangleA], vertexZ[triangleA], packAlphaPriority | color1); + vertexBuffer.put(vertexX[triangleB], vertexY[triangleB], vertexZ[triangleB], packAlphaPriority | color2); + vertexBuffer.put(vertexX[triangleC], vertexY[triangleC], vertexZ[triangleC], packAlphaPriority | color3); + + if (faceTextures != null) + { + pushUvForFace(faceTextures, u, v, face, uvBuffer); + } + + len += 3; } offset += len; @@ -404,16 +464,7 @@ class SceneUploader int color2 = color2s[face]; int color3 = color3s[face]; - int alpha = 0; - if (transparencies != null && (faceTextures == null || faceTextures[face] == -1)) - { - alpha = (transparencies[face] & 0xFF) << 24; - } - int priority = 0; - if (facePriorities != null) - { - priority = (facePriorities[face] & 0xff) << 16; - } + int packedAlphaPriority = packAlphaPriority(faceTextures, transparencies, facePriorities, face); int sin = 0, cos = 0; if (orientation != 0) @@ -460,7 +511,7 @@ class SceneUploader b += yOffset; c += zOffset; - vertexBuffer.put(a, b, c, alpha | priority | color1); + vertexBuffer.put(a, b, c, packedAlphaPriority | color1); a = vertexX[triangleB]; b = vertexY[triangleB]; @@ -479,7 +530,7 @@ class SceneUploader b += yOffset; c += zOffset; - vertexBuffer.put(a, b, c, alpha | priority | color2); + vertexBuffer.put(a, b, c, packedAlphaPriority | color2); a = vertexX[triangleC]; b = vertexY[triangleC]; @@ -498,28 +549,48 @@ class SceneUploader b += yOffset; c += zOffset; - vertexBuffer.put(a, b, c, alpha | priority | color3); + vertexBuffer.put(a, b, c, packedAlphaPriority | color3); float[][] u = model.getFaceTextureUCoordinates(); float[][] v = model.getFaceTextureVCoordinates(); - float[] uf, vf; if (padUvs || faceTextures != null) { - if (faceTextures != null && u != null && v != null && (uf = u[face]) != null && (vf = v[face]) != null) - { - float texture = faceTextures[face] + 1f; - uvBuffer.put(texture, uf[0], vf[0], 0f); - uvBuffer.put(texture, uf[1], vf[1], 0f); - uvBuffer.put(texture, uf[2], vf[2], 0f); - } - else - { - uvBuffer.put(0f, 0f, 0f, 0f); - uvBuffer.put(0f, 0f, 0f, 0f); - uvBuffer.put(0f, 0f, 0f, 0f); - } + pushUvForFace(faceTextures, u, v, face, uvBuffer); } return 3; } + + private static int packAlphaPriority(short[] faceTextures, byte[] faceTransparencies, byte[] facePriorities, int face) + { + int alpha = 0; + if (faceTransparencies != null && (faceTextures == null || faceTextures[face] == -1)) + { + alpha = (faceTransparencies[face] & 0xFF) << 24; + } + int priority = 0; + if (facePriorities != null) + { + priority = (facePriorities[face] & 0xff) << 16; + } + return alpha | priority; + } + + private static void pushUvForFace(short[] faceTextures, float[][] u, float[][] v, int face, GpuFloatBuffer uvBuffer) + { + float[] uf, vf; + if (faceTextures != null && u != null && v != null && (uf = u[face]) != null && (vf = v[face]) != null) + { + float texture = faceTextures[face] + 1f; + uvBuffer.put(texture, uf[0], vf[0], 0f); + uvBuffer.put(texture, uf[1], vf[1], 0f); + uvBuffer.put(texture, uf[2], vf[2], 0f); + } + else + { + uvBuffer.put(0, 0, 0, 0); + uvBuffer.put(0, 0, 0, 0); + uvBuffer.put(0, 0, 0, 0); + } + } } From 0ae95180fb8e538e67999e777987fe4bad3719da Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 30 Aug 2020 18:42:41 -0400 Subject: [PATCH 12/75] scene uploader: add stopwatch --- .../net/runelite/client/plugins/gpu/SceneUploader.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/SceneUploader.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/SceneUploader.java index 20b0b59f84..548eca7589 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/SceneUploader.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/SceneUploader.java @@ -24,8 +24,10 @@ */ package net.runelite.client.plugins.gpu; +import com.google.common.base.Stopwatch; import javax.inject.Inject; import javax.inject.Singleton; +import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; import net.runelite.api.Constants; import net.runelite.api.DecorativeObject; @@ -42,6 +44,7 @@ import net.runelite.api.Tile; import net.runelite.api.WallObject; @Singleton +@Slf4j class SceneUploader { @Inject @@ -53,6 +56,8 @@ class SceneUploader void upload(Scene scene, GpuIntBuffer vertexbuffer, GpuFloatBuffer uvBuffer) { + Stopwatch stopwatch = Stopwatch.createStarted(); + ++sceneId; offset = 0; uvoffset = 0; @@ -73,6 +78,9 @@ class SceneUploader } } } + + stopwatch.stop(); + log.debug("Scene upload time: {}", stopwatch); } private void upload(Tile tile, GpuIntBuffer vertexBuffer, GpuFloatBuffer uvBuffer) From 909f68b160f24709eb2c628b416a5383503f8496 Mon Sep 17 00:00:00 2001 From: Tomas Slusny Date: Mon, 1 Jun 2020 02:58:21 +0200 Subject: [PATCH 13/75] Merge UntradeableItemMapping and ItemMapping This enum never really made sense when we already have ItemMapping and its just confusing people. Signed-off-by: Tomas Slusny --- .../net/runelite/client/game/ItemManager.java | 32 ++++--- .../net/runelite/client/game/ItemMapping.java | 89 ++++++++++++------ .../client/game/UntradeableItemMapping.java | 90 ------------------- 3 files changed, 82 insertions(+), 129 deletions(-) delete mode 100644 runelite-client/src/main/java/net/runelite/client/game/UntradeableItemMapping.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 0a26e0f584..7650d6acc5 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 @@ -32,6 +32,7 @@ import java.awt.Color; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -290,7 +291,7 @@ public class ItemManager * Look up an item's price * * @param itemID item id - * @param ignoreUntradeableMap should the price returned ignore the {@link UntradeableItemMapping} + * @param ignoreUntradeableMap should the price returned ignore items that are not tradeable for coins in regular way * @return item price */ public int getItemPrice(int itemID, boolean ignoreUntradeableMap) @@ -311,24 +312,31 @@ public class ItemManager } itemID = WORN_ITEMS.getOrDefault(itemID, itemID); - if (!ignoreUntradeableMap) - { - UntradeableItemMapping p = UntradeableItemMapping.map(ItemVariationMapping.map(itemID)); - if (p != null) - { - return getItemPrice(p.getPriceID()) * p.getQuantity(); - } - } - int price = 0; - for (int mappedID : ItemMapping.map(itemID)) + + final Collection mappedItems = ItemMapping.map(itemID); + + if (mappedItems == null) { - ItemPrice ip = itemPrices.get(mappedID); + final ItemPrice ip = itemPrices.get(itemID); + if (ip != null) { price += ip.getPrice(); } } + else + { + for (final ItemMapping mappedItem : mappedItems) + { + if (ignoreUntradeableMap && mappedItem.isUntradeable()) + { + continue; + } + + price += getItemPrice(mappedItem.getTradeableItem(), ignoreUntradeableMap) * mappedItem.getQuantity(); + } + } return price; } diff --git a/runelite-client/src/main/java/net/runelite/client/game/ItemMapping.java b/runelite-client/src/main/java/net/runelite/client/game/ItemMapping.java index 1a1653de76..8446a70ddf 100644 --- a/runelite-client/src/main/java/net/runelite/client/game/ItemMapping.java +++ b/runelite-client/src/main/java/net/runelite/client/game/ItemMapping.java @@ -28,12 +28,14 @@ package net.runelite.client.game; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import java.util.Collection; -import java.util.Collections; +import javax.annotation.Nullable; +import lombok.Getter; import static net.runelite.api.ItemID.*; /** * Converts untradeable items to it's tradeable counterparts */ +@Getter public enum ItemMapping { // Barrows equipment @@ -249,6 +251,9 @@ public enum ItemMapping ITEM_CRYSTAL_BOW(CRYSTAL_WEAPON_SEED, CRYSTAL_BOW, CRYSTAL_BOW_24123, CRYSTAL_BOW_INACTIVE), ITEM_CRYSTAL_HALBERD(CRYSTAL_WEAPON_SEED, CRYSTAL_HALBERD, CRYSTAL_HALBERD_24125, CRYSTAL_HALBERD_INACTIVE), ITEM_CRYSTAL_SHIELD(CRYSTAL_WEAPON_SEED, CRYSTAL_SHIELD, CRYSTAL_SHIELD_24127, CRYSTAL_SHIELD_INACTIVE), + ITEM_CRYSTAL_HELMET(CRYSTAL_ARMOUR_SEED, CRYSTAL_HELM, CRYSTAL_HELM_INACTIVE), + ITEM_CRYSTAL_LEGS(CRYSTAL_ARMOUR_SEED, 2L, CRYSTAL_LEGS, CRYSTAL_LEGS_INACTIVE), + ITEM_CRYSTAL_BODY(CRYSTAL_ARMOUR_SEED, 3L, CRYSTAL_BODY, CRYSTAL_BODY_INACTIVE), // Bird nests ITEM_BIRD_NEST(BIRD_NEST_5075, BIRD_NEST, BIRD_NEST_5071, BIRD_NEST_5072, BIRD_NEST_5073, BIRD_NEST_5074, BIRD_NEST_7413, BIRD_NEST_13653, BIRD_NEST_22798, BIRD_NEST_22800, CLUE_NEST_EASY, CLUE_NEST_MEDIUM, CLUE_NEST_HARD, CLUE_NEST_ELITE), @@ -256,11 +261,38 @@ public enum ItemMapping // Ancestral robes ITEM_ANCESTRAL_HAT(ANCESTRAL_HAT, TWISTED_ANCESTRAL_HAT), ITEM_ANCESTRAL_ROBE_TOP(ANCESTRAL_ROBE_TOP, TWISTED_ANCESTRAL_ROBE_TOP), - ITEM_ANCESTRAL_ROBE_BOTTOM(ANCESTRAL_ROBE_BOTTOM, TWISTED_ANCESTRAL_ROBE_BOTTOM); + ITEM_ANCESTRAL_ROBE_BOTTOM(ANCESTRAL_ROBE_BOTTOM, TWISTED_ANCESTRAL_ROBE_BOTTOM), - private static final Multimap MAPPINGS = HashMultimap.create(); + // Graceful + ITEM_MARK_OF_GRACE(AMYLASE_CRYSTAL, true, 10L, MARK_OF_GRACE), + ITEM_GRACEFUL_HOOD(MARK_OF_GRACE, true, 28L, GRACEFUL_HOOD), + ITEM_GRACEFUL_TOP(MARK_OF_GRACE, true, 44L, GRACEFUL_TOP), + ITEM_GRACEFUL_LEGS(MARK_OF_GRACE, true, 48L, GRACEFUL_LEGS), + ITEM_GRACEFUL_GLOVES(MARK_OF_GRACE, true, 24L, GRACEFUL_GLOVES), + ITEM_GRACEFUL_BOOTS(MARK_OF_GRACE, true, 32L, GRACEFUL_BOOTS), + ITEM_GRACEFUL_CAPE(MARK_OF_GRACE, true, 32L, GRACEFUL_CAPE), + + // 10 golden nuggets = 100 soft clay + ITEM_GOLDEN_NUGGET(SOFT_CLAY, true, 10L, GOLDEN_NUGGET), + ITEM_PROSPECTOR_HELMET(GOLDEN_NUGGET, true, 32L, PROSPECTOR_HELMET), + ITEM_PROSPECTOR_JACKET(GOLDEN_NUGGET, true, 48L, PROSPECTOR_JACKET), + ITEM_PROSPECTOR_LEGS(GOLDEN_NUGGET, true, 40L, PROSPECTOR_LEGS), + ITEM_PROSPECTOR_BOOTS(GOLDEN_NUGGET, true, 24L, PROSPECTOR_BOOTS), + + // Converted to coins + ITEM_TATTERED_PAGE(COINS_995, true, 1000L, TATTERED_MOON_PAGE, TATTERED_SUN_PAGE, TATTERED_TEMPLE_PAGE), + ITEM_LONG_BONE(COINS_995, true, 1000L, LONG_BONE), + ITEM_CURVED_BONE(COINS_995, true, 2000L, CURVED_BONE), + ITEM_PERFECT_SHELL(COINS_995, true, 600L, PERFECT_SHELL), + ITEM_PERFECT_SNAIL_SHELL(COINS_995, true, 600L, PERFECT_SNAIL_SHELL), + ITEM_SNAIL_SHELL(COINS_995, true, 600L, SNAIL_SHELL), + ITEM_TORTOISE_SHELL(COINS_995, true, 250L, TORTOISE_SHELL); + + private static final Multimap MAPPINGS = HashMultimap.create(); private final int tradeableItem; private final int[] untradableItems; + private final long quantity; + private final boolean untradeable; static { @@ -268,15 +300,35 @@ public enum ItemMapping { for (int itemId : item.untradableItems) { - MAPPINGS.put(itemId, item.tradeableItem); + if (item.untradeable) + { + for (final Integer variation : ItemVariationMapping.getVariations(itemId)) + { + MAPPINGS.put(variation, item); + } + } + + MAPPINGS.put(itemId, item); } } } - ItemMapping(int tradeableItem, int... untradableItems) + ItemMapping(int tradeableItem, boolean untradeable, long quantity, int... untradableItems) { this.tradeableItem = tradeableItem; this.untradableItems = untradableItems; + this.quantity = quantity; + this.untradeable = untradeable; + } + + ItemMapping(int tradeableItem, long quantity, int... untradableItems) + { + this(tradeableItem, false, quantity, untradableItems); + } + + ItemMapping(int tradeableItem, int... untradableItems) + { + this(tradeableItem, 1L, untradableItems); } /** @@ -285,33 +337,16 @@ public enum ItemMapping * @param itemId the item id * @return the collection */ - public static Collection map(int itemId) + @Nullable + public static Collection map(int itemId) { - final Collection mapping = MAPPINGS.get(itemId); + final Collection mapping = MAPPINGS.get(itemId); - if (mapping == null || mapping.isEmpty()) + if (mapping.isEmpty()) { - return Collections.singleton(itemId); + return null; } return mapping; } - - /** - * Map an item from its untradeable version to its tradeable version - * - * @param itemId - * @return - */ - public static int mapFirst(int itemId) - { - final Collection mapping = MAPPINGS.get(itemId); - - if (mapping == null || mapping.isEmpty()) - { - return itemId; - } - - return mapping.iterator().next(); - } } diff --git a/runelite-client/src/main/java/net/runelite/client/game/UntradeableItemMapping.java b/runelite-client/src/main/java/net/runelite/client/game/UntradeableItemMapping.java deleted file mode 100644 index 5dd393acb1..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/game/UntradeableItemMapping.java +++ /dev/null @@ -1,90 +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.game; - -import com.google.common.collect.ImmutableMap; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import net.runelite.api.ItemID; - -@Getter -@RequiredArgsConstructor -public enum UntradeableItemMapping -{ - MARK_OF_GRACE(ItemID.MARK_OF_GRACE, 10, ItemID.AMYLASE_CRYSTAL), - GRACEFUL_HOOD(ItemID.GRACEFUL_HOOD, 28, ItemID.MARK_OF_GRACE), - GRACEFUL_TOP(ItemID.GRACEFUL_TOP, 44, ItemID.MARK_OF_GRACE), - GRACEFUL_LEGS(ItemID.GRACEFUL_LEGS, 48, ItemID.MARK_OF_GRACE), - GRACEFUL_GLOVES(ItemID.GRACEFUL_GLOVES, 24, ItemID.MARK_OF_GRACE), - GRACEFUL_BOOTS(ItemID.GRACEFUL_BOOTS, 32, ItemID.MARK_OF_GRACE), - GRACEFUL_CAPE(ItemID.GRACEFUL_CAPE, 32, ItemID.MARK_OF_GRACE), - - // 10 golden nuggets = 100 soft clay - GOLDEN_NUGGET(ItemID.GOLDEN_NUGGET, 10, ItemID.SOFT_CLAY), - PROSPECTOR_HELMET(ItemID.PROSPECTOR_HELMET, 32, ItemID.GOLDEN_NUGGET), - PROSPECTOR_JACKET(ItemID.PROSPECTOR_JACKET, 48, ItemID.GOLDEN_NUGGET), - PROSPECTOR_LEGS(ItemID.PROSPECTOR_LEGS, 40, ItemID.GOLDEN_NUGGET), - PROSPECTOR_BOOTS(ItemID.PROSPECTOR_BOOTS, 24, ItemID.GOLDEN_NUGGET), - - CRYSTAL_HELMET(ItemID.CRYSTAL_HELM, 1, ItemID.CRYSTAL_ARMOUR_SEED), - CRYSTAL_HELMET_INACTIVE(ItemID.CRYSTAL_HELM_INACTIVE, 1, ItemID.CRYSTAL_ARMOUR_SEED), - CRYSTAL_LEGS(ItemID.CRYSTAL_LEGS, 2, ItemID.CRYSTAL_ARMOUR_SEED), - CRYSTAL_LEGS_INACTIVE(ItemID.CRYSTAL_LEGS_INACTIVE, 2, ItemID.CRYSTAL_ARMOUR_SEED), - CRYSTAL_BODY(ItemID.CRYSTAL_BODY, 3, ItemID.CRYSTAL_ARMOUR_SEED), - CRYSTAL_BODY_INACTIVE(ItemID.CRYSTAL_BODY_INACTIVE, 3, ItemID.CRYSTAL_ARMOUR_SEED), - - TATTERED_MOON_PAGE(ItemID.TATTERED_MOON_PAGE, 1000, ItemID.COINS_995), - TATTERED_SUN_PAGE(ItemID.TATTERED_SUN_PAGE, 1000, ItemID.COINS_995), - TATTERED_TEMPLE_PAGE(ItemID.TATTERED_TEMPLE_PAGE, 1000, ItemID.COINS_995), - - LONG_BONE(ItemID.LONG_BONE, 1000, ItemID.COINS_995), - CURVED_BONE(ItemID.CURVED_BONE, 2000, ItemID.COINS_995), - PERFECT_SHELL(ItemID.PERFECT_SHELL, 600, ItemID.COINS_995), - PERFECT_SNAIL_SHELL(ItemID.PERFECT_SNAIL_SHELL, 600, ItemID.COINS_995), - SNAIL_SHELL(ItemID.SNAIL_SHELL, 600, ItemID.COINS_995), - TORTOISE_SHELL(ItemID.TORTOISE_SHELL, 250, ItemID.COINS_995); - - private static final ImmutableMap UNTRADEABLE_RECLAIM_MAP; - - private final int itemID; - private final int quantity; - private final int priceID; - - static - { - ImmutableMap.Builder map = ImmutableMap.builder(); - for (UntradeableItemMapping p : values()) - { - map.put(p.getItemID(), p); - } - UNTRADEABLE_RECLAIM_MAP = map.build(); - } - - public static UntradeableItemMapping map(int itemId) - { - return UNTRADEABLE_RECLAIM_MAP.get(itemId); - } -} - From 83a453b534764b80f28809e3a5bd6ee918a2efad Mon Sep 17 00:00:00 2001 From: Doron Galambos <15026706+dgalambos@users.noreply.github.com> Date: Sun, 20 Sep 2020 23:51:56 -0400 Subject: [PATCH 14/75] clues: Update Ardougne cryptic clue (#12520) Following the game update which introduced Mahogany Homes, this clue's text and location have been changed. --- .../runelite/client/plugins/cluescrolls/clues/CrypticClue.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CrypticClue.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CrypticClue.java index 1aa6f40605..0a5bf5a476 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CrypticClue.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CrypticClue.java @@ -200,7 +200,7 @@ public class CrypticClue extends ClueScroll implements TextClueScroll, NpcClueSc new CrypticClue("My life was spared but these voices remain, now guarding these iron gates is my bane.", "Key Master", new WorldPoint(1310, 1251, 0), "Speak to the Key Master in Cerberus' Lair."), new CrypticClue("Search the boxes in one of the tents in Al Kharid.", BOXES_361, new WorldPoint(3308, 3206, 0), "Search the boxes in the tent east of the Al Kharid Silk trader."), new CrypticClue("One of several rhyming brothers, in business attire with an obsession for paper work.", "Piles", new WorldPoint(3186, 3936, 0), "Speak to Piles in the Wilderness Resource Area. An entry fee of 7,500 coins is required, or less if Wilderness Diaries have been completed."), - new CrypticClue("Search the drawers on the first floor of a building overlooking Ardougne's Market.", DRAWERS_352, new WorldPoint(2657, 3322, 1), "Climb the ladder in the house north of the East Ardougne market."), + new CrypticClue("Search the drawers on the ground floor of a building facing Ardougne's Market.", DRAWERS_350, new WorldPoint(2653, 3320, 0), "Inside Noella's house north of the East Ardougne market."), new CrypticClue("'A bag belt only?', he asked his balding brothers.", "Abbot Langley", new WorldPoint(3058, 3487, 0), "Talk-to Abbot Langley in Monastery west of Edgeville"), new CrypticClue("Search the drawers upstairs in Falador's shield shop.", DRAWERS, new WorldPoint(2971, 3386, 1), "Cassie's Shield Shop at the northern Falador entrance."), new CrypticClue("Go to this building to be illuminated, and check the drawers while you are there.", "Market Guard", DRAWERS_350 , new WorldPoint(2512, 3641, 1), "Search the drawers in the first floor of the Lighthouse. Kill a Rellekka marketplace guard to obtain the key."), From e0e4961b41acb03d678e0b9d6ab2e7c47edb6d91 Mon Sep 17 00:00:00 2001 From: Doron Galambos <15026706+dgalambos@users.noreply.github.com> Date: Mon, 21 Sep 2020 00:06:44 -0400 Subject: [PATCH 15/75] clues: Update Falador cryptic clue (#12519) Following the game update which introduced Mahogany Homes, this clue text and location have been changed. --- .../runelite/client/plugins/cluescrolls/clues/CrypticClue.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CrypticClue.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CrypticClue.java index 0a5bf5a476..9360b6aa33 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CrypticClue.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CrypticClue.java @@ -244,7 +244,7 @@ public class CrypticClue extends ClueScroll implements TextClueScroll, NpcClueSc new CrypticClue("She's small but can build both literally and figuratively, as long as you have their favour.", "Lovada", new WorldPoint(1486, 3834, 0), "Speak to Lovada by the entrance to the blast mine in Lovakengj."), new CrypticClue("Dig in front of the icy arena where 1 of 4 was fought.", new WorldPoint(2874, 3757, 0), "North of Trollheim, where you fought Kamil from Desert Treasure."), new CrypticClue("Speak to Roavar.", "Roavar", new WorldPoint(3494, 3474, 0), "Talk to Roavar in the Canifis tavern."), - new CrypticClue("Search the drawers upstairs of houses in the eastern part of Falador.", DRAWERS_350, new WorldPoint(3035, 3347, 1), "House is located east of the eastern Falador bank and south of the fountain. The house is indicated by a cooking range icon on the minimap."), + new CrypticClue("Search the drawers downstairs of houses in the eastern part of Falador.", DRAWERS_350, new WorldPoint(3039, 3342, 0), "House is located east of the eastern Falador bank and south of the fountain. The house is indicated by a cooking range icon on the minimap."), new CrypticClue("Search the drawers found upstairs in East Ardougne's houses.", DRAWERS, new WorldPoint(2574, 3326, 1), "Upstairs of the pub north of the Ardougne Castle."), new CrypticClue("The far north eastern corner where 1 of 4 was defeated, the shadows still linger.", new WorldPoint(2744, 5116, 0), "Dig on the northeastern-most corner of the Shadow Dungeon. Bring a ring of visibility."), new CrypticClue("Search the drawers in a house in Draynor Village.", DRAWERS_350, new WorldPoint(3097, 3277, 0), "The drawer is located in the northernmost house in Draynor Village."), From ce9ba6fea840fbf5029d28a112e3d270b41e5f9c Mon Sep 17 00:00:00 2001 From: Doron Galambos <15026706+dgalambos@users.noreply.github.com> Date: Mon, 21 Sep 2020 00:25:33 -0400 Subject: [PATCH 16/75] game: Add Gu'Tanoth crumbling wall agility shortcut (#12506) --- .../src/main/java/net/runelite/client/game/AgilityShortcut.java | 1 + 1 file changed, 1 insertion(+) diff --git a/runelite-client/src/main/java/net/runelite/client/game/AgilityShortcut.java b/runelite-client/src/main/java/net/runelite/client/game/AgilityShortcut.java index 4c946ae24b..315232c4df 100644 --- a/runelite-client/src/main/java/net/runelite/client/game/AgilityShortcut.java +++ b/runelite-client/src/main/java/net/runelite/client/game/AgilityShortcut.java @@ -187,6 +187,7 @@ public enum AgilityShortcut AL_KHARID_WINDOW(70, "Window", new WorldPoint(3293, 3158, 0), BROKEN_WALL_33344, BIG_WINDOW), GWD_SARADOMIN_ROPE_NORTH(70, "Rope Descent", new WorldPoint(2912, 5300, 0), NULL_26371, NULL_26561), GWD_SARADOMIN_ROPE_SOUTH(70, "Rope Descent", new WorldPoint(2951, 5267, 0), NULL_26375, NULL_26562), + GU_TANOTH_CRUMBLING_WALL(71, "Rocks", new WorldPoint(2545, 3032, 0), CRUMBLING_WALL_40355, ROCKS_40356), SLAYER_TOWER_ADVANCED_CHAIN_FIRST(71, "Spiked Chain (Floor 2)", new WorldPoint(3447, 3578, 0), SPIKEY_CHAIN ), SLAYER_TOWER_ADVANCED_CHAIN_SECOND(71, "Spiked Chain (Floor 3)", new WorldPoint(3446, 3576, 0), SPIKEY_CHAIN_16538), STRONGHOLD_SLAYER_CAVE_TUNNEL(72, "Tunnel", new WorldPoint(2431, 9806, 0), TUNNEL_30174, TUNNEL_30175), From e1120614b0a218db22acb0d046ceaf3ce6cf8802 Mon Sep 17 00:00:00 2001 From: Hydrox Date: Mon, 21 Sep 2020 05:32:08 +0100 Subject: [PATCH 17/75] Pickaxe: add overhead mining animations (#12509) --- .../client/plugins/mining/Pickaxe.java | 59 +++++++++++++------ 1 file changed, 40 insertions(+), 19 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/mining/Pickaxe.java b/runelite-client/src/main/java/net/runelite/client/plugins/mining/Pickaxe.java index 3e910cdee5..2d5d246978 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/mining/Pickaxe.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/mining/Pickaxe.java @@ -26,7 +26,6 @@ package net.runelite.client.plugins.mining; import com.google.common.collect.ImmutableMap; import java.util.Map; -import lombok.AllArgsConstructor; import lombok.Getter; import static net.runelite.api.AnimationID.MINING_3A_PICKAXE; import static net.runelite.api.AnimationID.MINING_ADAMANT_PICKAXE; @@ -40,6 +39,20 @@ import static net.runelite.api.AnimationID.MINING_GILDED_PICKAXE; import static net.runelite.api.AnimationID.MINING_INFERNAL_PICKAXE; import static net.runelite.api.AnimationID.MINING_IRON_PICKAXE; import static net.runelite.api.AnimationID.MINING_MITHRIL_PICKAXE; +import static net.runelite.api.AnimationID.MINING_MOTHERLODE_3A; +import static net.runelite.api.AnimationID.MINING_MOTHERLODE_ADAMANT; +import static net.runelite.api.AnimationID.MINING_MOTHERLODE_BLACK; +import static net.runelite.api.AnimationID.MINING_MOTHERLODE_BRONZE; +import static net.runelite.api.AnimationID.MINING_MOTHERLODE_CRYSTAL; +import static net.runelite.api.AnimationID.MINING_MOTHERLODE_DRAGON; +import static net.runelite.api.AnimationID.MINING_MOTHERLODE_DRAGON_OR; +import static net.runelite.api.AnimationID.MINING_MOTHERLODE_DRAGON_UPGRADED; +import static net.runelite.api.AnimationID.MINING_MOTHERLODE_GILDED; +import static net.runelite.api.AnimationID.MINING_MOTHERLODE_INFERNAL; +import static net.runelite.api.AnimationID.MINING_MOTHERLODE_IRON; +import static net.runelite.api.AnimationID.MINING_MOTHERLODE_MITHRIL; +import static net.runelite.api.AnimationID.MINING_MOTHERLODE_RUNE; +import static net.runelite.api.AnimationID.MINING_MOTHERLODE_STEEL; import static net.runelite.api.AnimationID.MINING_RUNE_PICKAXE; import static net.runelite.api.AnimationID.MINING_STEEL_PICKAXE; import static net.runelite.api.ItemID.ADAMANT_PICKAXE; @@ -58,27 +71,26 @@ import static net.runelite.api.ItemID.STEEL_PICKAXE; import static net.runelite.api.ItemID._3RD_AGE_PICKAXE; import net.runelite.api.Player; -@AllArgsConstructor @Getter enum Pickaxe { - BRONZE(MINING_BRONZE_PICKAXE, BRONZE_PICKAXE), - IRON(MINING_IRON_PICKAXE, IRON_PICKAXE), - STEEL(MINING_STEEL_PICKAXE, STEEL_PICKAXE), - BLACK(MINING_BLACK_PICKAXE, BLACK_PICKAXE), - MITHRIL(MINING_MITHRIL_PICKAXE, MITHRIL_PICKAXE), - ADAMANT(MINING_ADAMANT_PICKAXE, ADAMANT_PICKAXE), - RUNE(MINING_RUNE_PICKAXE, RUNE_PICKAXE), - GILDED(MINING_GILDED_PICKAXE, GILDED_PICKAXE), - DRAGON(MINING_DRAGON_PICKAXE, DRAGON_PICKAXE), - DRAGON_OR(MINING_DRAGON_PICKAXE_OR, DRAGON_PICKAXEOR), - DRAGON_UPGRADED(MINING_DRAGON_PICKAXE_UPGRADED, DRAGON_PICKAXE_12797), - INFERNAL(MINING_INFERNAL_PICKAXE, INFERNAL_PICKAXE), - THIRDAGE(MINING_3A_PICKAXE, _3RD_AGE_PICKAXE), - CRYSTAL(MINING_CRYSTAL_PICKAXE, CRYSTAL_PICKAXE); + BRONZE(BRONZE_PICKAXE, MINING_BRONZE_PICKAXE, MINING_MOTHERLODE_BRONZE), + IRON(IRON_PICKAXE, MINING_IRON_PICKAXE, MINING_MOTHERLODE_IRON), + STEEL(STEEL_PICKAXE, MINING_STEEL_PICKAXE, MINING_MOTHERLODE_STEEL), + BLACK(BLACK_PICKAXE, MINING_BLACK_PICKAXE, MINING_MOTHERLODE_BLACK), + MITHRIL(MITHRIL_PICKAXE, MINING_MITHRIL_PICKAXE, MINING_MOTHERLODE_MITHRIL), + ADAMANT(ADAMANT_PICKAXE, MINING_ADAMANT_PICKAXE, MINING_MOTHERLODE_ADAMANT), + RUNE(RUNE_PICKAXE, MINING_RUNE_PICKAXE, MINING_MOTHERLODE_RUNE), + GILDED(GILDED_PICKAXE, MINING_GILDED_PICKAXE, MINING_MOTHERLODE_GILDED), + DRAGON(DRAGON_PICKAXE, MINING_DRAGON_PICKAXE, MINING_MOTHERLODE_DRAGON), + DRAGON_OR(DRAGON_PICKAXEOR, MINING_DRAGON_PICKAXE_OR, MINING_MOTHERLODE_DRAGON_OR), + DRAGON_UPGRADED(DRAGON_PICKAXE_12797, MINING_DRAGON_PICKAXE_UPGRADED, MINING_MOTHERLODE_DRAGON_UPGRADED), + INFERNAL(INFERNAL_PICKAXE, MINING_INFERNAL_PICKAXE, MINING_MOTHERLODE_INFERNAL), + THIRDAGE(_3RD_AGE_PICKAXE, MINING_3A_PICKAXE, MINING_MOTHERLODE_3A), + CRYSTAL(CRYSTAL_PICKAXE, MINING_CRYSTAL_PICKAXE, MINING_MOTHERLODE_CRYSTAL); - private final int animId; private final int itemId; + private final int[] animIds; private static final Map PICKAXE_ANIM_IDS; @@ -88,15 +100,24 @@ enum Pickaxe for (Pickaxe pickaxe : values()) { - builder.put(pickaxe.animId, pickaxe); + for (int animId : pickaxe.animIds) + { + builder.put(animId, pickaxe); + } } PICKAXE_ANIM_IDS = builder.build(); } + Pickaxe(int itemId, int ... animIds) + { + this.itemId = itemId; + this.animIds = animIds; + } + boolean matchesMiningAnimation(final Player player) { - return player != null && animId == player.getAnimation(); + return player != null && fromAnimation(player.getAnimation()) == this; } static Pickaxe fromAnimation(int animId) From 1ac2eae1bd207c2f32920de62c6d99d9c83ec3c9 Mon Sep 17 00:00:00 2001 From: Matthew C Date: Tue, 22 Sep 2020 08:06:27 +0900 Subject: [PATCH 18/75] cannon: sort spots alphabetically --- .../client/plugins/cannon/CannonSpots.java | 53 +++++++++---------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonSpots.java b/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonSpots.java index 03cbe9a8bd..67308b1b50 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonSpots.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonSpots.java @@ -32,46 +32,45 @@ import net.runelite.api.coords.WorldPoint; enum CannonSpots { - - BLOODVELDS(new WorldPoint(2439, 9821, 0), new WorldPoint(2448, 9821, 0), new WorldPoint(2472, 9832, 0), new WorldPoint(2453, 9817, 0), new WorldPoint(3597, 9743, 0)), - FIRE_GIANTS(new WorldPoint(2393, 9782, 0), new WorldPoint(2412, 9776, 0), new WorldPoint(2401, 9780, 0), new WorldPoint(3047, 10340, 0)), ABERRANT_SPECTRES(new WorldPoint(2456, 9791, 0)), - HELLHOUNDS(new WorldPoint(2431, 9776, 0), new WorldPoint(2413, 9786, 0), new WorldPoint(2783, 9686, 0), new WorldPoint(3198, 10071, 0)), + ANKOU(new WorldPoint(3177, 10193, 0)), + BANDIT(new WorldPoint(3037, 3700, 0)), + BEAR(new WorldPoint(3113, 3672, 0)), BLACK_DEMONS(new WorldPoint(2859, 9778, 0), new WorldPoint(2841, 9791, 0), new WorldPoint(1421, 10089, 1), new WorldPoint(3174, 10154, 0), new WorldPoint(3089, 9960, 0)), + BLACK_DRAGON(new WorldPoint(3239, 10206, 0)), BLACK_KNIGHTS(new WorldPoint(2906, 9685, 0), new WorldPoint(3053, 3852, 0)), - ELVES(new WorldPoint(2044, 4635, 0), new WorldPoint(3278, 6098, 0)), - SUQAHS(new WorldPoint(2114, 3943, 0)), - TROLLS(new WorldPoint(2401, 3856, 0), new WorldPoint(1242, 3517, 0)), - GREATER_DEMONS(new WorldPoint(1435, 10086, 2), new WorldPoint(3224, 10132, 0)), + BLOODVELDS(new WorldPoint(2439, 9821, 0), new WorldPoint(2448, 9821, 0), new WorldPoint(2472, 9832, 0), new WorldPoint(2453, 9817, 0), new WorldPoint(3597, 9743, 0)), + BLUE_DRAGON(new WorldPoint(1933, 8973, 1)), BRINE_RAT(new WorldPoint(2707, 10132, 0)), + CAVE_HORROR(new WorldPoint(3785, 9460, 0)), DAGGANOTH(new WorldPoint(2524, 10020, 0)), DARK_BEAST(new WorldPoint(1992, 4655, 0)), + DARK_WARRIOR(new WorldPoint(3030, 3632, 0)), DUST_DEVIL(new WorldPoint(3218, 9366, 0)), + EARTH_WARRIOR(new WorldPoint(3120, 9987, 0)), + ELDER_CHAOS_DRUID(new WorldPoint(3237, 3622, 0)), + ELVES(new WorldPoint(2044, 4635, 0), new WorldPoint(3278, 6098, 0)), + FIRE_GIANTS(new WorldPoint(2393, 9782, 0), new WorldPoint(2412, 9776, 0), new WorldPoint(2401, 9780, 0), new WorldPoint(3047, 10340, 0)), + GREATER_DEMONS(new WorldPoint(1435, 10086, 2), new WorldPoint(3224, 10132, 0)), + GREEN_DRAGON(new WorldPoint(3225, 10068, 0)), + HELLHOUNDS(new WorldPoint(2431, 9776, 0), new WorldPoint(2413, 9786, 0), new WorldPoint(2783, 9686, 0), new WorldPoint(3198, 10071, 0)), + HILL_GIANT(new WorldPoint(3044, 10318, 0)), + ICE_GIANT(new WorldPoint(3207, 10164, 0)), + ICE_WARRIOR(new WorldPoint(2955, 3876, 0)), KALPHITE(new WorldPoint(3307, 9528, 0)), LESSER_DEMON(new WorldPoint(2838, 9559, 0), new WorldPoint(3163, 10114, 0)), LIZARDMEN(new WorldPoint(1500, 3703, 0)), LIZARDMEN_SHAMAN(new WorldPoint(1423, 3715, 0)), - MINIONS_OF_SCARABAS(new WorldPoint(3297, 9252, 0)), - SMOKE_DEVIL(new WorldPoint(2398, 9444, 0)), - CAVE_HORROR(new WorldPoint(3785, 9460, 0)), - BLUE_DRAGON(new WorldPoint(1933, 8973, 1)), - ANKOU(new WorldPoint(3177, 10193, 0)), - BLACK_DRAGON(new WorldPoint(3239, 10206, 0)), - ICE_GIANT(new WorldPoint(3207, 10164, 0)), - GREEN_DRAGON(new WorldPoint(3225, 10068, 0)), - SPIDER(new WorldPoint(3169, 3886, 0)), - ROGUE(new WorldPoint(3285, 3930, 0)), - MAMMOTH(new WorldPoint(3160, 3619, 0)), - SKELETON(new WorldPoint(3018, 3592, 0)), - DARK_WARRIOR(new WorldPoint(3030, 3632, 0)), MAGIC_AXE(new WorldPoint(3190, 3960, 0)), - EARTH_WARRIOR(new WorldPoint(3120, 9987, 0)), - ICE_WARRIOR(new WorldPoint(2955, 3876, 0)), - BANDIT(new WorldPoint(3037, 3700, 0)), - BEAR(new WorldPoint(3113, 3672, 0)), - ELDER_CHAOS_DRUID(new WorldPoint(3237, 3622, 0)), - HILL_GIANT(new WorldPoint(3044, 10318, 0)), + MAMMOTH(new WorldPoint(3160, 3619, 0)), + MINIONS_OF_SCARABAS(new WorldPoint(3297, 9252, 0)), + ROGUE(new WorldPoint(3285, 3930, 0)), SCORPION(new WorldPoint(3233, 10335, 0)), + SKELETON(new WorldPoint(3018, 3592, 0)), + SMOKE_DEVIL(new WorldPoint(2398, 9444, 0)), + SPIDER(new WorldPoint(3169, 3886, 0)), + SUQAHS(new WorldPoint(2114, 3943, 0)), + TROLLS(new WorldPoint(2401, 3856, 0), new WorldPoint(1242, 3517, 0)), ZOMBIE(new WorldPoint(3172, 3677, 0)); @Getter From f311c36970405f01872c7e3531924c891b09278e Mon Sep 17 00:00:00 2001 From: Matthew C Date: Tue, 22 Sep 2020 08:06:59 +0900 Subject: [PATCH 19/75] cannon: update mammoth cannon spot --- .../java/net/runelite/client/plugins/cannon/CannonSpots.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonSpots.java b/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonSpots.java index 67308b1b50..f5b6f4a4a2 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonSpots.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonSpots.java @@ -62,7 +62,7 @@ enum CannonSpots LIZARDMEN(new WorldPoint(1500, 3703, 0)), LIZARDMEN_SHAMAN(new WorldPoint(1423, 3715, 0)), MAGIC_AXE(new WorldPoint(3190, 3960, 0)), - MAMMOTH(new WorldPoint(3160, 3619, 0)), + MAMMOTH(new WorldPoint(3168, 3595, 0)), MINIONS_OF_SCARABAS(new WorldPoint(3297, 9252, 0)), ROGUE(new WorldPoint(3285, 3930, 0)), SCORPION(new WorldPoint(3233, 10335, 0)), From d11bdf66f0a97524510e1104b931fc6b42086317 Mon Sep 17 00:00:00 2001 From: Matthew C Date: Tue, 22 Sep 2020 08:57:11 +0900 Subject: [PATCH 20/75] timers: add world placed to cannon timer tooltip --- .../java/net/runelite/client/plugins/timers/TimersPlugin.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java index ad0173b5fe..57b782f5e7 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java @@ -518,7 +518,8 @@ public class TimersPlugin extends Plugin if (config.showCannon() && (event.getMessage().equals(CANNON_FURNACE_MESSAGE) || event.getMessage().contains(CANNON_REPAIR_MESSAGE))) { - createGameTimer(CANNON); + TimerTimer cannonTimer = createGameTimer(CANNON); + cannonTimer.setTooltip(cannonTimer.getTooltip() + " - World " + client.getWorld()); } if (config.showCannon() && event.getMessage().equals(CANNON_PICKUP_MESSAGE)) From 553f5b18f3299617bf1cb0be4a89f80755b2ae59 Mon Sep 17 00:00:00 2001 From: Runelite auto updater Date: Wed, 23 Sep 2020 14:31:57 +0000 Subject: [PATCH 21/75] Release 1.6.27 --- cache-client/pom.xml | 2 +- cache-updater/pom.xml | 2 +- cache/pom.xml | 2 +- http-api/pom.xml | 2 +- http-service/pom.xml | 2 +- pom.xml | 4 ++-- runelite-api/pom.xml | 2 +- runelite-client/pom.xml | 2 +- runelite-script-assembler-plugin/pom.xml | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cache-client/pom.xml b/cache-client/pom.xml index 1bdc63df09..5afc4d3746 100644 --- a/cache-client/pom.xml +++ b/cache-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.27-SNAPSHOT + 1.6.27 cache-client diff --git a/cache-updater/pom.xml b/cache-updater/pom.xml index 0148fcf1b7..ad0e250fd7 100644 --- a/cache-updater/pom.xml +++ b/cache-updater/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.27-SNAPSHOT + 1.6.27 Cache Updater diff --git a/cache/pom.xml b/cache/pom.xml index c51b54cc0f..26ecf25566 100644 --- a/cache/pom.xml +++ b/cache/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.27-SNAPSHOT + 1.6.27 cache diff --git a/http-api/pom.xml b/http-api/pom.xml index 50fa7422a8..2e890265ea 100644 --- a/http-api/pom.xml +++ b/http-api/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.27-SNAPSHOT + 1.6.27 Web API diff --git a/http-service/pom.xml b/http-service/pom.xml index 10f644e7a2..234e8e8668 100644 --- a/http-service/pom.xml +++ b/http-service/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.27-SNAPSHOT + 1.6.27 Web Service diff --git a/pom.xml b/pom.xml index f7c8661aaf..13a83927c3 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.27-SNAPSHOT + 1.6.27 pom RuneLite @@ -61,7 +61,7 @@ https://github.com/runelite/runelite scm:git:git://github.com/runelite/runelite scm:git:git@github.com:runelite/runelite - HEAD + runelite-parent-1.6.27 diff --git a/runelite-api/pom.xml b/runelite-api/pom.xml index 8b02e31a98..cc2be4da0a 100644 --- a/runelite-api/pom.xml +++ b/runelite-api/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.27-SNAPSHOT + 1.6.27 runelite-api diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml index 389986206a..4a54c494d9 100644 --- a/runelite-client/pom.xml +++ b/runelite-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.27-SNAPSHOT + 1.6.27 client diff --git a/runelite-script-assembler-plugin/pom.xml b/runelite-script-assembler-plugin/pom.xml index 9fa24c0a36..4b4350c61f 100644 --- a/runelite-script-assembler-plugin/pom.xml +++ b/runelite-script-assembler-plugin/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.27-SNAPSHOT + 1.6.27 script-assembler-plugin From 1091bc42cd2a05fd935e9b87cd753893e5ff9ea8 Mon Sep 17 00:00:00 2001 From: Runelite auto updater Date: Wed, 23 Sep 2020 14:32:07 +0000 Subject: [PATCH 22/75] Bump for 1.6.28-SNAPSHOT --- cache-client/pom.xml | 2 +- cache-updater/pom.xml | 2 +- cache/pom.xml | 2 +- http-api/pom.xml | 2 +- http-service/pom.xml | 2 +- pom.xml | 4 ++-- runelite-api/pom.xml | 2 +- runelite-client/pom.xml | 2 +- runelite-script-assembler-plugin/pom.xml | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cache-client/pom.xml b/cache-client/pom.xml index 5afc4d3746..2a92de9feb 100644 --- a/cache-client/pom.xml +++ b/cache-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.27 + 1.6.28-SNAPSHOT cache-client diff --git a/cache-updater/pom.xml b/cache-updater/pom.xml index ad0e250fd7..2946721062 100644 --- a/cache-updater/pom.xml +++ b/cache-updater/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.27 + 1.6.28-SNAPSHOT Cache Updater diff --git a/cache/pom.xml b/cache/pom.xml index 26ecf25566..5e45843ec7 100644 --- a/cache/pom.xml +++ b/cache/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.27 + 1.6.28-SNAPSHOT cache diff --git a/http-api/pom.xml b/http-api/pom.xml index 2e890265ea..1348e6a3c3 100644 --- a/http-api/pom.xml +++ b/http-api/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.27 + 1.6.28-SNAPSHOT Web API diff --git a/http-service/pom.xml b/http-service/pom.xml index 234e8e8668..19864d0b8d 100644 --- a/http-service/pom.xml +++ b/http-service/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.27 + 1.6.28-SNAPSHOT Web Service diff --git a/pom.xml b/pom.xml index 13a83927c3..93716c2025 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.27 + 1.6.28-SNAPSHOT pom RuneLite @@ -61,7 +61,7 @@ https://github.com/runelite/runelite scm:git:git://github.com/runelite/runelite scm:git:git@github.com:runelite/runelite - runelite-parent-1.6.27 + HEAD diff --git a/runelite-api/pom.xml b/runelite-api/pom.xml index cc2be4da0a..b5183face6 100644 --- a/runelite-api/pom.xml +++ b/runelite-api/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.27 + 1.6.28-SNAPSHOT runelite-api diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml index 4a54c494d9..e2904e2be8 100644 --- a/runelite-client/pom.xml +++ b/runelite-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.27 + 1.6.28-SNAPSHOT client diff --git a/runelite-script-assembler-plugin/pom.xml b/runelite-script-assembler-plugin/pom.xml index 4b4350c61f..2f5ab0178c 100644 --- a/runelite-script-assembler-plugin/pom.xml +++ b/runelite-script-assembler-plugin/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.27 + 1.6.28-SNAPSHOT script-assembler-plugin From 9c362fb836b83a69dfeae4514b774f28cf573d7e Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 24 Sep 2020 18:44:54 -0400 Subject: [PATCH 23/75] containable frame: assume jdk 12/13/14 do not have fix for JDK-8231564 These versions are EOL, and although greater than 11, do not include JDK-8231564 --- .../main/java/net/runelite/client/ui/ContainableFrame.java | 5 +++++ .../java/net/runelite/client/ui/ContainableFrameTest.java | 3 +++ 2 files changed, 8 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/ui/ContainableFrame.java b/runelite-client/src/main/java/net/runelite/client/ui/ContainableFrame.java index 0a69897461..158e22a2b6 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/ContainableFrame.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/ContainableFrame.java @@ -76,6 +76,11 @@ public class ContainableFrame extends JFrame } String[] s = javaVersion.split("\\."); int major = Integer.parseInt(s[0]), minor = Integer.parseInt(s[1]), patch = Integer.parseInt(s[2]); + if (major == 12 || major == 13 || major == 14) + { + // These versions are since EOL & do not include JDK-8231564 + return false; + } return major > 11 || (major == 11 && minor > 0) || (major == 11 && minor == 0 && patch >= 8); } diff --git a/runelite-client/src/test/java/net/runelite/client/ui/ContainableFrameTest.java b/runelite-client/src/test/java/net/runelite/client/ui/ContainableFrameTest.java index d0b432ce39..8ee1b71a80 100644 --- a/runelite-client/src/test/java/net/runelite/client/ui/ContainableFrameTest.java +++ b/runelite-client/src/test/java/net/runelite/client/ui/ContainableFrameTest.java @@ -36,5 +36,8 @@ public class ContainableFrameTest assertTrue(ContainableFrame.jdk8231564("11.0.8")); assertFalse(ContainableFrame.jdk8231564("11.0.7")); assertFalse(ContainableFrame.jdk8231564("1.8.0_261")); + assertFalse(ContainableFrame.jdk8231564("12.0.0")); + assertFalse(ContainableFrame.jdk8231564("13.0.0")); + assertFalse(ContainableFrame.jdk8231564("14.0.0")); } } \ No newline at end of file From cbf48e7ef295e43204ff76c12b17657df1b0b508 Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 23 Sep 2020 17:05:10 -0400 Subject: [PATCH 24/75] overlay renderer: use isKeyPressed instead of tracking shift key state --- .../client/ui/overlay/OverlayRenderer.java | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java index 421b5d1dca..54fce55c51 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java @@ -46,6 +46,7 @@ import javax.swing.SwingUtilities; import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; import net.runelite.api.GameState; +import net.runelite.api.KeyCode; import net.runelite.api.MenuEntry; import net.runelite.api.events.BeforeRender; import net.runelite.api.events.ClientTick; @@ -90,7 +91,6 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener private boolean inOverlayManagingMode; private boolean inOverlayResizingMode; private boolean inOverlayDraggingMode; - private boolean inMenuEntryMode; private boolean startedMovingOverlay; private MenuEntry[] menuEntries; @@ -130,7 +130,6 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener resetOverlayManagementMode(); } - inMenuEntryMode = false; menuEntries = null; } } @@ -143,7 +142,8 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener return; } - if (!inMenuEntryMode && runeLiteConfig.menuEntryShift()) + final boolean shift = client.isKeyPressed(KeyCode.KC_SHIFT); + if (!shift && runeLiteConfig.menuEntryShift()) { return; } @@ -620,11 +620,6 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener { inOverlayManagingMode = true; } - - if (e.isShiftDown() && runeLiteConfig.menuEntryShift()) - { - inMenuEntryMode = true; - } } @Override @@ -635,11 +630,6 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener inOverlayManagingMode = false; resetOverlayManagementMode(); } - - if (!e.isShiftDown()) - { - inMenuEntryMode = false; - } } private void safeRender(Client client, Overlay overlay, OverlayLayer layer, Graphics2D graphics, Point point) From 1777b2d6bdf6f647334f494b7db2c8cc10a124ee Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 21 Sep 2020 17:08:10 -0400 Subject: [PATCH 25/75] raid plugin: simplify timer infobox logic by overriding render --- .../client/plugins/raids/RaidsPlugin.java | 25 ++++--------------- .../client/plugins/raids/RaidsTimer.java | 12 +++++++-- 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java index e251d701f6..853063c88b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java @@ -78,12 +78,12 @@ import net.runelite.client.eventbus.Subscribe; import net.runelite.client.events.ChatInput; import net.runelite.client.events.ConfigChanged; import net.runelite.client.events.OverlayMenuClicked; -import net.runelite.client.plugins.raids.events.RaidReset; -import net.runelite.client.plugins.raids.events.RaidScouted; import net.runelite.client.game.SpriteManager; import net.runelite.client.input.KeyManager; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.raids.events.RaidReset; +import net.runelite.client.plugins.raids.events.RaidScouted; import net.runelite.client.plugins.raids.solver.Layout; import net.runelite.client.plugins.raids.solver.LayoutSolver; import net.runelite.client.ui.overlay.OverlayManager; @@ -236,6 +236,7 @@ public class RaidsPlugin extends Plugin chatCommandManager.unregisterCommand(LAYOUT_COMMAND); overlayManager.remove(overlay); infoBoxManager.removeInfoBox(timer); + timer = null; inRaidChambers = false; reset(); keyManager.unregisterKeyListener(screenshotHotkeyListener); @@ -299,7 +300,7 @@ public class RaidsPlugin extends Plugin if (config.raidsTimer() && message.startsWith(RAID_START_MESSAGE)) { - timer = new RaidsTimer(this, Instant.now()); + timer = new RaidsTimer(this, Instant.now(), config); spriteManager.getSpriteAsync(TAB_QUESTS_BROWN_RAIDING_PARTY, 0, timer); infoBoxManager.addInfoBox(timer); } @@ -498,25 +499,9 @@ public class RaidsPlugin extends Plugin private void updateInfoBoxState() { - if (timer == null) - { - return; - } - - if (inRaidChambers && config.raidsTimer()) - { - if (!infoBoxManager.getInfoBoxes().contains(timer)) - { - infoBoxManager.addInfoBox(timer); - } - } - else + if (timer != null && !inRaidChambers) { infoBoxManager.removeInfoBox(timer); - } - - if (!inRaidChambers) - { timer = null; } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsTimer.java b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsTimer.java index 09c3beb722..a2e8425754 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsTimer.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsTimer.java @@ -33,9 +33,10 @@ import lombok.Setter; import net.runelite.client.plugins.Plugin; import net.runelite.client.ui.overlay.infobox.InfoBox; -public class RaidsTimer extends InfoBox +class RaidsTimer extends InfoBox { private final Instant startTime; + private final RaidsConfig config; private Instant floorTime; private LocalTime time; private LocalTime firstFloorTime; @@ -46,10 +47,11 @@ public class RaidsTimer extends InfoBox @Setter private boolean stopped; - public RaidsTimer(Plugin plugin, Instant startTime) + public RaidsTimer(Plugin plugin, Instant startTime, RaidsConfig raidsConfig) { super(null, plugin); this.startTime = startTime; + this.config = raidsConfig; floorTime = startTime; stopped = false; } @@ -146,4 +148,10 @@ public class RaidsTimer extends InfoBox return builder.toString(); } + + @Override + public boolean render() + { + return config.raidsTimer(); + } } From 8c00f6da8847f45b7dac4ba0537cf54a218762d2 Mon Sep 17 00:00:00 2001 From: Runemoro Date: Tue, 22 Sep 2020 08:02:27 -0400 Subject: [PATCH 26/75] gpu: replace toScreen function with a projection matrix The face sorting still requires the toScreen function to match exactly the same order that would be computed on CPU, but for the projection for rendering we can simplify it by replacing it with a projection matrix. This also drops the requirement of having a geometry shader to remove faces too close to or behind the camera. Upon closer inspection it looks like the client can draw faces which have at least one vertex which is >= 50 from the camera, so this now more accurately reflects the software renderer. Co-authored-by: Adam --- .../java/net/runelite/api/Perspective.java | 2 +- .../client/plugins/gpu/GpuPlugin.java | 51 ++++------ .../net/runelite/client/plugins/gpu/geom.glsl | 97 ------------------- .../net/runelite/client/plugins/gpu/vert.glsl | 20 ++-- 4 files changed, 32 insertions(+), 138 deletions(-) delete mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/gpu/geom.glsl diff --git a/runelite-api/src/main/java/net/runelite/api/Perspective.java b/runelite-api/src/main/java/net/runelite/api/Perspective.java index a248f9e0bb..ad052e27db 100644 --- a/runelite-api/src/main/java/net/runelite/api/Perspective.java +++ b/runelite-api/src/main/java/net/runelite/api/Perspective.java @@ -49,7 +49,7 @@ import net.runelite.api.widgets.WidgetInfo; */ public class Perspective { - private static final double UNIT = Math.PI / 1024d; // How much of the circle each unit of SINE/COSINE is + public static final double UNIT = Math.PI / 1024d; // How much of the circle each unit of SINE/COSINE is public static final int LOCAL_COORD_BITS = 7; public static final int LOCAL_TILE_SIZE = 1 << LOCAL_COORD_BITS; // 128 - size of a tile in local coordinates diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java index 3d751d9427..60cb6b2f8b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java @@ -37,6 +37,7 @@ import com.jogamp.opengl.GLDrawableFactory; import com.jogamp.opengl.GLException; import com.jogamp.opengl.GLFBODrawable; import com.jogamp.opengl.GLProfile; +import com.jogamp.opengl.math.Matrix4; import java.awt.Canvas; import java.awt.Dimension; import java.awt.Graphics2D; @@ -57,7 +58,6 @@ import jogamp.newt.awt.NewtFactoryAWT; import lombok.extern.slf4j.Slf4j; import net.runelite.api.BufferProvider; import net.runelite.api.Client; -import net.runelite.api.Constants; import net.runelite.api.GameState; import net.runelite.api.Model; import net.runelite.api.NodeCache; @@ -150,7 +150,6 @@ public class GpuPlugin extends Plugin implements DrawCallbacks static final Shader PROGRAM = new Shader() .add(GL4.GL_VERTEX_SHADER, "vert.glsl") - .add(GL4.GL_GEOMETRY_SHADER, "geom.glsl") .add(GL4.GL_FRAGMENT_SHADER, "frag.glsl"); static final Shader COMPUTE_PROGRAM = new Shader() @@ -768,26 +767,6 @@ public class GpuPlugin extends Plugin implements DrawCallbacks } } - private void createProjectionMatrix(float left, float right, float bottom, float top, float near, float far) - { - // create a standard orthographic projection - float tx = -((right + left) / (right - left)); - float ty = -((top + bottom) / (top - bottom)); - float tz = -((far + near) / (far - near)); - - gl.glUseProgram(glProgram); - - float[] matrix = new float[]{ - 2 / (right - left), 0, 0, 0, - 0, 2 / (top - bottom), 0, 0, - 0, 0, -2 / (far - near), 0, - tx, ty, tz, 1 - }; - gl.glUniformMatrix4fv(uniProjectionMatrix, 1, false, matrix, 0); - - gl.glUseProgram(0); - } - @Override public void drawScene(int cameraX, int cameraY, int cameraZ, int cameraPitch, int cameraYaw, int plane) { @@ -879,14 +858,6 @@ public class GpuPlugin extends Plugin implements DrawCallbacks private void resize(int canvasWidth, int canvasHeight, int viewportWidth, int viewportHeight) { - // If the viewport has changed, update the projection matrix - if (viewportWidth > 0 && viewportHeight > 0 && (viewportWidth != lastViewportWidth || viewportHeight != lastViewportHeight)) - { - lastViewportWidth = viewportWidth; - lastViewportHeight = viewportHeight; - createProjectionMatrix(0, viewportWidth, viewportHeight, 0, 0, Constants.SCENE_SIZE * Perspective.LOCAL_TILE_SIZE); - } - if (canvasWidth != lastCanvasWidth || canvasHeight != lastCanvasHeight) { lastCanvasWidth = canvasWidth; @@ -1151,6 +1122,15 @@ public class GpuPlugin extends Plugin implements DrawCallbacks gl.glUniform1f(uniBrightness, (float) textureProvider.getBrightness()); gl.glUniform1f(uniSmoothBanding, config.smoothBanding() ? 0f : 1f); + // Calculate projection matrix + Matrix4 projectionMatrix = new Matrix4(); + projectionMatrix.scale(client.getScale(), client.getScale(), 1); + projectionMatrix.multMatrix(makeProjectionMatrix(viewportWidth, viewportHeight, 50)); + projectionMatrix.rotate((float) (Math.PI - pitch * Perspective.UNIT), -1, 0, 0); + projectionMatrix.rotate((float) (yaw * Perspective.UNIT), 0, 1, 0); + projectionMatrix.translate(-client.getCameraX2(), -client.getCameraY2(), -client.getCameraZ2()); + gl.glUniformMatrix4fv(uniProjectionMatrix, 1, false, projectionMatrix.getMatrix(), 0); + for (int id = 0; id < textures.length; ++id) { Texture texture = textures[id]; @@ -1230,6 +1210,17 @@ public class GpuPlugin extends Plugin implements DrawCallbacks drawManager.processDrawComplete(this::screenshot); } + private float[] makeProjectionMatrix(float w, float h, float n) + { + return new float[] + { + 2 / w, 0, 0, 0, + 0, 2 / h, 0, 0, + 0, 0, -1, -1, + 0, 0, -2 * n, 0 + }; + } + private void drawUi(final int canvasHeight, final int canvasWidth) { final BufferProvider bufferProvider = client.getBufferProvider(); diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/geom.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/geom.glsl deleted file mode 100644 index 0f7f1bad23..0000000000 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/geom.glsl +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2018, 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. - */ - -#version 330 - -#define PI 3.1415926535897932384626433832795f -#define UNIT PI / 1024.0f - -layout(triangles) in; -layout(triangle_strip, max_vertices = 3) out; - -layout(std140) uniform uniforms { - int cameraYaw; - int cameraPitch; - int centerX; - int centerY; - int zoom; - int cameraX; - int cameraY; - int cameraZ; - ivec2 sinCosTable[2048]; -}; - -uniform mat4 projectionMatrix; - -in ivec3 vPosition[]; -in vec4 vColor[]; -in float vHsl[]; -in vec4 vUv[]; -in float vFogAmount[]; - -out vec4 Color; -centroid out float fHsl; -out vec4 fUv; -out float fogAmount; - -#include to_screen.glsl - -void main() { - ivec3 cameraPos = ivec3(cameraX, cameraY, cameraZ); - vec3 screenA = toScreen(vPosition[0] - cameraPos, cameraYaw, cameraPitch, centerX, centerY, zoom); - vec3 screenB = toScreen(vPosition[1] - cameraPos, cameraYaw, cameraPitch, centerX, centerY, zoom); - vec3 screenC = toScreen(vPosition[2] - cameraPos, cameraYaw, cameraPitch, centerX, centerY, zoom); - - if (-screenA.z < 50 || -screenB.z < 50 || -screenC.z < 50) { - // the client does not draw a triangle if any vertex distance is <50 - return; - } - - vec4 tmp = vec4(screenA.xyz, 1.0); - Color = vColor[0]; - fHsl = vHsl[0]; - fUv = vUv[0]; - fogAmount = vFogAmount[0]; - gl_Position = projectionMatrix * tmp; - EmitVertex(); - - tmp = vec4(screenB.xyz, 1.0); - Color = vColor[1]; - fHsl = vHsl[1]; - fUv = vUv[1]; - fogAmount = vFogAmount[1]; - gl_Position = projectionMatrix * tmp; - EmitVertex(); - - tmp = vec4(screenC.xyz, 1.0); - Color = vColor[2]; - fHsl = vHsl[2]; - fUv = vUv[2]; - fogAmount = vFogAmount[2]; - gl_Position = projectionMatrix * tmp; - EmitVertex(); - - EndPrimitive(); -} diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/vert.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/vert.glsl index 642aa688eb..ef2080d8d7 100644 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/vert.glsl +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/vert.glsl @@ -51,12 +51,12 @@ uniform float brightness; uniform int useFog; uniform int fogDepth; uniform int drawDistance; +uniform mat4 projectionMatrix; -out ivec3 vPosition; -out vec4 vColor; -out float vHsl; -out vec4 vUv; -out float vFogAmount; +out vec4 Color; +centroid out float fHsl; +out vec4 fUv; +out float fogAmount; #include hsl_to_rgb.glsl @@ -73,10 +73,10 @@ void main() vec3 rgb = hslToRgb(hsl); - vPosition = vertex; - vColor = vec4(rgb, 1.f - a); - vHsl = float(hsl); - vUv = uv; + gl_Position = projectionMatrix * vec4(vertex, 1.f); + Color = vec4(rgb, 1.f - a); + fHsl = float(hsl); + fUv = uv; int fogWest = max(FOG_SCENE_EDGE_MIN, cameraX - drawDistance); int fogEast = min(FOG_SCENE_EDGE_MAX, cameraX + drawDistance - TILE_SIZE); @@ -92,5 +92,5 @@ void main() max(0, (nearestEdgeDistance + FOG_CORNER_ROUNDING_SQUARED) / (secondNearestEdgeDistance + FOG_CORNER_ROUNDING_SQUARED)); - vFogAmount = fogFactorLinear(fogDistance, 0, fogDepth * TILE_SIZE) * useFog; + fogAmount = fogFactorLinear(fogDistance, 0, fogDepth * TILE_SIZE) * useFog; } From 47e0ac8032ab165e60642583b8e2e20ff9056d1f Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 25 Sep 2020 14:10:54 -0400 Subject: [PATCH 27/75] gpu: don't interpolate texture ids Despite the textures for each vertex of a face always being identical, this is now causing issues on Nvidia cards since the projection matrix update. --- .../net/runelite/client/plugins/gpu/frag.glsl | 14 ++++++-------- .../net/runelite/client/plugins/gpu/vert.glsl | 6 ++++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/frag.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/frag.glsl index 646d5dfd7d..16818b98ef 100644 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/frag.glsl +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/frag.glsl @@ -32,7 +32,8 @@ uniform vec4 fogColor; in vec4 Color; centroid in float fHsl; -in vec4 fUv; +flat in int textureId; +in vec2 fUv; in float fogAmount; out vec4 FragColor; @@ -40,20 +41,17 @@ out vec4 FragColor; #include hsl_to_rgb.glsl void main() { - float n = fUv.x; - int hsl = int(fHsl); vec3 rgb = hslToRgb(hsl) * smoothBanding + Color.rgb * (1.f - smoothBanding); vec4 smoothColor = vec4(rgb, Color.a); - if (n > 0.0) { - n -= 1.0; - int textureIdx = int(n); + if (textureId > 0) { + int textureIdx = textureId - 1; - vec2 uv = fUv.yz; + vec2 uv = fUv; vec2 animatedUv = uv + textureOffsets[textureIdx]; - vec4 textureColor = texture(textures, vec3(animatedUv, n)); + vec4 textureColor = texture(textures, vec3(animatedUv, float(textureIdx))); vec4 textureColorBrightness = pow(textureColor, vec4(brightness, brightness, brightness, 1.0f)); smoothColor = textureColorBrightness * smoothColor; diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/vert.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/vert.glsl index ef2080d8d7..0f1a626259 100644 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/vert.glsl +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/vert.glsl @@ -55,7 +55,8 @@ uniform mat4 projectionMatrix; out vec4 Color; centroid out float fHsl; -out vec4 fUv; +flat out int textureId; +out vec2 fUv; out float fogAmount; #include hsl_to_rgb.glsl @@ -76,7 +77,8 @@ void main() gl_Position = projectionMatrix * vec4(vertex, 1.f); Color = vec4(rgb, 1.f - a); fHsl = float(hsl); - fUv = uv; + textureId = int(uv.x); + fUv = uv.yz; int fogWest = max(FOG_SCENE_EDGE_MIN, cameraX - drawDistance); int fogEast = min(FOG_SCENE_EDGE_MAX, cameraX + drawDistance - TILE_SIZE); From 6df3016bb544b42c80639a432a898afccb82e2c2 Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 26 Sep 2020 00:04:57 -0400 Subject: [PATCH 28/75] overlay renderer: don't allow moving dynamic or tooltip overlays Originally this was fixed in d02ddfc1b2479134f660c507c73ecfb47e530e29 but then broken in d676542dc2f741ba1d821e73d3e7cf9f67df9626. Preferred positions don't make sense for either overlay type and for tooltips breaks them due to having them offset from the mouse position. --- .../net/runelite/client/ui/overlay/OverlayRenderer.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java index 54fce55c51..888c6431ae 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java @@ -381,6 +381,12 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener { for (Overlay overlay : overlayManager.getOverlays()) { + if (overlay.getPosition() == OverlayPosition.DYNAMIC || overlay.getPosition() == OverlayPosition.TOOLTIP) + { + // never allow moving dynamic or tooltip overlays + continue; + } + final Rectangle bounds = overlay.getBounds(); if (bounds.contains(mousePoint)) { From 908c6b4fe4d3f748c6534bae0b3ec4ee4c766dcd Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 21 Sep 2020 17:10:42 -0400 Subject: [PATCH 29/75] infobox: add support for multiple infobox groups Co-authored-by: Ron Young --- .../java/net/runelite/client/RuneLite.java | 10 +- .../client/config/RuneLiteConfig.java | 3 +- .../client/plugins/boosts/BoostIndicator.java | 6 + .../client/plugins/timers/TimerTimer.java | 6 + .../runelite/client/ui/overlay/Overlay.java | 22 ++ .../client/ui/overlay/OverlayRenderer.java | 38 ++- .../client/ui/overlay/infobox/InfoBox.java | 7 + .../ui/overlay/infobox/InfoBoxManager.java | 248 +++++++++++++++++- .../ui/overlay/infobox/InfoBoxOverlay.java | 53 +++- .../itemcharges/ItemChargePluginTest.java | 5 + .../client/plugins/raids/RaidsPluginTest.java | 5 + .../screenshot/ScreenshotPluginTest.java | 5 + .../overlay/infobox/InfoBoxManagerTest.java | 10 + 13 files changed, 383 insertions(+), 35 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/RuneLite.java b/runelite-client/src/main/java/net/runelite/client/RuneLite.java index 29d7fc32c0..7418be6c5e 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLite.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLite.java @@ -79,7 +79,6 @@ import net.runelite.client.ui.overlay.OverlayManager; import net.runelite.client.ui.overlay.OverlayRenderer; import net.runelite.client.ui.overlay.WidgetOverlay; import net.runelite.client.ui.overlay.infobox.InfoBoxManager; -import net.runelite.client.ui.overlay.infobox.InfoBoxOverlay; import net.runelite.client.ui.overlay.tooltip.TooltipOverlay; import net.runelite.client.ui.overlay.worldmap.WorldMapOverlay; import net.runelite.client.ws.PartyService; @@ -134,7 +133,7 @@ public class RuneLite private ClientUI clientUI; @Inject - private InfoBoxManager infoBoxManager; + private Provider infoBoxManager; @Inject private OverlayManager overlayManager; @@ -160,9 +159,6 @@ public class RuneLite @Inject private Provider commandManager; - @Inject - private Provider infoBoxOverlay; - @Inject private Provider tooltipOverlay; @@ -367,7 +363,6 @@ public class RuneLite eventBus.register(externalPluginManager); eventBus.register(overlayManager); eventBus.register(drawManager); - eventBus.register(infoBoxManager); eventBus.register(configManager); eventBus.register(discordService); @@ -376,6 +371,7 @@ public class RuneLite // Initialize chat colors chatMessageManager.get().loadColors(); + eventBus.register(infoBoxManager.get()); eventBus.register(partyService.get()); eventBus.register(overlayRenderer.get()); eventBus.register(friendsChatManager.get()); @@ -386,11 +382,9 @@ public class RuneLite eventBus.register(lootManager.get()); eventBus.register(chatboxPanelManager.get()); eventBus.register(hooks.get()); - eventBus.register(infoBoxOverlay.get()); // Add core overlays WidgetOverlay.createOverlays(client).forEach(overlayManager::add); - overlayManager.add(infoBoxOverlay.get()); overlayManager.add(worldMapOverlay.get()); overlayManager.add(tooltipOverlay.get()); } diff --git a/runelite-client/src/main/java/net/runelite/client/config/RuneLiteConfig.java b/runelite-client/src/main/java/net/runelite/client/config/RuneLiteConfig.java index f0f652aea8..4b6a960cbf 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/RuneLiteConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/config/RuneLiteConfig.java @@ -334,7 +334,8 @@ public interface RuneLiteConfig extends Config name = "Display infoboxes vertically", description = "Toggles the infoboxes to display vertically", position = 40, - section = overlaySettings + section = overlaySettings, + hidden = true ) default boolean infoBoxVertical() { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/boosts/BoostIndicator.java b/runelite-client/src/main/java/net/runelite/client/plugins/boosts/BoostIndicator.java index 802c05eac7..001e8ae5c8 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/boosts/BoostIndicator.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/boosts/BoostIndicator.java @@ -89,4 +89,10 @@ public class BoostIndicator extends InfoBox { return config.displayInfoboxes() && plugin.canShowBoosts() && plugin.getSkillsToDisplay().contains(getSkill()); } + + @Override + public String getName() + { + return "Boost " + skill.getName(); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimerTimer.java b/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimerTimer.java index 8a30de2ade..45728a4120 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimerTimer.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimerTimer.java @@ -45,4 +45,10 @@ class TimerTimer extends Timer { return timer; } + + @Override + public String getName() + { + return timer.name(); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/Overlay.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/Overlay.java index 771278185d..a440108944 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/Overlay.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/Overlay.java @@ -30,6 +30,7 @@ import java.awt.Rectangle; import java.util.ArrayList; import java.util.List; import javax.annotation.Nullable; +import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; import net.runelite.client.plugins.Plugin; @@ -52,6 +53,13 @@ public abstract class Overlay implements LayoutableRenderableEntity private boolean resizable; private boolean resettable = true; + /** + * Whether this overlay can be dragged onto other overlays & have + * other overlays dragged onto it. + */ + @Setter(AccessLevel.PROTECTED) + private boolean dragTargetable; + protected Overlay() { plugin = null; @@ -64,6 +72,7 @@ public abstract class Overlay implements LayoutableRenderableEntity /** * Overlay name, used for saving the overlay, needs to be unique + * * @return overlay name */ public String getName() @@ -74,4 +83,17 @@ public abstract class Overlay implements LayoutableRenderableEntity public void onMouseOver() { } + + /** + * Called when an overlay is dragged onto this, if dragTargetable is true. + * Return true to consume the mouse event and prevent the other + * overlay from being moved + * + * @param other the overlay being dragged + * @return + */ + public boolean onDrag(Overlay other) + { + return false; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java index 888c6431ae..7a4d7f08c1 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayRenderer.java @@ -77,6 +77,7 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener private static final Color SNAP_CORNER_ACTIVE_COLOR = new Color(0, 255, 0, 100); private static final Color MOVING_OVERLAY_COLOR = new Color(255, 255, 0, 100); private static final Color MOVING_OVERLAY_ACTIVE_COLOR = new Color(255, 255, 0, 200); + private static final Color MOVING_OVERLAY_TARGET_COLOR = Color.RED; private static final Color MOVING_OVERLAY_RESIZING_COLOR = new Color(255, 0, 255, 200); private final Client client; private final OverlayManager overlayManager; @@ -87,6 +88,7 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener private final Point overlayOffset = new Point(); private final Point mousePosition = new Point(); private Overlay currentManagedOverlay; + private Overlay dragTargetOverlay; private Rectangle currentManagedBounds; private boolean inOverlayManagingMode; private boolean inOverlayResizingMode; @@ -292,15 +294,28 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener { if (inOverlayManagingMode) { + Color boundsColor; if (inOverlayResizingMode && currentManagedOverlay == overlay) { - graphics.setColor(MOVING_OVERLAY_RESIZING_COLOR); + boundsColor = MOVING_OVERLAY_RESIZING_COLOR; + } + else if (inOverlayDraggingMode && currentManagedOverlay == overlay) + { + boundsColor = MOVING_OVERLAY_ACTIVE_COLOR; + } + else if (inOverlayDraggingMode && overlay.isDragTargetable() && currentManagedOverlay.isDragTargetable() + && currentManagedOverlay.getBounds().intersects(bounds)) + { + boundsColor = MOVING_OVERLAY_TARGET_COLOR; + assert currentManagedOverlay != overlay; + dragTargetOverlay = overlay; } else { - graphics.setColor(inOverlayDraggingMode && currentManagedOverlay == overlay ? MOVING_OVERLAY_ACTIVE_COLOR : MOVING_OVERLAY_COLOR); + boundsColor = MOVING_OVERLAY_COLOR; } + graphics.setColor(boundsColor); graphics.draw(bounds); graphics.setPaint(paint); } @@ -457,6 +472,12 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener return mouseEvent; } + if (dragTargetOverlay != null && !currentManagedOverlay.getBounds().intersects(dragTargetOverlay.getBounds())) + { + // No longer over drag target + dragTargetOverlay = null; + } + final Rectangle canvasRect = new Rectangle(client.getRealDimensions()); if (!canvasRect.contains(p)) @@ -584,7 +605,17 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener mousePosition.setLocation(-1, -1); - // do not snapcorner detached overlays + if (dragTargetOverlay != null) + { + if (dragTargetOverlay.onDrag(currentManagedOverlay)) + { + mouseEvent.consume(); + resetOverlayManagementMode(); + return mouseEvent; + } + } + + // Check if the overlay is over a snapcorner and move it if so, unless it is a detached overlay if (currentManagedOverlay.getPosition() != OverlayPosition.DETACHED && inOverlayDraggingMode) { final OverlayBounds snapCorners = this.snapCorners.translated(-SNAP_CORNER_SIZE.width, -SNAP_CORNER_SIZE.height); @@ -720,6 +751,7 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener inOverlayResizingMode = false; inOverlayDraggingMode = false; currentManagedOverlay = null; + dragTargetOverlay = null; currentManagedBounds = null; clientUI.setCursor(clientUI.getDefaultCursor()); } diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBox.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBox.java index 364979cf36..58459dff23 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBox.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBox.java @@ -81,4 +81,11 @@ public abstract class InfoBox { return false; } + + public String getName() + { + // Use a combination of plugin name and infobox implementation name to try and make each infobox as unique + // as possible by default + return plugin.getClass().getSimpleName() + "_" + getClass().getSimpleName(); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxManager.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxManager.java index efda12a992..66a6d7d913 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxManager.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxManager.java @@ -25,33 +25,76 @@ package net.runelite.client.ui.overlay.infobox; import com.google.common.base.Preconditions; +import com.google.common.base.Strings; import com.google.common.collect.ComparisonChain; import java.awt.Graphics; import java.awt.image.BufferedImage; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; +import java.util.stream.Collectors; import javax.inject.Inject; import javax.inject.Singleton; import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import net.runelite.api.MenuAction; +import net.runelite.client.config.ConfigManager; import net.runelite.client.config.RuneLiteConfig; +import net.runelite.client.eventbus.EventBus; import net.runelite.client.eventbus.Subscribe; import net.runelite.client.events.ConfigChanged; +import net.runelite.client.events.InfoBoxMenuClicked; +import net.runelite.client.ui.overlay.OverlayManager; +import net.runelite.client.ui.overlay.OverlayMenuEntry; +import net.runelite.client.ui.overlay.components.ComponentOrientation; +import net.runelite.client.ui.overlay.tooltip.TooltipManager; import net.runelite.client.util.AsyncBufferedImage; @Singleton @Slf4j public class InfoBoxManager { - private final List infoBoxes = new CopyOnWriteArrayList<>(); + private static final String INFOBOXLAYER_KEY = "infoboxlayer"; + private static final String INFOBOXOVERLAY_KEY = "infoboxoverlay"; + private static final String INFOBOXOVERLAY_ORIENTATION_PREFIX = "orient_"; + private static final String DEFAULT_LAYER = "InfoBoxOverlay"; + + private static final String DETACH = "Detach"; + private static final String FLIP = "Flip"; + private static final String DELETE = "Delete"; + + private static final OverlayMenuEntry DETACH_ME = new OverlayMenuEntry(MenuAction.RUNELITE_INFOBOX, DETACH, "InfoBox"); + private static final OverlayMenuEntry FLIP_ME = new OverlayMenuEntry(MenuAction.RUNELITE_INFOBOX, FLIP, "InfoBox Group"); + private static final OverlayMenuEntry DELETE_ME = new OverlayMenuEntry(MenuAction.RUNELITE_INFOBOX, DELETE, "InfoBox Group"); + + private final Map layers = new ConcurrentHashMap<>(); + private final RuneLiteConfig runeLiteConfig; + private final TooltipManager tooltipManager; + private final Client client; + private final EventBus eventBus; + private final OverlayManager overlayManager; + private final ConfigManager configManager; @Inject - private InfoBoxManager(final RuneLiteConfig runeLiteConfig) + private InfoBoxManager( + final RuneLiteConfig runeLiteConfig, + final TooltipManager tooltipManager, + final Client client, + final EventBus eventBus, + final OverlayManager overlayManager, + final ConfigManager configManager) { this.runeLiteConfig = runeLiteConfig; + this.tooltipManager = tooltipManager; + this.client = client; + this.eventBus = eventBus; + this.overlayManager = overlayManager; + this.configManager = configManager; } @Subscribe @@ -59,7 +102,33 @@ public class InfoBoxManager { if (event.getGroup().equals("runelite") && event.getKey().equals("infoBoxSize")) { - infoBoxes.forEach(this::updateInfoBoxImage); + layers.values().forEach(l -> l.getInfoBoxes().forEach(this::updateInfoBoxImage)); + } + } + + @Subscribe + public void onInfoBoxMenuClicked(InfoBoxMenuClicked event) + { + if (DETACH.equals(event.getEntry().getOption())) + { + // The layer name doesn't matter as long as it is unique + splitInfobox(event.getInfoBox().getName() + "_" + System.currentTimeMillis(), event.getInfoBox()); + } + else if (FLIP.equals(event.getEntry().getOption())) + { + InfoBoxOverlay infoBoxOverlay = layers.get(getLayer(event.getInfoBox())); + ComponentOrientation newOrientation = infoBoxOverlay.flip(); + setOrientation(infoBoxOverlay.getName(), newOrientation); + } + else if (DELETE.equals(event.getEntry().getOption())) + { + // This is just a merge into the default layer + InfoBoxOverlay source = layers.get(getLayer(event.getInfoBox())); + InfoBoxOverlay dest = layers.computeIfAbsent(DEFAULT_LAYER, this::makeOverlay); + if (source != dest) + { + mergeInfoBoxes(source, dest); + } } } @@ -70,14 +139,25 @@ public class InfoBoxManager updateInfoBoxImage(infoBox); + String layerName = getLayer(infoBox); + InfoBoxOverlay overlay = layers.computeIfAbsent(layerName, this::makeOverlay); + List menuEntries = infoBox.getMenuEntries(); + menuEntries.add(DETACH_ME); + menuEntries.add(FLIP_ME); + if (!layerName.equals(DEFAULT_LAYER)) + { + // Non default-group infoboxes have a delete option to delete the group + menuEntries.add(DELETE_ME); + } + synchronized (this) { - int idx = findInsertionIndex(infoBoxes, infoBox, (b1, b2) -> ComparisonChain + int idx = findInsertionIndex(overlay.getInfoBoxes(), infoBox, (b1, b2) -> ComparisonChain .start() .compare(b1.getPriority(), b2.getPriority()) .compare(b1.getPlugin().getName(), b2.getPlugin().getName()) .result()); - infoBoxes.add(idx, infoBox); + overlay.getInfoBoxes().add(idx, infoBox); } BufferedImage image = infoBox.getImage(); @@ -91,28 +171,40 @@ public class InfoBoxManager public synchronized void removeInfoBox(InfoBox infoBox) { - if (infoBoxes.remove(infoBox)) + if (infoBox == null) + { + return; + } + + if (layers.get(getLayer(infoBox)).getInfoBoxes().remove(infoBox)) { log.debug("Removed InfoBox {}", infoBox); } + + infoBox.getMenuEntries().remove(DETACH_ME); + infoBox.getMenuEntries().remove(FLIP_ME); + infoBox.getMenuEntries().remove(DELETE_ME); } public synchronized void removeIf(Predicate filter) { - if (infoBoxes.removeIf(filter)) + for (InfoBoxOverlay overlay : layers.values()) { - log.debug("Removed InfoBoxes for filter {}", filter); + if (overlay.getInfoBoxes().removeIf(filter)) + { + log.debug("Removed InfoBoxes for filter {} from {}", filter, overlay); + } } } public List getInfoBoxes() { - return Collections.unmodifiableList(infoBoxes); + return layers.values().stream().map(InfoBoxOverlay::getInfoBoxes).flatMap(Collection::stream).collect(Collectors.toList()); } public synchronized void cull() { - infoBoxes.removeIf(InfoBox::cull); + layers.values().forEach(l -> l.getInfoBoxes().removeIf(InfoBox::cull)); } public void updateInfoBoxImage(final InfoBox infoBox) @@ -152,6 +244,140 @@ public class InfoBoxManager infoBox.setScaledImage(resultImage); } + private InfoBoxOverlay makeOverlay(String name) + { + ComponentOrientation orientation = getOrientation(name); + if (orientation == null) + { + if (name.equals(DEFAULT_LAYER)) + { + // Fall back to old orientation config option + orientation = runeLiteConfig.infoBoxVertical() ? ComponentOrientation.VERTICAL : ComponentOrientation.HORIZONTAL; + setOrientation(name, orientation); + } + else + { + // Default infobox orientation + orientation = ComponentOrientation.HORIZONTAL; + } + } + + InfoBoxOverlay infoBoxOverlay = new InfoBoxOverlay( + this, + tooltipManager, + client, + runeLiteConfig, + eventBus, + name, + orientation); + overlayManager.add(infoBoxOverlay); + eventBus.register(infoBoxOverlay); + return infoBoxOverlay; + } + + private void removeOverlay(InfoBoxOverlay overlay) + { + unsetOrientation(overlay.getName()); + eventBus.unregister(overlay); + overlayManager.remove(overlay); + layers.remove(overlay.getName()); + } + + private synchronized void splitInfobox(String newLayer, InfoBox infoBox) + { + String layer = getLayer(infoBox); + InfoBoxOverlay oldOverlay = layers.get(layer); + // Find all infoboxes with the same name, as they are all within the same group and so move at once. + Collection filtered = oldOverlay.getInfoBoxes().stream() + .filter(i -> i.getName().equals(infoBox.getName())).collect(Collectors.toList()); + + oldOverlay.getInfoBoxes().removeAll(filtered); + if (oldOverlay.getInfoBoxes().isEmpty()) + { + log.debug("Deleted layer: {}", oldOverlay.getName()); + removeOverlay(oldOverlay); + } + + InfoBoxOverlay newOverlay = layers.computeIfAbsent(newLayer, this::makeOverlay); + newOverlay.getInfoBoxes().addAll(filtered); + + // Adjust config for new infoboxes + for (InfoBox i : filtered) + { + setLayer(i, newLayer); + + if (!i.getMenuEntries().contains(DELETE_ME)) + { + i.getMenuEntries().add(DELETE_ME); + } + } + + log.debug("Moving infobox named {} (layer {}) to layer {}: {} boxes", infoBox.getName(), layer, newLayer, filtered.size()); + } + + public synchronized void mergeInfoBoxes(InfoBoxOverlay source, InfoBoxOverlay dest) + { + Collection infoBoxesToMove = source.getInfoBoxes(); + boolean isDefault = dest.getName().equals(DEFAULT_LAYER); + + log.debug("Merging InfoBoxes from {} into {} ({} boxes)", source.getName(), dest.getName(), infoBoxesToMove.size()); + + for (InfoBox infoBox : infoBoxesToMove) + { + setLayer(infoBox, dest.getName()); + + if (isDefault) + { + infoBox.getMenuEntries().remove(DELETE_ME); + } + } + + dest.getInfoBoxes().addAll(infoBoxesToMove); + source.getInfoBoxes().clear(); + + // remove source + removeOverlay(source); + log.debug("Deleted layer: {}", source.getName()); + } + + private String getLayer(InfoBox infoBox) + { + String name = configManager.getConfiguration(INFOBOXLAYER_KEY, infoBox.getName()); + if (Strings.isNullOrEmpty(name)) + { + return DEFAULT_LAYER; + } + + return name; + } + + private void setLayer(InfoBox infoBox, String layer) + { + if (layer.equals(DEFAULT_LAYER)) + { + configManager.unsetConfiguration(INFOBOXLAYER_KEY, infoBox.getName()); + } + else + { + configManager.setConfiguration(INFOBOXLAYER_KEY, infoBox.getName(), layer); + } + } + + ComponentOrientation getOrientation(String name) + { + return configManager.getConfiguration(INFOBOXOVERLAY_KEY, INFOBOXOVERLAY_ORIENTATION_PREFIX + name, ComponentOrientation.class); + } + + void setOrientation(String name, ComponentOrientation orientation) + { + configManager.setConfiguration(INFOBOXOVERLAY_KEY, INFOBOXOVERLAY_ORIENTATION_PREFIX + name, orientation); + } + + void unsetOrientation(String name) + { + configManager.unsetConfiguration(INFOBOXOVERLAY_KEY, INFOBOXOVERLAY_ORIENTATION_PREFIX + name); + } + /** * Find insertion point for the given key into the given sorted list. If key already exists in the list, * return the index after the last occurrence. diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxOverlay.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxOverlay.java index 387eda76ab..f3abeb6627 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxOverlay.java @@ -33,8 +33,9 @@ import java.awt.Point; import java.awt.Rectangle; import java.util.Collections; import java.util.List; -import javax.inject.Inject; -import javax.inject.Singleton; +import java.util.concurrent.CopyOnWriteArrayList; +import lombok.Getter; +import lombok.NonNull; import net.runelite.api.Client; import net.runelite.api.MenuAction; import net.runelite.api.events.MenuOptionClicked; @@ -42,6 +43,7 @@ import net.runelite.client.config.RuneLiteConfig; import net.runelite.client.eventbus.EventBus; import net.runelite.client.eventbus.Subscribe; import net.runelite.client.events.InfoBoxMenuClicked; +import net.runelite.client.ui.overlay.Overlay; import net.runelite.client.ui.overlay.OverlayMenuEntry; import net.runelite.client.ui.overlay.OverlayPanel; import net.runelite.client.ui.overlay.OverlayPosition; @@ -51,7 +53,6 @@ import net.runelite.client.ui.overlay.components.LayoutableRenderableEntity; import net.runelite.client.ui.overlay.tooltip.Tooltip; import net.runelite.client.ui.overlay.tooltip.TooltipManager; -@Singleton public class InfoBoxOverlay extends OverlayPanel { private static final int GAP = 1; @@ -62,24 +63,33 @@ public class InfoBoxOverlay extends OverlayPanel private final Client client; private final RuneLiteConfig config; private final EventBus eventBus; + private final String name; + private ComponentOrientation orientation; + + @Getter + private final List infoBoxes = new CopyOnWriteArrayList<>(); private InfoBoxComponent hoveredComponent; - @Inject - private InfoBoxOverlay( + InfoBoxOverlay( InfoBoxManager infoboxManager, TooltipManager tooltipManager, Client client, RuneLiteConfig config, - EventBus eventBus) + EventBus eventBus, + String name, + @NonNull ComponentOrientation orientation) { this.tooltipManager = tooltipManager; this.infoboxManager = infoboxManager; this.client = client; this.config = config; this.eventBus = eventBus; + this.name = name; + this.orientation = orientation; setPosition(OverlayPosition.TOP_LEFT); setClearChildren(false); + setDragTargetable(true); panelComponent.setWrap(true); panelComponent.setBackgroundColor(null); @@ -87,11 +97,15 @@ public class InfoBoxOverlay extends OverlayPanel panelComponent.setGap(new Point(GAP, GAP)); } + @Override + public String getName() + { + return this.name; + } + @Override public Dimension render(Graphics2D graphics) { - final List infoBoxes = infoboxManager.getInfoBoxes(); - final boolean menuOpen = client.isMenuOpen(); if (!menuOpen) { @@ -106,9 +120,7 @@ public class InfoBoxOverlay extends OverlayPanel // Set preferred size to the size of DEFAULT_WRAP_COUNT infoboxes, including the padding - which is applied // to the last infobox prior to wrapping too. panelComponent.setPreferredSize(new Dimension(DEFAULT_WRAP_COUNT * (config.infoBoxSize() + GAP), DEFAULT_WRAP_COUNT * (config.infoBoxSize() + GAP))); - panelComponent.setOrientation(config.infoBoxVertical() - ? ComponentOrientation.VERTICAL - : ComponentOrientation.HORIZONTAL); + panelComponent.setOrientation(orientation); for (InfoBox box : infoBoxes) { @@ -177,7 +189,7 @@ public class InfoBoxOverlay extends OverlayPanel @Subscribe public void onMenuOptionClicked(MenuOptionClicked menuOptionClicked) { - if (menuOptionClicked.getMenuAction() != MenuAction.RUNELITE_INFOBOX) + if (menuOptionClicked.getMenuAction() != MenuAction.RUNELITE_INFOBOX || hoveredComponent == null) { return; } @@ -192,4 +204,21 @@ public class InfoBoxOverlay extends OverlayPanel eventBus.post(new InfoBoxMenuClicked(overlayMenuEntry, infoBox)); } } + + @Override + public boolean onDrag(Overlay source) + { + if (!(source instanceof InfoBoxOverlay)) + { + return false; + } + + infoboxManager.mergeInfoBoxes((InfoBoxOverlay) source, this); + return true; + } + + ComponentOrientation flip() + { + return orientation = orientation == ComponentOrientation.HORIZONTAL ? ComponentOrientation.VERTICAL : ComponentOrientation.HORIZONTAL; + } } diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/itemcharges/ItemChargePluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/itemcharges/ItemChargePluginTest.java index 6534225980..44694dfce8 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/itemcharges/ItemChargePluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/itemcharges/ItemChargePluginTest.java @@ -38,6 +38,7 @@ import net.runelite.api.events.ChatMessage; import net.runelite.client.Notifier; import net.runelite.client.config.RuneLiteConfig; import net.runelite.client.ui.overlay.OverlayManager; +import net.runelite.client.ui.overlay.infobox.InfoBoxManager; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -91,6 +92,10 @@ public class ItemChargePluginTest @Bind private Notifier notifier; + @Mock + @Bind + private InfoBoxManager infoBoxManager; + @Mock @Bind private ItemChargeConfig config; diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/raids/RaidsPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/raids/RaidsPluginTest.java index d687d98e5b..ca13ae0c88 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/raids/RaidsPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/raids/RaidsPluginTest.java @@ -34,6 +34,7 @@ import net.runelite.client.Notifier; import net.runelite.client.config.ChatColorConfig; import net.runelite.client.config.RuneLiteConfig; import net.runelite.client.ui.overlay.OverlayManager; +import net.runelite.client.ui.overlay.infobox.InfoBoxManager; import net.runelite.client.util.ImageCapture; import net.runelite.client.ws.PartyService; import net.runelite.http.api.chat.ChatClient; @@ -85,6 +86,10 @@ public class RaidsPluginTest @Inject RaidsPlugin raidsPlugin; + @Mock + @Bind + private InfoBoxManager infoBoxManager; + @Mock @Bind private PartyService partyService; diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/screenshot/ScreenshotPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/screenshot/ScreenshotPluginTest.java index 1aaaa3e23f..8b3a136ea8 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/screenshot/ScreenshotPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/screenshot/ScreenshotPluginTest.java @@ -45,6 +45,7 @@ import net.runelite.client.config.RuneLiteConfig; import net.runelite.client.ui.ClientUI; import net.runelite.client.ui.DrawManager; import net.runelite.client.ui.overlay.OverlayManager; +import net.runelite.client.ui.overlay.infobox.InfoBoxManager; import static org.junit.Assert.assertEquals; import org.junit.Before; import org.junit.Test; @@ -105,6 +106,10 @@ public class ScreenshotPluginTest @Bind private OverlayManager overlayManager; + @Mock + @Bind + private InfoBoxManager infoBoxManager; + @Before public void before() { diff --git a/runelite-client/src/test/java/net/runelite/client/ui/overlay/infobox/InfoBoxManagerTest.java b/runelite-client/src/test/java/net/runelite/client/ui/overlay/infobox/InfoBoxManagerTest.java index 49c9c2c484..a54af2ab8a 100644 --- a/runelite-client/src/test/java/net/runelite/client/ui/overlay/infobox/InfoBoxManagerTest.java +++ b/runelite-client/src/test/java/net/runelite/client/ui/overlay/infobox/InfoBoxManagerTest.java @@ -32,6 +32,8 @@ import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import javax.inject.Inject; +import net.runelite.api.Client; +import net.runelite.client.config.ConfigManager; import net.runelite.client.config.RuneLiteConfig; import net.runelite.client.plugins.Plugin; import static org.junit.Assert.assertEquals; @@ -53,6 +55,14 @@ public class InfoBoxManagerTest @Bind private RuneLiteConfig runeLiteConfig; + @Mock + @Bind + private ConfigManager configManager; + + @Mock + @Bind + private Client client; + @Before public void before() { From f01dce648a7dd18f7315aee2a54c8c88575d026c Mon Sep 17 00:00:00 2001 From: SirGirion Date: Sat, 26 Sep 2020 18:06:19 -0400 Subject: [PATCH 30/75] loot tracker: add bird houses --- .../loottracker/LootTrackerPlugin.java | 32 +++++++++++++++++++ .../loottracker/LootTrackerPluginTest.java | 25 +++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java index 0641f74e44..2daad6bda2 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java @@ -201,6 +201,20 @@ public class LootTrackerPlugin extends Plugin private static final String BIRDNEST_EVENT = "Bird nest"; private static final Set BIRDNEST_IDS = ImmutableSet.of(ItemID.BIRD_NEST, ItemID.BIRD_NEST_5071, ItemID.BIRD_NEST_5072, ItemID.BIRD_NEST_5073, ItemID.BIRD_NEST_5074, ItemID.BIRD_NEST_7413, ItemID.BIRD_NEST_13653, ItemID.BIRD_NEST_22798, ItemID.BIRD_NEST_22800); + // Birdhouses + private static final Pattern BIRDHOUSE_PATTERN = Pattern.compile("You dismantle and discard the trap, retrieving (?:(?:a|\\d{1,2}) nests?, )?10 dead birds, \\d{1,3} feathers and (\\d,?\\d{1,3}) Hunter XP\\."); + private static final Map BIRDHOUSE_XP_TO_TYPE = new ImmutableMap.Builder(). + put(280, "Regular Bird House"). + put(420, "Oak Bird House"). + put(560, "Willow Bird House"). + put(700, "Teak Bird House"). + put(820, "Maple Bird House"). + put(960, "Mahogany Bird House"). + put(1020, "Yew Bird House"). + put(1140, "Magic Bird House"). + put(1200, "Redwood Bird House"). + build(); + /* * This map is used when a pickpocket target has a different name in the chat message than their in-game name. * Note that if the two NPCs can be found in the same place, there is a chance of race conditions @@ -708,6 +722,23 @@ public class LootTrackerPlugin extends Plugin { // Player didn't have the key they needed. resetEvent(); + return; + } + + // Check if message is a birdhouse type + final Matcher matcher = BIRDHOUSE_PATTERN.matcher(message); + if (matcher.matches()) + { + final int xp = Integer.parseInt(matcher.group(1)); + final String type = BIRDHOUSE_XP_TO_TYPE.get(xp); + if (type == null) + { + log.debug("Unknown bird house type {}", xp); + return; + } + + setEvent(LootRecordType.EVENT, type, client.getRealSkillLevel(Skill.HUNTER)); + takeInventorySnapshot(); } } @@ -728,6 +759,7 @@ public class LootTrackerPlugin extends Plugin || SEEDPACK_EVENT.equals(eventType) || CASKET_EVENT.equals(eventType) || BIRDNEST_EVENT.equals(eventType) + || eventType.endsWith("Bird House") || eventType.startsWith("H.A.M. chest") || lootRecordType == LootRecordType.PICKPOCKET) { diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/loottracker/LootTrackerPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/loottracker/LootTrackerPluginTest.java index 12f6e4d990..52a7e5fd6d 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/loottracker/LootTrackerPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/loottracker/LootTrackerPluginTest.java @@ -289,4 +289,29 @@ public class LootTrackerPluginTest QueuedMessage queuedMessage = captor.getValue(); assertEquals("Your loot is worth around 60,021,020 coins.", queuedMessage.getRuneLiteFormattedMessage()); } + + @Test + public void testBirdhouses() + { + // No bird nests + ChatMessage chatMessage = new ChatMessage(null, ChatMessageType.SPAM, "", "You dismantle and discard the trap, retrieving 10 dead birds, 30 feathers and 1140 Hunter XP.", "", 0); + lootTrackerPlugin.onChatMessage(chatMessage); + + assertEquals("Magic Bird House", lootTrackerPlugin.eventType); + assertEquals(LootRecordType.EVENT, lootTrackerPlugin.lootRecordType); + + // Single bird nest + chatMessage = new ChatMessage(null, ChatMessageType.SPAM, "", "You dismantle and discard the trap, retrieving a nest, 10 dead birds, 50 feathers and 700 Hunter XP.", "", 0); + lootTrackerPlugin.onChatMessage(chatMessage); + + assertEquals("Teak Bird House", lootTrackerPlugin.eventType); + assertEquals(LootRecordType.EVENT, lootTrackerPlugin.lootRecordType); + + // Multiple nests + chatMessage = new ChatMessage(null, ChatMessageType.SPAM, "", "You dismantle and discard the trap, retrieving 2 nests, 10 dead birds, 40 feathers and 280 Hunter XP.", "", 0); + lootTrackerPlugin.onChatMessage(chatMessage); + + assertEquals("Regular Bird House", lootTrackerPlugin.eventType); + assertEquals(LootRecordType.EVENT, lootTrackerPlugin.lootRecordType); + } } From a8846cfef85b5a0b8c1bc10d582d95e61f613459 Mon Sep 17 00:00:00 2001 From: Eric Pratt Date: Sun, 27 Sep 2020 21:02:52 -0400 Subject: [PATCH 31/75] devtools: add movement flags --- .../plugins/devtools/DevToolsOverlay.java | 37 +++++++++- .../plugins/devtools/DevToolsPanel.java | 1 + .../plugins/devtools/DevToolsPlugin.java | 2 + .../client/plugins/devtools/MovementFlag.java | 70 +++++++++++++++++++ 4 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/devtools/MovementFlag.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsOverlay.java index 3119af3feb..b4a9f65337 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsOverlay.java @@ -35,6 +35,7 @@ import java.awt.Rectangle; import java.awt.Shape; import java.awt.geom.Rectangle2D; import java.util.List; +import java.util.Set; import javax.inject.Inject; import javax.inject.Singleton; import net.runelite.api.Client; @@ -114,7 +115,7 @@ class DevToolsOverlay extends Overlay renderNpcs(graphics); } - if (plugin.getGroundItems().isActive() || plugin.getGroundObjects().isActive() || plugin.getGameObjects().isActive() || plugin.getWalls().isActive() || plugin.getDecorations().isActive() || plugin.getTileLocation().isActive()) + if (plugin.getGroundItems().isActive() || plugin.getGroundObjects().isActive() || plugin.getGameObjects().isActive() || plugin.getWalls().isActive() || plugin.getDecorations().isActive() || plugin.getTileLocation().isActive() || plugin.getMovementFlags().isActive()) { renderTileObjects(graphics); } @@ -235,6 +236,11 @@ class DevToolsOverlay extends Overlay { renderTileTooltip(graphics, tile); } + + if (plugin.getMovementFlags().isActive()) + { + renderMovementInfo(graphics, tile); + } } } } @@ -249,6 +255,35 @@ class DevToolsOverlay extends Overlay } } + private void renderMovementInfo(Graphics2D graphics, Tile tile) + { + Polygon poly = Perspective.getCanvasTilePoly(client, tile.getLocalLocation()); + + if (poly == null || !poly.contains(client.getMouseCanvasPosition().getX(), client.getMouseCanvasPosition().getY())) + { + return; + } + + if (client.getCollisionMaps() != null) + { + int[][] flags = client.getCollisionMaps()[client.getPlane()].getFlags(); + int data = flags[tile.getSceneLocation().getX()][tile.getSceneLocation().getY()]; + + Set movementFlags = MovementFlag.getSetFlags(data); + + if (movementFlags.isEmpty()) + { + toolTipManager.add(new Tooltip("No movement flags")); + } + else + { + movementFlags.forEach(flag -> toolTipManager.add(new Tooltip(flag.toString()))); + } + + OverlayUtil.renderPolygon(graphics, poly, GREEN); + } + } + private void renderGroundItems(Graphics2D graphics, Tile tile, Player player) { ItemLayer itemLayer = tile.getItemLayer(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPanel.java index b42de607c9..0d599bb1fd 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPanel.java @@ -112,6 +112,7 @@ class DevToolsPanel extends PluginPanel container.add(plugin.getLineOfSight()); container.add(plugin.getValidMovement()); + container.add(plugin.getMovementFlags()); container.add(plugin.getInteracting()); container.add(plugin.getExamine()); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPlugin.java index 038533f1c4..e369574935 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPlugin.java @@ -129,6 +129,7 @@ public class DevToolsPlugin extends Plugin private DevToolsButton chunkBorders; private DevToolsButton mapSquares; private DevToolsButton validMovement; + private DevToolsButton movementFlags; private DevToolsButton lineOfSight; private DevToolsButton cameraPosition; private DevToolsButton worldMapLocation; @@ -175,6 +176,7 @@ public class DevToolsPlugin extends Plugin lineOfSight = new DevToolsButton("Line Of Sight"); validMovement = new DevToolsButton("Valid Movement"); + movementFlags = new DevToolsButton("Movement Flags"); interacting = new DevToolsButton("Interacting"); examine = new DevToolsButton("Examine"); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/MovementFlag.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/MovementFlag.java new file mode 100644 index 0000000000..487c255673 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/MovementFlag.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2020, Pratted + * 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.devtools; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import net.runelite.api.CollisionDataFlag; + +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * An enum that binds a name to each movement flag. + * + * @see CollisionDataFlag + */ +@AllArgsConstructor +enum MovementFlag +{ + BLOCK_MOVEMENT_NORTH_WEST(CollisionDataFlag.BLOCK_MOVEMENT_NORTH_WEST), + BLOCK_MOVEMENT_NORTH(CollisionDataFlag.BLOCK_MOVEMENT_NORTH), + BLOCK_MOVEMENT_NORTH_EAST(CollisionDataFlag.BLOCK_MOVEMENT_NORTH_EAST), + BLOCK_MOVEMENT_EAST(CollisionDataFlag.BLOCK_MOVEMENT_EAST), + BLOCK_MOVEMENT_SOUTH_EAST(CollisionDataFlag.BLOCK_MOVEMENT_SOUTH_EAST), + BLOCK_MOVEMENT_SOUTH(CollisionDataFlag.BLOCK_MOVEMENT_SOUTH), + BLOCK_MOVEMENT_SOUTH_WEST(CollisionDataFlag.BLOCK_MOVEMENT_SOUTH_WEST), + BLOCK_MOVEMENT_WEST(CollisionDataFlag.BLOCK_MOVEMENT_WEST), + + BLOCK_MOVEMENT_OBJECT(CollisionDataFlag.BLOCK_MOVEMENT_OBJECT), + BLOCK_MOVEMENT_FLOOR_DECORATION(CollisionDataFlag.BLOCK_MOVEMENT_FLOOR_DECORATION), + BLOCK_MOVEMENT_FLOOR(CollisionDataFlag.BLOCK_MOVEMENT_FLOOR), + BLOCK_MOVEMENT_FULL(CollisionDataFlag.BLOCK_MOVEMENT_FULL); + + @Getter + private int flag; + + /** + * @param collisionData The tile collision flags. + * @return The set of {@link MovementFlag}s that have been set. + */ + public static Set getSetFlags(int collisionData) + { + return Arrays.stream(values()) + .filter(movementFlag -> (movementFlag.flag & collisionData) != 0) + .collect(Collectors.toSet()); + } +} From deb46aedd6841ea64bdf762834c7807bc403da1a Mon Sep 17 00:00:00 2001 From: leejt Date: Fri, 25 Sep 2020 18:46:17 -0700 Subject: [PATCH 32/75] Add crowdsourcing plugin --- .../crowdsourcing/CrowdsourcingManager.java | 99 ++++++++ .../crowdsourcing/CrowdsourcingPlugin.java | 113 +++++++++ .../crowdsourcing/cooking/CookingData.java | 41 ++++ .../cooking/CrowdsourcingCooking.java | 113 +++++++++ .../dialogue/CrowdsourcingDialogue.java | 90 +++++++ .../dialogue/DialogueOptionsData.java | 36 +++ .../dialogue/NpcDialogueData.java | 37 +++ .../dialogue/PlayerDialogueData.java | 36 +++ .../movement/CrowdsourcingMovement.java | 84 +++++++ .../crowdsourcing/movement/MovementData.java | 43 ++++ .../music/CrowdsourcingMusic.java | 71 ++++++ .../crowdsourcing/music/MusicUnlockData.java | 39 +++ .../skilling/SkillingEndReason.java | 33 +++ .../crowdsourcing/skilling/SkillingState.java | 34 +++ .../thieving/CrowdsourcingThieving.java | 126 ++++++++++ .../thieving/PickpocketData.java | 43 ++++ .../woodcutting/CrowdsourcingWoodcutting.java | 232 ++++++++++++++++++ .../woodcutting/WoodcuttingData.java | 47 ++++ .../crowdsourcing/zmi/CrowdsourcingZMI.java | 141 +++++++++++ .../plugins/crowdsourcing/zmi/ZMIData.java | 42 ++++ 20 files changed, 1500 insertions(+) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/CrowdsourcingManager.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/CrowdsourcingPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/cooking/CookingData.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/cooking/CrowdsourcingCooking.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/dialogue/CrowdsourcingDialogue.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/dialogue/DialogueOptionsData.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/dialogue/NpcDialogueData.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/dialogue/PlayerDialogueData.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/movement/CrowdsourcingMovement.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/movement/MovementData.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/music/CrowdsourcingMusic.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/music/MusicUnlockData.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/skilling/SkillingEndReason.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/skilling/SkillingState.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/thieving/CrowdsourcingThieving.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/thieving/PickpocketData.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/woodcutting/CrowdsourcingWoodcutting.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/woodcutting/WoodcuttingData.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/zmi/CrowdsourcingZMI.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/zmi/ZMIData.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/CrowdsourcingManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/CrowdsourcingManager.java new file mode 100644 index 0000000000..661071a786 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/CrowdsourcingManager.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2019, Weird Gloop + * 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.crowdsourcing; + +import com.google.gson.Gson; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import javax.inject.Inject; +import javax.inject.Singleton; +import lombok.extern.slf4j.Slf4j; +import net.runelite.http.api.RuneLiteAPI; +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; + +@Slf4j +@Singleton +public class CrowdsourcingManager +{ + private static final String CROWDSOURCING_BASE = "https://crowdsource.runescape.wiki/runelite"; + private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); + private static final Gson GSON = RuneLiteAPI.GSON; + + @Inject + private OkHttpClient okHttpClient; + + private List data = new ArrayList<>(); + + public void storeEvent(Object event) + { + synchronized (this) + { + data.add(event); + } + } + + protected void submitToAPI() + { + List temp; + synchronized (this) + { + if (data.isEmpty()) + { + return; + } + temp = data; + data = new ArrayList<>(); + } + + Request r = new Request.Builder() + .url(CROWDSOURCING_BASE) + .post(RequestBody.create(JSON, GSON.toJson(temp))) + .build(); + + okHttpClient.newCall(r).enqueue(new Callback() + { + @Override + public void onFailure(Call call, IOException e) + { + log.debug("Error sending crowdsourcing data", e); + } + + @Override + public void onResponse(Call call, Response response) + { + log.debug("Successfully sent crowdsourcing data"); + response.close(); + } + }); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/CrowdsourcingPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/CrowdsourcingPlugin.java new file mode 100644 index 0000000000..5c639b6205 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/CrowdsourcingPlugin.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2019, Weird Gloop + * 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.crowdsourcing; + +import java.time.temporal.ChronoUnit; +import javax.inject.Inject; +import lombok.extern.slf4j.Slf4j; +import net.runelite.client.eventbus.EventBus; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.crowdsourcing.cooking.CrowdsourcingCooking; +import net.runelite.client.plugins.crowdsourcing.dialogue.CrowdsourcingDialogue; +import net.runelite.client.plugins.crowdsourcing.movement.CrowdsourcingMovement; +import net.runelite.client.plugins.crowdsourcing.music.CrowdsourcingMusic; +import net.runelite.client.plugins.crowdsourcing.thieving.CrowdsourcingThieving; +import net.runelite.client.plugins.crowdsourcing.woodcutting.CrowdsourcingWoodcutting; +import net.runelite.client.plugins.crowdsourcing.zmi.CrowdsourcingZMI; +import net.runelite.client.task.Schedule; + +@Slf4j +@PluginDescriptor( + name = "OSRS Wiki Crowdsourcing", + description = "Send data to the wiki to help figure out skilling success rates, burn rates, more. See osrs.wiki/RS:CROWD" +) +public class CrowdsourcingPlugin extends Plugin +{ + // Number of seconds to wait between trying to send data to the wiki. + private static final int SECONDS_BETWEEN_UPLOADS = 300; + + @Inject + private EventBus eventBus; + + @Inject + private CrowdsourcingManager manager; + + @Inject + private CrowdsourcingCooking cooking; + + @Inject + private CrowdsourcingDialogue dialogue; + + @Inject + private CrowdsourcingMovement movement; + + @Inject + private CrowdsourcingMusic music; + + @Inject + private CrowdsourcingThieving thieving; + + @Inject + private CrowdsourcingWoodcutting woodcutting; + + @Inject + private CrowdsourcingZMI zmi; + + @Override + protected void startUp() throws Exception + { + eventBus.register(cooking); + eventBus.register(dialogue); + eventBus.register(movement); + eventBus.register(music); + eventBus.register(thieving); + eventBus.register(woodcutting); + eventBus.register(zmi); + } + + @Override + protected void shutDown() throws Exception + { + eventBus.unregister(cooking); + eventBus.unregister(dialogue); + eventBus.unregister(movement); + eventBus.unregister(music); + eventBus.unregister(thieving); + eventBus.unregister(woodcutting); + eventBus.unregister(zmi); + } + + @Schedule( + period = SECONDS_BETWEEN_UPLOADS, + unit = ChronoUnit.SECONDS, + asynchronous = true + ) + public void submitToAPI() + { + manager.submitToAPI(); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/cooking/CookingData.java b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/cooking/CookingData.java new file mode 100644 index 0000000000..568361823a --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/cooking/CookingData.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019, Weird Gloop + * 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.crowdsourcing.cooking; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class CookingData +{ + private final String message; + private final boolean hasCookingGauntlets; + private final boolean inHosidiusKitchen; + private final boolean kourendElite; + private final int lastGameObjectClicked; + private final int level; +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/cooking/CrowdsourcingCooking.java b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/cooking/CrowdsourcingCooking.java new file mode 100644 index 0000000000..37058b5dbe --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/cooking/CrowdsourcingCooking.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2019, Weird Gloop + * 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.crowdsourcing.cooking; + +import javax.inject.Inject; +import net.runelite.api.ChatMessageType; +import net.runelite.api.Client; +import net.runelite.api.InventoryID; +import net.runelite.api.ItemContainer; +import net.runelite.api.ItemID; +import net.runelite.api.MenuAction; +import net.runelite.api.Player; +import net.runelite.api.Skill; +import net.runelite.api.Varbits; +import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.MenuOptionClicked; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.crowdsourcing.CrowdsourcingManager; + +public class CrowdsourcingCooking +{ + private static final int HOSIDIUS_KITCHEN_REGION = 6712; + + @Inject + private CrowdsourcingManager manager; + + @Inject + private Client client; + + private int lastGameObjectClicked; + + private boolean hasCookingGauntlets() + { + ItemContainer equipmentContainer = client.getItemContainer(InventoryID.EQUIPMENT); + if (equipmentContainer == null) + { + return false; + } + + return equipmentContainer.contains(ItemID.COOKING_GAUNTLETS); + } + + @Subscribe + public void onChatMessage(ChatMessage event) + { + if (event.getType() != ChatMessageType.SPAM) + { + return; + } + + final String message = event.getMessage(); + // Message prefixes taken from CookingPlugin + if (message.startsWith("You successfully cook") + || message.startsWith("You successfully bake") + || message.startsWith("You manage to cook") + || message.startsWith("You roast a") + || message.startsWith("You cook") + || message.startsWith("You accidentally burn") + || message.startsWith("You accidentally spoil")) + { + boolean inHosidiusKitchen = false; + Player local = client.getLocalPlayer(); + if (local != null && local.getWorldLocation().getRegionID() == HOSIDIUS_KITCHEN_REGION) + { + inHosidiusKitchen = true; + } + + int cookingLevel = client.getBoostedSkillLevel(Skill.COOKING); + boolean hasCookingGauntlets = hasCookingGauntlets(); + boolean kourendElite = client.getVar(Varbits.DIARY_KOUREND_ELITE) == 1; + CookingData data = new CookingData(message, hasCookingGauntlets, inHosidiusKitchen, kourendElite, lastGameObjectClicked, cookingLevel); + manager.storeEvent(data); + } + } + + @Subscribe + public void onMenuOptionClicked(MenuOptionClicked menuOptionClicked) + { + MenuAction action = menuOptionClicked.getMenuAction(); + if (action == MenuAction.ITEM_USE_ON_GAME_OBJECT + || action == MenuAction.GAME_OBJECT_FIRST_OPTION + || action == MenuAction.GAME_OBJECT_SECOND_OPTION + || action == MenuAction.GAME_OBJECT_THIRD_OPTION + || action == MenuAction.GAME_OBJECT_FOURTH_OPTION + || action == MenuAction.GAME_OBJECT_FIFTH_OPTION) + { + lastGameObjectClicked = menuOptionClicked.getId(); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/dialogue/CrowdsourcingDialogue.java b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/dialogue/CrowdsourcingDialogue.java new file mode 100644 index 0000000000..9f0a0e72ad --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/dialogue/CrowdsourcingDialogue.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2019, Weird Gloop + * 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.crowdsourcing.dialogue; + +import javax.inject.Inject; +import net.runelite.api.Client; +import net.runelite.api.events.GameTick; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetID; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.crowdsourcing.CrowdsourcingManager; + +public class CrowdsourcingDialogue +{ + private static final String USERNAME_TOKEN = "%USERNAME%"; + + @Inject + private Client client; + + @Inject + private CrowdsourcingManager manager; + + private String lastNpcDialogueText = null; + private String lastPlayerDialogueText = null; + private Widget[] dialogueOptions; + + private String sanitize(String dialogue) + { + String username = client.getLocalPlayer().getName(); + return dialogue.replaceAll(username, USERNAME_TOKEN); + } + + @Subscribe + public void onGameTick(GameTick tick) + { + Widget npcDialogueTextWidget = client.getWidget(WidgetInfo.DIALOG_NPC_TEXT); + if (npcDialogueTextWidget != null && !npcDialogueTextWidget.getText().equals(lastNpcDialogueText)) + { + lastNpcDialogueText = npcDialogueTextWidget.getText(); + String npcName = client.getWidget(WidgetInfo.DIALOG_NPC_NAME).getText(); + NpcDialogueData data = new NpcDialogueData(sanitize(lastNpcDialogueText), npcName); + manager.storeEvent(data); + } + + Widget playerDialogueTextWidget = client.getWidget(WidgetID.DIALOG_PLAYER_GROUP_ID, 4); + if (playerDialogueTextWidget != null && !playerDialogueTextWidget.getText().equals(lastPlayerDialogueText)) + { + lastPlayerDialogueText = playerDialogueTextWidget.getText(); + PlayerDialogueData data = new PlayerDialogueData(sanitize(lastPlayerDialogueText)); + manager.storeEvent(data); + } + + Widget playerDialogueOptionsWidget = client.getWidget(WidgetID.DIALOG_OPTION_GROUP_ID, 1); + if (playerDialogueOptionsWidget != null && playerDialogueOptionsWidget.getChildren() != dialogueOptions) + { + dialogueOptions = playerDialogueOptionsWidget.getChildren(); + String[] optionsText = new String[dialogueOptions.length]; + for (int i = 0; i < dialogueOptions.length; i++) + { + optionsText[i] = sanitize(dialogueOptions[i].getText()); + } + DialogueOptionsData data = new DialogueOptionsData(optionsText); + manager.storeEvent(data); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/dialogue/DialogueOptionsData.java b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/dialogue/DialogueOptionsData.java new file mode 100644 index 0000000000..141707fc00 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/dialogue/DialogueOptionsData.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2019, Weird Gloop + * 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.crowdsourcing.dialogue; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class DialogueOptionsData +{ + private final String[] options; +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/dialogue/NpcDialogueData.java b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/dialogue/NpcDialogueData.java new file mode 100644 index 0000000000..18af3f88fa --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/dialogue/NpcDialogueData.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2019, Weird Gloop + * 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.crowdsourcing.dialogue; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class NpcDialogueData +{ + private final String message; + private final String name; +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/dialogue/PlayerDialogueData.java b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/dialogue/PlayerDialogueData.java new file mode 100644 index 0000000000..b78db9407c --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/dialogue/PlayerDialogueData.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2019, Weird Gloop + * 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.crowdsourcing.dialogue; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class PlayerDialogueData +{ + private final String message; +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/movement/CrowdsourcingMovement.java b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/movement/CrowdsourcingMovement.java new file mode 100644 index 0000000000..7f21c2fda2 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/movement/CrowdsourcingMovement.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2019, Weird Gloop + * 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.crowdsourcing.movement; + +import javax.inject.Inject; +import net.runelite.api.Client; +import net.runelite.api.MenuAction; +import net.runelite.api.coords.LocalPoint; +import net.runelite.api.coords.WorldPoint; +import net.runelite.api.events.GameTick; +import net.runelite.api.events.MenuOptionClicked; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.crowdsourcing.CrowdsourcingManager; + +public class CrowdsourcingMovement +{ + @Inject + private Client client; + + @Inject + private CrowdsourcingManager manager; + + private WorldPoint lastPoint; + private int ticksStill; + private boolean lastIsInInstance; + private MenuOptionClicked lastClick; + + @Subscribe + public void onMenuOptionClicked(MenuOptionClicked menuOptionClicked) + { + if (menuOptionClicked.getMenuAction() != MenuAction.WALK + && !menuOptionClicked.getMenuOption().equals("Message")) + { + lastClick = menuOptionClicked; + } + } + + @Subscribe + public void onGameTick(GameTick tick) + { + LocalPoint local = LocalPoint.fromWorld(client, client.getLocalPlayer().getWorldLocation()); + WorldPoint nextPoint = WorldPoint.fromLocalInstance(client, local); + boolean nextIsInInstance = client.isInInstancedRegion(); + if (lastPoint != null) + { + int distance = nextPoint.distanceTo(lastPoint); + if (distance > 2 || nextIsInInstance != lastIsInInstance) + { + MovementData data = new MovementData(lastPoint, nextPoint, lastIsInInstance, nextIsInInstance, ticksStill, lastClick); + manager.storeEvent(data); + } + if (distance > 0) + { + ticksStill = 0; + } + } + ticksStill++; + lastPoint = nextPoint; + lastIsInInstance = nextIsInInstance; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/movement/MovementData.java b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/movement/MovementData.java new file mode 100644 index 0000000000..1b9c6aef9e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/movement/MovementData.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2019, Weird Gloop + * 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.crowdsourcing.movement; + +import lombok.AllArgsConstructor; +import lombok.Data; +import net.runelite.api.coords.WorldPoint; +import net.runelite.api.events.MenuOptionClicked; + +@Data +@AllArgsConstructor +public class MovementData +{ + private final WorldPoint start; + private final WorldPoint end; + private final boolean fromInstance; + private final boolean toInstance; + private final int ticks; + private MenuOptionClicked lastClick; +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/music/CrowdsourcingMusic.java b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/music/CrowdsourcingMusic.java new file mode 100644 index 0000000000..abca2a624f --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/music/CrowdsourcingMusic.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2019, Weird Gloop + * 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.crowdsourcing.music; + +import javax.inject.Inject; +import net.runelite.api.ChatMessageType; +import net.runelite.api.Client; +import net.runelite.api.coords.LocalPoint; +import net.runelite.api.coords.WorldPoint; +import net.runelite.api.events.ChatMessage; +import net.runelite.client.callback.ClientThread; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.crowdsourcing.CrowdsourcingManager; + +public class CrowdsourcingMusic +{ + private static final String MUSIC_UNLOCK_MESSAGE = "You have unlocked a new music track:"; + + @Inject + private Client client; + + @Inject + private ClientThread clientThread; + + @Inject + private CrowdsourcingManager manager; + + @Subscribe + public void onChatMessage(ChatMessage event) + { + if (event.getType() == ChatMessageType.GAMEMESSAGE) + { + String message = event.getMessage(); + if (message.contains(MUSIC_UNLOCK_MESSAGE)) + { + // Need to delay this by a tick because the location is not set until after the message + clientThread.invokeLater(() -> + { + LocalPoint local = LocalPoint.fromWorld(client, client.getLocalPlayer().getWorldLocation()); + WorldPoint location = WorldPoint.fromLocalInstance(client, local); + boolean isInInstance = client.isInInstancedRegion(); + MusicUnlockData data = new MusicUnlockData(location, isInInstance, message); + manager.storeEvent(data); + }); + } + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/music/MusicUnlockData.java b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/music/MusicUnlockData.java new file mode 100644 index 0000000000..55cdb96e62 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/music/MusicUnlockData.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019, Weird Gloop + * 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.crowdsourcing.music; + +import lombok.AllArgsConstructor; +import lombok.Data; +import net.runelite.api.coords.WorldPoint; + +@Data +@AllArgsConstructor +public class MusicUnlockData +{ + private final WorldPoint location; + private final boolean isInInstance; + private final String message; +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/skilling/SkillingEndReason.java b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/skilling/SkillingEndReason.java new file mode 100644 index 0000000000..7af79389f0 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/skilling/SkillingEndReason.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2019, Weird Gloop + * 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.crowdsourcing.skilling; + +public enum SkillingEndReason +{ + DEPLETED, + INVENTORY_FULL, + INTERRUPTED +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/skilling/SkillingState.java b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/skilling/SkillingState.java new file mode 100644 index 0000000000..85a4b87866 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/skilling/SkillingState.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019, Weird Gloop + * 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.crowdsourcing.skilling; + +public enum SkillingState +{ + READY, + CLICKED, + CUTTING, + RECOVERING +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/thieving/CrowdsourcingThieving.java b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/thieving/CrowdsourcingThieving.java new file mode 100644 index 0000000000..698e359265 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/thieving/CrowdsourcingThieving.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2019, Weird Gloop + * 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.crowdsourcing.thieving; + +import java.util.regex.Pattern; +import javax.inject.Inject; +import net.runelite.api.ChatMessageType; +import net.runelite.api.Client; +import net.runelite.api.InventoryID; +import net.runelite.api.ItemContainer; +import net.runelite.api.ItemID; +import net.runelite.api.NPC; +import net.runelite.api.Skill; +import net.runelite.api.Varbits; +import net.runelite.api.coords.WorldPoint; +import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.MenuOptionClicked; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.crowdsourcing.CrowdsourcingManager; + +public class CrowdsourcingThieving +{ + private static final String BLACKJACK_SUCCESS = "You smack the bandit over the head and render them unconscious."; + private static final String BLACKJACK_FAIL = "Your blow only glances off the bandit's head."; + private static final Pattern PICKPOCKET_SUCCESS = Pattern.compile("You pick .*'s pocket\\."); + private static final Pattern PICKPOCKET_FAIL = Pattern.compile("You fail to pick .*'s pocket\\."); + + @Inject + private Client client; + + @Inject + private CrowdsourcingManager manager; + + private int lastPickpocketTarget; + + private boolean hasGlovesOfSilence() + { + ItemContainer equipmentContainer = client.getItemContainer(InventoryID.EQUIPMENT); + if (equipmentContainer == null) + { + return false; + } + + return equipmentContainer.contains(ItemID.GLOVES_OF_SILENCE); + } + + private boolean hasThievingCape() + { + ItemContainer equipmentContainer = client.getItemContainer(InventoryID.EQUIPMENT); + if (equipmentContainer == null) + { + return false; + } + + return equipmentContainer.contains(ItemID.THIEVING_CAPE) || + equipmentContainer.contains(ItemID.THIEVING_CAPET) || + equipmentContainer.contains(ItemID.MAX_CAPE); + } + + private int getArdougneDiary() + { + int easy = client.getVar(Varbits.DIARY_ARDOUGNE_EASY); + int medium = client.getVar(Varbits.DIARY_ARDOUGNE_MEDIUM); + int hard = client.getVar(Varbits.DIARY_ARDOUGNE_HARD); + int elite = client.getVar(Varbits.DIARY_ARDOUGNE_ELITE); + return easy + 2 * medium + 4 * hard + 8 * elite; + } + + @Subscribe + public void onChatMessage(ChatMessage event) + { + if (event.getType() != ChatMessageType.SPAM) + { + return; + } + + String message = event.getMessage(); + if (BLACKJACK_SUCCESS.equals(message) + || BLACKJACK_FAIL.equals(message) + || PICKPOCKET_FAIL.matcher(message).matches() + || PICKPOCKET_SUCCESS.matcher(message).matches()) + { + WorldPoint location = client.getLocalPlayer().getWorldLocation(); + int ardougneDiary = getArdougneDiary(); + boolean silence = hasGlovesOfSilence(); + boolean thievingCape = hasThievingCape(); + int thievingLevel = client.getBoostedSkillLevel(Skill.THIEVING); + PickpocketData data = new PickpocketData(thievingLevel, lastPickpocketTarget, message, location, silence, thievingCape, ardougneDiary); + manager.storeEvent(data); + } + } + + @Subscribe + public void onMenuOptionClicked(MenuOptionClicked event) + { + if (event.getMenuOption().equals("Pickpocket") || event.getMenuOption().equals("Knock-Out")) + { + NPC[] cachedNPCs = client.getCachedNPCs(); + NPC npc = cachedNPCs[event.getId()]; + lastPickpocketTarget = npc.getId(); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/thieving/PickpocketData.java b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/thieving/PickpocketData.java new file mode 100644 index 0000000000..d45328b1a4 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/thieving/PickpocketData.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2019, Weird Gloop + * 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.crowdsourcing.thieving; + +import lombok.AllArgsConstructor; +import lombok.Data; +import net.runelite.api.coords.WorldPoint; + +@Data +@AllArgsConstructor +public class PickpocketData +{ + private final int level; + private final int target; + private final String message; + private final WorldPoint location; + private final boolean silence; + private final boolean thievingCape; + private final int ardougneDiary; +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/woodcutting/CrowdsourcingWoodcutting.java b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/woodcutting/CrowdsourcingWoodcutting.java new file mode 100644 index 0000000000..b3e1189103 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/woodcutting/CrowdsourcingWoodcutting.java @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2019, Weird Gloop + * 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.crowdsourcing.woodcutting; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.inject.Inject; +import net.runelite.api.AnimationID; +import net.runelite.api.ChatMessageType; +import net.runelite.api.Client; +import net.runelite.api.ItemID; +import net.runelite.api.MenuAction; +import net.runelite.api.ObjectID; +import net.runelite.api.Skill; +import net.runelite.api.coords.WorldPoint; +import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.GameObjectDespawned; +import net.runelite.api.events.GameTick; +import net.runelite.api.events.MenuOptionClicked; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.crowdsourcing.CrowdsourcingManager; +import net.runelite.client.plugins.crowdsourcing.skilling.SkillingEndReason; +import net.runelite.client.plugins.crowdsourcing.skilling.SkillingState; + +public class CrowdsourcingWoodcutting +{ + private static final String CHOPPING_MESSAGE = "You swing your axe at the tree."; + private static final String INVENTORY_FULL_MESSAGE = "Your inventory is too full to hold any more logs."; + private static final String NEST_MESSAGE = "A bird's nest falls out of the tree"; + private static final Set TREE_OBJECTS = new ImmutableSet.Builder(). + add(ObjectID.OAK_10820). + add(ObjectID.YEW). + add(ObjectID.TREE). + add(ObjectID.TREE_1278). + add(ObjectID.WILLOW). + add(ObjectID.WILLOW_10829). + add(ObjectID.WILLOW_10831). + add(ObjectID.WILLOW_10833). + add(ObjectID.SCRAPEY_TREE). + add(ObjectID.JUNGLE_TREE_15951). + add(ObjectID.JUNGLE_TREE_15954). + add(ObjectID.JUNGLE_TREE_15948). + add(ObjectID.MAPLE_TREE_10832). + add(ObjectID.MAHOGANY). + add(ObjectID.TEAK). + add(ObjectID.MAGIC_TREE_10834). + add(ObjectID.HOLLOW_TREE_10821). + add(ObjectID.HOLLOW_TREE_10830). + add(ObjectID.ACHEY_TREE). + build(); + + private static final Map AXE_ANIMS = new ImmutableMap.Builder(). + put(AnimationID.WOODCUTTING_BRONZE, ItemID.BRONZE_AXE). + put(AnimationID.WOODCUTTING_IRON, ItemID.IRON_AXE). + put(AnimationID.WOODCUTTING_STEEL, ItemID.STEEL_AXE). + put(AnimationID.WOODCUTTING_BLACK, ItemID.BLACK_AXE). + put(AnimationID.WOODCUTTING_MITHRIL, ItemID.MITHRIL_AXE). + put(AnimationID.WOODCUTTING_ADAMANT, ItemID.ADAMANT_AXE). + put(AnimationID.WOODCUTTING_RUNE, ItemID.RUNE_AXE). + put(AnimationID.WOODCUTTING_DRAGON, ItemID.DRAGON_AXE). + put(AnimationID.WOODCUTTING_INFERNAL, ItemID.INFERNAL_AXE). + put(AnimationID.WOODCUTTING_3A_AXE, ItemID._3RD_AGE_AXE). + put(AnimationID.WOODCUTTING_CRYSTAL, ItemID.CRYSTAL_AXE).build(); + + private static final Set SUCCESS_MESSAGES = new ImmutableSet.Builder(). + add("You get some logs."). + add("You get some oak logs."). + add("You get some willow logs."). + add("You get some teak logs."). + add("You get some teak logs and give them to Carpenter Kjallak."). + add("You get some maple logs."). + add("You get some maple logs and give them to Lumberjack Leif."). + add("You get some mahogany logs."). + add("You get some mahogany logs and give them to Carpenter Kjallak."). + add("You get some yew logs."). + add("You get some magic logs."). + add("You get some scrapey tree logs."). + add("You get some bark."). + build(); + + @Inject + private CrowdsourcingManager manager; + + @Inject + private Client client; + + private SkillingState state = SkillingState.RECOVERING; + private int lastExperimentEnd = 0; + private WorldPoint treeLocation; + private int treeId; + private int startTick; + private int axe; + private List chopTicks = new ArrayList<>(); + private List nestTicks = new ArrayList<>(); + + private void endExperiment(SkillingEndReason reason) + { + if (state == SkillingState.CUTTING) + { + int endTick = client.getTickCount(); + int woodcuttingLevel = client.getBoostedSkillLevel(Skill.WOODCUTTING); + WoodcuttingData data = new WoodcuttingData( + woodcuttingLevel, + startTick, + endTick, + chopTicks, + nestTicks, + axe, + treeId, + treeLocation, + reason + ); + manager.storeEvent(data); + + chopTicks = new ArrayList<>(); + nestTicks = new ArrayList<>(); + } + state = SkillingState.RECOVERING; + lastExperimentEnd = client.getTickCount(); + } + + @Subscribe + public void onChatMessage(ChatMessage event) + { + final String message = event.getMessage(); + final ChatMessageType type = event.getType(); + if (state == SkillingState.CLICKED && type == ChatMessageType.SPAM && message.equals(CHOPPING_MESSAGE)) + { + startTick = client.getTickCount(); + state = SkillingState.CUTTING; + } + else if (state == SkillingState.CUTTING && type == ChatMessageType.SPAM && SUCCESS_MESSAGES.contains(message)) + { + chopTicks.add(client.getTickCount()); + } + else if (state == SkillingState.CUTTING && type == ChatMessageType.GAMEMESSAGE && message.equals(INVENTORY_FULL_MESSAGE)) + { + endExperiment(SkillingEndReason.INVENTORY_FULL); + } + else if (state == SkillingState.CUTTING && type == ChatMessageType.GAMEMESSAGE && message.contains(NEST_MESSAGE)) + { + nestTicks.add(client.getTickCount()); + } + } + + @Subscribe + public void onGameTick(GameTick tick) + { + int animId = client.getLocalPlayer().getAnimation(); + if (state == SkillingState.CUTTING) + { + if (AXE_ANIMS.containsKey(animId)) + { + axe = AXE_ANIMS.get(animId); + } + else + { + endExperiment(SkillingEndReason.INTERRUPTED); + } + } + else if (animId != -1) + { + endExperiment(SkillingEndReason.INTERRUPTED); + } + else if (state == SkillingState.RECOVERING && client.getTickCount() - lastExperimentEnd >= 4) + { + state = SkillingState.READY; + } + else if (state == SkillingState.CLICKED && client.getTickCount() - lastExperimentEnd >= 20) + { + state = SkillingState.READY; + } + } + + @Subscribe + public void onMenuOptionClicked(MenuOptionClicked menuOptionClicked) + { + MenuAction menuAction = menuOptionClicked.getMenuAction(); + int id = menuOptionClicked.getId(); + if (state == SkillingState.READY && menuAction == MenuAction.GAME_OBJECT_FIRST_OPTION && TREE_OBJECTS.contains(id)) + { + state = SkillingState.CLICKED; + lastExperimentEnd = client.getTickCount(); + treeId = id; + treeLocation = WorldPoint.fromScene(client, menuOptionClicked.getActionParam(), menuOptionClicked.getWidgetId(), client.getPlane()); + } + else + { + endExperiment(SkillingEndReason.INTERRUPTED); + } + } + + @Subscribe + public void onGameObjectDespawned(GameObjectDespawned event) + { + if (state != SkillingState.CUTTING) + { + return; + } + if (treeId == event.getGameObject().getId() && treeLocation.equals(event.getTile().getWorldLocation())) + { + endExperiment(SkillingEndReason.DEPLETED); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/woodcutting/WoodcuttingData.java b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/woodcutting/WoodcuttingData.java new file mode 100644 index 0000000000..2cb6ccb2f2 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/woodcutting/WoodcuttingData.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019, Weird Gloop + * 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.crowdsourcing.woodcutting; + +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Data; +import net.runelite.api.coords.WorldPoint; +import net.runelite.client.plugins.crowdsourcing.skilling.SkillingEndReason; + +@Data +@AllArgsConstructor +public class WoodcuttingData +{ + private final int level; + private final int startTick; + private final int endTick; + private final List chopTicks; + private final List nestTicks; + private final int axe; + private final int treeId; + private final WorldPoint treeLocation; + private final SkillingEndReason reason; +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/zmi/CrowdsourcingZMI.java b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/zmi/CrowdsourcingZMI.java new file mode 100644 index 0000000000..2f76cd4741 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/zmi/CrowdsourcingZMI.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2019, Weird Gloop + * 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.crowdsourcing.zmi; + +import com.google.common.collect.HashMultiset; +import com.google.common.collect.Multiset; +import com.google.common.collect.Multisets; +import java.util.Arrays; +import javax.inject.Inject; +import net.runelite.api.Client; +import net.runelite.api.InventoryID; +import net.runelite.api.ItemContainer; +import net.runelite.api.MenuAction; +import net.runelite.api.Skill; +import net.runelite.api.Varbits; +import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.ItemContainerChanged; +import net.runelite.api.events.MenuOptionClicked; +import net.runelite.api.events.StatChanged; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.crowdsourcing.CrowdsourcingManager; + +public class CrowdsourcingZMI +{ + private static final String CHAT_MESSAGE_ZMI = "You bind the temple's power into runes."; + + @Inject + private CrowdsourcingManager manager; + + @Inject + private Client client; + + private int gameTickZMI = -1; + private int illegalActionTick = -1; + private int previousRunecraftXp = 0; + private int runecraftXpGained = 0; + private Multiset previousInventorySnapshot; + + private Multiset getInventorySnapshot() + { + final ItemContainer inventory = client.getItemContainer(InventoryID.INVENTORY); + Multiset inventorySnapshot = HashMultiset.create(); + + if (inventory != null) + { + Arrays.stream(inventory.getItems()) + .forEach(item -> inventorySnapshot.add(item.getId(), item.getQuantity())); + } + + return inventorySnapshot; + } + + @Subscribe + public void onMenuOptionClicked(MenuOptionClicked menuOptionClicked) + { + MenuAction action = menuOptionClicked.getMenuAction(); + + switch (action) + { + case ITEM_FIRST_OPTION: + case ITEM_SECOND_OPTION: + case ITEM_THIRD_OPTION: + case ITEM_FOURTH_OPTION: + case ITEM_FIFTH_OPTION: + case GROUND_ITEM_FIRST_OPTION: + case GROUND_ITEM_SECOND_OPTION: + case GROUND_ITEM_THIRD_OPTION: + case GROUND_ITEM_FOURTH_OPTION: + case GROUND_ITEM_FIFTH_OPTION: + case ITEM_DROP: + illegalActionTick = client.getTickCount(); + break; + } + } + + @Subscribe + public void onChatMessage(ChatMessage chatMessage) + { + if (chatMessage.getMessage().equals(CHAT_MESSAGE_ZMI)) + { + gameTickZMI = client.getTickCount(); + previousRunecraftXp = client.getSkillExperience(Skill.RUNECRAFT); + previousInventorySnapshot = getInventorySnapshot(); + } + } + + @Subscribe + public void onStatChanged(StatChanged statChanged) + { + if (gameTickZMI == client.getTickCount()) + { + int currentRunecraftXp = statChanged.getXp(); + runecraftXpGained = currentRunecraftXp - previousRunecraftXp; + } + } + + @Subscribe + public void onItemContainerChanged(ItemContainerChanged event) + { + int itemContainerChangedTick = client.getTickCount(); + + if (event.getItemContainer() != client.getItemContainer(InventoryID.INVENTORY) || gameTickZMI != itemContainerChangedTick) + { + return; + } + + int tickDelta = itemContainerChangedTick - illegalActionTick; + boolean ardougneMedium = client.getVar(Varbits.DIARY_ARDOUGNE_MEDIUM) == 1; + int runecraftBoostedLevel = client.getBoostedSkillLevel(Skill.RUNECRAFT); + Multiset currentInventorySnapshot = getInventorySnapshot(); + final Multiset itemsReceived = Multisets.difference(currentInventorySnapshot, previousInventorySnapshot); + final Multiset itemsRemoved = Multisets.difference(previousInventorySnapshot, currentInventorySnapshot); + + ZMIData data = new ZMIData(tickDelta, ardougneMedium, runecraftBoostedLevel, runecraftXpGained, itemsReceived, itemsRemoved); + manager.storeEvent(data); + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/zmi/ZMIData.java b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/zmi/ZMIData.java new file mode 100644 index 0000000000..5c51b4d8aa --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/zmi/ZMIData.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019, Weird Gloop + * 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.crowdsourcing.zmi; + +import com.google.common.collect.Multiset; +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class ZMIData +{ + private final int tickDelta; + private final boolean ardougneMedium; + private final int level; + private final int xpGained; + private final Multiset itemsReceived; + private final Multiset itemsRemoved; +} From c774117d93eadcea367553c5d0c985d7a56bfc33 Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 28 Sep 2020 20:03:37 -0400 Subject: [PATCH 33/75] loot tracker: fix tracking grubby chest Co-authored-by: Jonathan Lee --- .../loottracker/LootTrackerPlugin.java | 4 +- .../loottracker/LootTrackerPluginTest.java | 40 +++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java index 2daad6bda2..d89236ac25 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java @@ -147,7 +147,7 @@ public class LootTrackerPlugin extends Plugin private static final Pattern LARRAN_LOOTED_PATTERN = Pattern.compile("You have opened Larran's (big|small) chest .*"); private static final String STONE_CHEST_LOOTED_MESSAGE = "You steal some loot from the chest."; private static final String DORGESH_KAAN_CHEST_LOOTED_MESSAGE = "You find treasure inside!"; - private static final String GRUBBY_CHEST_LOOTED_MESSAGE = "You unlock the chest with your key."; + private static final String GRUBBY_CHEST_LOOTED_MESSAGE = "You have opened the Grubby Chest"; private static final Pattern HAM_CHEST_LOOTED_PATTERN = Pattern.compile("Your (?[a-z]+) key breaks in the lock.*"); private static final int HAM_STOREROOM_REGION = 10321; private static final Map CHEST_EVENT_TYPES = new ImmutableMap.Builder(). @@ -618,7 +618,7 @@ public class LootTrackerPlugin extends Plugin final String message = event.getMessage(); if (message.equals(CHEST_LOOTED_MESSAGE) || message.equals(STONE_CHEST_LOOTED_MESSAGE) - || message.equals(DORGESH_KAAN_CHEST_LOOTED_MESSAGE) || message.equals(GRUBBY_CHEST_LOOTED_MESSAGE) + || message.equals(DORGESH_KAAN_CHEST_LOOTED_MESSAGE) || message.startsWith(GRUBBY_CHEST_LOOTED_MESSAGE) || LARRAN_LOOTED_PATTERN.matcher(message).matches()) { final int regionID = client.getLocalPlayer().getWorldLocation().getRegionID(); diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/loottracker/LootTrackerPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/loottracker/LootTrackerPluginTest.java index 52a7e5fd6d..c8270644f6 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/loottracker/LootTrackerPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/loottracker/LootTrackerPluginTest.java @@ -49,6 +49,7 @@ import net.runelite.api.Skill; import net.runelite.api.coords.LocalPoint; import net.runelite.api.coords.WorldPoint; import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.ItemContainerChanged; import net.runelite.api.events.WidgetLoaded; import net.runelite.api.widgets.WidgetID; import net.runelite.client.account.SessionManager; @@ -314,4 +315,43 @@ public class LootTrackerPluginTest assertEquals("Regular Bird House", lootTrackerPlugin.eventType); assertEquals(LootRecordType.EVENT, lootTrackerPlugin.lootRecordType); } + + @Test + public void testGrubbyChest() + { + Player player = mock(Player.class); + when(player.getWorldLocation()).thenReturn(new WorldPoint(7323 >> 2, (7323 & 0xff) << 6, 0)); + when(client.getLocalPlayer()).thenReturn(player); + + LootTrackerPlugin lootTrackerPluginSpy = spy(this.lootTrackerPlugin); + doNothing().when(lootTrackerPluginSpy).addLoot(any(), anyInt(), any(), any(), any(Collection.class)); + + ItemContainer itemContainer = mock(ItemContainer.class); + when(itemContainer.getItems()).thenReturn(new Item[]{ + new Item(ItemID.TWISTED_BOW, 1), + new Item(ItemID.GRUBBY_KEY, 1) + }); + when(client.getItemContainer(InventoryID.INVENTORY)).thenReturn(itemContainer); + + ChatMessage chatMessage = new ChatMessage(null, ChatMessageType.SPAM, "", "You unlock the chest with your key.", "", 0); + lootTrackerPluginSpy.onChatMessage(chatMessage); + + when(itemContainer.getItems()).thenReturn(new Item[]{ + new Item(ItemID.TWISTED_BOW, 1) + }); + lootTrackerPluginSpy.onItemContainerChanged(new ItemContainerChanged(InventoryID.INVENTORY.getId(), itemContainer)); + + chatMessage = new ChatMessage(null, ChatMessageType.GAMEMESSAGE, "", "You have opened the Grubby Chest 2 times.", "", 0); + lootTrackerPluginSpy.onChatMessage(chatMessage); + + when(itemContainer.getItems()).thenReturn(new Item[]{ + new Item(ItemID.TWISTED_BOW, 1), + new Item(ItemID.SHARK, 42) + }); + lootTrackerPluginSpy.onItemContainerChanged(new ItemContainerChanged(InventoryID.INVENTORY.getId(), itemContainer)); + + verify(lootTrackerPluginSpy).addLoot("Grubby Chest", -1, LootRecordType.EVENT, null, Arrays.asList( + new ItemStack(ItemID.SHARK, 42, null) + )); + } } From 782e2db580d8388f2e448f04e252387826db55c4 Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 30 Sep 2020 10:56:26 -0400 Subject: [PATCH 34/75] slayer plugin: simplify a few regex patterns --- .../net/runelite/client/plugins/slayer/SlayerPlugin.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 d6f569e5d2..1ed8afbea0 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 @@ -105,13 +105,13 @@ public class SlayerPlugin extends Plugin private static final String CHAT_CANCEL_MESSAGE_ZUK = "You no longer have a slayer task as you left the Inferno."; private static final String CHAT_SUPERIOR_MESSAGE = "A superior foe has appeared..."; private static final String CHAT_BRACELET_SLAUGHTER = "Your bracelet of slaughter prevents your slayer"; - private static final Pattern CHAT_BRACELET_SLAUGHTER_REGEX = Pattern.compile("Your bracelet of slaughter prevents your slayer count from decreasing. It has (\\d{1,2}) charge[s]? left."); + private static final Pattern CHAT_BRACELET_SLAUGHTER_REGEX = Pattern.compile("Your bracelet of slaughter prevents your slayer count from decreasing. It has (\\d{1,2}) charges? left\\."); private static final String CHAT_BRACELET_EXPEDITIOUS = "Your expeditious bracelet helps you progress your"; - private static final Pattern CHAT_BRACELET_EXPEDITIOUS_REGEX = Pattern.compile("Your expeditious bracelet helps you progress your slayer (?:task )?faster. It has (\\d{1,2}) charge[s]? left."); + private static final Pattern CHAT_BRACELET_EXPEDITIOUS_REGEX = Pattern.compile("Your expeditious bracelet helps you progress your slayer (?:task )?faster. It has (\\d{1,2}) charges? left\\."); private static final String CHAT_BRACELET_SLAUGHTER_CHARGE = "Your bracelet of slaughter has "; - private static final Pattern CHAT_BRACELET_SLAUGHTER_CHARGE_REGEX = Pattern.compile("Your bracelet of slaughter has (\\d{1,2}) charge[s]? left."); + private static final Pattern CHAT_BRACELET_SLAUGHTER_CHARGE_REGEX = Pattern.compile("Your bracelet of slaughter has (\\d{1,2}) charges? left\\."); private static final String CHAT_BRACELET_EXPEDITIOUS_CHARGE = "Your expeditious bracelet has "; - private static final Pattern CHAT_BRACELET_EXPEDITIOUS_CHARGE_REGEX = Pattern.compile("Your expeditious bracelet has (\\d{1,2}) charge[s]? left."); + private static final Pattern CHAT_BRACELET_EXPEDITIOUS_CHARGE_REGEX = Pattern.compile("Your expeditious bracelet has (\\d{1,2}) charges? left\\."); private static final Pattern COMBAT_BRACELET_TASK_UPDATE_MESSAGE = Pattern.compile("^You still need to kill (\\d+) monsters to complete your current Slayer assignment"); //NPC messages From e30e6853cae2a2a025e5f40f3a7d8dc4c9a8731e Mon Sep 17 00:00:00 2001 From: Michal Zielinski Date: Wed, 30 Sep 2020 14:37:54 -0400 Subject: [PATCH 35/75] menu entry swapper: add clean swap swaps the clean/use option --- .../menuentryswapper/MenuEntrySwapperConfig.java | 11 +++++++++++ .../menuentryswapper/MenuEntrySwapperPlugin.java | 2 ++ 2 files changed, 13 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperConfig.java index 91e0686121..f0a305e9f1 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperConfig.java @@ -133,6 +133,17 @@ public interface MenuEntrySwapperConfig extends Config return false; } + @ConfigItem( + keyName = "swapHerbs", + name = "Clean", + description = "Swap Clean with Use on Herbs", + section = itemSection + ) + default boolean swapHerbs() + { + return false; + } + @ConfigItem( keyName = "swapContract", name = "Contract", diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java index 652c7846c5..453d66e611 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java @@ -325,6 +325,8 @@ public class MenuEntrySwapperPlugin extends Plugin swap("bury", "use", config::swapBones); + swap("clean", "use", config::swapHerbs); + swap("collect-note", "collect-item", () -> config.swapGEItemCollect() == GEItemCollectMode.ITEMS); swap("collect-notes", "collect-items", () -> config.swapGEItemCollect() == GEItemCollectMode.ITEMS); From 34c8bfb2f4a1815a8f5bab4e2bad58685a230d55 Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 30 Sep 2020 20:04:04 -0400 Subject: [PATCH 36/75] gpu: use noperspective for hsl interpolation Prior to 8c00f6da8847f45b7dac4ba0537cf54a218762d2 the interpolation of fragment shader inputs appears to not have been accounting for perspective. This broke texturing due to the texture ids being interpolated for perspective which was fixed in 47e0ac8032ab165e60642583b8e2e20ff9056d1f. The hsl values similarly require not accounting for perspective, so the noperspective interpolation qualifier has been added. It is unclear to me why the geometry shader removal would affect the interpolation like this since it was emitting vertices with the correct z values. --- .../main/resources/net/runelite/client/plugins/gpu/frag.glsl | 2 +- .../main/resources/net/runelite/client/plugins/gpu/vert.glsl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/frag.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/frag.glsl index 16818b98ef..54ddd3cfe6 100644 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/frag.glsl +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/frag.glsl @@ -31,7 +31,7 @@ uniform float smoothBanding; uniform vec4 fogColor; in vec4 Color; -centroid in float fHsl; +noperspective centroid in float fHsl; flat in int textureId; in vec2 fUv; in float fogAmount; diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/vert.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/vert.glsl index 0f1a626259..613d95aa51 100644 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/vert.glsl +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/vert.glsl @@ -54,7 +54,7 @@ uniform int drawDistance; uniform mat4 projectionMatrix; out vec4 Color; -centroid out float fHsl; +noperspective centroid out float fHsl; flat out int textureId; out vec2 fUv; out float fogAmount; From 32638ccf637daf3661f8598e137825e4641a0734 Mon Sep 17 00:00:00 2001 From: Jordan Atwood Date: Tue, 8 Sep 2020 21:03:13 -0700 Subject: [PATCH 37/75] KeyManager: Add debug logging We receive numerous support queries regarding unknown or errantly-set keybinds causing issues during normal gameplay for our users, especially since the plugin hub has been released and more plugins are able to register keybinds. This commit adds logging for keybind registration and activation for ease of troubleshooting. --- .../net/runelite/client/input/KeyManager.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/input/KeyManager.java b/runelite-client/src/main/java/net/runelite/client/input/KeyManager.java index b464e2fc84..37519aa9e0 100644 --- a/runelite-client/src/main/java/net/runelite/client/input/KeyManager.java +++ b/runelite-client/src/main/java/net/runelite/client/input/KeyManager.java @@ -30,10 +30,12 @@ import java.util.concurrent.CopyOnWriteArrayList; import javax.annotation.Nullable; import javax.inject.Inject; import javax.inject.Singleton; +import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; import net.runelite.api.GameState; @Singleton +@Slf4j public class KeyManager { private final Client client; @@ -50,13 +52,18 @@ public class KeyManager { if (!keyListeners.contains(keyListener)) { + log.debug("Registering key listener: {}", keyListener); keyListeners.add(keyListener); } } public void unregisterKeyListener(KeyListener keyListener) { - keyListeners.remove(keyListener); + final boolean unregistered = keyListeners.remove(keyListener); + if (unregistered) + { + log.debug("Unregistered key listener: {}", keyListener); + } } public void processKeyPressed(KeyEvent keyEvent) @@ -73,6 +80,8 @@ public class KeyManager continue; } + log.debug("Processing key pressed {} for key listener {}", keyEvent.paramString(), keyListener); + keyListener.keyPressed(keyEvent); if (keyEvent.isConsumed()) { @@ -95,6 +104,8 @@ public class KeyManager continue; } + log.debug("Processing key released {} for key listener {}", keyEvent.paramString(), keyListener); + keyListener.keyReleased(keyEvent); if (keyEvent.isConsumed()) { @@ -117,6 +128,8 @@ public class KeyManager continue; } + log.debug("Processing key typed {} for key listener {}", keyEvent.paramString(), keyListener); + keyListener.keyTyped(keyEvent); if (keyEvent.isConsumed()) { From 1799c9e593061d2a54932b6b7332f37b081d54fa Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 1 Oct 2020 10:03:50 -0400 Subject: [PATCH 38/75] loottracker: submit loot when not logged in This is to aid the wiki drop log project. The data is not otherwise stored. --- .../api/loottracker/LootTrackerClient.java | 21 ++++++++++------ .../loottracker/LootTrackerController.java | 18 +++++++++---- .../LootTrackerControllerTest.java | 6 ++++- .../loottracker/LootTrackerConfig.java | 2 +- .../plugins/loottracker/LootTrackerPanel.java | 4 +-- .../loottracker/LootTrackerPlugin.java | 25 +++++++++++-------- .../loottracker/LootTrackerPluginTest.java | 5 ++++ 7 files changed, 54 insertions(+), 27 deletions(-) diff --git a/http-api/src/main/java/net/runelite/http/api/loottracker/LootTrackerClient.java b/http-api/src/main/java/net/runelite/http/api/loottracker/LootTrackerClient.java index 3908ec01c8..4fc68a6ba5 100644 --- a/http-api/src/main/java/net/runelite/http/api/loottracker/LootTrackerClient.java +++ b/http-api/src/main/java/net/runelite/http/api/loottracker/LootTrackerClient.java @@ -35,7 +35,9 @@ import java.util.Collection; import java.util.List; import java.util.UUID; import java.util.concurrent.CompletableFuture; -import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; import net.runelite.http.api.RuneLiteAPI; import static net.runelite.http.api.RuneLiteAPI.JSON; @@ -48,13 +50,15 @@ import okhttp3.RequestBody; import okhttp3.Response; @Slf4j -@AllArgsConstructor +@RequiredArgsConstructor public class LootTrackerClient { private static final Gson GSON = RuneLiteAPI.GSON; private final OkHttpClient client; - private final UUID uuid; + @Getter + @Setter + private UUID uuid; public CompletableFuture submit(Collection lootRecords) { @@ -64,13 +68,16 @@ public class LootTrackerClient .addPathSegment("loottracker") .build(); - Request request = new Request.Builder() - .header(RuneLiteAPI.RUNELITE_AUTH, uuid.toString()) - .post(RequestBody.create(JSON, GSON.toJson(lootRecords))) + Request.Builder requestBuilder = new Request.Builder(); + if (uuid != null) + { + requestBuilder.header(RuneLiteAPI.RUNELITE_AUTH, uuid.toString()); + } + requestBuilder.post(RequestBody.create(JSON, GSON.toJson(lootRecords))) .url(url) .build(); - client.newCall(request).enqueue(new Callback() + client.newCall(requestBuilder.build()).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) diff --git a/http-service/src/main/java/net/runelite/http/service/loottracker/LootTrackerController.java b/http-service/src/main/java/net/runelite/http/service/loottracker/LootTrackerController.java index 9b810f672f..ef39ac0d50 100644 --- a/http-service/src/main/java/net/runelite/http/service/loottracker/LootTrackerController.java +++ b/http-service/src/main/java/net/runelite/http/service/loottracker/LootTrackerController.java @@ -65,14 +65,22 @@ public class LootTrackerController @RequestMapping(method = RequestMethod.POST) public void storeLootRecord(HttpServletRequest request, HttpServletResponse response, @RequestBody Collection records) throws IOException { - SessionEntry e = auth.handle(request, response); - if (e == null) + SessionEntry session = null; + if (request.getHeader(RuneLiteAPI.RUNELITE_AUTH) != null) { - response.setStatus(HttpStatusCodes.STATUS_CODE_UNAUTHORIZED); - return; + session = auth.handle(request, response); + if (session == null) + { + // error is set here on the response, so we shouldn't continue + return; + } } + Integer userId = session == null ? null : session.getUser(); - service.store(records, e.getUser()); + if (userId != null) + { + service.store(records, userId); + } response.setStatus(HttpStatusCodes.STATUS_CODE_OK); try (Jedis jedis = redisPool.getResource()) diff --git a/http-service/src/test/java/net/runelite/http/service/loottracker/LootTrackerControllerTest.java b/http-service/src/test/java/net/runelite/http/service/loottracker/LootTrackerControllerTest.java index ddfac3e375..685479a638 100644 --- a/http-service/src/test/java/net/runelite/http/service/loottracker/LootTrackerControllerTest.java +++ b/http-service/src/test/java/net/runelite/http/service/loottracker/LootTrackerControllerTest.java @@ -27,6 +27,7 @@ package net.runelite.http.service.loottracker; import java.io.IOException; import java.time.Instant; import java.util.Collections; +import java.util.UUID; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import net.runelite.http.api.RuneLiteAPI; @@ -91,7 +92,10 @@ public class LootTrackerControllerTest lootRecord.setDrops(Collections.singletonList(new GameItem(4151, 1))); String data = RuneLiteAPI.GSON.toJson(Collections.singletonList(lootRecord)); - mockMvc.perform(post("/loottracker").content(data).contentType(MediaType.APPLICATION_JSON)) + mockMvc.perform(post("/loottracker") + .header(RuneLiteAPI.RUNELITE_AUTH, UUID.nameUUIDFromBytes("test".getBytes())) + .content(data) + .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()); verify(lootTrackerService).store(eq(Collections.singletonList(lootRecord)), anyInt()); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerConfig.java index 368d06fa4c..abf9234a5e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerConfig.java @@ -82,7 +82,7 @@ public interface LootTrackerConfig extends Config @ConfigItem( keyName = "saveLoot", name = "Submit loot tracker data", - description = "Submit loot tracker data (requires being logged in)" + description = "Submit loot tracker data" ) default boolean saveLoot() { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java index 343fb198bc..f90563954a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java @@ -279,7 +279,7 @@ class LootTrackerPanel extends PluginPanel reset.addActionListener(e -> { final LootTrackerClient client = plugin.getLootTrackerClient(); - final boolean syncLoot = client != null && config.syncPanel(); + final boolean syncLoot = client.getUuid() != null && config.syncPanel(); final int result = JOptionPane.showOptionDialog(overallPanel, syncLoot ? SYNC_RESET_ALL_WARNING_TEXT : NO_SYNC_RESET_ALL_WARNING_TEXT, "Are you sure?", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, @@ -568,7 +568,7 @@ class LootTrackerPanel extends PluginPanel LootTrackerClient client = plugin.getLootTrackerClient(); // Without loot being grouped we have no way to identify single kills to be deleted - if (client != null && groupLoot && config.syncPanel()) + if (client.getUuid() != null && groupLoot && config.syncPanel()) { client.delete(box.getId()); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java index d89236ac25..ed085b081c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java @@ -262,9 +262,6 @@ public class LootTrackerPlugin extends Plugin @Inject private LootManager lootManager; - @Inject - private OkHttpClient okHttpClient; - private LootTrackerPanel panel; private NavigationButton navButton; @VisibleForTesting @@ -281,6 +278,7 @@ public class LootTrackerPlugin extends Plugin private Multiset inventorySnapshot; @Getter(AccessLevel.PACKAGE) + @Inject private LootTrackerClient lootTrackerClient; private final List queuedLoots = new ArrayList<>(); @@ -313,6 +311,12 @@ public class LootTrackerPlugin extends Plugin return list; } + @Provides + LootTrackerClient provideLootTrackerClient(OkHttpClient okHttpClient) + { + return new LootTrackerClient(okHttpClient); + } + @Provides LootTrackerConfig provideConfig(ConfigManager configManager) { @@ -325,11 +329,11 @@ public class LootTrackerPlugin extends Plugin AccountSession accountSession = sessionManager.getAccountSession(); if (accountSession.getUuid() != null) { - lootTrackerClient = new LootTrackerClient(okHttpClient, accountSession.getUuid()); + lootTrackerClient.setUuid(accountSession.getUuid()); } else { - lootTrackerClient = null; + lootTrackerClient.setUuid(null); } } @@ -337,7 +341,7 @@ public class LootTrackerPlugin extends Plugin public void onSessionClose(SessionClose sessionClose) { submitLoot(); - lootTrackerClient = null; + lootTrackerClient.setUuid(null); } @Subscribe @@ -373,7 +377,7 @@ public class LootTrackerPlugin extends Plugin AccountSession accountSession = sessionManager.getAccountSession(); if (accountSession != null) { - lootTrackerClient = new LootTrackerClient(okHttpClient, accountSession.getUuid()); + lootTrackerClient.setUuid(accountSession.getUuid()); clientThread.invokeLater(() -> { @@ -420,7 +424,7 @@ public class LootTrackerPlugin extends Plugin { submitLoot(); clientToolbar.removeNavigation(navButton); - lootTrackerClient = null; + lootTrackerClient.setUuid(null); chestLooted = false; } @@ -831,15 +835,14 @@ public class LootTrackerPlugin extends Plugin queuedLoots.clear(); } - if (lootTrackerClient == null || !config.saveLoot()) + if (!config.saveLoot()) { return null; } log.debug("Submitting {} loot records", copy.size()); - CompletableFuture future = lootTrackerClient.submit(copy); - return future; + return lootTrackerClient.submit(copy); } private void setEvent(LootRecordType lootRecordType, String eventType, Object metadata) diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/loottracker/LootTrackerPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/loottracker/LootTrackerPluginTest.java index c8270644f6..3080e6c5c8 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/loottracker/LootTrackerPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/loottracker/LootTrackerPluginTest.java @@ -61,6 +61,7 @@ import net.runelite.client.game.SpriteManager; import net.runelite.client.ui.overlay.infobox.InfoBoxManager; import net.runelite.http.api.item.ItemPrice; import net.runelite.http.api.loottracker.LootRecordType; +import net.runelite.http.api.loottracker.LootTrackerClient; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import org.junit.Before; @@ -135,6 +136,10 @@ public class LootTrackerPluginTest @Bind private ChatMessageManager chatMessageManager; + @Mock + @Bind + private LootTrackerClient lootTrackerClient; + @Before public void setUp() { From ca8f78281dd236a22907203df4436a5d7f2b8fdc Mon Sep 17 00:00:00 2001 From: Matthew C Date: Thu, 1 Oct 2020 07:23:42 +0900 Subject: [PATCH 39/75] inventoryviewer: Add a keybind toggle to hide the overlay. --- .../InventoryViewerConfig.java | 58 +++++++++++++++++++ .../InventoryViewerOverlay.java | 14 ++++- .../InventoryViewerPlugin.java | 27 +++++++++ 3 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/inventoryviewer/InventoryViewerConfig.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inventoryviewer/InventoryViewerConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/inventoryviewer/InventoryViewerConfig.java new file mode 100644 index 0000000000..2c3dc07102 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/inventoryviewer/InventoryViewerConfig.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2020, Matthew C + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.inventoryviewer; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; +import net.runelite.client.config.Keybind; + +@ConfigGroup(InventoryViewerConfig.GROUP) +public interface InventoryViewerConfig extends Config +{ + String GROUP = "inventoryViewer"; + + @ConfigItem( + keyName = "toggleKeybind", + name = "Toggle Overlay", + description = "Binds a key (combination) to toggle the overlay.", + position = 0 + ) + default Keybind toggleKeybind() + { + return Keybind.NOT_SET; + } + + @ConfigItem( + keyName = "hiddenDefault", + name = "Hidden by default", + description = "Whether or not the overlay is hidden by default.", + position = 1 + ) + default boolean hiddenDefault() + { + return false; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inventoryviewer/InventoryViewerOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/inventoryviewer/InventoryViewerOverlay.java index e69f29be98..2896a9cf65 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/inventoryviewer/InventoryViewerOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/inventoryviewer/InventoryViewerOverlay.java @@ -49,9 +49,10 @@ class InventoryViewerOverlay extends OverlayPanel private final Client client; private final ItemManager itemManager; + private boolean hidden; @Inject - private InventoryViewerOverlay(Client client, ItemManager itemManager) + private InventoryViewerOverlay(Client client, ItemManager itemManager, InventoryViewerConfig config) { setPosition(OverlayPosition.BOTTOM_RIGHT); panelComponent.setWrap(true); @@ -60,11 +61,17 @@ class InventoryViewerOverlay extends OverlayPanel panelComponent.setOrientation(ComponentOrientation.HORIZONTAL); this.itemManager = itemManager; this.client = client; + this.hidden = config.hiddenDefault(); } @Override public Dimension render(Graphics2D graphics) { + if (hidden) + { + return null; + } + final ItemContainer itemContainer = client.getItemContainer(InventoryID.INVENTORY); if (itemContainer == null) @@ -102,4 +109,9 @@ class InventoryViewerOverlay extends OverlayPanel ItemComposition itemComposition = itemManager.getItemComposition(item.getId()); return itemManager.getImage(item.getId(), item.getQuantity(), itemComposition.isStackable()); } + + protected void toggle() + { + hidden = !hidden; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inventoryviewer/InventoryViewerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/inventoryviewer/InventoryViewerPlugin.java index a9aa9f6453..434821522e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/inventoryviewer/InventoryViewerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/inventoryviewer/InventoryViewerPlugin.java @@ -24,10 +24,14 @@ */ package net.runelite.client.plugins.inventoryviewer; +import com.google.inject.Provides; import javax.inject.Inject; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.input.KeyManager; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.ui.overlay.OverlayManager; +import net.runelite.client.util.HotkeyListener; @PluginDescriptor( name = "Inventory Viewer", @@ -37,21 +41,44 @@ import net.runelite.client.ui.overlay.OverlayManager; ) public class InventoryViewerPlugin extends Plugin { + @Inject + private InventoryViewerConfig config; + @Inject private InventoryViewerOverlay overlay; @Inject private OverlayManager overlayManager; + @Inject + private KeyManager keyManager; + + @Provides + InventoryViewerConfig getConfig(ConfigManager configManager) + { + return configManager.getConfig(InventoryViewerConfig.class); + } + @Override public void startUp() { overlayManager.add(overlay); + keyManager.registerKeyListener(hotkeyListener); } @Override public void shutDown() { overlayManager.remove(overlay); + keyManager.unregisterKeyListener(hotkeyListener); } + + private final HotkeyListener hotkeyListener = new HotkeyListener(() -> config.toggleKeybind()) + { + @Override + public void hotkeyPressed() + { + overlay.toggle(); + } + }; } \ No newline at end of file From cd8a40807df455486e84c33ecfa9ec36c4f8136a Mon Sep 17 00:00:00 2001 From: Matthew C Date: Sun, 13 Sep 2020 16:26:09 +0900 Subject: [PATCH 40/75] runecraft: cleanup, refactor AbyssRifts, updateRifts() --- .../client/plugins/runecraft/AbyssRifts.java | 42 +++++------ .../plugins/runecraft/RunecraftConfig.java | 4 +- .../plugins/runecraft/RunecraftPlugin.java | 75 ++----------------- 3 files changed, 29 insertions(+), 92 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/AbyssRifts.java b/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/AbyssRifts.java index 1ecedafe00..dae33bdb84 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/AbyssRifts.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/AbyssRifts.java @@ -26,6 +26,8 @@ package net.runelite.client.plugins.runecraft; import com.google.common.collect.ImmutableMap; import java.util.Map; +import java.util.function.Predicate; +import lombok.AllArgsConstructor; import lombok.Getter; import static net.runelite.api.ItemID.AIR_RUNE; import static net.runelite.api.ItemID.BLOOD_RUNE; @@ -42,21 +44,22 @@ import static net.runelite.api.ItemID.SOUL_RUNE; import static net.runelite.api.ItemID.WATER_RUNE; import net.runelite.api.ObjectID; -public enum AbyssRifts +@AllArgsConstructor +enum AbyssRifts { - AIR_RIFT(ObjectID.AIR_RIFT, AIR_RUNE), - BLOOD_RIFT(ObjectID.BLOOD_RIFT, BLOOD_RUNE), - BODY_RIFT(ObjectID.BODY_RIFT, BODY_RUNE), - CHAOS_RIFT(ObjectID.CHAOS_RIFT, CHAOS_RUNE), - COSMIC_RIFT(ObjectID.COSMIC_RIFT, COSMIC_RUNE), - DEATH_RIFT(ObjectID.DEATH_RIFT, DEATH_RUNE), - EARTH_RIFT(ObjectID.EARTH_RIFT, EARTH_RUNE), - FIRE_RIFT(ObjectID.FIRE_RIFT, FIRE_RUNE), - LAW_RIFT(ObjectID.LAW_RIFT, LAW_RUNE), - MIND_RIFT(ObjectID.MIND_RIFT, MIND_RUNE), - NATURE_RIFT(ObjectID.NATURE_RIFT, NATURE_RUNE), - SOUL_RIFT(ObjectID.SOUL_RIFT, SOUL_RUNE), - WATER_RIFT(ObjectID.WATER_RIFT, WATER_RUNE); + AIR_RIFT(ObjectID.AIR_RIFT, AIR_RUNE, RunecraftConfig::showAir), + BLOOD_RIFT(ObjectID.BLOOD_RIFT, BLOOD_RUNE, RunecraftConfig::showBlood), + BODY_RIFT(ObjectID.BODY_RIFT, BODY_RUNE, RunecraftConfig::showBody), + CHAOS_RIFT(ObjectID.CHAOS_RIFT, CHAOS_RUNE, RunecraftConfig::showChaos), + COSMIC_RIFT(ObjectID.COSMIC_RIFT, COSMIC_RUNE, RunecraftConfig::showCosmic), + DEATH_RIFT(ObjectID.DEATH_RIFT, DEATH_RUNE, RunecraftConfig::showDeath), + EARTH_RIFT(ObjectID.EARTH_RIFT, EARTH_RUNE, RunecraftConfig::showEarth), + FIRE_RIFT(ObjectID.FIRE_RIFT, FIRE_RUNE, RunecraftConfig::showFire), + LAW_RIFT(ObjectID.LAW_RIFT, LAW_RUNE, RunecraftConfig::showLaw), + MIND_RIFT(ObjectID.MIND_RIFT, MIND_RUNE, RunecraftConfig::showMind), + NATURE_RIFT(ObjectID.NATURE_RIFT, NATURE_RUNE, RunecraftConfig::showNature), + SOUL_RIFT(ObjectID.SOUL_RIFT, SOUL_RUNE, RunecraftConfig::showSoul), + WATER_RIFT(ObjectID.WATER_RIFT, WATER_RUNE, RunecraftConfig::showWater); @Getter private final int objectId; @@ -64,6 +67,9 @@ public enum AbyssRifts @Getter private final int itemId; + @Getter + private final Predicate configEnabled; + private static final Map rifts; static @@ -78,13 +84,7 @@ public enum AbyssRifts rifts = builder.build(); } - AbyssRifts(int objectId, int itemId) - { - this.objectId = objectId; - this.itemId = itemId; - } - - public static AbyssRifts getRift(int id) + static AbyssRifts getRift(int id) { return rifts.get(id); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/RunecraftConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/RunecraftConfig.java index fee0ae86ff..cddc8da594 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/RunecraftConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/RunecraftConfig.java @@ -30,9 +30,11 @@ import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; import net.runelite.client.config.ConfigSection; -@ConfigGroup("runecraft") +@ConfigGroup(RunecraftConfig.GROUP) public interface RunecraftConfig extends Config { + String GROUP = "runecraft"; + @ConfigSection( name = "Rift Settings", description = "Abyss rift overlay settings", diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/RunecraftPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/RunecraftPlugin.java index 64d73b8a18..0aeb45da57 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/RunecraftPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/RunecraftPlugin.java @@ -26,6 +26,7 @@ package net.runelite.client.plugins.runecraft; import com.google.common.collect.ImmutableList; import com.google.inject.Provides; +import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -34,7 +35,6 @@ import javax.inject.Inject; import lombok.AccessLevel; import lombok.Getter; import net.runelite.api.ChatMessageType; -import net.runelite.api.Client; import net.runelite.api.DecorativeObject; import net.runelite.api.GameState; import net.runelite.api.InventoryID; @@ -55,19 +55,6 @@ import net.runelite.client.eventbus.Subscribe; import net.runelite.client.events.ConfigChanged; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; -import static net.runelite.client.plugins.runecraft.AbyssRifts.AIR_RIFT; -import static net.runelite.client.plugins.runecraft.AbyssRifts.BLOOD_RIFT; -import static net.runelite.client.plugins.runecraft.AbyssRifts.BODY_RIFT; -import static net.runelite.client.plugins.runecraft.AbyssRifts.CHAOS_RIFT; -import static net.runelite.client.plugins.runecraft.AbyssRifts.COSMIC_RIFT; -import static net.runelite.client.plugins.runecraft.AbyssRifts.DEATH_RIFT; -import static net.runelite.client.plugins.runecraft.AbyssRifts.EARTH_RIFT; -import static net.runelite.client.plugins.runecraft.AbyssRifts.FIRE_RIFT; -import static net.runelite.client.plugins.runecraft.AbyssRifts.LAW_RIFT; -import static net.runelite.client.plugins.runecraft.AbyssRifts.MIND_RIFT; -import static net.runelite.client.plugins.runecraft.AbyssRifts.NATURE_RIFT; -import static net.runelite.client.plugins.runecraft.AbyssRifts.SOUL_RIFT; -import static net.runelite.client.plugins.runecraft.AbyssRifts.WATER_RIFT; import net.runelite.client.ui.overlay.OverlayManager; @PluginDescriptor( @@ -97,9 +84,6 @@ public class RunecraftPlugin extends Plugin @Getter(AccessLevel.PACKAGE) private NPC darkMage; - @Inject - private Client client; - @Inject private OverlayManager overlayManager; @@ -142,7 +126,7 @@ public class RunecraftPlugin extends Plugin @Subscribe public void onConfigChanged(ConfigChanged event) { - if (event.getGroup().equals("runecraft")) + if (event.getGroup().equals(RunecraftConfig.GROUP)) { updateRifts(); } @@ -234,57 +218,8 @@ public class RunecraftPlugin extends Plugin private void updateRifts() { rifts.clear(); - if (config.showAir()) - { - rifts.add(AIR_RIFT); - } - if (config.showBlood()) - { - rifts.add(BLOOD_RIFT); - } - if (config.showBody()) - { - rifts.add(BODY_RIFT); - } - if (config.showChaos()) - { - rifts.add(CHAOS_RIFT); - } - if (config.showCosmic()) - { - rifts.add(COSMIC_RIFT); - } - if (config.showDeath()) - { - rifts.add(DEATH_RIFT); - } - if (config.showEarth()) - { - rifts.add(EARTH_RIFT); - } - if (config.showFire()) - { - rifts.add(FIRE_RIFT); - } - if (config.showLaw()) - { - rifts.add(LAW_RIFT); - } - if (config.showMind()) - { - rifts.add(MIND_RIFT); - } - if (config.showNature()) - { - rifts.add(NATURE_RIFT); - } - if (config.showSoul()) - { - rifts.add(SOUL_RIFT); - } - if (config.showWater()) - { - rifts.add(WATER_RIFT); - } + Arrays.stream(AbyssRifts.values()) + .filter(r -> r.getConfigEnabled().test(config)) + .forEach(rifts::add); } } From bf4173c45f52666f303f4b710b4a8ab67ad71c6b Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 14 Sep 2020 01:08:16 -0700 Subject: [PATCH 41/75] config: Add infobox component outline option Co-authored-by: Jordan Atwood --- .../net/runelite/client/config/RuneLiteConfig.java | 14 +++++++++++++- .../ui/overlay/components/InfoBoxComponent.java | 2 ++ .../client/ui/overlay/infobox/InfoBoxOverlay.java | 1 + 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/config/RuneLiteConfig.java b/runelite-client/src/main/java/net/runelite/client/config/RuneLiteConfig.java index 4b6a960cbf..d68ae06f02 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/RuneLiteConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/config/RuneLiteConfig.java @@ -355,11 +355,23 @@ public interface RuneLiteConfig extends Config return 35; } + @ConfigItem( + keyName = "infoBoxTextOutline", + name = "Outline infobox text", + description = "Draw a full outline instead of a simple shadow for infobox text", + position = 43, + section = overlaySettings + ) + default boolean infoBoxTextOutline() + { + return false; + } + @ConfigItem( keyName = "overlayBackgroundColor", name = "Overlay Color", description = "Configures the background color of infoboxes and overlays", - position = 43, + position = 44, section = overlaySettings ) @Alpha diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/InfoBoxComponent.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/InfoBoxComponent.java index cb02a50672..2c777ec2ed 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/InfoBoxComponent.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/InfoBoxComponent.java @@ -53,6 +53,7 @@ public class InfoBoxComponent implements LayoutableRenderableEntity private Dimension preferredSize = new Dimension(DEFAULT_SIZE, DEFAULT_SIZE); private String text; private Color color = Color.WHITE; + private boolean outline; private Color backgroundColor = ComponentConstants.STANDARD_BACKGROUND_COLOR; private BufferedImage image; @Getter @@ -94,6 +95,7 @@ public class InfoBoxComponent implements LayoutableRenderableEntity { final TextComponent textComponent = new TextComponent(); textComponent.setColor(color); + textComponent.setOutline(outline); textComponent.setText(text); textComponent.setPosition(new Point(baseX + ((size - metrics.stringWidth(text)) / 2), baseY + size - SEPARATOR)); textComponent.render(graphics); diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxOverlay.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxOverlay.java index f3abeb6627..9e85df2edd 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/infobox/InfoBoxOverlay.java @@ -138,6 +138,7 @@ public class InfoBoxOverlay extends OverlayPanel { infoBoxComponent.setColor(color); } + infoBoxComponent.setOutline(config.infoBoxTextOutline()); infoBoxComponent.setImage(box.getScaledImage()); infoBoxComponent.setTooltip(box.getTooltip()); infoBoxComponent.setPreferredSize(new Dimension(config.infoBoxSize(), config.infoBoxSize())); From e2399fe176b346d0a38e57d00aa7c51d65ff0cc5 Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 3 Oct 2020 10:39:11 -0400 Subject: [PATCH 42/75] key manager: move key event logging to trace level This is way too chatty and is debug logging eg. passwords on the login screen. Instead log when a key is consumed at debug level. --- .../main/java/net/runelite/client/input/KeyManager.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/input/KeyManager.java b/runelite-client/src/main/java/net/runelite/client/input/KeyManager.java index 37519aa9e0..1de44da134 100644 --- a/runelite-client/src/main/java/net/runelite/client/input/KeyManager.java +++ b/runelite-client/src/main/java/net/runelite/client/input/KeyManager.java @@ -80,11 +80,12 @@ public class KeyManager continue; } - log.debug("Processing key pressed {} for key listener {}", keyEvent.paramString(), keyListener); + log.trace("Processing key pressed {} for key listener {}", keyEvent.paramString(), keyListener); keyListener.keyPressed(keyEvent); if (keyEvent.isConsumed()) { + log.debug("Consuming key pressed {} for key listener {}", keyEvent.paramString(), keyListener); break; } } @@ -104,11 +105,12 @@ public class KeyManager continue; } - log.debug("Processing key released {} for key listener {}", keyEvent.paramString(), keyListener); + log.trace("Processing key released {} for key listener {}", keyEvent.paramString(), keyListener); keyListener.keyReleased(keyEvent); if (keyEvent.isConsumed()) { + log.debug("Consuming key released {} for listener {}", keyEvent.paramString(), keyListener); break; } } @@ -128,11 +130,12 @@ public class KeyManager continue; } - log.debug("Processing key typed {} for key listener {}", keyEvent.paramString(), keyListener); + log.trace("Processing key typed {} for key listener {}", keyEvent.paramString(), keyListener); keyListener.keyTyped(keyEvent); if (keyEvent.isConsumed()) { + log.debug("Consuming key typed {} for key listener {}", keyEvent.paramString(), keyListener); break; } } From dadee6e4b0d1ede06ba32531a01162f95f6f89fd Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 3 Oct 2020 00:53:38 -0400 Subject: [PATCH 43/75] item charges: fix resetting dodgy necklace, ring of forging, and amulet of chemistry Co-authored-by: SirGirion --- .../plugins/itemcharges/ItemChargePlugin.java | 58 ++++++++++++++----- 1 file changed, 43 insertions(+), 15 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargePlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargePlugin.java index 97ff94a705..651cc1185e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargePlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargePlugin.java @@ -33,6 +33,7 @@ import java.awt.image.BufferedImage; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.inject.Inject; +import lombok.extern.slf4j.Slf4j; import net.runelite.api.ChatMessageType; import net.runelite.api.Client; import net.runelite.api.EquipmentInventorySlot; @@ -42,15 +43,18 @@ import net.runelite.api.ItemContainer; import net.runelite.api.ItemID; import net.runelite.api.Varbits; import net.runelite.api.events.ChatMessage; -import net.runelite.client.events.ConfigChanged; import net.runelite.api.events.ItemContainerChanged; import net.runelite.api.events.ScriptCallbackEvent; import net.runelite.api.events.VarbitChanged; +import net.runelite.api.events.WidgetLoaded; import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetID; 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.events.ConfigChanged; import net.runelite.client.game.ItemManager; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; @@ -62,6 +66,7 @@ import net.runelite.client.ui.overlay.infobox.InfoBoxManager; description = "Show number of item charges remaining", tags = {"inventory", "notifications", "overlay"} ) +@Slf4j public class ItemChargePlugin extends Plugin { private static final Pattern DODGY_CHECK_PATTERN = Pattern.compile( @@ -77,7 +82,7 @@ public class ItemChargePlugin extends Plugin "You bind the temple's power into (mud|lava|steam|dust|smoke|mist) runes\\."); private static final String BINDING_BREAK_TEXT = "Your Binding necklace has disintegrated."; private static final Pattern RING_OF_FORGING_CHECK_PATTERN = Pattern.compile( - "You can smelt ([0-9+]+|one) more pieces? of iron ore before a ring melts\\."); + "You can smelt ([0-9]+|one) more pieces? of iron ore before a ring melts\\."); private static final String RING_OF_FORGING_USED_TEXT = "You retrieve a bar of iron."; private static final String RING_OF_FORGING_BREAK_TEXT = "Your Ring of Forging has melted."; private static final Pattern AMULET_OF_CHEMISTRY_CHECK_PATTERN = Pattern.compile( @@ -109,6 +114,9 @@ public class ItemChargePlugin extends Plugin @Inject private Client client; + @Inject + private ClientThread clientThread; + @Inject private OverlayManager overlayManager; @@ -418,6 +426,36 @@ public class ItemChargePlugin extends Plugin } } + @Subscribe + public void onWidgetLoaded(WidgetLoaded widgetLoaded) + { + if (widgetLoaded.getGroupId() == WidgetID.DIALOG_SPRITE_GROUP_ID) + { + clientThread.invokeLater(() -> + { + Widget sprite = client.getWidget(WidgetInfo.DIALOG_SPRITE_SPRITE); + if (sprite != null) + { + switch (sprite.getItemId()) + { + case ItemID.DODGY_NECKLACE: + log.debug("Reset dodgy necklace"); + updateDodgyNecklaceCharges(MAX_DODGY_CHARGES); + break; + case ItemID.RING_OF_FORGING: + log.debug("Reset ring of forging"); + updateRingOfForgingCharges(MAX_RING_OF_FORGING_CHARGES); + break; + case ItemID.AMULET_OF_CHEMISTRY: + log.debug("Reset amulet of chemistry"); + updateAmuletOfChemistryCharges(MAX_AMULET_OF_CHEMISTRY_CHARGES); + break; + } + } + }); + } + } + private void updateDodgyNecklaceCharges(final int value) { config.dodgyNecklace(value); @@ -536,20 +574,10 @@ public class ItemChargePlugin extends Plugin return; } - switch (widgetDestroyItemName.getText()) + if (widgetDestroyItemName.getText().equals("Binding necklace")) { - case "Binding necklace": - updateBindingNecklaceCharges(MAX_BINDING_CHARGES); - break; - case "Dodgy necklace": - updateDodgyNecklaceCharges(MAX_DODGY_CHARGES); - break; - case "Ring of forging": - updateRingOfForgingCharges(MAX_RING_OF_FORGING_CHARGES); - break; - case "Amulet of chemistry": - updateAmuletOfChemistryCharges(MAX_AMULET_OF_CHEMISTRY_CHARGES); - break; + log.debug("Reset binding necklace"); + updateBindingNecklaceCharges(MAX_BINDING_CHARGES); } } From d9876976d6b084f9f3e484966f8ff1eb69131c96 Mon Sep 17 00:00:00 2001 From: Jordan Atwood Date: Mon, 14 Sep 2020 18:45:35 -0700 Subject: [PATCH 44/75] util: Remove unused and unneeded methods ImageUtil had previously provided a number of image fill and outline methods which accepted Predicates to control the fill and outline behavior, but they are not used anywhere in the client or by external plugins. This commit removes these methods and the is(Not)?FullyTransparent ColorUtil methods which existed solely as Predicate defaults for them. --- .../net/runelite/client/util/ColorUtil.java | 10 ---- .../net/runelite/client/util/ImageUtil.java | 52 ++----------------- .../runelite/client/util/ColorUtilTest.java | 24 --------- .../runelite/client/util/ImageUtilTest.java | 34 ------------ 4 files changed, 3 insertions(+), 117 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/util/ColorUtil.java b/runelite-client/src/main/java/net/runelite/client/util/ColorUtil.java index 1933f8337b..c0a0102ee1 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/ColorUtil.java +++ b/runelite-client/src/main/java/net/runelite/client/util/ColorUtil.java @@ -131,16 +131,6 @@ public class ColorUtil return String.format("%08x", color.getRGB()); } - static boolean isFullyTransparent(final Color color) - { - return color.getAlpha() == 0; - } - - static boolean isNotFullyTransparent(final Color color) - { - return !isFullyTransparent(color); - } - /** * Determines if the passed hex string is an alpha hex color. * diff --git a/runelite-client/src/main/java/net/runelite/client/util/ImageUtil.java b/runelite-client/src/main/java/net/runelite/client/util/ImageUtil.java index 2d4246a041..b30a606345 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/ImageUtil.java +++ b/runelite-client/src/main/java/net/runelite/client/util/ImageUtil.java @@ -40,7 +40,6 @@ import java.util.Arrays; import java.util.List; import javax.imageio.ImageIO; import javax.swing.GrayFilter; -import java.util.function.Predicate; import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; import net.runelite.api.IndexedSprite; @@ -294,21 +293,7 @@ public class ImageUtil */ public static BufferedImage outlineImage(final BufferedImage image, final Color color) { - return outlineImage(image, color, ColorUtil::isNotFullyTransparent, false); - } - - /** - * Outlines pixels of a BufferedImage with the given color, using a given predicate to colorize - * the given image for outlining. - * - * @param image The image to be outlined. - * @param color The color to use for the outline. - * @param fillCondition The predicate to be consumed by {@link #fillImage(BufferedImage, Color, Predicate) fillImage(BufferedImage, Color, Predicate)} - * @return The BufferedImage with its edges outlined with the given color. - */ - public static BufferedImage outlineImage(final BufferedImage image, final Color color, final Predicate fillCondition) - { - return outlineImage(image, color, fillCondition, false); + return outlineImage(image, color, false); } /** @@ -323,23 +308,7 @@ public class ImageUtil */ public static BufferedImage outlineImage(final BufferedImage image, final Color color, final Boolean outlineCorners) { - return outlineImage(image, color, ColorUtil::isNotFullyTransparent, outlineCorners); - } - - /** - * Outlines pixels of a BufferedImage with the given color, using a given predicate to colorize - * the given image for outlining. Optionally outlines corners in addition to edges. - * - * @param image The image to be outlined. - * @param color The color to use for the outline. - * @param fillCondition The predicate to be consumed by {@link #fillImage(BufferedImage, Color, Predicate) fillImage(BufferedImage, Color, Predicate)} - * @param outlineCorners Whether to draw an outline around corners, or only around edges. - * @return The BufferedImage with its edges--and optionally, corners--outlined - * with the given color. - */ - public static BufferedImage outlineImage(final BufferedImage image, final Color color, final Predicate fillCondition, final Boolean outlineCorners) - { - final BufferedImage filledImage = fillImage(image, color, fillCondition); + final BufferedImage filledImage = fillImage(image, color); final BufferedImage outlinedImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB); final Graphics2D g2d = outlinedImage.createGraphics(); @@ -398,21 +367,6 @@ public class ImageUtil * @return The given image with all non-transparent pixels set to the given color. */ public static BufferedImage fillImage(final BufferedImage image, final Color color) - { - return fillImage(image, color, ColorUtil::isNotFullyTransparent); - } - - /** - * Fills pixels of the given image with the given color based on a given fill condition - * predicate. - * - * @param image The image which should have its non-transparent pixels filled. - * @param color The color with which to fill pixels. - * @param fillCondition The condition on which to fill pixels with the given color. - * @return The given image with all pixels fulfilling the fill condition predicate - * set to the given color. - */ - static BufferedImage fillImage(final BufferedImage image, final Color color, final Predicate fillCondition) { final BufferedImage filledImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB); for (int x = 0; x < filledImage.getWidth(); x++) @@ -420,7 +374,7 @@ public class ImageUtil for (int y = 0; y < filledImage.getHeight(); y++) { final Color pixelColor = new Color(image.getRGB(x, y), true); - if (!fillCondition.test(pixelColor)) + if (pixelColor.getAlpha() == 0) { continue; } diff --git a/runelite-client/src/test/java/net/runelite/client/util/ColorUtilTest.java b/runelite-client/src/test/java/net/runelite/client/util/ColorUtilTest.java index 0399a3634b..acbf6a44b1 100644 --- a/runelite-client/src/test/java/net/runelite/client/util/ColorUtilTest.java +++ b/runelite-client/src/test/java/net/runelite/client/util/ColorUtilTest.java @@ -28,8 +28,6 @@ import java.awt.Color; import java.util.HashMap; import java.util.Map; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; import org.junit.Test; public class ColorUtilTest @@ -103,26 +101,4 @@ public class ColorUtilTest assertEquals(hex, ColorUtil.colorToHexCode(color)); }); } - - @Test - public void isFullyTransparent() - { - for (Color color : COLOR_HEXSTRING_MAP.keySet()) - { - assertFalse(ColorUtil.isFullyTransparent(color)); - } - assertTrue(ColorUtil.isFullyTransparent(new Color(0, 0, 0, 0))); - assertFalse(ColorUtil.isFullyTransparent(new Color(0, 0, 0, 1))); - } - - @Test - public void isNotFullyTransparent() - { - for (Color color : COLOR_HEXSTRING_MAP.keySet()) - { - assertTrue(ColorUtil.isNotFullyTransparent(color)); - } - assertFalse(ColorUtil.isNotFullyTransparent(new Color(0, 0, 0, 0))); - assertTrue(ColorUtil.isNotFullyTransparent(new Color(0, 0, 0, 1))); - } } diff --git a/runelite-client/src/test/java/net/runelite/client/util/ImageUtilTest.java b/runelite-client/src/test/java/net/runelite/client/util/ImageUtilTest.java index 44dc88d778..3daf0482d2 100644 --- a/runelite-client/src/test/java/net/runelite/client/util/ImageUtilTest.java +++ b/runelite-client/src/test/java/net/runelite/client/util/ImageUtilTest.java @@ -35,7 +35,6 @@ import java.awt.image.BufferedImage; import java.awt.image.DataBuffer; import java.awt.image.DataBufferInt; import java.util.Arrays; -import java.util.function.Predicate; import javax.annotation.Nonnull; import org.apache.commons.lang3.ArrayUtils; import static org.junit.Assert.assertEquals; @@ -256,11 +255,6 @@ public class ImageUtilTest assertTrue(bufferedImagesEqual(centeredPixel(GRAY), ImageUtil.fillImage(centeredPixel(BLACK), GRAY))); assertTrue(bufferedImagesEqual(solidColor(3, 3, GREEN), ImageUtil.fillImage(solidColor(3, 3, BLACK), GREEN))); assertTrue(bufferedImagesEqual(oneByOne(BLACK_TRANSPARENT), ImageUtil.fillImage(oneByOne(BLACK_TRANSPARENT), WHITE))); - - // fillImage(BufferedImage image, Color color, Predicate fillCondition) - BufferedImage expected = solidColor(CORNER_SIZE, CORNER_SIZE, WHITE); - expected.setRGB(0, 0, new Color(0, true).getRGB()); - assertTrue(bufferedImagesEqual(expected, ImageUtil.fillImage(BLACK_PIXEL_TOP_LEFT, WHITE, ColorUtil::isFullyTransparent))); } @Test @@ -287,39 +281,11 @@ public class ImageUtilTest expected.setRGB(1, 1, new Color(0, true).getRGB()); assertTrue(bufferedImagesEqual(expected, ImageUtil.outlineImage(BLACK_PIXEL_TOP_LEFT, WHITE))); - // outlineImage(BufferedImage image, Color color, Predicate fillCondition) - BufferedImage test = new BufferedImage(CORNER_SIZE, CORNER_SIZE, BufferedImage.TYPE_INT_ARGB); - test.setRGB(0, 0, BLACK.getRGB()); - test.setRGB(1, 0, GRAY.getRGB()); - expected = test; - expected.setRGB(0, 1, BLUE.getRGB()); - assertTrue(bufferedImagesEqual(expected, ImageUtil.outlineImage(test, BLUE, (color -> color.equals(BLACK))))); - // outlineImage(BufferedImage image, Color color, Boolean outlineCorners) expected = solidColor(CORNER_SIZE, CORNER_SIZE, WHITE); expected.setRGB(0, 0, BLACK.getRGB()); assertTrue(bufferedImagesEqual(expected, ImageUtil.outlineImage(BLACK_PIXEL_TOP_LEFT, WHITE, true))); assertTrue(bufferedImagesEqual(solidColor(3, 3, BLACK), ImageUtil.outlineImage(centeredPixel(BLACK), BLACK, true))); - - // outlineImage(BufferedImage image, Color color, Predicate fillCondition, Boolean outlineCorners) - test = new BufferedImage(5, 5, BufferedImage.TYPE_INT_ARGB); - test.setRGB(2, 2, BLACK.getRGB()); - test.setRGB(1, 2, new Color(50, 50, 50).getRGB()); - test.setRGB(3, 2, new Color(100, 100, 100).getRGB()); - test.setRGB(2, 3, new Color(150, 150, 150).getRGB()); - expected = test; - expected.setRGB(2, 1, RED.getRGB()); - expected.setRGB(3, 1, RED.getRGB()); - expected.setRGB(4, 1, RED.getRGB()); - expected.setRGB(4, 2, RED.getRGB()); - expected.setRGB(1, 3, RED.getRGB()); - expected.setRGB(3, 3, RED.getRGB()); - expected.setRGB(4, 3, RED.getRGB()); - expected.setRGB(1, 4, RED.getRGB()); - expected.setRGB(2, 4, RED.getRGB()); - expected.setRGB(3, 4, RED.getRGB()); - Predicate testPredicate = (color -> ColorUtil.isNotFullyTransparent(color) && color.getRed() > 75 && color.getGreen() > 75 && color.getBlue() > 75); - assertTrue(bufferedImagesEqual(expected, ImageUtil.outlineImage(test, RED, testPredicate, true))); } /** From 9499c365ea019d593187bd1db12ead9952c38a52 Mon Sep 17 00:00:00 2001 From: Sean Patiag Date: Mon, 14 Sep 2020 20:33:08 -0700 Subject: [PATCH 45/75] chatfilter: Add option to filter game chat --- .../plugins/chatfilter/ChatFilterConfig.java | 11 +++++++++++ .../plugins/chatfilter/ChatFilterPlugin.java | 14 +++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) 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 b5d7cf3f77..2b9a45c2e9 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 @@ -121,6 +121,17 @@ public interface ChatFilterConfig extends Config return false; } + @ConfigItem( + keyName = "filterGameChat", + name = "Filter Game Chat", + description = "Filter your game chat messages", + position = 8 + ) + default boolean filterGameChat() + { + return false; + } + @ConfigItem( keyName = "collapseGameChat", name = "Collapse Game Chat", 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 1e413ea759..57be625ee4 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 @@ -179,6 +179,18 @@ public class ChatFilterPlugin extends Plugin blockMessage = message == null; } break; + case GAMEMESSAGE: + case ENGINE: + case ITEM_EXAMINE: + case NPC_EXAMINE: + case OBJECT_EXAMINE: + case SPAM: + if (config.filterGameChat()) + { + message = censorMessage(null, message); + blockMessage = message == null; + } + break; case LOGINLOGOUTNOTIFICATION: if (config.filterLogin()) { @@ -269,7 +281,7 @@ public class ChatFilterPlugin extends Plugin { String strippedMessage = jagexPrintableCharMatcher.retainFrom(message) .replace('\u00A0', ' '); - if (shouldFilterByName(username)) + if (username != null && shouldFilterByName(username)) { switch (config.filterType()) { From dabc933fa827431f73133695e7903cff2b19c84b Mon Sep 17 00:00:00 2001 From: Hexagon Date: Sun, 4 Oct 2020 22:44:46 -0300 Subject: [PATCH 46/75] cannonplugin: Check world when drawing overlay --- .../net/runelite/client/plugins/cannon/CannonOverlay.java | 2 +- .../net/runelite/client/plugins/cannon/CannonPlugin.java | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonOverlay.java index ebee009d3b..69e553da75 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonOverlay.java @@ -62,7 +62,7 @@ class CannonOverlay extends Overlay @Override public Dimension render(Graphics2D graphics) { - if (!plugin.isCannonPlaced() || plugin.getCannonPosition() == null) + if (!plugin.isCannonPlaced() || plugin.getCannonPosition() == null || plugin.getCannonWorld() != client.getWorld()) { return null; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonPlugin.java index 7492978edf..4d426ae9b6 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonPlugin.java @@ -86,6 +86,9 @@ public class CannonPlugin extends Plugin @Getter private WorldPoint cannonPosition; + @Getter + private int cannonWorld = -1; + @Getter private GameObject cannon; @@ -139,6 +142,7 @@ public class CannonPlugin extends Plugin overlayManager.remove(cannonOverlay); overlayManager.remove(cannonSpotOverlay); cannonPlaced = false; + cannonWorld = -1; cannonPosition = null; cannonBallNotificationSent = false; cballsLeft = 0; @@ -247,6 +251,7 @@ public class CannonPlugin extends Plugin && localPlayer.getAnimation() == AnimationID.BURYING_BONES) { cannonPosition = gameObject.getWorldLocation(); + cannonWorld = client.getWorld(); cannon = gameObject; } } @@ -257,7 +262,7 @@ public class CannonPlugin extends Plugin { Projectile projectile = event.getProjectile(); - if ((projectile.getId() == CANNONBALL || projectile.getId() == GRANITE_CANNONBALL) && cannonPosition != null) + if ((projectile.getId() == CANNONBALL || projectile.getId() == GRANITE_CANNONBALL) && cannonPosition != null && cannonWorld == client.getWorld()) { WorldPoint projectileLoc = WorldPoint.fromLocal(client, projectile.getX1(), projectile.getY1(), client.getPlane()); From 1ce047433682bf223f2284d8fdabfb44eb162e36 Mon Sep 17 00:00:00 2001 From: Jack Hodkinson Date: Mon, 5 Oct 2020 09:18:16 +0100 Subject: [PATCH 47/75] Sort plugin search results with closer test matches higher (#12432) --- .../client/plugins/config/PluginHubPanel.java | 26 ++-- .../client/plugins/config/PluginListItem.java | 11 +- .../plugins/config/PluginListPanel.java | 24 +--- .../client/plugins/config/PluginSearch.java | 91 ++++++++++++++ .../plugins/config/SearchablePlugin.java | 39 ++++++ .../java/net/runelite/client/util/Text.java | 2 +- .../plugins/config/PluginSearchTest.java | 113 ++++++++++++++++++ 7 files changed, 269 insertions(+), 37 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/config/PluginSearch.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/config/SearchablePlugin.java create mode 100644 runelite-client/src/test/java/net/runelite/client/plugins/config/PluginSearchTest.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginHubPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginHubPanel.java index e17482f4ce..aa5dbd5074 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginHubPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginHubPanel.java @@ -87,7 +87,6 @@ import net.runelite.client.ui.components.IconTextField; import net.runelite.client.util.ImageUtil; import net.runelite.client.util.LinkBrowser; import net.runelite.client.util.SwingUtil; -import net.runelite.client.util.Text; import net.runelite.client.util.VerificationException; @Slf4j @@ -115,13 +114,15 @@ class PluginHubPanel extends PluginPanel CONFIGURE_ICON_HOVER = new ImageIcon(ImageUtil.alphaOffset(configureIcon, -100)); } - private class PluginItem extends JPanel + private class PluginItem extends JPanel implements SearchablePlugin { private static final int HEIGHT = 70; private static final int ICON_WIDTH = 48; private static final int BOTTOM_LINE_HEIGHT = 16; private final ExternalPluginManifest manifest; + + @Getter private final List keywords = new ArrayList<>(); @Getter @@ -333,6 +334,12 @@ class PluginHubPanel extends PluginPanel .addComponent(addrm, BOTTOM_LINE_HEIGHT, BOTTOM_LINE_HEIGHT, BOTTOM_LINE_HEIGHT)) .addGap(5))); } + + @Override + public String getSearchableName() + { + return manifest.getDisplayName(); + } } private final PluginListPanel pluginListPanel; @@ -547,22 +554,19 @@ class PluginHubPanel extends PluginPanel Stream stream = plugins.stream(); - String search = searchBar.getText(); - boolean isSearching = search != null && !search.trim().isEmpty(); + String query = searchBar.getText(); + boolean isSearching = query != null && !query.trim().isEmpty(); if (isSearching) { - String[] searchArray = SPACES.split(search.toLowerCase()); - stream = stream - .filter(p -> Text.matchesSearchTerms(searchArray, p.keywords)) - .sorted(Comparator.comparing(p -> p.manifest.getDisplayName())); + PluginSearch.search(plugins, query).forEach(mainPanel::add); } else { - stream = stream - .sorted(Comparator.comparing(PluginItem::isInstalled).thenComparing(p -> p.manifest.getDisplayName())); + stream + .sorted(Comparator.comparing(PluginItem::isInstalled).thenComparing(p -> p.manifest.getDisplayName())) + .forEach(mainPanel::add); } - stream.forEach(mainPanel::add); mainPanel.revalidate(); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java index f6c36315a9..c71bcc8788 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java @@ -53,7 +53,7 @@ import net.runelite.client.ui.PluginPanel; import net.runelite.client.util.ImageUtil; import net.runelite.client.util.SwingUtil; -class PluginListItem extends JPanel +class PluginListItem extends JPanel implements SearchablePlugin { private static final ImageIcon CONFIG_ICON; private static final ImageIcon CONFIG_ICON_HOVER; @@ -188,7 +188,14 @@ class PluginListItem extends JPanel } } - boolean isPinned() + @Override + public String getSearchableName() + { + return pluginConfig.getName(); + } + + @Override + public boolean isPinned() { return pinButton.isSelected(); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListPanel.java index feff6d644f..0130688bc8 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListPanel.java @@ -263,33 +263,11 @@ class PluginListPanel extends PluginPanel private void onSearchBarChanged() { final String text = searchBar.getText(); - pluginList.forEach(mainPanel::remove); - - showMatchingPlugins(true, text); - showMatchingPlugins(false, text); - + PluginSearch.search(pluginList, text).forEach(mainPanel::add); revalidate(); } - private void showMatchingPlugins(boolean pinned, String text) - { - if (text.isEmpty()) - { - pluginList.stream().filter(item -> pinned == item.isPinned()).forEach(mainPanel::add); - return; - } - - final String[] searchTerms = text.toLowerCase().split(" "); - pluginList.forEach(listItem -> - { - if (pinned == listItem.isPinned() && Text.matchesSearchTerms(searchTerms, listItem.getKeywords())) - { - mainPanel.add(listItem); - } - }); - } - void openConfigurationPanel(String configGroup) { for (PluginListItem pluginListItem : pluginList) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginSearch.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginSearch.java new file mode 100644 index 0000000000..3cd3dad6cc --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginSearch.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2020, Jack Hodkinson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.config; + +import com.google.common.base.Splitter; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; +import net.runelite.client.util.Text; +import org.apache.commons.lang3.StringUtils; + +public class PluginSearch +{ + private static final Splitter SPLITTER = Splitter.on(" ").trimResults().omitEmptyStrings(); + + public static List search(Collection searchablePlugins, String query) + { + return searchablePlugins.stream() + .filter(plugin -> Text.matchesSearchTerms(SPLITTER.split(query.toLowerCase()), plugin.getKeywords())) + .sorted(comparator(query)) + .collect(Collectors.toList()); + } + + private static Comparator comparator(String query) + { + if (StringUtils.isBlank(query)) + { + return Comparator.nullsLast(Comparator.comparing(SearchablePlugin::isPinned, Comparator.nullsLast(Comparator.reverseOrder()))) + .thenComparing(SearchablePlugin::getSearchableName, Comparator.nullsLast(Comparator.naturalOrder())); + } + Iterable queryPieces = SPLITTER.split(query.toLowerCase()); + return Comparator.nullsLast(Comparator.comparing((SearchablePlugin sp) -> query.equalsIgnoreCase(sp.getSearchableName()), Comparator.reverseOrder())) + .thenComparing(sp -> + { + if (sp.getSearchableName() == null) + { + return 0L; + } + return stream(SPLITTER.split(sp.getSearchableName())) + .filter(piece -> stream(queryPieces).anyMatch(qp -> containsOrIsContainedBy(piece.toLowerCase(), qp))) + .count(); + }, Comparator.reverseOrder()) + .thenComparing(sp -> + { + if (sp.getKeywords() == null) + { + return 0L; + } + return stream(sp.getKeywords()) + .filter(piece -> stream(queryPieces).anyMatch(qp -> containsOrIsContainedBy(piece.toLowerCase(), qp))) + .count(); + }, Comparator.reverseOrder()) + .thenComparing(SearchablePlugin::isPinned, Comparator.nullsLast(Comparator.reverseOrder())) + .thenComparing(SearchablePlugin::getSearchableName, Comparator.nullsLast(Comparator.naturalOrder())); + } + + private static Stream stream(Iterable iterable) + { + return StreamSupport.stream(iterable.spliterator(), false); + } + + private static boolean containsOrIsContainedBy(String a, String b) + { + return a.contains(b) || b.contains(a); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/SearchablePlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/SearchablePlugin.java new file mode 100644 index 0000000000..c8656787fa --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/SearchablePlugin.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2020, Jack Hodkinson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.config; + +import java.util.List; + +public interface SearchablePlugin +{ + String getSearchableName(); + + List getKeywords(); + + default boolean isPinned() + { + return false; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/util/Text.java b/runelite-client/src/main/java/net/runelite/client/util/Text.java index b98b8dbe8e..3050554a71 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/Text.java +++ b/runelite-client/src/main/java/net/runelite/client/util/Text.java @@ -221,7 +221,7 @@ public class Text * * @return true if all search terms matches at least one keyword, or false if otherwise. */ - public static boolean matchesSearchTerms(String[] searchTerms, final Collection keywords) + public static boolean matchesSearchTerms(Iterable searchTerms, final Collection keywords) { for (String term : searchTerms) { diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/config/PluginSearchTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/config/PluginSearchTest.java new file mode 100644 index 0000000000..798bd7e672 --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/config/PluginSearchTest.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2020, Jack Hodkinson + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.config; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.Before; +import org.junit.Test; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; + +public class PluginSearchTest +{ + private Map plugins; + + @Before + public void setUp() + { + plugins = new HashMap<>(); + plugins.put("Discord", new TestSearchablePlugin("Discord", false, "action", "activity", "external", "integration", "status")); + plugins.put("Emojis", new TestSearchablePlugin("Emojis", true, "replaces", "common", "emoticons")); + plugins.put("Grand Exchange", new TestSearchablePlugin("Grand Exchange", true, "external", "integration", "notifications", "panel", "prices", "trade")); + plugins.put("Status Bars", new TestSearchablePlugin("Status Bars", false, "Draws", "status", "bars")); + } + + @Test + public void emptyQueryReturnsPluginsInAlphabeticalOrderWithPinnedItemsFirst() + { + List results = PluginSearch.search(plugins.values(), " "); + assertThat(results, containsInAnyOrder(plugins.values().toArray(new SearchablePlugin[] {}))); + } + + @Test + public void searchReturnsMatchingPlugins() + { + List results = PluginSearch.search(plugins.values(), "sTATus"); + assertThat(results, hasSize(2)); + assertThat(results, containsInAnyOrder(plugins.get("Discord"), plugins.get("Status Bars"))); + } + + @Test + public void searchOrdersItemsWithMatchesInTitleFirst() + { + List results = PluginSearch.search(plugins.values(), "STATUS"); + assertThat(results.get(0), equalTo(plugins.get("Status Bars"))); + } + + @Test + public void searchOrdersPinnedItemsFirstIfThereAreNoExactMatches() + { + List results = PluginSearch.search(plugins.values(), "integrat"); + assertThat(results, contains(plugins.get("Grand Exchange"), plugins.get("Discord"))); + } + + private static class TestSearchablePlugin implements SearchablePlugin + { + private final String name; + private final boolean pinned; + private final List keywords; + + public TestSearchablePlugin(String name, boolean pinned, String... keywords) + { + this.name = name; + this.pinned = pinned; + this.keywords = Arrays.asList(keywords); + } + + @Override + public String getSearchableName() + { + return name; + } + + @Override + public boolean isPinned() + { + return pinned; + } + + @Override + public List getKeywords() + { + return keywords; + } + } +} From 2f91100f170efbbfbdc9262293b8e78280ffe392 Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 4 Oct 2020 23:13:27 -0400 Subject: [PATCH 48/75] examine plugin: format quantity of large item stacks --- .../client/plugins/examine/ExaminePlugin.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/examine/ExaminePlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/examine/ExaminePlugin.java index b2a6db3028..976082dcd7 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/examine/ExaminePlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/examine/ExaminePlugin.java @@ -128,6 +128,21 @@ public class ExaminePlugin extends Plugin Widget widget = client.getWidget(widgetGroup, widgetChild); WidgetItem widgetItem = widget.getWidgetItem(event.getActionParam()); quantity = widgetItem != null && widgetItem.getId() >= 0 ? widgetItem.getQuantity() : 1; + + // Examine on inventory items with more than 100000 quantity is handled locally and shows the item stack + // count, instead of sending the examine packet, so that you can see how many items are in the stack. + // Replace that message with one that formats the quantity using the quantity formatter instead. + if (quantity >= 100_000) + { + int itemId = event.getId(); + final ChatMessageBuilder message = new ChatMessageBuilder() + .append(QuantityFormatter.formatNumber(quantity)).append(" x ").append(itemManager.getItemComposition(itemId).getName()); + chatMessageManager.queue(QueuedMessage.builder() + .type(ChatMessageType.ITEM_EXAMINE) + .runeLiteFormattedMessage(message.build()) + .build()); + event.consume(); + } break; } case EXAMINE_ITEM_GROUND: From 6946140d556760bcabf199aa2e999c5837268bc4 Mon Sep 17 00:00:00 2001 From: Jacob Mischka Date: Sat, 3 Oct 2020 19:41:53 -0500 Subject: [PATCH 49/75] Fix screenshot with hidpi scaling with GPU plugin Fixes #12463 --- .../java/net/runelite/client/plugins/gpu/GpuPlugin.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java index 60cb6b2f8b..80fb90742f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java @@ -1303,6 +1303,13 @@ public class GpuPlugin extends Plugin implements DrawCallbacks height = dim.height; } + if (OSType.getOSType() != OSType.MacOS) + { + final AffineTransform t = ((Graphics2D) canvas.getGraphics()).getTransform(); + width = getScaledValue(t.getScaleX(), width); + height = getScaledValue(t.getScaleY(), height); + } + ByteBuffer buffer = ByteBuffer.allocateDirect(width * height * 4) .order(ByteOrder.nativeOrder()); From c0a57a62e8f6939f3fbdb50b312626eaabb9b87d Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 5 Oct 2020 18:26:24 -0400 Subject: [PATCH 50/75] gpu: dispose of graphics objects --- .../java/net/runelite/client/plugins/gpu/GpuPlugin.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java index 80fb90742f..18a2d5acd6 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java @@ -1305,9 +1305,11 @@ public class GpuPlugin extends Plugin implements DrawCallbacks if (OSType.getOSType() != OSType.MacOS) { - final AffineTransform t = ((Graphics2D) canvas.getGraphics()).getTransform(); + final Graphics2D graphics = (Graphics2D) canvas.getGraphics(); + final AffineTransform t = graphics.getTransform(); width = getScaledValue(t.getScaleX(), width); height = getScaledValue(t.getScaleY(), height); + graphics.dispose(); } ByteBuffer buffer = ByteBuffer.allocateDirect(width * height * 4) @@ -1595,12 +1597,14 @@ public class GpuPlugin extends Plugin implements DrawCallbacks } else { - final AffineTransform t = ((Graphics2D) canvas.getGraphics()).getTransform(); + final Graphics2D graphics = (Graphics2D) canvas.getGraphics(); + final AffineTransform t = graphics.getTransform(); gl.glViewport( getScaledValue(t.getScaleX(), x), getScaledValue(t.getScaleY(), y), getScaledValue(t.getScaleX(), width), getScaledValue(t.getScaleY(), height)); + graphics.dispose(); } } From dc414b1adf761dce5b302fe0a4c582e814a3c198 Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 6 Oct 2020 14:57:23 -0400 Subject: [PATCH 51/75] ground markers: add tile labels --- .../groundmarkers/ColorTileMarker.java | 4 + .../groundmarkers/GroundMarkerOverlay.java | 20 +++- .../groundmarkers/GroundMarkerPlugin.java | 111 +++++++++++++----- .../groundmarkers/GroundMarkerPoint.java | 6 +- 4 files changed, 106 insertions(+), 35 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/ColorTileMarker.java b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/ColorTileMarker.java index 6a6a30a065..1b5891d138 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/ColorTileMarker.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/ColorTileMarker.java @@ -25,6 +25,7 @@ package net.runelite.client.plugins.groundmarkers; import java.awt.Color; +import javax.annotation.Nullable; import lombok.Value; import net.runelite.api.coords.WorldPoint; @@ -36,5 +37,8 @@ import net.runelite.api.coords.WorldPoint; class ColorTileMarker { private WorldPoint worldPoint; + @Nullable private Color color; + @Nullable + private String label; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerOverlay.java index 768d90b51b..31d3e1bf08 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerOverlay.java @@ -25,14 +25,17 @@ */ package net.runelite.client.plugins.groundmarkers; +import com.google.common.base.Strings; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.Polygon; import java.util.Collection; +import javax.annotation.Nullable; import javax.inject.Inject; import net.runelite.api.Client; import net.runelite.api.Perspective; +import net.runelite.api.Point; import net.runelite.api.coords.LocalPoint; import net.runelite.api.coords.WorldPoint; import net.runelite.client.ui.overlay.Overlay; @@ -79,13 +82,13 @@ public class GroundMarkerOverlay extends Overlay tileColor = config.markerColor(); } - drawTile(graphics, worldPoint, tileColor); + drawTile(graphics, worldPoint, tileColor, point.getLabel()); } return null; } - private void drawTile(Graphics2D graphics, WorldPoint point, Color color) + private void drawTile(Graphics2D graphics, WorldPoint point, Color color, @Nullable String label) { WorldPoint playerLocation = client.getLocalPlayer().getWorldLocation(); @@ -101,11 +104,18 @@ public class GroundMarkerOverlay extends Overlay } Polygon poly = Perspective.getCanvasTilePoly(client, lp); - if (poly == null) + if (poly != null) { - return; + OverlayUtil.renderPolygon(graphics, poly, color); } - OverlayUtil.renderPolygon(graphics, poly, color); + if (!Strings.isNullOrEmpty(label)) + { + Point canvasTextLocation = Perspective.getCanvasTextLocation(client, graphics, lp, label, 0); + if (canvasTextLocation != null) + { + OverlayUtil.renderTextLocation(graphics, canvasTextLocation, label, color); + } + } } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerPlugin.java index e0f37dbb4a..f4b17c6358 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerPlugin.java @@ -52,6 +52,7 @@ import net.runelite.api.events.MenuEntryAdded; import net.runelite.api.events.MenuOptionClicked; import net.runelite.client.config.ConfigManager; import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.game.chatbox.ChatboxPanelManager; import net.runelite.client.input.KeyManager; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; @@ -68,6 +69,7 @@ public class GroundMarkerPlugin extends Plugin private static final String CONFIG_GROUP = "groundMarker"; private static final String MARK = "Mark tile"; private static final String UNMARK = "Unmark tile"; + private static final String LABEL = "Label tile"; private static final String WALK_HERE = "Walk here"; private static final String REGION_PREFIX = "region_"; @@ -97,6 +99,9 @@ public class GroundMarkerPlugin extends Plugin @Inject private KeyManager keyManager; + @Inject + private ChatboxPanelManager chatboxPanelManager; + private void savePoints(int regionId, Collection points) { if (points == null || points.isEmpty()) @@ -166,15 +171,31 @@ public class GroundMarkerPlugin extends Plugin return points.stream() .map(point -> new ColorTileMarker( WorldPoint.fromRegion(point.getRegionId(), point.getRegionX(), point.getRegionY(), point.getZ()), - point.getColor())) + point.getColor(), point.getLabel())) .flatMap(colorTile -> { final Collection localWorldPoints = WorldPoint.toLocalInstance(client, colorTile.getWorldPoint()); - return localWorldPoints.stream().map(wp -> new ColorTileMarker(wp, colorTile.getColor())); + return localWorldPoints.stream().map(wp -> new ColorTileMarker(wp, colorTile.getColor(), colorTile.getLabel())); }) .collect(Collectors.toList()); } + @Override + public void startUp() + { + overlayManager.add(overlay); + overlayManager.add(minimapOverlay); + loadPoints(); + } + + @Override + public void shutDown() + { + overlayManager.remove(overlay); + overlayManager.remove(minimapOverlay); + points.clear(); + } + @Subscribe public void onGameStateChanged(GameStateChanged gameStateChanged) { @@ -200,17 +221,26 @@ public class GroundMarkerPlugin extends Plugin return; } - MenuEntry[] menuEntries = client.getMenuEntries(); - menuEntries = Arrays.copyOf(menuEntries, menuEntries.length + 1); - MenuEntry menuEntry = menuEntries[menuEntries.length - 1] = new MenuEntry(); - final WorldPoint worldPoint = WorldPoint.fromLocalInstance(client, selectedSceneTile.getLocalLocation()); final int regionId = worldPoint.getRegionID(); - final GroundMarkerPoint point = new GroundMarkerPoint(regionId, worldPoint.getRegionX(), worldPoint.getRegionY(), client.getPlane(), config.markerColor()); + final GroundMarkerPoint point = new GroundMarkerPoint(regionId, worldPoint.getRegionX(), worldPoint.getRegionY(), client.getPlane(), null, null); + final boolean exists = getPoints(regionId).contains(point); - menuEntry.setOption(getPoints(regionId).contains(point) ? UNMARK : MARK); - menuEntry.setTarget(event.getTarget()); - menuEntry.setType(MenuAction.RUNELITE.getId()); + MenuEntry[] menuEntries = client.getMenuEntries(); + menuEntries = Arrays.copyOf(menuEntries, menuEntries.length + (exists ? 2 : 1)); + + MenuEntry mark = menuEntries[menuEntries.length - 1] = new MenuEntry(); + mark.setOption(exists ? UNMARK : MARK); + mark.setTarget(event.getTarget()); + mark.setType(MenuAction.RUNELITE.getId()); + + if (exists) + { + MenuEntry label = menuEntries[menuEntries.length - 2] = new MenuEntry(); + label.setOption(LABEL); + label.setTarget(event.getTarget()); + label.setType(MenuAction.RUNELITE.getId()); + } client.setMenuEntries(menuEntries); } @@ -219,8 +249,7 @@ public class GroundMarkerPlugin extends Plugin @Subscribe public void onMenuOptionClicked(MenuOptionClicked event) { - if (event.getMenuAction().getId() != MenuAction.RUNELITE.getId() || - !(event.getMenuOption().equals(MARK) || event.getMenuOption().equals(UNMARK))) + if (event.getMenuAction().getId() != MenuAction.RUNELITE.getId()) { return; } @@ -230,23 +259,16 @@ public class GroundMarkerPlugin extends Plugin { return; } - markTile(target.getLocalLocation()); - } - @Override - protected void startUp() - { - overlayManager.add(overlay); - overlayManager.add(minimapOverlay); - loadPoints(); - } - - @Override - protected void shutDown() - { - overlayManager.remove(overlay); - overlayManager.remove(minimapOverlay); - points.clear(); + final String option = event.getMenuOption(); + if (option.equals(MARK) || option.equals(UNMARK)) + { + markTile(target.getLocalLocation()); + } + else if (option.equals(LABEL)) + { + labelTile(target); + } } private void markTile(LocalPoint localPoint) @@ -259,7 +281,7 @@ public class GroundMarkerPlugin extends Plugin WorldPoint worldPoint = WorldPoint.fromLocalInstance(client, localPoint); int regionId = worldPoint.getRegionID(); - GroundMarkerPoint point = new GroundMarkerPoint(regionId, worldPoint.getRegionX(), worldPoint.getRegionY(), client.getPlane(), config.markerColor()); + GroundMarkerPoint point = new GroundMarkerPoint(regionId, worldPoint.getRegionX(), worldPoint.getRegionY(), client.getPlane(), config.markerColor(), null); log.debug("Updating point: {} - {}", point, worldPoint); List groundMarkerPoints = new ArrayList<>(getPoints(regionId)); @@ -276,4 +298,35 @@ public class GroundMarkerPlugin extends Plugin loadPoints(); } + + private void labelTile(Tile tile) + { + LocalPoint localPoint = tile.getLocalLocation(); + WorldPoint worldPoint = WorldPoint.fromLocalInstance(client, localPoint); + final int regionId = worldPoint.getRegionID(); + + chatboxPanelManager.openTextInput("Tile label") + .onDone((input) -> + { + input = Strings.emptyToNull(input); + + GroundMarkerPoint searchPoint = new GroundMarkerPoint(regionId, worldPoint.getRegionX(), worldPoint.getRegionY(), client.getPlane(), null, null); + Collection points = getPoints(regionId); + GroundMarkerPoint existing = points.stream() + .filter(p -> p.equals(searchPoint)) + .findFirst().orElse(null); + if (existing == null) + { + return; + } + + GroundMarkerPoint newPoint = new GroundMarkerPoint(regionId, worldPoint.getRegionX(), worldPoint.getRegionY(), client.getPlane(), existing.getColor(), input); + points.remove(searchPoint); + points.add(newPoint); + savePoints(regionId, points); + + loadPoints(); + }) + .build(); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerPoint.java b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerPoint.java index 3e10a654c0..29d7d6f92c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerPoint.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerPoint.java @@ -26,6 +26,7 @@ package net.runelite.client.plugins.groundmarkers; import java.awt.Color; +import javax.annotation.Nullable; import lombok.EqualsAndHashCode; import lombok.Value; @@ -33,12 +34,15 @@ import lombok.Value; * Used for serialization of ground marker points. */ @Value -@EqualsAndHashCode(exclude = { "color" }) +@EqualsAndHashCode(exclude = { "color", "label" }) class GroundMarkerPoint { private int regionId; private int regionX; private int regionY; private int z; + @Nullable private Color color; + @Nullable + private String label; } From 4dfb561632e5cd880c7476a478f22eeefa269a4b Mon Sep 17 00:00:00 2001 From: Max Weber Date: Mon, 5 Oct 2020 21:32:49 -0600 Subject: [PATCH 52/75] timetracking: correctly bound Catherby patches --- .../timetracking/farming/FarmingTracker.java | 8 +++-- .../timetracking/farming/FarmingWorld.java | 29 ++++++++++++++++--- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingTracker.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingTracker.java index c45a9309d7..16ae00c136 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingTracker.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingTracker.java @@ -92,9 +92,13 @@ public class FarmingTracker } } - FarmingRegion region = farmingWorld.getRegions().get(location.getRegionID()); - if (region != null && region.isInBounds(location)) + for (FarmingRegion region : farmingWorld.getRegions().get(location.getRegionID())) { + if (!region.isInBounds(location)) + { + continue; + } + // Write config with new varbits // timetracking...=: String group = TimeTrackingConfig.CONFIG_GROUP + "." + client.getUsername() + "." + region.getRegionID(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingWorld.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingWorld.java index 51a08292fb..6ba40c76b1 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingWorld.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingWorld.java @@ -25,6 +25,9 @@ */ package net.runelite.client.plugins.timetracking.farming; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; +import com.google.common.collect.Multimaps; import com.google.inject.Singleton; import java.util.Collections; import java.util.Comparator; @@ -42,7 +45,7 @@ import net.runelite.client.plugins.timetracking.Tab; class FarmingWorld { @Getter - private Map regions = new HashMap<>(); + private Multimap regions = HashMultimap.create(); @Getter private Map> tabs = new HashMap<>(); @@ -83,10 +86,28 @@ class FarmingWorld new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER), new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB) - )); + ) + { + @Override + public boolean isInBounds(WorldPoint loc) + { + if (loc.getY() < 3456) + { + return loc.getX() <= 2840 && loc.getY() > 3440; + } + return true; + } + }, 11061, 11318, 11317); add(new FarmingRegion("Catherby", 11317, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE) - )); + ) + { + @Override + public boolean isInBounds(WorldPoint loc) + { + return loc.getX() > 2840 || loc.getY() < 3440; + } + }); add(new FarmingRegion("Champions' Guild", 12596, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.BUSH) @@ -255,7 +276,7 @@ class FarmingWorld )); // Finalize - this.regions = Collections.unmodifiableMap(regions); + this.regions = Multimaps.unmodifiableMultimap(regions); Map> umtabs = new TreeMap<>(); for (Map.Entry> e : tabs.entrySet()) { From 77ca99f9e050b0d8171b71d2debea089691e9a59 Mon Sep 17 00:00:00 2001 From: Runelite auto updater Date: Wed, 7 Oct 2020 14:17:31 +0000 Subject: [PATCH 53/75] Release 1.6.28 --- cache-client/pom.xml | 2 +- cache-updater/pom.xml | 2 +- cache/pom.xml | 2 +- http-api/pom.xml | 2 +- http-service/pom.xml | 2 +- pom.xml | 4 ++-- runelite-api/pom.xml | 2 +- runelite-client/pom.xml | 2 +- runelite-script-assembler-plugin/pom.xml | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cache-client/pom.xml b/cache-client/pom.xml index 2a92de9feb..6f5cb6dfc9 100644 --- a/cache-client/pom.xml +++ b/cache-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.28-SNAPSHOT + 1.6.28 cache-client diff --git a/cache-updater/pom.xml b/cache-updater/pom.xml index 2946721062..ecad225673 100644 --- a/cache-updater/pom.xml +++ b/cache-updater/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.28-SNAPSHOT + 1.6.28 Cache Updater diff --git a/cache/pom.xml b/cache/pom.xml index 5e45843ec7..b04b30b54c 100644 --- a/cache/pom.xml +++ b/cache/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.28-SNAPSHOT + 1.6.28 cache diff --git a/http-api/pom.xml b/http-api/pom.xml index 1348e6a3c3..64f4cfb401 100644 --- a/http-api/pom.xml +++ b/http-api/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.28-SNAPSHOT + 1.6.28 Web API diff --git a/http-service/pom.xml b/http-service/pom.xml index 19864d0b8d..5bde4644df 100644 --- a/http-service/pom.xml +++ b/http-service/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.28-SNAPSHOT + 1.6.28 Web Service diff --git a/pom.xml b/pom.xml index 93716c2025..77e792d015 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.28-SNAPSHOT + 1.6.28 pom RuneLite @@ -61,7 +61,7 @@ https://github.com/runelite/runelite scm:git:git://github.com/runelite/runelite scm:git:git@github.com:runelite/runelite - HEAD + runelite-parent-1.6.28 diff --git a/runelite-api/pom.xml b/runelite-api/pom.xml index b5183face6..bda8613bd1 100644 --- a/runelite-api/pom.xml +++ b/runelite-api/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.28-SNAPSHOT + 1.6.28 runelite-api diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml index e2904e2be8..c8395ee448 100644 --- a/runelite-client/pom.xml +++ b/runelite-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.28-SNAPSHOT + 1.6.28 client diff --git a/runelite-script-assembler-plugin/pom.xml b/runelite-script-assembler-plugin/pom.xml index 2f5ab0178c..5d8d895714 100644 --- a/runelite-script-assembler-plugin/pom.xml +++ b/runelite-script-assembler-plugin/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.28-SNAPSHOT + 1.6.28 script-assembler-plugin From d19405129a4dc6a2a2debabf74fb2df57732fbf2 Mon Sep 17 00:00:00 2001 From: Runelite auto updater Date: Wed, 7 Oct 2020 14:17:41 +0000 Subject: [PATCH 54/75] Bump for 1.6.29-SNAPSHOT --- cache-client/pom.xml | 2 +- cache-updater/pom.xml | 2 +- cache/pom.xml | 2 +- http-api/pom.xml | 2 +- http-service/pom.xml | 2 +- pom.xml | 4 ++-- runelite-api/pom.xml | 2 +- runelite-client/pom.xml | 2 +- runelite-script-assembler-plugin/pom.xml | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cache-client/pom.xml b/cache-client/pom.xml index 6f5cb6dfc9..b28ebd7468 100644 --- a/cache-client/pom.xml +++ b/cache-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.28 + 1.6.29-SNAPSHOT cache-client diff --git a/cache-updater/pom.xml b/cache-updater/pom.xml index ecad225673..be7a38ed8f 100644 --- a/cache-updater/pom.xml +++ b/cache-updater/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.28 + 1.6.29-SNAPSHOT Cache Updater diff --git a/cache/pom.xml b/cache/pom.xml index b04b30b54c..6ff9dce234 100644 --- a/cache/pom.xml +++ b/cache/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.28 + 1.6.29-SNAPSHOT cache diff --git a/http-api/pom.xml b/http-api/pom.xml index 64f4cfb401..7ab9b099f0 100644 --- a/http-api/pom.xml +++ b/http-api/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.28 + 1.6.29-SNAPSHOT Web API diff --git a/http-service/pom.xml b/http-service/pom.xml index 5bde4644df..f4f411a0d4 100644 --- a/http-service/pom.xml +++ b/http-service/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.28 + 1.6.29-SNAPSHOT Web Service diff --git a/pom.xml b/pom.xml index 77e792d015..7379cfe1f0 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.28 + 1.6.29-SNAPSHOT pom RuneLite @@ -61,7 +61,7 @@ https://github.com/runelite/runelite scm:git:git://github.com/runelite/runelite scm:git:git@github.com:runelite/runelite - runelite-parent-1.6.28 + HEAD diff --git a/runelite-api/pom.xml b/runelite-api/pom.xml index bda8613bd1..05c657f102 100644 --- a/runelite-api/pom.xml +++ b/runelite-api/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.28 + 1.6.29-SNAPSHOT runelite-api diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml index c8395ee448..72c6d34166 100644 --- a/runelite-client/pom.xml +++ b/runelite-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.28 + 1.6.29-SNAPSHOT client diff --git a/runelite-script-assembler-plugin/pom.xml b/runelite-script-assembler-plugin/pom.xml index 5d8d895714..97f4a47795 100644 --- a/runelite-script-assembler-plugin/pom.xml +++ b/runelite-script-assembler-plugin/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.28 + 1.6.29-SNAPSHOT script-assembler-plugin From d0589c80b942f4cef77700c464a84043f77a1553 Mon Sep 17 00:00:00 2001 From: Jordan Atwood Date: Thu, 8 Oct 2020 13:53:58 -0700 Subject: [PATCH 55/75] kourendlibrary: Don't reset state when not finding Varlamore Envoy The Varlamore Envoy is a quest item which, while having a known location in the library once a rotation is determined, cannot be found under certain circumstances. (when the Depths of Despair quest has not been started, has been collected while completing the quest, and after the quest is completed) Previously searching a bookcase which could contain this book and not finding it would cause a state reset. This commit keeps those null finds from resetting the known library state. --- .../plugins/kourendlibrary/Library.java | 12 ++- .../plugins/kourendlibrary/LibraryTest.java | 77 +++++++++++++++++++ 2 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 runelite-client/src/test/java/net/runelite/client/plugins/kourendlibrary/LibraryTest.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/Library.java b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/Library.java index 214c780ea1..69b1a5deec 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/Library.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/Library.java @@ -122,8 +122,9 @@ class Library if (bookcase.isBookSet()) { // Bookcase is set from a previous mark - // Check for a mismatch, unless it is now null and had a dark manuscript - if (book != bookcase.getBook() && !(book == null && bookcase.getBook().isDarkManuscript())) + // Check for a mismatch, unless it is now null and had a dark manuscript or Varlamore Envoy + if (book != bookcase.getBook() + && !(book == null && (bookcase.getBook().isDarkManuscript() || bookcase.getBook() == VARLAMORE_ENVOY))) { reset(); } @@ -140,8 +141,11 @@ class Library if (state == SolvedState.COMPLETE) { - // Reset if we found nothing when we expected something that wasn't a Dark Manuscript, since the layout has changed - if (book == null && !bookcase.getPossibleBooks().isEmpty() && bookcase.getPossibleBooks().stream().noneMatch(Book::isDarkManuscript)) + // Reset if we found nothing when we expected something that wasn't a Dark Manuscript or Varlamore Envoy + // since the layout has changed + if (book == null + && !bookcase.getPossibleBooks().isEmpty() + && bookcase.getPossibleBooks().stream().noneMatch(b -> b.isDarkManuscript() || b == VARLAMORE_ENVOY)) { reset(); } diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/kourendlibrary/LibraryTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/kourendlibrary/LibraryTest.java new file mode 100644 index 0000000000..2f782bc0a4 --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/kourendlibrary/LibraryTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2020 Jordan Atwood + * 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.kourendlibrary; + +import com.google.inject.Guice; +import com.google.inject.Inject; +import com.google.inject.testing.fieldbinder.BoundFieldModule; +import net.runelite.api.coords.WorldPoint; +import static org.junit.Assert.assertEquals; +import org.junit.Before; +import org.junit.Test; + +public class LibraryTest +{ + @Inject + private Library library; + + @Before + public void before() + { + Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this); + } + + @Test + public void testVarlamoreEnvoyFindingProcess() + { + library.mark(new WorldPoint(1610, 3799, 1), null); + library.mark(new WorldPoint(1608, 3799, 1), null); + library.mark(new WorldPoint(1615, 3799, 1), null); + library.mark(new WorldPoint(1616, 3799, 1), null); + library.mark(new WorldPoint(1621, 3799, 1), null); + library.mark(new WorldPoint(1624, 3796, 1), null); + library.mark(new WorldPoint(1624, 3792, 1), null); + library.mark(new WorldPoint(1624, 3791, 1), null); + library.mark(new WorldPoint(1623, 3789, 1), null); + + assertEquals(SolvedState.NO_DATA, library.getState()); + + library.mark(new WorldPoint(1621, 3789, 1), Book.WINTERTODT_PARABLE); + + assertEquals(SolvedState.INCOMPLETE, library.getState()); + + library.mark(new WorldPoint(1618, 3799, 2), null); + library.mark(new WorldPoint(1613, 3792, 2), null); + library.mark(new WorldPoint(1618, 3790, 2), Book.TRANSPORTATION_INCANTATIONS); + library.mark(new WorldPoint(1609, 3816, 2), Book.RICKTORS_DIARY_7); + + assertEquals(SolvedState.COMPLETE, library.getState()); + + // The Varlamore Envoy book can be found in this bookcase, but should not cause a state reset if not found + library.mark(new WorldPoint(1622, 3816, 2), null); + + assertEquals(SolvedState.COMPLETE, library.getState()); + } +} From aed923ba0aac902a1c58060baff91bf15e5df329 Mon Sep 17 00:00:00 2001 From: Minhs2 <32379779+Minhs2@users.noreply.github.com> Date: Thu, 8 Oct 2020 14:45:52 -0700 Subject: [PATCH 56/75] slayer: Add Sourhog task (#12622) --- .../src/main/java/net/runelite/client/plugins/slayer/Task.java | 1 + 1 file changed, 1 insertion(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/Task.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/Task.java index 6407c933e1..1c9d9e81e2 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/Task.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/Task.java @@ -155,6 +155,7 @@ enum Task SKELETAL_WYVERNS("Skeletal wyverns", ItemID.SKELETAL_WYVERN), SKELETONS("Skeletons", ItemID.SKELETON_GUARD), SMOKE_DEVILS("Smoke devils", ItemID.SMOKE_DEVIL), + SOURHOGS("Sourhogs", ItemID.SOURHOG_FOOT), SPIDERS("Spiders", ItemID.HUGE_SPIDER), SPIRITUAL_CREATURES("Spiritual creatures", ItemID.DRAGON_BOOTS, "Spiritual ranger", "Spiritual mage", "Spiritual warrior"), STEEL_DRAGONS("Steel dragons", ItemID.STEEL_DRAGON), From a3a647e206f92889bd7e7998a4c287987a06841e Mon Sep 17 00:00:00 2001 From: Cyborger1 <45152844+Cyborger1@users.noreply.github.com> Date: Sat, 26 Sep 2020 16:41:21 -0400 Subject: [PATCH 57/75] farming: Fix harvestable health-check crops contract status This commit fixes a bug which caused the farming contract manager to ncorrectly report the status of a crop which had been health-checked as IN_PROGRESS for an active contract, instead of OCCUPIED. --- .../plugins/timetracking/farming/FarmingContractManager.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingContractManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingContractManager.java index e1684097f6..a67ef5aea2 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingContractManager.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingContractManager.java @@ -257,6 +257,8 @@ public class FarmingContractManager && !hasEmptyPatch) { summary = SummaryState.OCCUPIED; + // Don't let this run into the "Completed" section! + continue; } } From 6d78e6884dc2e3e8ff3a45e0e4877a908ae1321c Mon Sep 17 00:00:00 2001 From: Cyborger1 <45152844+Cyborger1@users.noreply.github.com> Date: Sat, 26 Sep 2020 16:43:50 -0400 Subject: [PATCH 58/75] farming: Fix contract status for plots with diseased/dead crops This commit fixes the farming contract manager's behavior when encountering crops which have become diseased. Previously, it would not take this into account and incorrectly report the crop as IN_PROGRESS or READY. --- .../farming/FarmingContractInfoBox.java | 20 +- .../farming/FarmingContractManager.java | 63 +- .../farming/FarmingContractManagerTest.java | 577 ++++++++++++++++++ 3 files changed, 644 insertions(+), 16 deletions(-) create mode 100644 runelite-client/src/test/java/net/runelite/client/plugins/timetracking/farming/FarmingContractManagerTest.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingContractInfoBox.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingContractInfoBox.java index c27154da88..e60a135698 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingContractInfoBox.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingContractInfoBox.java @@ -82,9 +82,23 @@ class FarmingContractInfoBox extends InfoBox contractColor = ColorScheme.PROGRESS_ERROR_COLOR; break; case IN_PROGRESS: - contractDescription = "Ready " + TabContentPanel.getFormattedEstimate(manager.getCompletionTime() - Instant.now().getEpochSecond(), - config.timeFormatMode()); - contractColor = Color.GRAY; + CropState cropState = manager.getContractCropState(); + switch (cropState) + { + case DISEASED: + contractDescription = "Diseased"; + contractColor = cropState.getColor(); + break; + case DEAD: + contractDescription = "Dead"; + contractColor = cropState.getColor(); + break; + default: + contractDescription = "Ready " + TabContentPanel.getFormattedEstimate(manager.getCompletionTime() - Instant.now().getEpochSecond(), + config.timeFormatMode()); + contractColor = Color.GRAY; + break; + } break; case EMPTY: case UNKNOWN: diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingContractManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingContractManager.java index a67ef5aea2..faad199b16 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingContractManager.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingContractManager.java @@ -58,6 +58,9 @@ public class FarmingContractManager @Getter private SummaryState summary = SummaryState.UNKNOWN; + @Getter + private CropState contractCropState; + @Inject private Client client; @@ -229,7 +232,10 @@ public class FarmingContractManager PatchImplementation patchImplementation = contract.getPatchImplementation(); boolean hasEmptyPatch = false; + boolean hasDiseasedPatch = false; + boolean hasDeadPatch = false; completionTime = Long.MAX_VALUE; + contractCropState = null; for (FarmingPatch patch : farmingWorld.getFarmingGuildRegion().getPatches()) { if (patch.getImplementation() != patchImplementation) @@ -244,17 +250,22 @@ public class FarmingContractManager } Produce produce = prediction.getProduce(); + CropState state = prediction.getCropState(); if (completionTime == Long.MAX_VALUE) { if (produce == null || produce == Produce.WEEDS) { - summary = SummaryState.EMPTY; + // Don't report the empty state if there's a dead or diseased one + if (!(hasDiseasedPatch || hasDeadPatch)) + { + summary = SummaryState.EMPTY; + } hasEmptyPatch = true; continue; } - if ((contract.requiresHealthCheck() && prediction.getCropState() == CropState.HARVESTABLE) - && !hasEmptyPatch) + if ((contract.requiresHealthCheck() && state == CropState.HARVESTABLE) + && !(hasEmptyPatch || hasDiseasedPatch || hasDeadPatch)) { summary = SummaryState.OCCUPIED; // Don't let this run into the "Completed" section! @@ -262,27 +273,53 @@ public class FarmingContractManager } } - if (produce != contract) + // Herbs always turn into ANYHERB when dead, so let them through. + if (produce != contract && produce != Produce.ANYHERB) { - if (!hasEmptyPatch && completionTime == Long.MAX_VALUE) + if (!(hasEmptyPatch || hasDiseasedPatch || hasDeadPatch) && completionTime == Long.MAX_VALUE) { summary = SummaryState.OCCUPIED; } } else { - long estimatedTime = Math.min(prediction.getDoneEstimate(), completionTime); - - if (estimatedTime <= Instant.now().getEpochSecond()) + // Ignore if crop is dead but there's another one in progress (either normal or diseased) + if (state == CropState.DEAD && (hasDiseasedPatch || completionTime != Long.MAX_VALUE)) { - summary = SummaryState.COMPLETED; - completionTime = 0; - break; + continue; + } + + // Ignore if crop is diseased but there's another patch in progress + if (state == CropState.DISEASED && completionTime != Long.MAX_VALUE) + { + continue; + } + + contractCropState = state; + if (contractCropState == CropState.DISEASED) + { + hasDiseasedPatch = true; + summary = SummaryState.IN_PROGRESS; + } + else if (contractCropState == CropState.DEAD) + { + hasDeadPatch = true; + summary = SummaryState.IN_PROGRESS; } else { - summary = SummaryState.IN_PROGRESS; - completionTime = estimatedTime; + long estimatedTime = Math.min(prediction.getDoneEstimate(), completionTime); + if (estimatedTime <= Instant.now().getEpochSecond()) + { + summary = SummaryState.COMPLETED; + completionTime = 0; + break; + } + else + { + summary = SummaryState.IN_PROGRESS; + completionTime = estimatedTime; + } } } } diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/timetracking/farming/FarmingContractManagerTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/timetracking/farming/FarmingContractManagerTest.java new file mode 100644 index 0000000000..3d3c4ad454 --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/timetracking/farming/FarmingContractManagerTest.java @@ -0,0 +1,577 @@ +/* + * Copyright (c) 2020 Cyborger1 + * 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.timetracking.farming; + +import com.google.inject.Guice; +import com.google.inject.testing.fieldbinder.Bind; +import com.google.inject.testing.fieldbinder.BoundFieldModule; +import java.time.Instant; +import java.util.EnumMap; +import java.util.Map; +import java.util.concurrent.ScheduledExecutorService; +import javax.inject.Inject; +import net.runelite.api.Client; +import net.runelite.api.Varbits; +import net.runelite.client.Notifier; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.game.ItemManager; +import net.runelite.client.plugins.timetracking.SummaryState; +import net.runelite.client.plugins.timetracking.TimeTrackingConfig; +import net.runelite.client.ui.overlay.infobox.InfoBoxManager; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.ArgumentMatchers.any; +import org.mockito.Mock; +import static org.mockito.Mockito.when; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class FarmingContractManagerTest +{ + private Map farmingGuildPatches = new EnumMap<>(Varbits.class); + + @Inject + private FarmingContractManager farmingContractManager; + + @Inject + private FarmingWorld farmingWorld; + + @Mock + @Bind + private TimeTrackingConfig config; + + @Mock + @Bind + private Client client; + + @Mock + @Bind + private FarmingTracker farmingTracker; + + @Mock + @Bind + private ConfigManager configManager; + + @Mock + @Bind + private Notifier notifier; + + @Mock + @Bind + private ItemManager itemManager; + + @Mock + @Bind + private InfoBoxManager infoBoxManager; + + @Mock + @Bind + private ScheduledExecutorService executor; + + @Before + public void before() + { + Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this); + + for (FarmingPatch p : farmingWorld.getFarmingGuildRegion().getPatches()) + { + farmingGuildPatches.put(p.getVarbit(), p); + } + + // Consider all patches to be empty by default + when(farmingTracker.predictPatch(any(FarmingPatch.class))) + .thenReturn(new PatchPrediction(null, null, 0, 0, 0)); + } + + @Test + public void cabbageContractOnionHarvestableAndCabbageHarvestable() + { + final long unixNow = Instant.now().getEpochSecond(); + + // Get the two allotment patches + final FarmingPatch patch1 = farmingGuildPatches.get(Varbits.FARMING_4773); + final FarmingPatch patch2 = farmingGuildPatches.get(Varbits.FARMING_4774); + + assertNotNull(patch1); + assertNotNull(patch2); + + // Specify the two allotment patches + when(farmingTracker.predictPatch(patch1)) + .thenReturn(new PatchPrediction(Produce.ONION, CropState.HARVESTABLE, unixNow, 3, 3)); + when(farmingTracker.predictPatch(patch2)) + .thenReturn(new PatchPrediction(Produce.CABBAGE, CropState.HARVESTABLE, unixNow, 3, 3)); + + farmingContractManager.setContract(Produce.CABBAGE); + + assertEquals(SummaryState.COMPLETED, farmingContractManager.getSummary()); + } + + @Test + public void cabbageContractOnionHarvestableAndPotatoHarvestable() + { + final long unixNow = Instant.now().getEpochSecond(); + + // Get the two allotment patches + final FarmingPatch patch1 = farmingGuildPatches.get(Varbits.FARMING_4773); + final FarmingPatch patch2 = farmingGuildPatches.get(Varbits.FARMING_4774); + + assertNotNull(patch1); + assertNotNull(patch2); + + // Specify the two allotment patches + when(farmingTracker.predictPatch(patch1)) + .thenReturn(new PatchPrediction(Produce.ONION, CropState.HARVESTABLE, unixNow, 3, 3)); + when(farmingTracker.predictPatch(patch2)) + .thenReturn(new PatchPrediction(Produce.POTATO, CropState.HARVESTABLE, unixNow, 3, 3)); + + farmingContractManager.setContract(Produce.CABBAGE); + + assertEquals(SummaryState.OCCUPIED, farmingContractManager.getSummary()); + } + + @Test + public void cabbageContractOnionHarvestableAndEmptyPatch() + { + final long unixNow = Instant.now().getEpochSecond(); + + // Get the two allotment patches + final FarmingPatch patch = farmingGuildPatches.get(Varbits.FARMING_4773); + + assertNotNull(patch); + + // Specify the two allotment patches + when(farmingTracker.predictPatch(patch)) + .thenReturn(new PatchPrediction(Produce.ONION, CropState.HARVESTABLE, unixNow, 3, 3)); + + farmingContractManager.setContract(Produce.CABBAGE); + + assertEquals(SummaryState.EMPTY, farmingContractManager.getSummary()); + } + + @Test + public void cabbageContractOnionHarvestableAndCabbageGrowing() + { + final long unixNow = Instant.now().getEpochSecond(); + final long expectedTime = unixNow + 60; + + // Get the two allotment patches + final FarmingPatch patch1 = farmingGuildPatches.get(Varbits.FARMING_4773); + final FarmingPatch patch2 = farmingGuildPatches.get(Varbits.FARMING_4774); + + assertNotNull(patch1); + assertNotNull(patch2); + + // Specify the two allotment patches + when(farmingTracker.predictPatch(patch1)) + .thenReturn(new PatchPrediction(Produce.ONION, CropState.HARVESTABLE, unixNow, 3, 3)); + when(farmingTracker.predictPatch(patch2)) + .thenReturn(new PatchPrediction( + Produce.CABBAGE, CropState.GROWING, expectedTime, 2, 3)); + + farmingContractManager.setContract(Produce.CABBAGE); + + assertEquals(SummaryState.IN_PROGRESS, farmingContractManager.getSummary()); + assertEquals(CropState.GROWING, farmingContractManager.getContractCropState()); + assertEquals(expectedTime, farmingContractManager.getCompletionTime()); + } + + @Test + public void cabbageContractOnionHarvestableAndCabbageDiseased() + { + final long unixNow = Instant.now().getEpochSecond(); + + // Get the two allotment patches + final FarmingPatch patch1 = farmingGuildPatches.get(Varbits.FARMING_4773); + final FarmingPatch patch2 = farmingGuildPatches.get(Varbits.FARMING_4774); + + assertNotNull(patch1); + assertNotNull(patch2); + + // Specify the two allotment patches + when(farmingTracker.predictPatch(patch1)) + .thenReturn(new PatchPrediction(Produce.ONION, CropState.HARVESTABLE, unixNow, 3, 3)); + when(farmingTracker.predictPatch(patch2)) + .thenReturn(new PatchPrediction(Produce.CABBAGE, CropState.DISEASED, 0, 2, 3)); + + farmingContractManager.setContract(Produce.CABBAGE); + + assertEquals(SummaryState.IN_PROGRESS, farmingContractManager.getSummary()); + assertEquals(CropState.DISEASED, farmingContractManager.getContractCropState()); + } + + @Test + public void cabbageContractOnionHarvestableAndCabbageDead() + { + final long unixNow = Instant.now().getEpochSecond(); + + // Get the two allotment patches + final FarmingPatch patch1 = farmingGuildPatches.get(Varbits.FARMING_4773); + final FarmingPatch patch2 = farmingGuildPatches.get(Varbits.FARMING_4774); + + assertNotNull(patch1); + assertNotNull(patch2); + + // Specify the two allotment patches + when(farmingTracker.predictPatch(patch1)) + .thenReturn(new PatchPrediction(Produce.ONION, CropState.HARVESTABLE, unixNow, 3, 3)); + when(farmingTracker.predictPatch(patch2)) + .thenReturn(new PatchPrediction(Produce.CABBAGE, CropState.DEAD, 0, 2, 3)); + + farmingContractManager.setContract(Produce.CABBAGE); + + assertEquals(SummaryState.IN_PROGRESS, farmingContractManager.getSummary()); + assertEquals(CropState.DEAD, farmingContractManager.getContractCropState()); + } + + @Test + public void cabbageContractCabbageGrowingAndCabbageDead() + { + final long unixNow = Instant.now().getEpochSecond(); + final long expected = unixNow + 60; + + // Get the two allotment patches + final FarmingPatch patch1 = farmingGuildPatches.get(Varbits.FARMING_4773); + final FarmingPatch patch2 = farmingGuildPatches.get(Varbits.FARMING_4774); + + assertNotNull(patch1); + assertNotNull(patch2); + + // Specify the two allotment patches + when(farmingTracker.predictPatch(patch1)) + .thenReturn(new PatchPrediction(Produce.CABBAGE, CropState.GROWING, expected, 2, 3)); + when(farmingTracker.predictPatch(patch2)) + .thenReturn(new PatchPrediction(Produce.CABBAGE, CropState.DEAD, 0, 2, 3)); + + farmingContractManager.setContract(Produce.CABBAGE); + + assertEquals(SummaryState.IN_PROGRESS, farmingContractManager.getSummary()); + } + + @Test + public void cabbageContractCabbageHarvestableAndCabbageDead() + { + final long unixNow = Instant.now().getEpochSecond(); + + // Get the two allotment patches + final FarmingPatch patch1 = farmingGuildPatches.get(Varbits.FARMING_4773); + final FarmingPatch patch2 = farmingGuildPatches.get(Varbits.FARMING_4774); + + assertNotNull(patch1); + assertNotNull(patch2); + + // Specify the two allotment patches + when(farmingTracker.predictPatch(patch1)) + .thenReturn(new PatchPrediction(Produce.CABBAGE, CropState.DEAD, 0, 2, 3)); + when(farmingTracker.predictPatch(patch2)) + .thenReturn(new PatchPrediction(Produce.CABBAGE, CropState.HARVESTABLE, unixNow, 3, 3)); + + farmingContractManager.setContract(Produce.CABBAGE); + + assertEquals(SummaryState.COMPLETED, farmingContractManager.getSummary()); + } + + @Test + public void cabbageContractTwoCabbagesGrowing() + { + final long unixNow = Instant.now().getEpochSecond(); + final long expected1 = unixNow + 60; + final long expected2 = unixNow + 120; + + // Get the two allotment patches + final FarmingPatch patch1 = farmingGuildPatches.get(Varbits.FARMING_4773); + final FarmingPatch patch2 = farmingGuildPatches.get(Varbits.FARMING_4774); + + assertNotNull(patch1); + assertNotNull(patch2); + + // Specify the two allotment patches + when(farmingTracker.predictPatch(patch1)) + .thenReturn(new PatchPrediction(Produce.CABBAGE, CropState.GROWING, expected1, 2, 3)); + when(farmingTracker.predictPatch(patch2)) + .thenReturn(new PatchPrediction(Produce.CABBAGE, CropState.GROWING, expected2, 1, 3)); + + farmingContractManager.setContract(Produce.CABBAGE); + + assertEquals(SummaryState.IN_PROGRESS, farmingContractManager.getSummary()); + assertEquals(CropState.GROWING, farmingContractManager.getContractCropState()); + // Prefer closest estimated time + assertEquals(expected1, farmingContractManager.getCompletionTime()); + } + + @Test + public void cabbageContractCabbageGrowingAndCabbageDiseased() + { + final long unixNow = Instant.now().getEpochSecond(); + final long expectedTime = unixNow + 60; + + // Get the two allotment patches + final FarmingPatch patch1 = farmingGuildPatches.get(Varbits.FARMING_4773); + final FarmingPatch patch2 = farmingGuildPatches.get(Varbits.FARMING_4774); + + assertNotNull(patch1); + assertNotNull(patch2); + + // Specify the two allotment patches + when(farmingTracker.predictPatch(patch1)) + .thenReturn(new PatchPrediction(Produce.CABBAGE, CropState.DISEASED, 0, 2, 3)); + when(farmingTracker.predictPatch(patch2)) + .thenReturn(new PatchPrediction(Produce.CABBAGE, CropState.GROWING, expectedTime, 2, 3)); + + farmingContractManager.setContract(Produce.CABBAGE); + + assertEquals(SummaryState.IN_PROGRESS, farmingContractManager.getSummary()); + // Prefer healthy cabbages + assertEquals(CropState.GROWING, farmingContractManager.getContractCropState()); + assertEquals(expectedTime, farmingContractManager.getCompletionTime()); + } + + @Test + public void cabbageContractCabbageDiseasedAndCabbageGrowing() + { + final long unixNow = Instant.now().getEpochSecond(); + final long expectedTime = unixNow + 60; + + // Get the two allotment patches + final FarmingPatch patch1 = farmingGuildPatches.get(Varbits.FARMING_4773); + final FarmingPatch patch2 = farmingGuildPatches.get(Varbits.FARMING_4774); + + assertNotNull(patch1); + assertNotNull(patch2); + + // Specify the two allotment patches + when(farmingTracker.predictPatch(patch1)) + .thenReturn(new PatchPrediction(Produce.CABBAGE, CropState.GROWING, expectedTime, 2, 3)); + when(farmingTracker.predictPatch(patch2)) + .thenReturn(new PatchPrediction(Produce.CABBAGE, CropState.DISEASED, 0, 2, 3)); + + farmingContractManager.setContract(Produce.CABBAGE); + + assertEquals(SummaryState.IN_PROGRESS, farmingContractManager.getSummary()); + // Prefer healthy cabbages + assertEquals(CropState.GROWING, farmingContractManager.getContractCropState()); + assertEquals(expectedTime, farmingContractManager.getCompletionTime()); + } + + @Test + public void cabbageContractCabbageDeadAndCabbageDiseased() + { + // Get the two allotment patches + final FarmingPatch patch1 = farmingGuildPatches.get(Varbits.FARMING_4773); + final FarmingPatch patch2 = farmingGuildPatches.get(Varbits.FARMING_4774); + + assertNotNull(patch1); + assertNotNull(patch2); + + // Specify the two allotment patches + when(farmingTracker.predictPatch(patch1)) + .thenReturn(new PatchPrediction(Produce.CABBAGE, CropState.DISEASED, 0, 2, 3)); + when(farmingTracker.predictPatch(patch2)) + .thenReturn(new PatchPrediction(Produce.CABBAGE, CropState.DEAD, 0, 2, 3)); + + farmingContractManager.setContract(Produce.CABBAGE); + + assertEquals(SummaryState.IN_PROGRESS, farmingContractManager.getSummary()); + // Prefer diseased cabbages + assertEquals(CropState.DISEASED, farmingContractManager.getContractCropState()); + } + + @Test + public void cabbageContractCabbageHarvestableAndEmptyPatch() + { + final long unixNow = Instant.now().getEpochSecond(); + + final FarmingPatch patch = farmingGuildPatches.get(Varbits.FARMING_4773); + + assertNotNull(patch); + + when(farmingTracker.predictPatch(patch)) + .thenReturn(new PatchPrediction(Produce.CABBAGE, CropState.HARVESTABLE, unixNow, 3, 3)); + + farmingContractManager.setContract(Produce.CABBAGE); + + assertEquals(SummaryState.COMPLETED, farmingContractManager.getSummary()); + } + + @Test + public void cabbageContractCabbageDiseasedAndEmptyPatch() + { + final FarmingPatch patch = farmingGuildPatches.get(Varbits.FARMING_4773); + + assertNotNull(patch); + + when(farmingTracker.predictPatch(patch)) + .thenReturn(new PatchPrediction(Produce.CABBAGE, CropState.DISEASED, 0, 2, 3)); + + farmingContractManager.setContract(Produce.CABBAGE); + + assertEquals(SummaryState.IN_PROGRESS, farmingContractManager.getSummary()); + assertEquals(CropState.DISEASED, farmingContractManager.getContractCropState()); + } + + @Test + public void cabbageContractCabbageDeadAndEmptyPatch() + { + final FarmingPatch patch = farmingGuildPatches.get(Varbits.FARMING_4773); + + assertNotNull(patch); + + when(farmingTracker.predictPatch(patch)) + .thenReturn(new PatchPrediction(Produce.CABBAGE, CropState.DEAD, 0, 2, 3)); + + farmingContractManager.setContract(Produce.CABBAGE); + + assertEquals(SummaryState.IN_PROGRESS, farmingContractManager.getSummary()); + assertEquals(CropState.DEAD, farmingContractManager.getContractCropState()); + } + + @Test + public void redberriesContractRedberriesHarvestable() + { + final long unixNow = Instant.now().getEpochSecond(); + + // Get the bush patch + final FarmingPatch patch = farmingGuildPatches.get(Varbits.FARMING_4772); + + assertNotNull(patch); + + // For berries, Harvestable means already checked + when(farmingTracker.predictPatch(patch)) + .thenReturn(new PatchPrediction(Produce.REDBERRIES, CropState.HARVESTABLE, unixNow, 3, 3)); + + farmingContractManager.setContract(Produce.REDBERRIES); + + assertEquals(SummaryState.OCCUPIED, farmingContractManager.getSummary()); + } + + @Test + public void redberriesContractRedberriesGrown() + { + final long unixNow = Instant.now().getEpochSecond(); + + // Get the bush patch + final FarmingPatch patch = farmingGuildPatches.get(Varbits.FARMING_4772); + + assertNotNull(patch); + + // For berries, Growing on the last stage is ready to be checked + when(farmingTracker.predictPatch(patch)) + .thenReturn(new PatchPrediction(Produce.REDBERRIES, CropState.GROWING, unixNow, 3, 3)); + + farmingContractManager.setContract(Produce.REDBERRIES); + + assertEquals(SummaryState.COMPLETED, farmingContractManager.getSummary()); + } + + @Test + public void redberriesContractRedberriesGrowing() + { + final long unixNow = Instant.now().getEpochSecond(); + final long expectedCompletion = unixNow + 60; + + // Get the bush patch + final FarmingPatch patch = farmingGuildPatches.get(Varbits.FARMING_4772); + + assertNotNull(patch); + + // Not ready to check + when(farmingTracker.predictPatch(patch)) + .thenReturn(new PatchPrediction(Produce.REDBERRIES, CropState.GROWING, expectedCompletion, 2, 3)); + + farmingContractManager.setContract(Produce.REDBERRIES); + + assertEquals(SummaryState.IN_PROGRESS, farmingContractManager.getSummary()); + assertEquals(CropState.GROWING, farmingContractManager.getContractCropState()); + assertEquals(expectedCompletion, farmingContractManager.getCompletionTime()); + } + + @Test + public void redberriesContractRedberriesDiseased() + { + // Get the bush patch + final FarmingPatch patch = farmingGuildPatches.get(Varbits.FARMING_4772); + + assertNotNull(patch); + + when(farmingTracker.predictPatch(patch)) + .thenReturn(new PatchPrediction(Produce.REDBERRIES, CropState.DISEASED, 0, 2, 3)); + + farmingContractManager.setContract(Produce.REDBERRIES); + + assertEquals(SummaryState.IN_PROGRESS, farmingContractManager.getSummary()); + assertEquals(CropState.DISEASED, farmingContractManager.getContractCropState()); + } + + @Test + public void redberriesContractRedberriesDead() + { + // Get the bush patch + final FarmingPatch patch = farmingGuildPatches.get(Varbits.FARMING_4772); + + assertNotNull(patch); + + when(farmingTracker.predictPatch(patch)) + .thenReturn(new PatchPrediction(Produce.REDBERRIES, CropState.DEAD, 0, 2, 3)); + + farmingContractManager.setContract(Produce.REDBERRIES); + + assertEquals(SummaryState.IN_PROGRESS, farmingContractManager.getSummary()); + assertEquals(CropState.DEAD, farmingContractManager.getContractCropState()); + } + + @Test + public void redberriesContractCadavaDead() + { + // Get the bush patch + final FarmingPatch patch = farmingGuildPatches.get(Varbits.FARMING_4772); + + assertNotNull(patch); + + when(farmingTracker.predictPatch(patch)) + .thenReturn(new PatchPrediction(Produce.CADAVABERRIES, CropState.DEAD, 0, 2, 3)); + + farmingContractManager.setContract(Produce.REDBERRIES); + assertEquals(SummaryState.OCCUPIED, farmingContractManager.getSummary()); + } + + @Test + public void guamContractGuamDead() + { + // Get the bush patch + final FarmingPatch patch = farmingGuildPatches.get(Varbits.FARMING_4775); + + assertNotNull(patch); + + when(farmingTracker.predictPatch(patch)) + .thenReturn(new PatchPrediction(Produce.GUAM, CropState.DEAD, 0, 2, 3)); + + farmingContractManager.setContract(Produce.GUAM); + + assertEquals(SummaryState.IN_PROGRESS, farmingContractManager.getSummary()); + assertEquals(CropState.DEAD, farmingContractManager.getContractCropState()); + } +} From f89b9cb8785b1cff178420d00f92f3a9f4bbf346 Mon Sep 17 00:00:00 2001 From: SirGirion Date: Mon, 12 Oct 2020 11:10:43 -0700 Subject: [PATCH 59/75] motherlode: Add option to show collected ore/gem icons --- .../plugins/motherlode/MotherlodeConfig.java | 10 ++ .../motherlode/MotherlodeGemOverlay.java | 85 ++++++++---- .../motherlode/MotherlodeOreOverlay.java | 126 +++++++++++------- 3 files changed, 146 insertions(+), 75 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/motherlode/MotherlodeConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/motherlode/MotherlodeConfig.java index a32ec18c0f..320c07758a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/motherlode/MotherlodeConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/motherlode/MotherlodeConfig.java @@ -123,4 +123,14 @@ public interface MotherlodeConfig extends Config { return true; } + + @ConfigItem( + keyName = "showLootIcons", + name = "Show ore icons", + description = "Display collected ores and gems as item images instead of text" + ) + default boolean showLootIcons() + { + return false; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/motherlode/MotherlodeGemOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/motherlode/MotherlodeGemOverlay.java index 2406b85737..5d6c86a445 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/motherlode/MotherlodeGemOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/motherlode/MotherlodeGemOverlay.java @@ -29,11 +29,15 @@ import java.awt.Graphics2D; import java.time.Duration; import java.time.Instant; import javax.inject.Inject; +import net.runelite.api.ItemID; import static net.runelite.api.MenuAction.RUNELITE_OVERLAY_CONFIG; +import net.runelite.client.game.ItemManager; import static net.runelite.client.ui.overlay.OverlayManager.OPTION_CONFIGURE; import net.runelite.client.ui.overlay.OverlayMenuEntry; import net.runelite.client.ui.overlay.OverlayPanel; import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.components.ComponentOrientation; +import net.runelite.client.ui.overlay.components.ImageComponent; import net.runelite.client.ui.overlay.components.LineComponent; import net.runelite.client.ui.overlay.components.TitleComponent; @@ -42,15 +46,17 @@ public class MotherlodeGemOverlay extends OverlayPanel private final MotherlodePlugin plugin; private final MotherlodeSession motherlodeSession; private final MotherlodeConfig config; + private final ItemManager itemManager; @Inject - MotherlodeGemOverlay(MotherlodePlugin plugin, MotherlodeSession motherlodeSession, MotherlodeConfig config) + MotherlodeGemOverlay(MotherlodePlugin plugin, MotherlodeSession motherlodeSession, MotherlodeConfig config, ItemManager itemManager) { super(plugin); setPosition(OverlayPosition.TOP_LEFT); this.plugin = plugin; this.motherlodeSession = motherlodeSession; this.config = config; + this.itemManager = itemManager; getMenuEntries().add(new OverlayMenuEntry(RUNELITE_OVERLAY_CONFIG, OPTION_CONFIGURE, "Gem overlay")); } @@ -77,38 +83,61 @@ public class MotherlodeGemOverlay extends OverlayPanel int emeraldsFound = session.getEmeraldsFound(); int sapphiresFound = session.getSapphiresFound(); - panelComponent.getChildren().add(TitleComponent.builder().text("Gems found").build()); - - if (diamondsFound > 0) + if (config.showLootIcons()) { - panelComponent.getChildren().add(LineComponent.builder() - .left("Diamonds:") - .right(Integer.toString(diamondsFound)) - .build()); + panelComponent.setOrientation(ComponentOrientation.HORIZONTAL); + if (diamondsFound > 0) + { + panelComponent.getChildren().add(new ImageComponent(itemManager.getImage(ItemID.UNCUT_DIAMOND, diamondsFound, true))); + } + if (rubiesFound > 0) + { + panelComponent.getChildren().add(new ImageComponent(itemManager.getImage(ItemID.UNCUT_RUBY, rubiesFound, true))); + } + if (emeraldsFound > 0) + { + panelComponent.getChildren().add(new ImageComponent(itemManager.getImage(ItemID.UNCUT_EMERALD, emeraldsFound, true))); + } + if (sapphiresFound > 0) + { + panelComponent.getChildren().add(new ImageComponent(itemManager.getImage(ItemID.UNCUT_SAPPHIRE, sapphiresFound, true))); + } } - - if (rubiesFound > 0) + else { - panelComponent.getChildren().add(LineComponent.builder() - .left("Rubies:") - .right(Integer.toString(rubiesFound)) - .build()); - } + panelComponent.setOrientation(ComponentOrientation.VERTICAL); + panelComponent.getChildren().add(TitleComponent.builder().text("Gems found").build()); + if (diamondsFound > 0) + { + panelComponent.getChildren().add(LineComponent.builder() + .left("Diamonds:") + .right(Integer.toString(diamondsFound)) + .build()); + } - if (emeraldsFound > 0) - { - panelComponent.getChildren().add(LineComponent.builder() - .left("Emeralds:") - .right(Integer.toString(emeraldsFound)) - .build()); - } + if (rubiesFound > 0) + { + panelComponent.getChildren().add(LineComponent.builder() + .left("Rubies:") + .right(Integer.toString(rubiesFound)) + .build()); + } - if (sapphiresFound > 0) - { - panelComponent.getChildren().add(LineComponent.builder() - .left("Sapphires:") - .right(Integer.toString(sapphiresFound)) - .build()); + if (emeraldsFound > 0) + { + panelComponent.getChildren().add(LineComponent.builder() + .left("Emeralds:") + .right(Integer.toString(emeraldsFound)) + .build()); + } + + if (sapphiresFound > 0) + { + panelComponent.getChildren().add(LineComponent.builder() + .left("Sapphires:") + .right(Integer.toString(sapphiresFound)) + .build()); + } } return super.render(graphics); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/motherlode/MotherlodeOreOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/motherlode/MotherlodeOreOverlay.java index 83a85bd4fb..4c7a2c50f4 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/motherlode/MotherlodeOreOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/motherlode/MotherlodeOreOverlay.java @@ -27,8 +27,12 @@ package net.runelite.client.plugins.motherlode; import java.awt.Dimension; import java.awt.Graphics2D; import javax.inject.Inject; +import net.runelite.api.ItemID; +import net.runelite.client.game.ItemManager; import net.runelite.client.ui.overlay.OverlayPanel; import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.components.ComponentOrientation; +import net.runelite.client.ui.overlay.components.ImageComponent; import net.runelite.client.ui.overlay.components.LineComponent; import net.runelite.client.ui.overlay.components.TitleComponent; @@ -37,14 +41,16 @@ public class MotherlodeOreOverlay extends OverlayPanel private final MotherlodePlugin plugin; private final MotherlodeSession motherlodeSession; private final MotherlodeConfig config; + private final ItemManager itemManager; @Inject - MotherlodeOreOverlay(MotherlodePlugin plugin, MotherlodeSession motherlodeSession, MotherlodeConfig config) + MotherlodeOreOverlay(MotherlodePlugin plugin, MotherlodeSession motherlodeSession, MotherlodeConfig config, ItemManager itemManager) { setPosition(OverlayPosition.TOP_LEFT); this.plugin = plugin; this.motherlodeSession = motherlodeSession; this.config = config; + this.itemManager = itemManager; } @Override @@ -71,56 +77,82 @@ public class MotherlodeOreOverlay extends OverlayPanel return null; } - panelComponent.getChildren().add(TitleComponent.builder().text("Ores found").build()); - - if (nuggetsFound > 0) + if (config.showLootIcons()) { - panelComponent.getChildren().add(LineComponent.builder() - .left("Nuggets:") - .right(Integer.toString(nuggetsFound)) - .build()); + panelComponent.setOrientation(ComponentOrientation.HORIZONTAL); + if (nuggetsFound > 0) + { + panelComponent.getChildren().add(new ImageComponent(itemManager.getImage(ItemID.GOLDEN_NUGGET, nuggetsFound, true))); + } + if (coalFound > 0) + { + panelComponent.getChildren().add(new ImageComponent(itemManager.getImage(ItemID.COAL, coalFound, true))); + } + if (goldFound > 0) + { + panelComponent.getChildren().add(new ImageComponent(itemManager.getImage(ItemID.GOLD_ORE, goldFound, true))); + } + if (mithrilFound > 0) + { + panelComponent.getChildren().add(new ImageComponent(itemManager.getImage(ItemID.MITHRIL_ORE, mithrilFound, true))); + } + if (adamantiteFound > 0) + { + panelComponent.getChildren().add(new ImageComponent(itemManager.getImage(ItemID.ADAMANTITE_ORE, adamantiteFound, true))); + } + if (runiteFound > 0) + { + panelComponent.getChildren().add(new ImageComponent(itemManager.getImage(ItemID.RUNITE_ORE, runiteFound, true))); + } } - - if (coalFound > 0) + else { - panelComponent.getChildren().add(LineComponent.builder() - .left("Coal:") - .right(Integer.toString(coalFound)) - .build()); - } - - if (goldFound > 0) - { - panelComponent.getChildren().add(LineComponent.builder() - .left("Gold:") - .right(Integer.toString(goldFound)) - .build()); - } - - if (mithrilFound > 0) - { - panelComponent.getChildren().add(LineComponent.builder() - .left("Mithril:") - .right(Integer.toString(mithrilFound)) - .build()); - } - - if (adamantiteFound > 0) - { - panelComponent.getChildren().add(LineComponent.builder() - .left("Adamantite:") - .right(Integer.toString(adamantiteFound)) - .build()); - } - - if (runiteFound > 0) - { - panelComponent.getChildren().add(LineComponent.builder() - .left("Runite:") - .right(Integer.toString(runiteFound)) - .build()); + panelComponent.setOrientation(ComponentOrientation.VERTICAL); + panelComponent.getChildren().add(TitleComponent.builder().text("Ores found").build()); + if (nuggetsFound > 0) + { + panelComponent.getChildren().add(LineComponent.builder() + .left("Nuggets:") + .right(Integer.toString(nuggetsFound)) + .build()); + } + if (coalFound > 0) + { + panelComponent.getChildren().add(LineComponent.builder() + .left("Coal:") + .right(Integer.toString(coalFound)) + .build()); + } + if (goldFound > 0) + { + panelComponent.getChildren().add(LineComponent.builder() + .left("Gold:") + .right(Integer.toString(goldFound)) + .build()); + } + if (mithrilFound > 0) + { + panelComponent.getChildren().add(LineComponent.builder() + .left("Mithril:") + .right(Integer.toString(mithrilFound)) + .build()); + } + if (adamantiteFound > 0) + { + panelComponent.getChildren().add(LineComponent.builder() + .left("Adamantite:") + .right(Integer.toString(adamantiteFound)) + .build()); + } + if (runiteFound > 0) + { + panelComponent.getChildren().add(LineComponent.builder() + .left("Runite:") + .right(Integer.toString(runiteFound)) + .build()); + } } return super.render(graphics); } -} \ No newline at end of file +} From 6fdaf498d555975ef361128f3f85f2c40258e397 Mon Sep 17 00:00:00 2001 From: Konrad Kozera Date: Tue, 13 Oct 2020 10:58:16 +0200 Subject: [PATCH 60/75] ImageUtil: add error log with path (#12631) --- .../java/net/runelite/client/util/ImageUtil.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/util/ImageUtil.java b/runelite-client/src/main/java/net/runelite/client/util/ImageUtil.java index b30a606345..66e9930ab5 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/ImageUtil.java +++ b/runelite-client/src/main/java/net/runelite/client/util/ImageUtil.java @@ -351,6 +351,19 @@ public class ImageUtil } catch (IllegalArgumentException e) { + final String filePath; + + if (path.startsWith("/")) + { + filePath = path; + } + else + { + filePath = c.getPackage().getName().replace(".", "/") + "/" + path; + } + + log.warn("Failed to load image from class: {}, path: {}", c.getName(), filePath); + throw new IllegalArgumentException(path, e); } catch (IOException e) From 9e390812e387de81ef09e1a1509d5c2d04e4d0e2 Mon Sep 17 00:00:00 2001 From: melkypie <5113962+melkypie@users.noreply.github.com> Date: Tue, 13 Oct 2020 01:17:36 +0300 Subject: [PATCH 61/75] motherlode: allow sack overlay to be resizable Also makes the overlay adhere to overlayBackgroundColor config --- .../plugins/motherlode/MotherlodeSackOverlay.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/motherlode/MotherlodeSackOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/motherlode/MotherlodeSackOverlay.java index 4d2135224c..c69f09f6fe 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/motherlode/MotherlodeSackOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/motherlode/MotherlodeSackOverlay.java @@ -35,23 +35,20 @@ import static net.runelite.api.MenuAction.RUNELITE_OVERLAY_CONFIG; import net.runelite.api.Varbits; import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetInfo; -import net.runelite.client.ui.overlay.Overlay; import static net.runelite.client.ui.overlay.OverlayManager.OPTION_CONFIGURE; import net.runelite.client.ui.overlay.OverlayMenuEntry; +import net.runelite.client.ui.overlay.OverlayPanel; import net.runelite.client.ui.overlay.OverlayPosition; import net.runelite.client.ui.overlay.components.ComponentConstants; import net.runelite.client.ui.overlay.components.LineComponent; -import net.runelite.client.ui.overlay.components.PanelComponent; -class MotherlodeSackOverlay extends Overlay +class MotherlodeSackOverlay extends OverlayPanel { private static final Color DANGER = new Color(150, 0, 0, 150); private final Client client; private final MotherlodeConfig config; private final MotherlodePlugin plugin; - private final PanelComponent panelComponent = new PanelComponent(); - @Inject MotherlodeSackOverlay(Client client, MotherlodeConfig config, MotherlodePlugin plugin) { @@ -73,7 +70,6 @@ class MotherlodeSackOverlay extends Overlay Widget sack = client.getWidget(WidgetInfo.MOTHERLODE_MINE); - panelComponent.getChildren().clear(); panelComponent.setBackgroundColor(ComponentConstants.STANDARD_BACKGROUND_COLOR); if (sack != null) @@ -119,6 +115,6 @@ class MotherlodeSackOverlay extends Overlay } } - return panelComponent.render(graphics); + return super.render(graphics); } } From 54fd10491c4cccde0f41fe8a2b4076adeb0c6128 Mon Sep 17 00:00:00 2001 From: Joey Wilson Date: Tue, 13 Oct 2020 19:01:43 -0400 Subject: [PATCH 62/75] Add item mapping for unidentified minerals (#12552) --- .../src/main/java/net/runelite/client/game/ItemMapping.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/game/ItemMapping.java b/runelite-client/src/main/java/net/runelite/client/game/ItemMapping.java index 8446a70ddf..4014382771 100644 --- a/runelite-client/src/main/java/net/runelite/client/game/ItemMapping.java +++ b/runelite-client/src/main/java/net/runelite/client/game/ItemMapping.java @@ -279,6 +279,9 @@ public enum ItemMapping ITEM_PROSPECTOR_LEGS(GOLDEN_NUGGET, true, 40L, PROSPECTOR_LEGS), ITEM_PROSPECTOR_BOOTS(GOLDEN_NUGGET, true, 24L, PROSPECTOR_BOOTS), + // 10 unidentified minerals = 100 soft clay + ITEM_UNIDENTIFIED_MINERALS(SOFT_CLAY, true, 10L, UNIDENTIFIED_MINERALS), + // Converted to coins ITEM_TATTERED_PAGE(COINS_995, true, 1000L, TATTERED_MOON_PAGE, TATTERED_SUN_PAGE, TATTERED_TEMPLE_PAGE), ITEM_LONG_BONE(COINS_995, true, 1000L, LONG_BONE), From 5ef40cc6ca81680adf332c74a41c678aaa996928 Mon Sep 17 00:00:00 2001 From: Bram91 <7499230+Bram91@users.noreply.github.com> Date: Sat, 10 Oct 2020 10:23:48 +0200 Subject: [PATCH 63/75] XPGlobes: allow stacking vertically. --- .../plugins/xpglobes/XpGlobesConfig.java | 18 +++++++++++++ .../plugins/xpglobes/XpGlobesOverlay.java | 27 +++++++++++++++---- .../plugins/xpglobes/XpGlobesPlugin.java | 17 ++++++++++++ 3 files changed, 57 insertions(+), 5 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xpglobes/XpGlobesConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/xpglobes/XpGlobesConfig.java index dd5773044a..c32a8c598a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xpglobes/XpGlobesConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xpglobes/XpGlobesConfig.java @@ -171,4 +171,22 @@ public interface XpGlobesConfig extends Config { return 10; } + + @ConfigItem( + keyName = "alignOrbsVertically", + name = "Vertical Orbs", + description = "Aligns the orbs vertically instead of horizontally.", + hidden = true + ) + default boolean alignOrbsVertically() + { + return false; + } + + @ConfigItem( + keyName = "alignOrbsVertically", + name = "", + description = "" + ) + void setAlignOrbsVertically(Boolean alignOrbsVertically); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xpglobes/XpGlobesOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/xpglobes/XpGlobesOverlay.java index 4beb3f6347..23da426c22 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xpglobes/XpGlobesOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xpglobes/XpGlobesOverlay.java @@ -40,6 +40,7 @@ import java.time.Instant; import java.util.List; import javax.inject.Inject; import net.runelite.api.Client; +import static net.runelite.api.MenuAction.RUNELITE_OVERLAY; import static net.runelite.api.MenuAction.RUNELITE_OVERLAY_CONFIG; import net.runelite.api.Point; import net.runelite.client.game.SkillIconManager; @@ -63,6 +64,7 @@ public class XpGlobesOverlay extends Overlay private static final int PROGRESS_RADIUS_REMAINDER = 0; private static final int TOOLTIP_RECT_SIZE_X = 150; private static final Color DARK_OVERLAY_COLOR = new Color(0, 0, 0, 180); + static final String FLIP_ACTION = "Flip"; private final Client client; private final XpGlobesPlugin plugin; @@ -91,6 +93,7 @@ public class XpGlobesOverlay extends Overlay this.xpTooltip.getComponent().setPreferredSize(new Dimension(TOOLTIP_RECT_SIZE_X, 0)); setPosition(OverlayPosition.TOP_CENTER); getMenuEntries().add(new OverlayMenuEntry(RUNELITE_OVERLAY_CONFIG, OPTION_CONFIGURE, "XP Globes overlay")); + getMenuEntries().add(new OverlayMenuEntry(RUNELITE_OVERLAY, FLIP_ACTION, "XP Globes overlay")); } @Override @@ -103,18 +106,32 @@ public class XpGlobesOverlay extends Overlay return null; } - int curDrawX = 0; + int curDrawPosition = 0; for (final XpGlobe xpGlobe : xpGlobes) { int startXp = xpTrackerService.getStartGoalXp(xpGlobe.getSkill()); int goalXp = xpTrackerService.getEndGoalXp(xpGlobe.getSkill()); - renderProgressCircle(graphics, xpGlobe, startXp, goalXp, curDrawX, 0, getBounds()); - curDrawX += MINIMUM_STEP + config.xpOrbSize(); + if (config.alignOrbsVertically()) + { + renderProgressCircle(graphics, xpGlobe, startXp, goalXp, 0, curDrawPosition, getBounds()); + } + else + { + renderProgressCircle(graphics, xpGlobe, startXp, goalXp, curDrawPosition, 0, getBounds()); + } + curDrawPosition += MINIMUM_STEP + config.xpOrbSize(); } - // Get width of markers + // Get length of markers final int markersLength = (queueSize * (config.xpOrbSize())) + ((MINIMUM_STEP) * (queueSize - 1)); - return new Dimension(markersLength, config.xpOrbSize()); + if (config.alignOrbsVertically()) + { + return new Dimension(config.xpOrbSize(), markersLength); + } + else + { + return new Dimension(markersLength, config.xpOrbSize()); + } } private double getSkillProgress(int startXp, int currentXp, int goalXp) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xpglobes/XpGlobesPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/xpglobes/XpGlobesPlugin.java index 6c2e39301e..b692bda0b3 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xpglobes/XpGlobesPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xpglobes/XpGlobesPlugin.java @@ -35,11 +35,13 @@ import javax.inject.Inject; import lombok.Getter; import net.runelite.api.Client; import net.runelite.api.Experience; +import net.runelite.api.MenuAction; import net.runelite.api.Skill; import net.runelite.api.events.GameStateChanged; import net.runelite.api.events.StatChanged; import net.runelite.client.config.ConfigManager; import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.OverlayMenuClicked; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDependency; import net.runelite.client.plugins.PluginDescriptor; @@ -166,6 +168,21 @@ public class XpGlobesPlugin extends Plugin globeCache = new XpGlobe[Skill.values().length - 1]; } + @Subscribe + public void onOverlayMenuClicked(final OverlayMenuClicked event) + { + if (!(event.getEntry().getMenuAction() == MenuAction.RUNELITE_OVERLAY + && event.getOverlay() == overlay)) + { + return; + } + + if (event.getEntry().getOption().equals(XpGlobesOverlay.FLIP_ACTION)) + { + config.setAlignOrbsVertically(!config.alignOrbsVertically()); + } + } + @Subscribe public void onGameStateChanged(GameStateChanged event) { From 0a8514fa5f8bc9927716d1b9d2b0d8dde8886317 Mon Sep 17 00:00:00 2001 From: pilino1234 Date: Wed, 14 Oct 2020 11:31:27 +0200 Subject: [PATCH 64/75] Worldhopper: Update league world color for Trailblazer Color source: https://secure.runescape.com/m=news/gielinor-gazette-september-2020?oldschool=1 --- .../net/runelite/client/plugins/worldhopper/WorldTableRow.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldTableRow.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldTableRow.java index d3f6315ba2..3abf40fa2f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldTableRow.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldTableRow.java @@ -62,7 +62,7 @@ class WorldTableRow extends JPanel private static final Color TOURNAMENT_WORLD = new Color(79, 145, 255); private static final Color MEMBERS_WORLD = new Color(210, 193, 53); private static final Color FREE_WORLD = new Color(200, 200, 200); - private static final Color LEAGUE_WORLD = new Color(157, 237, 1); + private static final Color LEAGUE_WORLD = new Color(133, 177, 178); static { From a2d693fc742d3b6351460564811b89889c40b279 Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 16 Oct 2020 12:16:15 -0400 Subject: [PATCH 65/75] chat commands: unregister bh and lms commands --- .../chatcommands/ChatCommandsPlugin.java | 6 ++- .../chatcommands/ChatCommandsPluginTest.java | 47 +++++++++---------- 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java index 81e4b4618d..5fce86024c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java @@ -202,7 +202,10 @@ public class ChatCommandsPlugin extends Plugin chatCommandManager.unregisterCommand(CMB_COMMAND_STRING); chatCommandManager.unregisterCommand(PRICE_COMMAND_STRING); chatCommandManager.unregisterCommand(LEVEL_COMMAND_STRING); + chatCommandManager.unregisterCommand(BOUNTY_HUNTER_HUNTER_COMMAND); + chatCommandManager.unregisterCommand(BOUNTY_HUNTER_ROGUE_COMMAND); chatCommandManager.unregisterCommand(CLUES_COMMAND_STRING); + chatCommandManager.unregisterCommand(LAST_MAN_STANDING_COMMAND); chatCommandManager.unregisterCommand(KILLCOUNT_COMMAND_STRING); chatCommandManager.unregisterCommand(QP_COMMAND_STRING); chatCommandManager.unregisterCommand(PB_COMMAND); @@ -1049,7 +1052,8 @@ public class ChatCommandsPlugin extends Plugin * @param chatMessage The chat message containing the command. * @param message The chat message */ - private void playerSkillLookup(ChatMessage chatMessage, String message) + @VisibleForTesting + void playerSkillLookup(ChatMessage chatMessage, String message) { if (!config.lvl()) { diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/chatcommands/ChatCommandsPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/chatcommands/ChatCommandsPluginTest.java index db61d333d7..7bdf59971c 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/chatcommands/ChatCommandsPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/chatcommands/ChatCommandsPluginTest.java @@ -24,12 +24,12 @@ */ package net.runelite.client.plugins.chatcommands; +import com.google.common.collect.Sets; import com.google.inject.Guice; import com.google.inject.testing.fieldbinder.Bind; import com.google.inject.testing.fieldbinder.BoundFieldModule; import java.io.IOException; import java.util.concurrent.ScheduledExecutorService; -import java.util.function.BiConsumer; import javax.inject.Inject; import net.runelite.api.ChatMessageType; import static net.runelite.api.ChatMessageType.FRIENDSCHATNOTIFICATION; @@ -54,7 +54,7 @@ import net.runelite.http.api.hiscore.HiscoreClient; import net.runelite.http.api.hiscore.HiscoreSkill; import net.runelite.http.api.hiscore.SingleHiscoreSkillResult; import net.runelite.http.api.hiscore.Skill; -import org.junit.After; +import static org.junit.Assert.assertEquals; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -64,6 +64,7 @@ import org.mockito.Mock; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -120,15 +121,28 @@ public class ChatCommandsPluginTest { Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this); - when(client.getUsername()).thenReturn(PLAYER_NAME); + Player player = mock(Player.class); + when(player.getName()).thenReturn(PLAYER_NAME); + when(client.getLocalPlayer()).thenReturn(player); - chatCommandsPlugin.startUp(); + when(client.getUsername()).thenReturn(PLAYER_NAME); } - @After - public void after() + @Test + public void testStartupShutdown() { + chatCommandsPlugin.startUp(); chatCommandsPlugin.shutDown(); + + ArgumentCaptor registerCaptor = ArgumentCaptor.forClass(String.class); + verify(chatCommandManager, atLeastOnce()).registerCommand(registerCaptor.capture(), any()); + verify(chatCommandManager, atLeastOnce()).registerCommandAsync(registerCaptor.capture(), any()); + verify(chatCommandManager, atLeastOnce()).registerCommandAsync(registerCaptor.capture(), any(), any()); + + ArgumentCaptor unregisterCaptor = ArgumentCaptor.forClass(String.class); + verify(chatCommandManager, atLeastOnce()).unregisterCommand(unregisterCaptor.capture()); + + assertEquals(Sets.newHashSet(registerCaptor.getAllValues()), Sets.newHashSet(unregisterCaptor.getAllValues())); } @Test @@ -448,10 +462,6 @@ public class ChatCommandsPluginTest @Test public void testAdventureLogCountersPage() { - Player player = mock(Player.class); - when(player.getName()).thenReturn(PLAYER_NAME); - when(client.getLocalPlayer()).thenReturn(player); - Widget advLogWidget = mock(Widget.class); Widget advLogExploitsTextWidget = mock(Widget.class); when(advLogWidget.getChild(ChatCommandsPlugin.ADV_LOG_EXPLOITS_TEXT_INDEX)).thenReturn(advLogExploitsTextWidget); @@ -505,10 +515,6 @@ public class ChatCommandsPluginTest @Test public void testAdventurerLogCountersPage2() { - Player player = mock(Player.class); - when(player.getName()).thenReturn(PLAYER_NAME); - when(client.getLocalPlayer()).thenReturn(player); - Widget advLogWidget = mock(Widget.class); Widget advLogExploitsTextWidget = mock(Widget.class); when(advLogWidget.getChild(ChatCommandsPlugin.ADV_LOG_EXPLOITS_TEXT_INDEX)).thenReturn(advLogExploitsTextWidget); @@ -559,10 +565,6 @@ public class ChatCommandsPluginTest @Test public void testNotYourAdventureLogCountersPage() { - Player player = mock(Player.class); - when(player.getName()).thenReturn(PLAYER_NAME); - when(client.getLocalPlayer()).thenReturn(player); - Widget advLogWidget = mock(Widget.class); Widget advLogExploitsTextWidget = mock(Widget.class); when(advLogWidget.getChild(ChatCommandsPlugin.ADV_LOG_EXPLOITS_TEXT_INDEX)).thenReturn(advLogExploitsTextWidget); @@ -585,14 +587,7 @@ public class ChatCommandsPluginTest @Test public void testPlayerSkillLookup() throws IOException { - Player player = mock(Player.class); - when(player.getName()).thenReturn(PLAYER_NAME); - when(client.getLocalPlayer()).thenReturn(player); - when(chatCommandsConfig.lvl()).thenReturn(true); - ArgumentCaptor> captor = ArgumentCaptor.forClass(BiConsumer.class); - verify(chatCommandManager).registerCommandAsync(eq("!lvl"), captor.capture()); - BiConsumer value = captor.getValue(); SingleHiscoreSkillResult skillResult = new SingleHiscoreSkillResult(); skillResult.setPlayer(PLAYER_NAME); @@ -606,7 +601,7 @@ public class ChatCommandsPluginTest chatMessage.setType(ChatMessageType.PUBLICCHAT); chatMessage.setName(PLAYER_NAME); chatMessage.setMessageNode(messageNode); - value.accept(chatMessage, "!lvl zulrah"); + chatCommandsPlugin.playerSkillLookup(chatMessage, "!lvl zulrah"); verify(messageNode).setRuneLiteFormatMessage("Level Zulrah: 1000 Rank: 10"); } From 901f294e06a46985bd8b179206685b87efebb04e Mon Sep 17 00:00:00 2001 From: dekvall Date: Tue, 6 Oct 2020 03:38:13 +0200 Subject: [PATCH 66/75] chat-commands: add league points command --- .../chatcommands/ChatCommandsConfig.java | 13 +++++++++- .../chatcommands/ChatCommandsPlugin.java | 24 ++++++++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsConfig.java index 30fc30ab96..4a8d46bd4e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsConfig.java @@ -157,6 +157,17 @@ public interface ChatCommandsConfig extends Config @ConfigItem( position = 11, + keyName = "lp", + name = "LP Command", + description = "Configures whether the League Points command is enabled
!lp" + ) + default boolean lp() + { + return true; + } + + @ConfigItem( + position = 12, keyName = "clearSingleWord", name = "Clear Single Word", description = "Enable hot key to clear single word at a time" @@ -167,7 +178,7 @@ public interface ChatCommandsConfig extends Config } @ConfigItem( - position = 12, + position = 13, keyName = "clearEntireChatBox", name = "Clear Chat Box", description = "Enable hotkey to clear entire chat box" diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java index 5fce86024c..0424e0a961 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java @@ -126,6 +126,7 @@ public class ChatCommandsPlugin extends Plugin private static final String PB_COMMAND = "!pb"; private static final String GC_COMMAND_STRING = "!gc"; private static final String DUEL_ARENA_COMMAND = "!duels"; + private static final String LEAGUE_POINTS_COMMAND = "!lp"; @VisibleForTesting static final int ADV_LOG_EXPLOITS_TEXT_INDEX = 1; @@ -184,6 +185,7 @@ public class ChatCommandsPlugin extends Plugin chatCommandManager.registerCommandAsync(BOUNTY_HUNTER_ROGUE_COMMAND, this::bountyHunterRogueLookup); chatCommandManager.registerCommandAsync(CLUES_COMMAND_STRING, this::clueLookup); chatCommandManager.registerCommandAsync(LAST_MAN_STANDING_COMMAND, this::lastManStandingLookup); + chatCommandManager.registerCommandAsync(LEAGUE_POINTS_COMMAND, this::leaguePointsLookup); chatCommandManager.registerCommandAsync(KILLCOUNT_COMMAND_STRING, this::killCountLookup, this::killCountSubmit); chatCommandManager.registerCommandAsync(QP_COMMAND_STRING, this::questPointsLookup, this::questPointsSubmit); chatCommandManager.registerCommandAsync(PB_COMMAND, this::personalBestLookup, this::personalBestSubmit); @@ -206,6 +208,7 @@ public class ChatCommandsPlugin extends Plugin chatCommandManager.unregisterCommand(BOUNTY_HUNTER_ROGUE_COMMAND); chatCommandManager.unregisterCommand(CLUES_COMMAND_STRING); chatCommandManager.unregisterCommand(LAST_MAN_STANDING_COMMAND); + chatCommandManager.unregisterCommand(LEAGUE_POINTS_COMMAND); chatCommandManager.unregisterCommand(KILLCOUNT_COMMAND_STRING); chatCommandManager.unregisterCommand(QP_COMMAND_STRING); chatCommandManager.unregisterCommand(PB_COMMAND); @@ -1218,6 +1221,16 @@ public class ChatCommandsPlugin extends Plugin } } + private void leaguePointsLookup(ChatMessage chatMessage, String message) + { + if (!config.lp()) + { + return; + } + + minigameLookup(chatMessage, HiscoreSkill.LEAGUE_POINTS); + } + private void bountyHunterHunterLookup(ChatMessage chatMessage, String message) { if (!config.bh()) @@ -1254,7 +1267,13 @@ public class ChatCommandsPlugin extends Plugin { final Skill hiscoreSkill; final HiscoreLookup lookup = getCorrectLookupFor(chatMessage); - final HiscoreResult result = hiscoreClient.lookup(lookup.getName(), lookup.getEndpoint()); + + // League points only exist on the league hiscores + final HiscoreEndpoint endPoint = minigame == HiscoreSkill.LEAGUE_POINTS ? + HiscoreEndpoint.LEAGUE : + lookup.getEndpoint(); + + final HiscoreResult result = hiscoreClient.lookup(lookup.getName(), endPoint); if (result == null) { @@ -1273,6 +1292,9 @@ public class ChatCommandsPlugin extends Plugin case LAST_MAN_STANDING: hiscoreSkill = result.getLastManStanding(); break; + case LEAGUE_POINTS: + hiscoreSkill = result.getLeaguePoints(); + break; default: log.warn("error looking up {} score: not implemented", minigame.getName().toLowerCase()); return; From 7a6c4ecc515b20b2f6007565fcd70d63cffae135 Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 16 Oct 2020 16:06:54 -0400 Subject: [PATCH 67/75] Add pluginhub user count tracking --- .../pluginhub/PluginHubController.java | 144 ++++++++++++++++++ .../src/main/resources/application.yaml | 4 +- .../externalplugins/ExternalPluginClient.java | 50 +++++- .../ExternalPluginManager.java | 40 +++-- 4 files changed, 216 insertions(+), 22 deletions(-) create mode 100644 http-service/src/main/java/net/runelite/http/service/pluginhub/PluginHubController.java diff --git a/http-service/src/main/java/net/runelite/http/service/pluginhub/PluginHubController.java b/http-service/src/main/java/net/runelite/http/service/pluginhub/PluginHubController.java new file mode 100644 index 0000000000..fde79591e1 --- /dev/null +++ b/http-service/src/main/java/net/runelite/http/service/pluginhub/PluginHubController.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2020, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.http.service.pluginhub; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import java.time.Duration; +import java.time.Instant; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.stream.IntStream; +import javax.servlet.http.HttpServletRequest; +import net.runelite.http.service.util.redis.RedisPool; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.CacheControl; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import redis.clients.jedis.Jedis; + +@RestController +@RequestMapping("/pluginhub") +public class PluginHubController +{ + @Value("${pluginhub.stats.days:7}") + private int days; + + @Value("${pluginhub.stats.expire:90}") + private int expireDays; + + @Autowired + private RedisPool redisPool; + + private final Cache pluginCache = CacheBuilder.newBuilder() + .maximumSize(512L) + .build(); + + private Map pluginCounts = Collections.emptyMap(); + + @GetMapping + public ResponseEntity> get() + { + if (pluginCounts.isEmpty()) + { + return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE) + .cacheControl(CacheControl.noCache()) + .build(); + } + + return ResponseEntity.ok() + .cacheControl(CacheControl.maxAge(30, TimeUnit.MINUTES).cachePublic()) + .body(pluginCounts); + } + + @PostMapping + public void submit(HttpServletRequest request, @RequestBody String[] plugins) + { + final String date = Instant.now().atZone(ZoneOffset.UTC).format(DateTimeFormatter.ISO_LOCAL_DATE); + final String ip = request.getHeader("X-Forwarded-For"); + try (Jedis jedis = redisPool.getResource()) + { + for (String plugin : plugins) + { + if (!plugin.matches("[a-z0-9-]+")) + { + continue; + } + + jedis.pfadd("pluginhub." + plugin + "." + date, ip); + + if (pluginCache.getIfPresent(plugin) == null) + { + jedis.sadd("pluginhub.plugins", plugin); + // additionally set the ttl on the hyperloglog since it might be a new key + jedis.expire("pluginhub." + plugin + "." + date, (int) (Duration.ofDays(expireDays).toMillis() / 1000L)); + + pluginCache.put(plugin, plugin); + } + } + } + } + + @Scheduled(fixedDelay = 1_8000_000, initialDelay = 30_000) // 30 minutes with 30 second initial delay + public void rebuildCounts() + { + Map counts = new HashMap<>(); + try (Jedis jedis = redisPool.getResource()) + { + Set plugins = jedis.smembers("pluginhub.plugins"); + ZonedDateTime time = Instant.now().atZone(ZoneOffset.UTC); + + for (String plugin : plugins) + { + // When called with multiple keys, pfcount returns the approximated + // cardinality of the union of the HyperLogLogs. We use this to determine + // the number of users in the last N days. + String[] keys = IntStream.range(0, days - 1) + .mapToObj(time::minusDays) + .map(zdt -> "pluginhub." + plugin + "." + zdt.format(DateTimeFormatter.ISO_LOCAL_DATE)) + .toArray(String[]::new); + long cnt = jedis.pfcount(keys); + if (cnt > 0) + { + counts.put(plugin, cnt); + } + } + } + pluginCounts = counts; + } +} diff --git a/http-service/src/main/resources/application.yaml b/http-service/src/main/resources/application.yaml index e3b8b07425..3e56fb066d 100644 --- a/http-service/src/main/resources/application.yaml +++ b/http-service/src/main/resources/application.yaml @@ -25,10 +25,10 @@ minio: secretkey: /PZCxzmsJzwCHYlogcymuprniGCaaLUOET2n6yMP bucket: runelite -# Redis client for temporary data storage +# Redis client redis: pool.size: 10 - host: http://localhost:6379 + host: tcp://localhost:6379 mongo: jndiName: java:comp/env/mongodb/runelite diff --git a/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginClient.java b/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginClient.java index cd023c8c3a..0df12bf1ed 100644 --- a/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginClient.java +++ b/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginClient.java @@ -39,23 +39,28 @@ import java.security.cert.CertificateFactory; import java.util.List; import javax.imageio.ImageIO; import javax.inject.Inject; +import lombok.extern.slf4j.Slf4j; import net.runelite.client.RuneLiteProperties; -import net.runelite.http.api.RuneLiteAPI; import net.runelite.client.util.VerificationException; +import net.runelite.http.api.RuneLiteAPI; +import okhttp3.Call; +import okhttp3.Callback; import okhttp3.HttpUrl; import okhttp3.OkHttpClient; import okhttp3.Request; +import okhttp3.RequestBody; import okhttp3.Response; import okio.BufferedSource; +@Slf4j public class ExternalPluginClient { - private final OkHttpClient cachingClient; + private final OkHttpClient okHttpClient; @Inject - public ExternalPluginClient(OkHttpClient cachingClient) + private ExternalPluginClient(OkHttpClient okHttpClient) { - this.cachingClient = cachingClient; + this.okHttpClient = okHttpClient; } public List downloadManifest() throws IOException, VerificationException @@ -64,7 +69,7 @@ public class ExternalPluginClient .newBuilder() .addPathSegments("manifest.js") .build(); - try (Response res = cachingClient.newCall(new Request.Builder().url(manifest).build()).execute()) + try (Response res = okHttpClient.newCall(new Request.Builder().url(manifest).build()).execute()) { if (res.code() != 200) { @@ -110,7 +115,7 @@ public class ExternalPluginClient .addPathSegment(plugin.getCommit() + ".png") .build(); - try (Response res = cachingClient.newCall(new Request.Builder().url(url).build()).execute()) + try (Response res = okHttpClient.newCall(new Request.Builder().url(url).build()).execute()) { byte[] bytes = res.body().bytes(); // We don't stream so the lock doesn't block the edt trying to load something at the same time @@ -134,4 +139,37 @@ public class ExternalPluginClient throw new RuntimeException(e); } } + + void submitPlugins(List plugins) + { + if (plugins.isEmpty()) + { + return; + } + + HttpUrl url = RuneLiteAPI.getApiBase().newBuilder() + .addPathSegment("pluginhub") + .build(); + + Request request = new Request.Builder() + .url(url) + .post(RequestBody.create(RuneLiteAPI.JSON, RuneLiteAPI.GSON.toJson(plugins))) + .build(); + + okHttpClient.newCall(request).enqueue(new Callback() + { + @Override + public void onFailure(Call call, IOException e) + { + log.debug("Error submitting plugins", e); + } + + @Override + public void onResponse(Call call, Response response) + { + log.debug("Submitted plugin list"); + response.close(); + } + }); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginManager.java b/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginManager.java index bd6baf39ac..504331f166 100644 --- a/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginManager.java +++ b/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginManager.java @@ -40,8 +40,10 @@ import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Random; import java.util.Set; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import java.util.function.Function; import javax.inject.Inject; import javax.inject.Named; @@ -80,23 +82,33 @@ public class ExternalPluginManager @Named("safeMode") private boolean safeMode; - @Inject - private ConfigManager configManager; + private final ConfigManager configManager; + private final ExternalPluginClient externalPluginClient; + private final ScheduledExecutorService executor; + private final PluginManager pluginManager; + private final EventBus eventBus; + private final OkHttpClient okHttpClient; @Inject - private ExternalPluginClient externalPluginClient; + private ExternalPluginManager( + ConfigManager configManager, + ExternalPluginClient externalPluginClient, + ScheduledExecutorService executor, + PluginManager pluginManager, + EventBus eventBus, + OkHttpClient okHttpClient + ) + { + this.configManager = configManager; + this.externalPluginClient = externalPluginClient; + this.executor = executor; + this.pluginManager = pluginManager; + this.eventBus = eventBus; + this.okHttpClient = okHttpClient; - @Inject - private PluginManager pluginManager; - - @Inject - private ScheduledExecutorService executor; - - @Inject - private EventBus eventBus; - - @Inject - private OkHttpClient okHttpClient; + executor.scheduleWithFixedDelay(() -> externalPluginClient.submitPlugins(getInstalledExternalPlugins()), + new Random().nextInt(60), 180, TimeUnit.MINUTES); + } public void loadExternalPlugins() throws PluginInstantiationException { From 41c185a9d71b4d799580f4fe17bbed61a0c29115 Mon Sep 17 00:00:00 2001 From: dekvall Date: Tue, 6 Oct 2020 04:48:48 +0200 Subject: [PATCH 68/75] xp-tracker: support league & dmm modifiers for kills remaining --- .../runelite/client/plugins/xptracker/XpState.java | 4 ++-- .../client/plugins/xptracker/XpTrackerPlugin.java | 3 ++- .../client/plugins/xptracker/XpWorldType.java | 14 ++++++++++---- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpState.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpState.java index 16602b0cb4..fac3c16410 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpState.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpState.java @@ -123,7 +123,7 @@ class XpState * @param npc currently interacted NPC * @param npcHealth health of currently interacted NPC */ - void updateNpcExperience(Skill skill, NPC npc, Integer npcHealth) + void updateNpcExperience(Skill skill, NPC npc, Integer npcHealth, int xpModifier) { if (npc == null || npc.getCombatLevel() <= 0 || npcHealth == null) { @@ -131,7 +131,7 @@ class XpState } final XpStateSingle state = getSkill(skill); - final int actionExp = (int) (npcHealth * getCombatXPModifier(skill)); + final int actionExp = (int) (npcHealth * getCombatXPModifier(skill) * xpModifier); final XpAction action = state.getXpAction(XpActionType.ACTOR_HEALTH); if (action.isActionsHistoryInitialized()) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java index a13fba8575..82c742e47f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java @@ -377,8 +377,9 @@ public class XpTrackerPlugin extends Plugin final Actor interacting = client.getLocalPlayer().getInteracting(); if (interacting instanceof NPC && COMBAT.contains(skill)) { + final int xpModifier = worldSetToType(client.getWorldType()).getXpModifier(); final NPC npc = (NPC) interacting; - xpState.updateNpcExperience(skill, npc, npcManager.getHealth(npc.getId())); + xpState.updateNpcExperience(skill, npc, npcManager.getHealth(npc.getId()), xpModifier); } final XpUpdateResult updateResult = xpState.updateSkill(skill, currentXp, startGoalXp, endGoalXp); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpWorldType.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpWorldType.java index b998a2f8b0..cc62695c92 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpWorldType.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpWorldType.java @@ -24,14 +24,20 @@ */ package net.runelite.client.plugins.xptracker; +import lombok.Getter; +import lombok.RequiredArgsConstructor; import net.runelite.api.WorldType; +@Getter +@RequiredArgsConstructor enum XpWorldType { - NORMAL, - TOURNEY, - DMM, - LEAGUE; + NORMAL(1), + TOURNEY(1), + DMM(5), + LEAGUE(5); + + private final int xpModifier; static XpWorldType of(WorldType type) { From d97bef593a809494d6913812813958303115a4a8 Mon Sep 17 00:00:00 2001 From: Broooklyn <54762282+Broooklyn@users.noreply.github.com> Date: Tue, 22 Sep 2020 16:58:15 -0400 Subject: [PATCH 69/75] chatcommands: add shorthand names for all agility courses --- .../chatcommands/ChatCommandsPlugin.java | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java index 0424e0a961..0f262f724b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java @@ -1731,6 +1731,112 @@ public class ChatCommandsPlugin extends Plugin case "ape atoll": return "Ape Atoll Agility"; + // Draynor Village Rooftop Course + case "draynor": + case "draynor agility": + return "Draynor Village Rooftop"; + + // Al-Kharid Rooftop Course + case "al kharid": + case "al kharid agility": + case "al-kharid": + case "al-kharid agility": + case "alkharid": + case "alkharid agility": + return "Al-Kharid Rooftop"; + + // Varrock Rooftop Course + case "varrock": + case "varrock agility": + return "Varrock Rooftop"; + + // Canifis Rooftop Course + case "canifis": + case "canifis agility": + return "Canifis Rooftop"; + + // Falador Rooftop Course + case "fally": + case "fally agility": + case "falador": + case "falador agility": + return "Falador Rooftop"; + + // Seers' Village Rooftop Course + case "seers": + case "seers agility": + case "seers village": + case "seers village agility": + case "seers'": + case "seers' agility": + case "seers' village": + case "seers' village agility": + case "seer's": + case "seer's agility": + case "seer's village": + case "seer's village agility": + return "Seers' Village Rooftop"; + + // Pollnivneach Rooftop Course + case "pollnivneach": + case "pollnivneach agility": + return "Pollnivneach Rooftop"; + + // Rellekka Rooftop Course + case "rellekka": + case "rellekka agility": + return "Rellekka Rooftop"; + + // Ardougne Rooftop Course + case "ardy": + case "ardy agility": + case "ardy rooftop": + case "ardougne": + case "ardougne agility": + return "Ardougne Rooftop"; + + // Agility Pyramid + case "ap": + case "pyramid": + return "Agility Pyramid"; + + // Barbarian Outpost + case "barb": + case "barb outpost": + return "Barbarian Outpost"; + + // Brimhaven Agility Arena + case "brimhaven": + case "brimhaven agility": + return "Agility Arena"; + + // Dorgesh-Kaan Agility Course + case "dorg": + case "dorgesh kaan": + case "dorgesh-kaan": + return "Dorgesh-Kaan Agility"; + + // Gnome Stronghold Agility Course + case "gnome stronghold": + return "Gnome Stronghold Agility"; + + // Penguin Agility + case "penguin": + return "Penguin Agility"; + + // Werewolf Agility + case "werewolf": + return "Werewolf Agility"; + + // Skullball + case "skullball": + return "Werewolf Skullball"; + + // Wilderness Agility Course + case "wildy": + case "wildy agility": + return "Wilderness Agility"; + default: return WordUtils.capitalize(boss); } From 9214480a02c98e0835f59ca181c373e689209d7b Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 26 Sep 2020 22:41:53 -0400 Subject: [PATCH 70/75] xptracker: change online tracker link to wiseoldman --- .../client/plugins/xptracker/XpInfoBox.java | 2 +- .../runelite/client/plugins/xptracker/XpPanel.java | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBox.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBox.java index fdd083262e..d2d5d8bf3c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBox.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBox.java @@ -122,7 +122,7 @@ class XpInfoBox extends JPanel container.setBackground(ColorScheme.DARKER_GRAY_COLOR); // Create open xp tracker menu - final JMenuItem openXpTracker = new JMenuItem("Open online tracker"); + final JMenuItem openXpTracker = new JMenuItem("Open Wise Old Man"); openXpTracker.addActionListener(e -> LinkBrowser.browse(XpPanel.buildXpTrackerUrl(client.getLocalPlayer(), skill))); // Create reset menu diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpPanel.java index 3c63ccf76e..e50cd3a050 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpPanel.java @@ -81,7 +81,7 @@ class XpPanel extends PluginPanel overallPanel.setVisible(false); // this will only become visible when the player gets exp // Create open xp tracker menu - final JMenuItem openXpTracker = new JMenuItem("Open online tracker"); + final JMenuItem openXpTracker = new JMenuItem("Open Wise Old Man"); openXpTracker.addActionListener(e -> LinkBrowser.browse(XpPanel.buildXpTrackerUrl(client.getLocalPlayer(), Skill.OVERALL))); // Create reset all menu @@ -149,13 +149,13 @@ class XpPanel extends PluginPanel return new HttpUrl.Builder() .scheme("https") - .host("runelite.net") - .addPathSegment("xp") - .addPathSegment("show") - .addPathSegment(skill.getName().toLowerCase()) + .host("wiseoldman.net") + .addPathSegment("players") .addPathSegment(player.getName()) - .addPathSegment("1week") - .addPathSegment("now") + .addPathSegment("gained") + .addPathSegment("skilling") + .addQueryParameter("metric", skill.getName().toLowerCase()) + .addQueryParameter("period", "week") .build() .toString(); } From 6457681c5577fb3c51a149833d66edf12a57487e Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 17 Oct 2020 19:36:36 -0400 Subject: [PATCH 71/75] progress bar: optimize bar drawing It is not necessary to draw the full bar background since it is immediately drawn over by the foreground --- .../client/ui/overlay/components/ProgressBarComponent.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/ProgressBarComponent.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/ProgressBarComponent.java index 01f52d6ad0..8ad38171ae 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/ProgressBarComponent.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/ProgressBarComponent.java @@ -114,7 +114,7 @@ public class ProgressBarComponent implements LayoutableRenderableEntity // Draw bar graphics.setColor(backgroundColor); - graphics.fillRect(barX, barY, width, height); + graphics.fillRect(barX + progressFill, barY, width - progressFill, height); graphics.setColor(foregroundColor); graphics.fillRect(barX, barY, progressFill, height); From b4cfb7085149f50fbe079b555d41d24fd4fc1505 Mon Sep 17 00:00:00 2001 From: sam Date: Tue, 22 Sep 2020 22:55:23 -0700 Subject: [PATCH 72/75] menu entry swapper: add withdraw/deposit swap for chambers of xeric storage --- .../net/runelite/api/widgets/WidgetID.java | 3 ++ .../MenuEntrySwapperPlugin.java | 28 ++++++++++++++----- .../menuentryswapper/ShiftDepositMode.java | 15 +++++----- .../menuentryswapper/ShiftWithdrawMode.java | 17 ++++++----- 4 files changed, 42 insertions(+), 21 deletions(-) diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java index 5463211ca9..9d56b8d797 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java @@ -156,6 +156,9 @@ public class WidgetID public static final int HALLOWED_SEPULCHRE_TIMER_GROUP_ID = 668; public static final int BANK_PIN_GROUP_ID = 213; public static final int HEALTH_OVERLAY_BAR_GROUP_ID = 303; + public static final int CHAMBERS_OF_XERIC_STORAGE_UNIT_PRIVATE_GROUP_ID = 271; + public static final int CHAMBERS_OF_XERIC_STORAGE_UNIT_SHARED_GROUP_ID = 550; + public static final int CHAMBERS_OF_XERIC_STORAGE_UNIT_INVENTORY_GROUP_ID = 551; static class WorldMap { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java index 453d66e611..c8ad0fb91e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java @@ -520,14 +520,18 @@ public class MenuEntrySwapperPlugin extends Plugin // is what builds the context menu row which is what the eventual click will use // Swap to shift-click deposit behavior - // Deposit- op 1 is the current withdraw amount 1/5/10/x for deposit box interface + // Deposit- op 1 is the current withdraw amount 1/5/10/x for deposit box interface and chambers of xeric storage unit. // Deposit- op 2 is the current withdraw amount 1/5/10/x for bank interface if (shiftModifier() && config.bankDepositShiftClick() != ShiftDepositMode.OFF - && menuEntryAdded.getType() == MenuAction.CC_OP.getId() && (menuEntryAdded.getIdentifier() == 2 || menuEntryAdded.getIdentifier() == 1) - && menuEntryAdded.getOption().startsWith("Deposit-")) + && menuEntryAdded.getType() == MenuAction.CC_OP.getId() + && (menuEntryAdded.getIdentifier() == 2 || menuEntryAdded.getIdentifier() == 1) + && (menuEntryAdded.getOption().startsWith("Deposit-") || menuEntryAdded.getOption().startsWith("Store") || menuEntryAdded.getOption().startsWith("Donate"))) { ShiftDepositMode shiftDepositMode = config.bankDepositShiftClick(); - final int opId = WidgetInfo.TO_GROUP(menuEntryAdded.getActionParam1()) == WidgetID.DEPOSIT_BOX_GROUP_ID ? shiftDepositMode.getIdentifierDepositBox() : shiftDepositMode.getIdentifier(); + final int widgetGroupId = WidgetInfo.TO_GROUP(menuEntryAdded.getActionParam1()); + final int opId = widgetGroupId == WidgetID.DEPOSIT_BOX_GROUP_ID ? shiftDepositMode.getIdentifierDepositBox() + : widgetGroupId == WidgetID.CHAMBERS_OF_XERIC_STORAGE_UNIT_INVENTORY_GROUP_ID ? shiftDepositMode.getIdentifierChambersStorageUnit() + : shiftDepositMode.getIdentifier(); final int actionId = opId >= 6 ? MenuAction.CC_OP_LOW_PRIORITY.getId() : MenuAction.CC_OP.getId(); bankModeSwap(actionId, opId); } @@ -536,11 +540,21 @@ public class MenuEntrySwapperPlugin extends Plugin // Deposit- op 1 is the current withdraw amount 1/5/10/x if (shiftModifier() && config.bankWithdrawShiftClick() != ShiftWithdrawMode.OFF && menuEntryAdded.getType() == MenuAction.CC_OP.getId() && menuEntryAdded.getIdentifier() == 1 - && menuEntryAdded.getOption().startsWith("Withdraw-")) + && menuEntryAdded.getOption().startsWith("Withdraw")) { ShiftWithdrawMode shiftWithdrawMode = config.bankWithdrawShiftClick(); - final int actionId = shiftWithdrawMode.getMenuAction().getId(); - final int opId = shiftWithdrawMode.getIdentifier(); + final int widgetGroupId = WidgetInfo.TO_GROUP(menuEntryAdded.getActionParam1()); + final int actionId, opId; + if (widgetGroupId == WidgetID.CHAMBERS_OF_XERIC_STORAGE_UNIT_PRIVATE_GROUP_ID || widgetGroupId == WidgetID.CHAMBERS_OF_XERIC_STORAGE_UNIT_SHARED_GROUP_ID) + { + actionId = MenuAction.CC_OP.getId(); + opId = shiftWithdrawMode.getIdentifierChambersStorageUnit(); + } + else + { + actionId = shiftWithdrawMode.getMenuAction().getId(); + opId = shiftWithdrawMode.getIdentifier(); + } bankModeSwap(actionId, opId); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/ShiftDepositMode.java b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/ShiftDepositMode.java index 7b6a9fea0f..add9d4afe8 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/ShiftDepositMode.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/ShiftDepositMode.java @@ -31,17 +31,18 @@ import lombok.RequiredArgsConstructor; @RequiredArgsConstructor public enum ShiftDepositMode { - DEPOSIT_1("Deposit-1", 3, 2), - DEPOSIT_5("Deposit-5", 4, 3), - DEPOSIT_10("Deposit-10", 5, 4), - DEPOSIT_X("Deposit-X", 6, 6), - DEPOSIT_ALL("Deposit-All", 8, 5), - EXTRA_OP("Eat/Wield/Etc.", 9, 0), - OFF("Off", 0, 0); + DEPOSIT_1("Deposit-1", 3, 2, 1), + DEPOSIT_5("Deposit-5", 4, 3, 2), + DEPOSIT_10("Deposit-10", 5, 4, 3), + DEPOSIT_X("Deposit-X", 6, 6, 5), + DEPOSIT_ALL("Deposit-All", 8, 5, 4), + EXTRA_OP("Eat/Wield/Etc.", 9, 0, 0), + OFF("Off", 0, 0, 0); private final String name; private final int identifier; private final int identifierDepositBox; + private final int identifierChambersStorageUnit; @Override public String toString() diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/ShiftWithdrawMode.java b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/ShiftWithdrawMode.java index 30260d8fd6..bbe09b1393 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/ShiftWithdrawMode.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/ShiftWithdrawMode.java @@ -32,17 +32,20 @@ import net.runelite.api.MenuAction; @RequiredArgsConstructor public enum ShiftWithdrawMode { - WITHDRAW_1("Withdraw-1", MenuAction.CC_OP, 2), - WITHDRAW_5("Withdraw-5", MenuAction.CC_OP, 3), - WITHDRAW_10("Withdraw-10", MenuAction.CC_OP, 4), - WITHDRAW_X("Withdraw-X", MenuAction.CC_OP, 5), - WITHDRAW_ALL("Withdraw-All", MenuAction.CC_OP_LOW_PRIORITY, 7), - WITHDRAW_ALL_BUT_1("Withdraw-All-But-1", MenuAction.CC_OP_LOW_PRIORITY, 8), - OFF("Off", MenuAction.UNKNOWN, 0); + WITHDRAW_1("Withdraw-1", MenuAction.CC_OP, 2, 1), + WITHDRAW_5("Withdraw-5", MenuAction.CC_OP, 3, 2), + WITHDRAW_10("Withdraw-10", MenuAction.CC_OP, 4, 3), + WITHDRAW_X("Withdraw-X", MenuAction.CC_OP, 5, 5), + WITHDRAW_ALL("Withdraw-All", MenuAction.CC_OP_LOW_PRIORITY, 7, 4), + // chambers of xeric storage units do not have an "all-but-1" option, so this option will choose "Withdraw-all" + // instead when using the storage unit. + WITHDRAW_ALL_BUT_1("Withdraw-All-But-1", MenuAction.CC_OP_LOW_PRIORITY, 8, 4), + OFF("Off", MenuAction.UNKNOWN, 0, 0); private final String name; private final MenuAction menuAction; private final int identifier; + private final int identifierChambersStorageUnit; @Override public String toString() From cfa6942678647d1ef7934adc5c779e85077dc9fa Mon Sep 17 00:00:00 2001 From: Max Weber Date: Sun, 18 Oct 2020 16:42:08 -0600 Subject: [PATCH 73/75] kourendlibrary: add config to hide dark manuscripts --- .../plugins/kourendlibrary/KourendLibraryConfig.java | 10 ++++++++++ .../plugins/kourendlibrary/KourendLibraryOverlay.java | 3 ++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryConfig.java index 6c2bd860d3..e52f277da9 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryConfig.java @@ -63,6 +63,16 @@ public interface KourendLibraryConfig extends Config return false; } + @ConfigItem( + keyName = "hideDarkManuscript", + name = "Hide Dark Manuscript", + description = "Whether to hide Dark Manuscripts" + ) + default boolean hideDarkManuscript() + { + return false; + } + @ConfigItem( keyName = "showTutorialOverlay", name = "Show tutorial overlay", diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryOverlay.java index 31d304e782..8c0ec25e50 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryOverlay.java @@ -127,7 +127,8 @@ class KourendLibraryOverlay extends Overlay bookIsKnown = true; } - if (book == Book.VARLAMORE_ENVOY && config.hideVarlamoreEnvoy()) + if ((book == Book.VARLAMORE_ENVOY && config.hideVarlamoreEnvoy()) + || (book != null && book.isDarkManuscript() && config.hideDarkManuscript())) { continue; } From 204b79ae17d98ca75713c705410f57df14af92f1 Mon Sep 17 00:00:00 2001 From: Trey Date: Tue, 6 Oct 2020 07:00:53 +0200 Subject: [PATCH 74/75] kourendlibrary: hide Varlamore Envoy outside of the Depths of Despair --- .../plugins/kourendlibrary/KourendLibraryConfig.java | 8 ++++---- .../plugins/kourendlibrary/KourendLibraryOverlay.java | 2 +- .../plugins/kourendlibrary/KourendLibraryPanel.java | 6 ++---- .../plugins/kourendlibrary/KourendLibraryPlugin.java | 11 +++++++++++ 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryConfig.java index e52f277da9..c143ac28b4 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryConfig.java @@ -54,11 +54,11 @@ public interface KourendLibraryConfig extends Config } @ConfigItem( - keyName = "hideVarlamoreEnvoy", - name = "Hide Varlamore Envoy", - description = "Whether to hide Varlamore Envoy, as it is only required in the Depths of Despair quest, and is never asked for." + keyName = "alwaysShowVarlamoreEnvoy", + name = "Show Varlamore Envoy", + description = "Varlamore Envoy is only needed during the Depths of Despair, and is never asked for" ) - default boolean hideVarlamoreEnvoy() + default boolean alwaysShowVarlamoreEnvoy() { return false; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryOverlay.java index 8c0ec25e50..58c54dc7d5 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryOverlay.java @@ -127,7 +127,7 @@ class KourendLibraryOverlay extends Overlay bookIsKnown = true; } - if ((book == Book.VARLAMORE_ENVOY && config.hideVarlamoreEnvoy()) + if ((book == Book.VARLAMORE_ENVOY && !plugin.showVarlamoreEnvoy()) || (book != null && book.isDarkManuscript() && config.hideDarkManuscript())) { continue; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPanel.java index bf5cffb417..73d5a1937f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPanel.java @@ -53,7 +53,6 @@ class KourendLibraryPanel extends PluginPanel private static final ImageIcon RESET_HOVER_ICON; private final KourendLibraryPlugin plugin; - private final KourendLibraryConfig config; private final Library library; private final HashMap bookPanels = new HashMap<>(); @@ -66,12 +65,11 @@ class KourendLibraryPanel extends PluginPanel } @Inject - KourendLibraryPanel(KourendLibraryPlugin plugin, KourendLibraryConfig config, Library library) + KourendLibraryPanel(KourendLibraryPlugin plugin, Library library) { super(); this.plugin = plugin; - this.config = config; this.library = library; } @@ -89,7 +87,7 @@ class KourendLibraryPanel extends PluginPanel c.gridy = 0; Stream.of(Book.values()) .filter(b -> !b.isDarkManuscript()) - .filter(b -> !config.hideVarlamoreEnvoy() || b != Book.VARLAMORE_ENVOY) + .filter(b -> b != Book.VARLAMORE_ENVOY || plugin.showVarlamoreEnvoy()) .sorted(Comparator.comparing(Book::getShortName)) .forEach(b -> { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java index b08a7f2d9a..d0bd3c11cf 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java @@ -59,6 +59,8 @@ import net.runelite.api.events.ItemContainerChanged; import net.runelite.api.events.MenuOptionClicked; import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetInfo; +import net.runelite.api.Quest; +import net.runelite.api.QuestState; import net.runelite.client.config.ConfigManager; import net.runelite.client.eventbus.Subscribe; import net.runelite.client.game.ItemManager; @@ -113,6 +115,7 @@ public class KourendLibraryPlugin extends Plugin private WorldPoint lastBookcaseClick = null; private WorldPoint lastBookcaseAnimatedOn = null; private EnumSet playerBooks = null; + private QuestState depthsOfDespairState = QuestState.FINISHED; @Getter(AccessLevel.PACKAGE) private final Set npcsToMark = new HashSet<>(); @@ -262,6 +265,7 @@ public class KourendLibraryPlugin extends Plugin { if (inRegion) { + panel.reload(); clientToolbar.addNavigation(navButton); } else @@ -277,6 +281,8 @@ public class KourendLibraryPlugin extends Plugin return; } + depthsOfDespairState = Quest.THE_DEPTHS_OF_DESPAIR.getState(client); + if (lastBookcaseAnimatedOn != null) { Widget find = client.getWidget(WidgetInfo.DIALOG_SPRITE_SPRITE); @@ -412,6 +418,11 @@ public class KourendLibraryPlugin extends Plugin } } + boolean showVarlamoreEnvoy() + { + return config.alwaysShowVarlamoreEnvoy() || depthsOfDespairState == QuestState.IN_PROGRESS; + } + static boolean isLibraryCustomer(int npcId) { return npcId == NpcID.VILLIA || npcId == NpcID.PROFESSOR_GRACKLEBONE || npcId == NpcID.SAM_7049; From 274df60d3c3e2a4615e03d375f427052875cd535 Mon Sep 17 00:00:00 2001 From: SirGirion Date: Mon, 19 Oct 2020 21:46:14 -0400 Subject: [PATCH 75/75] timetracking: add compost bin time trackers (#12619) --- .../main/java/net/runelite/api/Varbits.java | 1 + .../timetracking/farming/CropState.java | 4 +- .../timetracking/farming/FarmingTabPanel.java | 6 + .../timetracking/farming/FarmingWorld.java | 19 +- .../farming/PatchImplementation.java | 176 ++++++++++++++++++ .../timetracking/farming/PatchState.java | 2 +- .../plugins/timetracking/farming/Produce.java | 14 +- .../farming/PatchImplementationTest.java | 5 +- 8 files changed, 216 insertions(+), 11 deletions(-) diff --git a/runelite-api/src/main/java/net/runelite/api/Varbits.java b/runelite-api/src/main/java/net/runelite/api/Varbits.java index 73e12683dd..3277622f4d 100644 --- a/runelite-api/src/main/java/net/runelite/api/Varbits.java +++ b/runelite-api/src/main/java/net/runelite/api/Varbits.java @@ -439,6 +439,7 @@ public enum Varbits FARMING_7909(7909), FARMING_7910(7910), FARMING_7911(7911), + FARMING_7912(7912), /** * Transmog controllers for grapes diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/CropState.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/CropState.java index 2d9ffced83..50190234e8 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/CropState.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/CropState.java @@ -36,7 +36,9 @@ public enum CropState HARVESTABLE(ColorScheme.PROGRESS_COMPLETE_COLOR), GROWING(ColorScheme.PROGRESS_COMPLETE_COLOR), DISEASED(ColorScheme.PROGRESS_INPROGRESS_COLOR), - DEAD(ColorScheme.PROGRESS_ERROR_COLOR); + DEAD(ColorScheme.PROGRESS_ERROR_COLOR), + EMPTY(ColorScheme.MEDIUM_GRAY_COLOR), + FILLING(ColorScheme.PROGRESS_INPROGRESS_COLOR); private final Color color; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingTabPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingTabPanel.java index 870ca5020f..1c068790c8 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingTabPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingTabPanel.java @@ -177,6 +177,12 @@ public class FarmingTabPanel extends TabContentPanel case DEAD: panel.getEstimate().setText("Dead"); break; + case EMPTY: + panel.getEstimate().setText("Empty"); + break; + case FILLING: + panel.getEstimate().setText("Filling"); + break; } /* Hide any fully grown weeds' progress bar. */ diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingWorld.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingWorld.java index 6ba40c76b1..9d9812b319 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingWorld.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingWorld.java @@ -73,7 +73,8 @@ class FarmingWorld new FarmingPatch("North", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER), - new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB) + new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB), + new FarmingPatch("", Varbits.FARMING_4775, PatchImplementation.COMPOST) )); add(new FarmingRegion("Brimhaven", 11058, @@ -85,7 +86,8 @@ class FarmingWorld new FarmingPatch("North", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER), - new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB) + new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB), + new FarmingPatch("", Varbits.FARMING_4775, PatchImplementation.COMPOST) ) { @Override @@ -133,7 +135,8 @@ class FarmingWorld new FarmingPatch("North West", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("South East", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER), - new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB) + new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB), + new FarmingPatch("", Varbits.FARMING_4775, PatchImplementation.COMPOST) ) { @Override @@ -167,7 +170,8 @@ class FarmingWorld new FarmingPatch("North East", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("South West", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER), - new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB) + new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB), + new FarmingPatch("", Varbits.FARMING_4775, PatchImplementation.COMPOST) )); add(new FarmingRegion("Kourend", 6711, new FarmingPatch("", Varbits.FARMING_7904, PatchImplementation.SPIRIT_TREE) @@ -205,7 +209,8 @@ class FarmingWorld new FarmingPatch("North West", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("South East", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER), - new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB) + new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB), + new FarmingPatch("", Varbits.FARMING_4775, PatchImplementation.COMPOST) )); @@ -260,6 +265,7 @@ class FarmingWorld new FarmingPatch("", Varbits.FARMING_7906, PatchImplementation.FLOWER), new FarmingPatch("North", Varbits.FARMING_4773, PatchImplementation.ALLOTMENT), new FarmingPatch("South", Varbits.FARMING_4774, PatchImplementation.ALLOTMENT), + new FarmingPatch("", Varbits.FARMING_7912, PatchImplementation.GIANT_COMPOST), new FarmingPatch("", Varbits.FARMING_7904, PatchImplementation.CACTUS), new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.SPIRIT_TREE), new FarmingPatch("", Varbits.FARMING_7909, PatchImplementation.FRUIT_TREE), @@ -272,7 +278,8 @@ class FarmingWorld new FarmingPatch("North", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER), - new FarmingPatch("", Varbits.FARMING_4775, PatchImplementation.CRYSTAL_TREE) + new FarmingPatch("", Varbits.FARMING_4775, PatchImplementation.CRYSTAL_TREE), + new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.COMPOST) // TODO: Find correct varbit )); // Finalize diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/PatchImplementation.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/PatchImplementation.java index 401269d34d..c4af8c2596 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/PatchImplementation.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/PatchImplementation.java @@ -2603,6 +2603,182 @@ public enum PatchImplementation } return null; } + }, + COMPOST(Tab.SPECIAL, "Compost Bin") + { + @Override + PatchState forVarbitValue(int value) + { + if (value == 0) + { + // Compost bin[Examine] 7808 + return new PatchState(Produce.EMPTY_COMPOST_BIN, CropState.EMPTY, 0); + } + if (value >= 1 && value <= 15) + { + // Compost bin[Examine,Dump] 3830 + return new PatchState(Produce.COMPOST, CropState.FILLING, value - 1); + } + if (value >= 16 && value <= 30) + { + // Compost bin[Take,Examine,Dump] 19050,19051,19052,19053,19054,19055,19056,19057,19058,19059,19060,19061,19062,19063,19064 + return new PatchState(Produce.COMPOST, CropState.HARVESTABLE, value - 16); + } + if (value == 31 || value == 32) + { + // Compost bin[Open,Examine,Dump] 3849,3849 + return new PatchState(Produce.COMPOST, CropState.GROWING, value - 31); + } + if (value >= 33 && value <= 47) + { + // Compost bin[Examine,Dump] 19066,19067,19068,19069,19070,19071,19072,19073,19074,19075,19076,19077,19078,19079,19080 + return new PatchState(Produce.SUPERCOMPOST, CropState.FILLING, value - 33); // 33 means there is 1 item + } + if (value >= 48 && value <= 62) + { + // Compost bin[Take,Examine,Dump] 19097 + return new PatchState(Produce.SUPERCOMPOST, CropState.HARVESTABLE, value - 48); // 48 means there is only 1 bucket left + } + if (value == 94) + { + // Compost bin[Open,Examine,Dump] 3850 + return new PatchState(Produce.COMPOST, CropState.GROWING, Produce.COMPOST.getStages() - 1); + } + if (value == 95 || value == 96) + { + // Compost bin[Open,Examine,Dump] 19082,19082 + return new PatchState(Produce.SUPERCOMPOST, CropState.GROWING, value - 95); + } + if (value == 126) + { + // Compost bin[Open,Examine,Dump] 19082 + return new PatchState(Produce.SUPERCOMPOST, CropState.GROWING, Produce.SUPERCOMPOST.getStages() - 1); + } + if (value >= 129 && value <= 143) + { + // Giant compost bin[Close,Examine,Dump] 19098..19111,20099 + return new PatchState(Produce.ROTTEN_TOMATO, CropState.FILLING, value - 129); + } + if (value >= 144 && value <= 158) + { + // Giant compost bin[Take,Examine,Dump] 20102..20116 + return new PatchState(Produce.ROTTEN_TOMATO, CropState.HARVESTABLE, value - 144); + } + if (value >= 159 && value <= 160) + { + // Giant compost bin[Open,Examine,Dump] 20100 + return new PatchState(Produce.ROTTEN_TOMATO, CropState.GROWING, value - 159); + } + if (value >= 176 && value <= 190) + { + // Compost bin[Take,Examine,Dump] 30502,30503,30504,30505,30506,30507,30508,30509,30510,30511,30512,30513,30514,30515,30516 + return new PatchState(Produce.ULTRACOMPOST, CropState.HARVESTABLE, value - 176); + } + return null; + } + }, + GIANT_COMPOST(Tab.SPECIAL, "Giant Compost Bin") + { + @Override + PatchState forVarbitValue(int value) + { + if (value == 0) + { + // Big compost bin[Examine] 33762 + return new PatchState(Produce.EMPTY_GIANT_COMPOST_BIN, CropState.EMPTY, 0); + } + if (value >= 1 && value <= 15) + { + // Big compost bin[Examine,Dump] 33763..33777 + return new PatchState(Produce.GIANT_COMPOST, CropState.FILLING, value - 1); + } + if (value >= 16 && value <= 30) + { + // Big compost bin[Take,Examine,Dump] 33795..33809 + return new PatchState(Produce.GIANT_COMPOST, CropState.HARVESTABLE, value - 16); + } + if (value >= 33 && value <= 47) + { + // Big compost bin[Examine,Dump] 33825..33839 + return new PatchState(Produce.GIANT_SUPERCOMPOST, CropState.FILLING, value - 33); + } + if (value >= 48 && value <= 62) + { + // Big compost bin[Take,Examine,Dump] 33857..33871 + return new PatchState(Produce.GIANT_SUPERCOMPOST, CropState.HARVESTABLE, value - 48); + } + if (value >= 63 && value <= 77) + { + // Big compost bin[Examine,Dump] 33778..33792 + return new PatchState(Produce.GIANT_COMPOST, CropState.FILLING, 15 + value - 63); + } + if (value >= 78 && value <= 92) + { + // Giant compost bin[Take,Examine,Dump] 33810..33824 + return new PatchState(Produce.GIANT_COMPOST, CropState.HARVESTABLE, 15 + value - 78); + } + if (value == 93) + { + // Giant compost bin[Open,Examine,Dump] 33794 + return new PatchState(Produce.GIANT_COMPOST, CropState.GROWING, Produce.GIANT_COMPOST.getStages() - 1); + } + if (value >= 97 && value <= 99) + { + // Giant compost bin[Open,Examin,Dump] 33855,33855 + return new PatchState(Produce.GIANT_SUPERCOMPOST, CropState.GROWING, value - 97); // Once closed, starts rotting (super compost) + } + if (value >= 100 && value <= 114) + { + // Giant compost bin[Take,Examine,Dump] 33872..33886 + return new PatchState(Produce.GIANT_SUPERCOMPOST, CropState.HARVESTABLE, 15 + value - 100); + } + if (value >= 127 && value <= 128) + { + // Giant compost bin[Open,Examine,Dump] 33793,33793 + return new PatchState(Produce.GIANT_COMPOST, CropState.GROWING, value - 127); + } + if (value >= 129 && value <= 143) + { + // Giant compost bin[Close,Examine,Dump] 33887..33901 + return new PatchState(Produce.GIANT_ROTTEN_TOMATO, CropState.FILLING, value - 129); + } + if (value >= 144 && value <= 158) + { + // Giant compost bin[Take,Examine,Dump] 33919..33933 + return new PatchState(Produce.GIANT_ROTTEN_TOMATO, CropState.HARVESTABLE, value - 144); + } + if (value >= 159 && value <= 160) + { + // Giant compost bin[Open,Examine,Dump] 33917,33917 + return new PatchState(Produce.GIANT_ROTTEN_TOMATO, CropState.GROWING, value - 159); + } + if (value >= 161 && value <= 175) + { + // Giant compost bin[Examine,Dump] 33840..33854 + return new PatchState(Produce.GIANT_SUPERCOMPOST, CropState.FILLING, 15 + value - 161); // 161 means there are 16 items + } + if (value >= 176 && value <= 205) + { + // Giant compost bin[Take,Examine,Dump] 33957..33986 + return new PatchState(Produce.GIANT_ULTRACOMPOST, CropState.HARVESTABLE, value - 176); + } + if (value >= 207 && value <= 221) + { + // Giant compost bin[Take,Examine,Dump] 33934..33948 + return new PatchState(Produce.GIANT_ROTTEN_TOMATO, CropState.HARVESTABLE, 15 + value - 207); + } + if (value == 222) + { + // Giant compost bin[Open,Examine,Dump] 33918 + return new PatchState(Produce.GIANT_ROTTEN_TOMATO, CropState.GROWING, Produce.GIANT_ROTTEN_TOMATO.getStages() - 1); + } + if (value >= 223 && value <= 237) + { + // Giant compost bin[Close,Examine,Dump] 33902..33916 + return new PatchState(Produce.GIANT_ROTTEN_TOMATO, CropState.FILLING, 15 + value - 223); + } + return null; + } }; @Nullable diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/PatchState.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/PatchState.java index 2857882b3f..1f2655c248 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/PatchState.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/PatchState.java @@ -35,7 +35,7 @@ class PatchState int getStages() { - return cropState == CropState.HARVESTABLE ? produce.getHarvestStages() : produce.getStages(); + return cropState == CropState.HARVESTABLE || cropState == CropState.FILLING ? produce.getHarvestStages() : produce.getStages(); } int getTickRate() diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/Produce.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/Produce.java index 8909a54b92..ae19316f54 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/Produce.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/Produce.java @@ -131,7 +131,19 @@ public enum Produce CELASTRUS("Celastrus", "Celastrus tree", PatchImplementation.CELASTRUS, ItemID.BATTLESTAFF, 160, 6, 0, 4), REDWOOD("Redwood", "Redwood tree", PatchImplementation.REDWOOD, ItemID.REDWOOD_LOGS, 640, 11), HESPORI("Hespori", NullItemID.NULL_23044, 640, 4, 0, 2), - CRYSTAL_TREE("Crystal tree", ItemID.CRYSTAL_SHARDS, 80, 7); + CRYSTAL_TREE("Crystal tree", ItemID.CRYSTAL_SHARDS, 80, 7), + + // Compost bins + EMPTY_COMPOST_BIN("Compost Bin", PatchImplementation.COMPOST, ItemID.COMPOST_BIN, 0, 1, 0, 0), // Dummy produce for the empty state + COMPOST("Compost", PatchImplementation.COMPOST, ItemID.COMPOST, 40, 3, 0, 15), + SUPERCOMPOST("Supercompost", PatchImplementation.COMPOST, ItemID.SUPERCOMPOST, 40, 3, 0, 15), + ULTRACOMPOST("Ultracompost", PatchImplementation.COMPOST, ItemID.ULTRACOMPOST, 0, 3, 0, 15), // Ultra doesn't compost, + ROTTEN_TOMATO("Rotten Tomato", PatchImplementation.COMPOST, ItemID.ROTTEN_TOMATO, 40, 3, 0, 15), + EMPTY_GIANT_COMPOST_BIN("Giant Compost Bin", PatchImplementation.COMPOST, ItemID.COMPOST_BIN, 0, 1, 0, 0), // Dummy produce for the empty state + GIANT_COMPOST("Compost", PatchImplementation.GIANT_COMPOST, ItemID.COMPOST, 40, 3, 0, 30), + GIANT_SUPERCOMPOST("Supercompost", PatchImplementation.GIANT_COMPOST, ItemID.SUPERCOMPOST, 40, 3, 0, 30), + GIANT_ULTRACOMPOST("Ultracompost", PatchImplementation.GIANT_COMPOST, ItemID.ULTRACOMPOST, 0, 3, 0, 30), // Ultra doesn't compost + GIANT_ROTTEN_TOMATO("Rotten Tomato", PatchImplementation.GIANT_COMPOST, ItemID.ROTTEN_TOMATO, 40, 3, 0, 30); /** * User-visible name diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/timetracking/farming/PatchImplementationTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/timetracking/farming/PatchImplementationTest.java index 6ed2f81f60..66ed12bca1 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/timetracking/farming/PatchImplementationTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/timetracking/farming/PatchImplementationTest.java @@ -52,7 +52,7 @@ public class PatchImplementationTest collector.checkThat(pfx + ": produce", s.getProduce(), notNullValue()); collector.checkThat(pfx + ": negative stage", s.getStage(), greaterThanOrEqualTo(0)); int stages = s.getProduce().getStages(); - if (s.getCropState() == CropState.HARVESTABLE) + if (s.getCropState() == CropState.HARVESTABLE || s.getCropState() == CropState.FILLING) { stages = s.getProduce().getHarvestStages(); } @@ -63,7 +63,8 @@ public class PatchImplementationTest } if (s.getCropState() == CropState.GROWING && s.getProduce() != Produce.WEEDS && s.getStage() < stages) { - harvestStages.computeIfAbsent(s.getProduce(), k -> new boolean[s.getProduce().getStages()])[s.getStage()] = true; + final int realStages = stages; + harvestStages.computeIfAbsent(s.getProduce(), k -> new boolean[realStages])[s.getStage()] = true; } } }