From 559410b53768088b6fd920b9a5aa15715d78a465 Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 4 Jun 2021 16:13:34 -0400 Subject: [PATCH 01/16] mta: clear hint arrow when leaving enchantment and alchemist room --- .../runelite/client/plugins/mta/alchemy/AlchemyRoom.java | 8 ++++++++ .../client/plugins/mta/enchantment/EnchantmentRoom.java | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/mta/alchemy/AlchemyRoom.java b/runelite-client/src/main/java/net/runelite/client/plugins/mta/alchemy/AlchemyRoom.java index d76c24a838..72ed002a2a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/mta/alchemy/AlchemyRoom.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/mta/alchemy/AlchemyRoom.java @@ -96,6 +96,7 @@ public class AlchemyRoom extends MTARoom private AlchemyItem best; private Cupboard suggestion; + private boolean hintSet; @Inject private AlchemyRoom(Client client, MTAConfig config, MTAPlugin plugin, ItemManager itemManager, InfoBoxManager infoBoxManager) @@ -221,6 +222,11 @@ public class AlchemyRoom extends MTARoom if (!inside()) { reset(); + if (hintSet) + { + client.clearHintArrow(); + hintSet = false; + } } } } @@ -381,6 +387,7 @@ public class AlchemyRoom extends MTARoom { client.setHintArrow(object.getWorldLocation()); found = true; + hintSet = true; } BufferedImage image = itemManager.getImage(alchemyItem.getId()); @@ -395,6 +402,7 @@ public class AlchemyRoom extends MTARoom if (!found && suggestion != null) { client.setHintArrow(suggestion.gameObject.getWorldLocation()); + hintSet = true; } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/mta/enchantment/EnchantmentRoom.java b/runelite-client/src/main/java/net/runelite/client/plugins/mta/enchantment/EnchantmentRoom.java index 5989d79d8a..7dd0a017a9 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/mta/enchantment/EnchantmentRoom.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/mta/enchantment/EnchantmentRoom.java @@ -50,6 +50,7 @@ public class EnchantmentRoom extends MTARoom private final Client client; private final List dragonstones = new ArrayList<>(); + private boolean hintSet; @Inject private EnchantmentRoom(MTAConfig config, Client client) @@ -64,6 +65,11 @@ public class EnchantmentRoom extends MTARoom if (gameStateChanged.getGameState() == GameState.LOADING) { dragonstones.clear(); + if (hintSet) + { + client.clearHintArrow(); + hintSet = false; + } } } @@ -79,10 +85,12 @@ public class EnchantmentRoom extends MTARoom if (nearest != null) { client.setHintArrow(nearest); + hintSet = true; } else { client.clearHintArrow(); + hintSet = false; } } From eddb7276a2693b56e189d5221bc1b0496d147236 Mon Sep 17 00:00:00 2001 From: LlemonDuck Date: Fri, 4 Jun 2021 19:15:11 -0400 Subject: [PATCH 02/16] idle notifier: reset six hour notif on startup Six hour notification could trigger incorrectly when the plugin was enabled if the player disabled -> logged out -> logged in -> enabled, since the notification timer was not reset. --- .../client/plugins/idlenotifier/IdleNotifierPlugin.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java index f25c479d77..bfc455b405 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java @@ -110,6 +110,13 @@ public class IdleNotifierPlugin extends Plugin return configManager.getConfig(IdleNotifierConfig.class); } + @Override + protected void startUp() throws Exception + { + // can't tell when 6hr will be if enabled while already logged in + sixHourWarningTime = null; + } + @Subscribe public void onAnimationChanged(AnimationChanged event) { From 879e3ce73789c8be38f993d52d38e11644868441 Mon Sep 17 00:00:00 2001 From: SkylerPIlot Date: Sat, 5 Jun 2021 14:18:45 -0700 Subject: [PATCH 03/16] spec counter: add Alchemical Hydra This keeps the counter up when the hydra changes phases Signed-off-by: SkylerPIlot --- .../java/net/runelite/client/plugins/specialcounter/Boss.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/Boss.java b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/Boss.java index 295194a961..e0ba59d3d6 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/Boss.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/Boss.java @@ -45,7 +45,8 @@ enum Boss KING_BLACK_DRAGON(NpcID.KING_BLACK_DRAGON, NpcID.KING_BLACK_DRAGON_2642, NpcID.KING_BLACK_DRAGON_6502), KRIL_TSUROTH(NpcID.KRIL_TSUTSAROTH, NpcID.KRIL_TSUTSAROTH_6495), VENETENATIS(NpcID.VENENATIS, NpcID.VENENATIS_6610), - VETION(NpcID.VETION, NpcID.VETION_REBORN); + VETION(NpcID.VETION, NpcID.VETION_REBORN), + ALCHEMICAL_HYDRA(NpcID.ALCHEMICAL_HYDRA, NpcID.ALCHEMICAL_HYDRA_8616, NpcID.ALCHEMICAL_HYDRA_8617, NpcID.ALCHEMICAL_HYDRA_8618, NpcID.ALCHEMICAL_HYDRA_8619, NpcID.ALCHEMICAL_HYDRA_8620, NpcID.ALCHEMICAL_HYDRA_8621, NpcID.ALCHEMICAL_HYDRA_8622, NpcID.ALCHEMICAL_HYDRA_8634); private final Set ids; From d3a2e4bbc2950d9cdd8cc08b65da2ee494bbaa13 Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 5 Jun 2021 10:26:18 -0400 Subject: [PATCH 04/16] config manager: disallow : in key names The config service rewrites . -> : and : -> . for storage since mongodb does not support . in field names. This causes any keys set with : to get changed after going to the config service and back. --- .../src/main/java/net/runelite/client/config/ConfigManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java b/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java index 0a821d9a7b..7e66bb0fdc 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java +++ b/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java @@ -485,7 +485,7 @@ public class ConfigManager public void setConfiguration(String groupName, String profile, String key, @NonNull String value) { - if (Strings.isNullOrEmpty(groupName) || Strings.isNullOrEmpty(key)) + if (Strings.isNullOrEmpty(groupName) || Strings.isNullOrEmpty(key) || key.indexOf(':') != -1) { throw new IllegalArgumentException(); } From c271580e74a659de31eb2169ca7e1ac27a080bd7 Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 4 Jun 2021 23:40:45 -0400 Subject: [PATCH 05/16] chat commands: fix tob hm/sm kc/pb tracking The : in the boss name breaks for logged in users, so remap it to a clean name instead. Also add short names. --- .../chatcommands/ChatCommandsPlugin.java | 46 ++++++++++++++++--- .../chatcommands/ChatCommandsPluginTest.java | 15 ++++++ 2 files changed, 55 insertions(+), 6 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 33f282d513..8acde22888 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 @@ -98,7 +98,7 @@ public class ChatCommandsPlugin extends Plugin private static final String COX_TEAM_SIZES = "(?:\\d+(?:\\+|-\\d+)? players|Solo)"; private static final Pattern RAIDS_PB_PATTERN = Pattern.compile("Congratulations - your raid is complete!
Team size: " + COX_TEAM_SIZES + " Duration: (?[0-9:]+(?:\\.[0-9]+)?) \\(new personal best\\)"); private static final Pattern RAIDS_DURATION_PATTERN = Pattern.compile("Congratulations - your raid is complete!
Team size: " + COX_TEAM_SIZES + " Duration: [0-9:.]+ Personal best: (?[0-9:]+(?:\\.[0-9]+)?)"); - private static final Pattern TOB_WAVE_PB_PATTERN = Pattern.compile("^.*Theatre of Blood wave completion time: (?[0-9:]+(?:\\.[0-9]+)?) \\(Personal best!\\)"); + private static final Pattern TOB_WAVE_PB_PATTERN = Pattern.compile("^.*Theatre of Blood wave completion time: (?[0-9:]+(?:\\.[0-9]+)?) \\((Personal best!|new personal best)\\)"); private static final Pattern TOB_WAVE_DURATION_PATTERN = Pattern.compile("^.*Theatre of Blood wave completion time: [0-9:.]+
Personal best: (?[0-9:]+(?:\\.[0-9]+)?)"); private static final Pattern KILL_DURATION_PATTERN = Pattern.compile("(?i)^(?:(?:Fight |Lap |Challenge |Corrupted challenge )?duration:|Subdued in) [0-9:.]+\\. Personal best: (?:)?(?[0-9:]+(?:\\.[0-9]+)?)"); private static final Pattern NEW_PB_PATTERN = Pattern.compile("(?i)^(?:(?:Fight |Lap |Challenge |Corrupted challenge )?duration:|Subdued in) (?[0-9:]+(?:\\.[0-9]+)?) \\(new personal best\\)"); @@ -245,6 +245,11 @@ public class ChatCommandsPlugin extends Plugin configManager.setRSProfileConfiguration("killcount", boss.toLowerCase(), killcount); } + private void unsetKc(String boss) + { + configManager.unsetRSProfileConfiguration("killcount", boss.toLowerCase()); + } + private int getKc(String boss) { Integer killCount = configManager.getRSProfileConfiguration("killcount", boss.toLowerCase(), int.class); @@ -256,6 +261,11 @@ public class ChatCommandsPlugin extends Plugin configManager.setRSProfileConfiguration("personalbest", boss.toLowerCase(), seconds); } + private void unsetPb(String boss) + { + configManager.unsetRSProfileConfiguration("personalbest", boss.toLowerCase()); + } + private double getPb(String boss) { Double personalBest = configManager.getRSProfileConfiguration("personalbest", boss.toLowerCase(), double.class); @@ -280,19 +290,30 @@ public class ChatCommandsPlugin extends Plugin String boss = matcher.group(1); int kc = Integer.parseInt(matcher.group(2)); - boss = KILLCOUNT_RENAMES.getOrDefault(boss, boss); + String renamedBoss = KILLCOUNT_RENAMES + .getOrDefault(boss, boss) + // The config service doesn't support keys with colons in them + .replace(":", ""); + if (boss != renamedBoss) + { + // Unset old TOB kc + unsetKc(boss); + unsetPb(boss); + unsetKc(boss.replace(":", ".")); + unsetPb(boss.replace(":", ".")); + } - setKc(boss, kc); + setKc(renamedBoss, kc); // We either already have the pb, or need to remember the boss for the upcoming pb if (lastPb > -1) { - log.debug("Got out-of-order personal best for {}: {}", boss, lastPb); - setPb(boss, lastPb); + log.debug("Got out-of-order personal best for {}: {}", renamedBoss, lastPb); + setPb(renamedBoss, lastPb); lastPb = -1; } else { - lastBossKill = boss; + lastBossKill = renamedBoss; lastBossTime = client.getTickCount(); } return; @@ -1687,6 +1708,19 @@ public class ChatCommandsPlugin extends Plugin case "raids 2": return "Theatre of Blood"; + case "Theatre of Blood: Story Mode": + case "tob sm": + case "tob story mode": + case "tob story": + return "Theatre of Blood Story Mode"; + + case "Theatre of Blood: Hard Mode": + case "tob cm": + case "tob hm": + case "tob hard mode": + case "tob hard": + return "Theatre of Blood Hard Mode"; + // agility course case "prif": case "prifddinas": 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 775470f944..dc1c1251e9 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 @@ -194,6 +194,21 @@ public class ChatCommandsPluginTest verify(configManager).setRSProfileConfiguration("personalbest", "theatre of blood", 37 * 60 + 4.4); } + @Test + public void testTheatreOfBloodStoryMode() + { + ChatMessage chatMessage = new ChatMessage(null, GAMEMESSAGE, "", + "Theatre of Blood wave completion time: 5:04 (new personal best)
" + + "Theatre of Blood total completion time: 24:39 (new personal best)", null, 0); + chatCommandsPlugin.onChatMessage(chatMessage); + + ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", "Your completed Theatre of Blood: Story Mode count is: 73.", null, 0); + chatCommandsPlugin.onChatMessage(chatMessageEvent); + + verify(configManager).setRSProfileConfiguration("killcount", "theatre of blood story mode", 73); + verify(configManager).setRSProfileConfiguration("personalbest", "theatre of blood story mode", 5 * 60 + 4.0); + } + @Test public void testWintertodt() { From 70dc7f56bc6e1c08d094378d920426359e9b44e1 Mon Sep 17 00:00:00 2001 From: Jordan Atwood Date: Tue, 23 Feb 2021 23:54:32 -0800 Subject: [PATCH 06/16] clues: Fix three step cryptic clue getLocations NPE Prior to this commit, ThreeStepCrypticClue simply returned a concatenation of the active step locations without filtering the mapped locations in any way. This could lead to NPEs in the plugin as some cryptic clues have null locations for steps which have no specific location or have a variable location. This commit addresses this by making CrypticClue's location field `@Nullable`, filtering null locations from ThreeStepCrypticClue's getLocations stream, and by adding a test to ensure ThreeStepCrypticClue's getLocations method cannot yield any null entries in its return value. --- .../plugins/cluescrolls/clues/CrypticClue.java | 3 ++- .../cluescrolls/clues/ThreeStepCrypticClue.java | 2 ++ .../clues/ThreeStepCrypticClueTest.java | 17 +++++++++++++++++ 3 files changed, 21 insertions(+), 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 3cde11fec9..5f9a99deee 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 @@ -330,6 +330,7 @@ public class CrypticClue extends ClueScroll implements TextClueScroll, NpcClueSc private final String text; private final String npc; private final int objectId; + @Nullable private final WorldPoint location; private final String solution; @Nullable @@ -371,7 +372,7 @@ public class CrypticClue extends ClueScroll implements TextClueScroll, NpcClueSc this(text, npc, objectId, location, solution, null); } - private CrypticClue(String text, String npc, int objectId, WorldPoint location, String solution, @Nullable String questionText) + private CrypticClue(String text, String npc, int objectId, @Nullable WorldPoint location, String solution, @Nullable String questionText) { this.text = text; this.npc = npc; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/ThreeStepCrypticClue.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/ThreeStepCrypticClue.java index 52c78d94c8..9672d4b351 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/ThreeStepCrypticClue.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/ThreeStepCrypticClue.java @@ -30,6 +30,7 @@ import java.util.AbstractMap; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; import lombok.Getter; import lombok.RequiredArgsConstructor; import net.runelite.api.InventoryID; @@ -167,6 +168,7 @@ public class ThreeStepCrypticClue extends ClueScroll implements TextClueScroll, return clueSteps.stream() .filter(s -> !s.getValue()) .map(s -> s.getKey().getLocation()) + .filter(Objects::nonNull) .toArray(WorldPoint[]::new); } diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/cluescrolls/clues/ThreeStepCrypticClueTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/cluescrolls/clues/ThreeStepCrypticClueTest.java index a3ca74a577..f9ed47a555 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/cluescrolls/clues/ThreeStepCrypticClueTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/cluescrolls/clues/ThreeStepCrypticClueTest.java @@ -24,6 +24,10 @@ */ package net.runelite.client.plugins.cluescrolls.clues; +import com.google.common.base.Joiner; +import net.runelite.api.coords.WorldPoint; +import net.runelite.client.util.Text; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import org.junit.Test; @@ -34,4 +38,17 @@ public class ThreeStepCrypticClueTest { assertNull(ThreeStepCrypticClue.forText("", "")); } + + @Test + public void nonNullLocations() + { + final String clueText = Joiner.on("

").join(CrypticClue.CLUES.stream().map(CrypticClue::getText).toArray()); + final ThreeStepCrypticClue clue = ThreeStepCrypticClue.forText(Text.sanitizeMultilineText(clueText).toLowerCase(), clueText); + + assertNotNull(clue); + for (final WorldPoint location : clue.getLocations()) + { + assertNotNull(location); + } + } } From 1dd82db10c4e3ed3230fcc515413ba88eea86a8c Mon Sep 17 00:00:00 2001 From: ThePharros <18340303+ThePharros@users.noreply.github.com> Date: Sun, 6 Jun 2021 14:16:06 -0400 Subject: [PATCH 07/16] screenshot: Add option to screenshot collection log entries (#13625) --- .../plugins/screenshot/ScreenshotConfig.java | 14 +++++++++++++- .../plugins/screenshot/ScreenshotPlugin.java | 8 ++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotConfig.java index 44d9d58b51..1dd5c53174 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotConfig.java @@ -253,11 +253,23 @@ public interface ScreenshotConfig extends Config return false; } + @ConfigItem( + keyName = "collectionLogEntries", + name = "Screenshot collection log entries", + description = "Take a screenshot when completing an entry in the collection log", + position = 18, + section = whatSection + ) + default boolean screenshotCollectionLogEntries() + { + return true; + } + @ConfigItem( keyName = "hotkey", name = "Screenshot hotkey", description = "When you press this key a screenshot will be taken", - position = 18 + position = 19 ) default Keybind hotkey() { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java index dbda81a49c..28449b69ca 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java @@ -95,6 +95,7 @@ import net.runelite.client.util.Text; @Slf4j public class ScreenshotPlugin extends Plugin { + private static final String COLLECTION_LOG_TEXT = "New item added to your collection log: "; private static final String CHEST_LOOTED_MESSAGE = "You find some treasure in the chest!"; private static final Map CHEST_LOOT_EVENTS = ImmutableMap.of(12127, "The Gauntlet"); private static final int GAUNTLET_REGION = 7512; @@ -456,6 +457,13 @@ public class ScreenshotPlugin extends Plugin takeScreenshot(fileName, "Duels"); } } + + if (config.screenshotCollectionLogEntries() && chatMessage.startsWith(COLLECTION_LOG_TEXT)) + { + String entry = Text.removeTags(chatMessage).substring(COLLECTION_LOG_TEXT.length()); + String fileName = "Collection log (" + entry + ")"; + takeScreenshot(fileName, "Collection Log"); + } } @Subscribe From 3f7146a6c2c6cd8a82f86aa8723991b3d7863d37 Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 6 Jun 2021 14:48:59 -0400 Subject: [PATCH 08/16] chat colors: fix clan chat message highlight keys The old friends chat uses "clan chat" as its key name for historical reasons, and so the new clan chat uses "clan" --- .../client/config/ChatColorConfig.java | 4 +- .../client/config/ChatColorConfigTest.java | 57 +++++++++++++++++++ 2 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 runelite-client/src/test/java/net/runelite/client/config/ChatColorConfigTest.java diff --git a/runelite-client/src/main/java/net/runelite/client/config/ChatColorConfig.java b/runelite-client/src/main/java/net/runelite/client/config/ChatColorConfig.java index 90fc5adbe0..da8eb41f12 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/ChatColorConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/config/ChatColorConfig.java @@ -185,7 +185,7 @@ public interface ChatColorConfig extends Config @ConfigItem( position = 14, - keyName = "opaqueClanChatMessageHighlight", + keyName = "opaqueClanMessageHighlight", name = "Clan chat message highlight", description = "Color of highlights in Clan Chat messages", section = opaqueSection @@ -582,7 +582,7 @@ public interface ChatColorConfig extends Config @ConfigItem( position = 64, - keyName = "transparentClanChatMessageHighlight", + keyName = "transparentClanMessageHighlight", name = "Clan chat message highlight (transparent)", description = "Color of highlights in Clan Chat messages (transparent)", section = transparentSection diff --git a/runelite-client/src/test/java/net/runelite/client/config/ChatColorConfigTest.java b/runelite-client/src/test/java/net/runelite/client/config/ChatColorConfigTest.java new file mode 100644 index 0000000000..e3b140fe34 --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/config/ChatColorConfigTest.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2021, 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.config; + +import java.lang.reflect.Method; +import java.util.HashSet; +import java.util.Set; +import static org.junit.Assert.fail; +import org.junit.Test; + +public class ChatColorConfigTest +{ + @Test + public void testUniqueKeys() + { + final Set configKeyNames = new HashSet<>(); + + for (Method method : ChatColorConfig.class.getMethods()) + { + final ConfigItem annotation = method.getAnnotation(ConfigItem.class); + if (annotation == null) + { + continue; + } + + final String configKeyName = annotation.keyName(); + if (configKeyNames.contains(configKeyName)) + { + fail("keyName " + configKeyName + " is duplicated in " + ChatColorConfig.class); + } + + configKeyNames.add(configKeyName); + } + } +} \ No newline at end of file From d69a5ddd8b88c659f09b459c5a5ce145c31d739a Mon Sep 17 00:00:00 2001 From: Jordan Atwood Date: Mon, 3 May 2021 00:16:18 -0700 Subject: [PATCH 09/16] status bars: Add counter text drop shadow This drop shadow was present prior to the plugin refactor in 1165780f427e1d9f86f2d658b050bf04a7ae237e which erroneously removed it. --- .../plugins/statusbars/BarRenderer.java | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/statusbars/BarRenderer.java b/runelite-client/src/main/java/net/runelite/client/plugins/statusbars/BarRenderer.java index 84742e2f63..03cc2f326c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/statusbars/BarRenderer.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/statusbars/BarRenderer.java @@ -25,12 +25,14 @@ */ package net.runelite.client.plugins.statusbars; -import lombok.RequiredArgsConstructor; -import net.runelite.client.ui.FontManager; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Image; +import java.awt.Point; import java.util.function.Supplier; +import lombok.RequiredArgsConstructor; +import net.runelite.client.ui.FontManager; +import net.runelite.client.ui.overlay.components.TextComponent; @RequiredArgsConstructor class BarRenderer @@ -100,28 +102,26 @@ class BarRenderer private void renderIconsAndCounters(StatusBarsConfig config, Graphics2D graphics, int x, int y) { - graphics.setFont(FontManager.getRunescapeSmallFont()); - graphics.setColor(Color.WHITE); - String counterText = Integer.toString(currentValue); - final int widthOfCounter = graphics.getFontMetrics().stringWidth(counterText); - int centerText = (WIDTH - PADDING) / 2 - (widthOfCounter / 2); - final Image icon = iconSupplier.get(); + final boolean skillIconEnabled = config.enableSkillIcon(); + + if (skillIconEnabled) + { + final Image icon = iconSupplier.get(); + graphics.drawImage(icon, x + ICON_AND_COUNTER_OFFSET_X + PADDING, y + ICON_AND_COUNTER_OFFSET_Y - icon.getWidth(null), null); + } if (config.enableCounter()) { - if (config.enableSkillIcon()) - { - graphics.drawImage(icon, x + ICON_AND_COUNTER_OFFSET_X + PADDING, y + ICON_AND_COUNTER_OFFSET_Y - icon.getWidth(null), null); - graphics.drawString(counterText, x + centerText + PADDING, y + SKILL_ICON_HEIGHT); - } - else - { - graphics.drawString(counterText, x + centerText + PADDING, y + COUNTER_ICON_HEIGHT); - } - } - else if (config.enableSkillIcon()) - { - graphics.drawImage(icon, x + ICON_AND_COUNTER_OFFSET_X + PADDING, y + ICON_AND_COUNTER_OFFSET_Y - icon.getWidth(null), null); + graphics.setFont(FontManager.getRunescapeSmallFont()); + final String counterText = Integer.toString(currentValue); + final int widthOfCounter = graphics.getFontMetrics().stringWidth(counterText); + final int centerText = (WIDTH - PADDING) / 2 - (widthOfCounter / 2); + final int yOffset = skillIconEnabled ? SKILL_ICON_HEIGHT : COUNTER_ICON_HEIGHT; + + final TextComponent textComponent = new TextComponent(); + textComponent.setText(counterText); + textComponent.setPosition(new Point(x + centerText + PADDING, y + yOffset)); + textComponent.render(graphics); } } From c09c96cd784bc88d0c76f3fd202327d73a421d49 Mon Sep 17 00:00:00 2001 From: Hydrox6 Date: Thu, 25 Feb 2021 10:56:27 +0000 Subject: [PATCH 10/16] world map overlay: make icons display fully on the map when edge snapped --- .../ui/overlay/worldmap/WorldMapOverlay.java | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/worldmap/WorldMapOverlay.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/worldmap/WorldMapOverlay.java index 8b2fc1d187..d2e13ccad3 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/worldmap/WorldMapOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/worldmap/WorldMapOverlay.java @@ -131,7 +131,24 @@ public class WorldMapOverlay extends Overlay if (worldPoint.isSnapToEdge()) { - if (worldMapRectangle.contains(drawPoint.getX(), drawPoint.getY())) + // Get a smaller rect for edge-snapped icons so they display correctly at the edge + final Rectangle snappedRect = widget.getBounds(); + snappedRect.grow(-image.getWidth() / 2, -image.getHeight() / 2); + + final Rectangle unsnappedRect = new Rectangle(snappedRect); + if (worldPoint.getImagePoint() != null) + { + int dx = worldPoint.getImagePoint().getX() - (image.getWidth() / 2); + int dy = worldPoint.getImagePoint().getY() - (image.getHeight() / 2); + unsnappedRect.translate(dx, dy); + } + // Make the unsnap rect slightly smaller so a smaller snapped image doesn't cause a freak out + if (worldPoint.isCurrentlyEdgeSnapped()) + { + unsnappedRect.grow(-image.getWidth(), -image.getHeight()); + } + + if (unsnappedRect.contains(drawPoint.getX(), drawPoint.getY())) { if (worldPoint.isCurrentlyEdgeSnapped()) { @@ -141,7 +158,7 @@ public class WorldMapOverlay extends Overlay } else { - drawPoint = clipToRectangle(drawPoint, worldMapRectangle); + drawPoint = clipToRectangle(drawPoint, snappedRect); if (!worldPoint.isCurrentlyEdgeSnapped()) { worldPoint.setCurrentlyEdgeSnapped(true); From d75a50a23660b38fd3e404bf9588bb8d369aabf8 Mon Sep 17 00:00:00 2001 From: Hydrox6 Date: Sun, 28 Feb 2021 00:32:15 +0000 Subject: [PATCH 11/16] world map mouse listener: consume click if icon is clicked on This stops the map from being closed when clicking on an icon overlapping the close button. --- .../ui/overlay/worldmap/WorldMapOverlayMouseListener.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/worldmap/WorldMapOverlayMouseListener.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/worldmap/WorldMapOverlayMouseListener.java index 949c578ec2..71af9a55dc 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/worldmap/WorldMapOverlayMouseListener.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/worldmap/WorldMapOverlayMouseListener.java @@ -60,6 +60,12 @@ public class WorldMapOverlayMouseListener extends MouseAdapter if (SwingUtilities.isLeftMouseButton(e) && !worldMapPoints.isEmpty()) { Point mousePos = client.getMouseCanvasPosition(); + final Widget view = client.getWidget(WidgetInfo.WORLD_MAP_VIEW); + + if (view == null) + { + return e; + } for (WorldMapPoint worldMapPoint : worldMapPoints) { @@ -77,6 +83,7 @@ public class WorldMapOverlayMouseListener extends MouseAdapter RenderOverview renderOverview = client.getRenderOverview(); renderOverview.setWorldMapPositionTarget(target); } + e.consume(); return worldMapPoint.onClick(e); } } From 89a3838275aff64b6ceb2d282e673603a0071e9a Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 7 Jun 2021 18:52:40 -0400 Subject: [PATCH 12/16] api: add setCameraYawTarget --- runelite-api/src/main/java/net/runelite/api/Client.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/runelite-api/src/main/java/net/runelite/api/Client.java b/runelite-api/src/main/java/net/runelite/api/Client.java index 1fc9afb87e..5be3523f3a 100644 --- a/runelite-api/src/main/java/net/runelite/api/Client.java +++ b/runelite-api/src/main/java/net/runelite/api/Client.java @@ -619,12 +619,19 @@ public interface Client extends GameEngine boolean isMenuOpen(); /** - * Gets the angle of the map, or camera yaw. + * Gets the angle of the map, or target camera yaw. * * @return the map angle */ int getMapAngle(); + /** + * Set the target camera yaw + * + * @param cameraYawTarget + */ + void setCameraYawTarget(int cameraYawTarget); + /** * Checks whether the client window is currently resized. * From c9f5f93b52f42098845c7e6871bb984a1979dec6 Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 7 Jun 2021 18:53:47 -0400 Subject: [PATCH 13/16] camera: add option to preserve yaw when world hopping --- .../client/plugins/camera/CameraConfig.java | 11 ++++++++++ .../client/plugins/camera/CameraPlugin.java | 22 ++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/camera/CameraConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/camera/CameraConfig.java index 5f019c82ca..250cb8f247 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/camera/CameraConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/camera/CameraConfig.java @@ -191,4 +191,15 @@ public interface CameraConfig extends Config { return false; } + + @ConfigItem( + keyName = "preserveYaw", + name = "Preserve yaw on world hop", + description = "Preserves the camera yaw (left/right) when world hopping.", + position = 14 + ) + default boolean preserveYaw() + { + return false; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/camera/CameraPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/camera/CameraPlugin.java index f650d3efba..b2ac448629 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/camera/CameraPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/camera/CameraPlugin.java @@ -43,6 +43,7 @@ import net.runelite.api.VarPlayer; import net.runelite.api.events.BeforeRender; import net.runelite.api.events.ClientTick; import net.runelite.api.events.FocusChanged; +import net.runelite.api.events.GameStateChanged; import net.runelite.api.events.MenuEntryAdded; import net.runelite.api.events.ScriptCallbackEvent; import net.runelite.api.events.ScriptPreFired; @@ -89,7 +90,8 @@ public class CameraPlugin extends Plugin implements KeyListener, MouseListener * Whether or not the current menu has any non-ignored menu entries */ private boolean menuHasEntries; - + private int savedCameraYaw; + @Inject private Client client; @@ -418,6 +420,24 @@ public class CameraPlugin extends Plugin implements KeyListener, MouseListener } } + @Subscribe + public void onGameStateChanged(GameStateChanged gameStateChanged) + { + switch (gameStateChanged.getGameState()) + { + case HOPPING: + savedCameraYaw = client.getMapAngle(); + break; + case LOGGED_IN: + if (savedCameraYaw != 0 && config.preserveYaw()) + { + client.setCameraYawTarget(savedCameraYaw); + } + savedCameraYaw = 0; + break; + } + } + /** * The event that is triggered when a mouse button is pressed * In this method the right click is changed to a middle-click to enable rotating the camera From d228fa70fc3366f82cc01a0be8d828a6312bd3d5 Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 8 Jun 2021 22:59:15 -0400 Subject: [PATCH 14/16] chat commands: update tob new pb pattern The old text (Personal best!) is no longer used --- .../client/plugins/chatcommands/ChatCommandsPlugin.java | 2 +- .../client/plugins/chatcommands/ChatCommandsPluginTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 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 8acde22888..6a2cfc1323 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 @@ -98,7 +98,7 @@ public class ChatCommandsPlugin extends Plugin private static final String COX_TEAM_SIZES = "(?:\\d+(?:\\+|-\\d+)? players|Solo)"; private static final Pattern RAIDS_PB_PATTERN = Pattern.compile("Congratulations - your raid is complete!
Team size: " + COX_TEAM_SIZES + " Duration: (?[0-9:]+(?:\\.[0-9]+)?) \\(new personal best\\)"); private static final Pattern RAIDS_DURATION_PATTERN = Pattern.compile("Congratulations - your raid is complete!
Team size: " + COX_TEAM_SIZES + " Duration: [0-9:.]+ Personal best: (?[0-9:]+(?:\\.[0-9]+)?)"); - private static final Pattern TOB_WAVE_PB_PATTERN = Pattern.compile("^.*Theatre of Blood wave completion time: (?[0-9:]+(?:\\.[0-9]+)?) \\((Personal best!|new personal best)\\)"); + private static final Pattern TOB_WAVE_PB_PATTERN = Pattern.compile("^.*Theatre of Blood wave completion time: (?[0-9:]+(?:\\.[0-9]+)?) \\(new personal best\\)"); private static final Pattern TOB_WAVE_DURATION_PATTERN = Pattern.compile("^.*Theatre of Blood wave completion time: [0-9:.]+
Personal best: (?[0-9:]+(?:\\.[0-9]+)?)"); private static final Pattern KILL_DURATION_PATTERN = Pattern.compile("(?i)^(?:(?:Fight |Lap |Challenge |Corrupted challenge )?duration:|Subdued in) [0-9:.]+\\. Personal best: (?:)?(?[0-9:]+(?:\\.[0-9]+)?)"); private static final Pattern NEW_PB_PATTERN = Pattern.compile("(?i)^(?:(?:Fight |Lap |Challenge |Corrupted challenge )?duration:|Subdued in) (?[0-9:]+(?:\\.[0-9]+)?) \\(new personal best\\)"); 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 dc1c1251e9..5fa71d9b86 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 @@ -157,7 +157,7 @@ public class ChatCommandsPluginTest @Test public void testTheatreOfBlood() { - ChatMessage chatMessage = new ChatMessage(null, GAMEMESSAGE, "", "Wave 'The Final Challenge' complete! Duration: 5:04
Theatre of Blood wave completion time: 37:04 (Personal best!)", null, 0); + ChatMessage chatMessage = new ChatMessage(null, GAMEMESSAGE, "", "Wave 'The Final Challenge' complete! Duration: 5:04
Theatre of Blood wave completion time: 37:04 (new personal best)", null, 0); chatCommandsPlugin.onChatMessage(chatMessage); ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", "Your completed Theatre of Blood count is: 73.", null, 0); @@ -167,7 +167,7 @@ public class ChatCommandsPluginTest verify(configManager).setRSProfileConfiguration("personalbest", "theatre of blood", 37 * 60 + 4.0); // Precise times - ChatMessage chatMessagePrecise = new ChatMessage(null, GAMEMESSAGE, "", "Wave 'The Final Challenge' complete! Duration: 5:04
Theatre of Blood wave completion time: 37:04.20 (Personal best!)", null, 0); + ChatMessage chatMessagePrecise = new ChatMessage(null, GAMEMESSAGE, "", "Wave 'The Final Challenge' complete! Duration: 5:04
Theatre of Blood wave completion time: 37:04.20 (new personal best)", null, 0); chatCommandsPlugin.onChatMessage(chatMessagePrecise); chatCommandsPlugin.onChatMessage(chatMessageEvent); From 4abd69977bb741ebd866886909325553221b0e14 Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 9 Jun 2021 15:25:03 -0400 Subject: [PATCH 15/16] gpu: use renderbuffer for aa fbo Previously this bound first a render buffer and then a texture to the aa fbo color attachment0, making the renderbuffer unused. Just remove the texture, causing the render buffer to be used. --- .../runelite/client/plugins/gpu/GpuPlugin.java | 18 +----------------- 1 file changed, 1 insertion(+), 17 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 2c54f1744b..93d0046a9f 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 @@ -199,7 +199,6 @@ public class GpuPlugin extends Plugin implements DrawCallbacks private int vboUiHandle; private int fboSceneHandle; - private int texSceneHandle; private int rboSceneHandle; // scene vertex buffer @@ -297,7 +296,7 @@ public class GpuPlugin extends Plugin implements DrawCallbacks { try { - texSceneHandle = fboSceneHandle = rboSceneHandle = -1; // AA FBO + fboSceneHandle = rboSceneHandle = -1; // AA FBO unorderedModels = smallModels = largeModels = 0; drawingModel = false; @@ -737,28 +736,13 @@ public class GpuPlugin extends Plugin implements DrawCallbacks gl.glRenderbufferStorageMultisample(gl.GL_RENDERBUFFER, aaSamples, gl.GL_RGBA, width, height); gl.glFramebufferRenderbuffer(gl.GL_FRAMEBUFFER, gl.GL_COLOR_ATTACHMENT0, gl.GL_RENDERBUFFER, rboSceneHandle); - // Create texture - texSceneHandle = glGenTexture(gl); - gl.glBindTexture(gl.GL_TEXTURE_2D_MULTISAMPLE, texSceneHandle); - gl.glTexImage2DMultisample(gl.GL_TEXTURE_2D_MULTISAMPLE, aaSamples, gl.GL_RGBA, width, height, true); - - // Bind texture - gl.glFramebufferTexture2D(gl.GL_FRAMEBUFFER, gl.GL_COLOR_ATTACHMENT0, gl.GL_TEXTURE_2D_MULTISAMPLE, texSceneHandle, 0); - // Reset - gl.glBindTexture(gl.GL_TEXTURE_2D_MULTISAMPLE, 0); gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, 0); gl.glBindRenderbuffer(gl.GL_RENDERBUFFER, 0); } private void shutdownAAFbo() { - if (texSceneHandle != -1) - { - glDeleteTexture(gl, texSceneHandle); - texSceneHandle = -1; - } - if (fboSceneHandle != -1) { glDeleteFrameBuffer(gl, fboSceneHandle); From e3f2b28fac0049a4f24107fb89736f9eb4fa43df Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 9 Jun 2021 21:53:47 -0400 Subject: [PATCH 16/16] menu swapper: add con cape tele to poh --- .../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 cd9b9abc31..e9ed2db3fb 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 @@ -419,6 +419,17 @@ public interface MenuEntrySwapperConfig extends Config return false; } + @ConfigItem( + keyName = "swapTeleToPoh", + name = "Tele to POH", + description = "Swap Wear with Tele to POH on the construction cape", + section = itemSection + ) + default boolean swapTeleToPoh() + { + return false; + } + @ConfigItem( keyName = "swapAbyssTeleport", name = "Teleport to Abyss", 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 eaa1795f4f..11a73a776b 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 @@ -347,6 +347,8 @@ public class MenuEntrySwapperPlugin extends Plugin swap("value", "sell 10", () -> shiftModifier() && config.shopSell() == SellMode.SELL_10); swap("value", "sell 50", () -> shiftModifier() && config.shopSell() == SellMode.SELL_50); + swap("wear", "tele to poh", config::swapTeleToPoh); + swap("wear", "rub", config::swapTeleportItem); swap("wear", "teleport", config::swapTeleportItem); swap("wield", "teleport", config::swapTeleportItem);