From 533bcd26be5e526681c001521e353ceb989efe2f Mon Sep 17 00:00:00 2001 From: Tal s Date: Thu, 28 Oct 2021 00:21:43 +0300 Subject: [PATCH] woodcutting: Add clue nest notification config (#14132) --- .../woodcutting/WoodcuttingConfig.java | 16 ++- .../woodcutting/WoodcuttingPlugin.java | 22 +++- .../woodcutting/config/ClueNestTier.java | 52 ++++++++ .../woodcutting/WoodcuttingPluginTest.java | 122 ++++++++++++++++++ 4 files changed, 209 insertions(+), 3 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/config/ClueNestTier.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/WoodcuttingConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/WoodcuttingConfig.java index 9385d4d9da..3f02ca6359 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/WoodcuttingConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/WoodcuttingConfig.java @@ -28,6 +28,7 @@ import net.runelite.client.config.Config; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; import net.runelite.client.config.Units; +import net.runelite.client.plugins.woodcutting.config.ClueNestTier; @ConfigGroup("woodcutting") public interface WoodcuttingConfig extends Config @@ -57,6 +58,17 @@ public interface WoodcuttingConfig extends Config @ConfigItem( position = 3, + keyName = "clueNestNotifyTier", + name = "Clue nest notification", + description = "Configures the clue tier from which to start notifying of a clue nest spawn" + ) + default ClueNestTier clueNestNotifyTier() + { + return ClueNestTier.BEGINNER; + } + + @ConfigItem( + position = 4, keyName = "showWoodcuttingStats", name = "Show session stats", description = "Configures whether to display woodcutting session stats" @@ -67,7 +79,7 @@ public interface WoodcuttingConfig extends Config } @ConfigItem( - position = 4, + position = 5, keyName = "showRedwoods", name = "Show Redwood trees", description = "Configures whether to show a indicator for redwood trees" @@ -78,7 +90,7 @@ public interface WoodcuttingConfig extends Config } @ConfigItem( - position = 5, + position = 6, keyName = "showRespawnTimers", name = "Show respawn timers", description = "Configures whether to display the respawn timer overlay" diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/WoodcuttingPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/WoodcuttingPlugin.java index c9ed030b3a..6fe527781e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/WoodcuttingPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/WoodcuttingPlugin.java @@ -51,6 +51,7 @@ import net.runelite.api.events.GameObjectDespawned; import net.runelite.api.events.GameObjectSpawned; import net.runelite.api.events.GameStateChanged; import net.runelite.api.events.GameTick; +import net.runelite.api.events.ItemSpawned; import net.runelite.client.Notifier; import net.runelite.client.config.ConfigManager; import net.runelite.client.eventbus.Subscribe; @@ -58,6 +59,7 @@ import net.runelite.client.events.OverlayMenuClicked; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDependency; import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.woodcutting.config.ClueNestTier; import net.runelite.client.plugins.xptracker.XpTrackerPlugin; import net.runelite.client.ui.overlay.OverlayManager; import net.runelite.client.ui.overlay.OverlayMenuEntry; @@ -107,6 +109,7 @@ public class WoodcuttingPlugin extends Plugin private final List respawns = new ArrayList<>(); private boolean recentlyLoggedIn; private int currentPlane; + private ClueNestTier clueTierSpawned; @Provides WoodcuttingConfig getConfig(ConfigManager configManager) @@ -130,6 +133,7 @@ public class WoodcuttingPlugin extends Plugin treeObjects.clear(); session = null; axe = null; + clueTierSpawned = null; } @Subscribe @@ -148,6 +152,7 @@ public class WoodcuttingPlugin extends Plugin public void onGameTick(GameTick gameTick) { recentlyLoggedIn = false; + clueTierSpawned = null; currentPlane = client.getPlane(); respawns.removeIf(TreeRespawn::isExpired); @@ -190,11 +195,26 @@ public class WoodcuttingPlugin extends Plugin if (event.getMessage().contains("A bird's nest falls out of the tree") && config.showNestNotification()) { - notifier.notify("A bird nest has spawned!"); + if (clueTierSpawned == null || clueTierSpawned.ordinal() >= config.clueNestNotifyTier().ordinal()) + { + notifier.notify("A bird nest has spawned!"); + } + // Clear the clue tier that has previously spawned + clueTierSpawned = null; } } } + @Subscribe + public void onItemSpawned(ItemSpawned itemSpawned) + { + if (clueTierSpawned == null) + { + // This will be set only if one of the clue nests has spawned. It will then be reset the next game tick. + clueTierSpawned = ClueNestTier.getTierFromItem(itemSpawned.getItem().getId()); + } + } + @Subscribe public void onGameObjectSpawned(final GameObjectSpawned event) { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/config/ClueNestTier.java b/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/config/ClueNestTier.java new file mode 100644 index 0000000000..2e87136ede --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/config/ClueNestTier.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2021, Tal + * 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.woodcutting.config; + +import com.google.common.collect.ImmutableMap; +import net.runelite.api.ItemID; + +public enum ClueNestTier +{ + BEGINNER, + EASY, + MEDIUM, + HARD, + ELITE, + DISABLED; + + + private static final ImmutableMap CLUE_NEST_ID_TO_TIER = new ImmutableMap.Builder() + .put(ItemID.CLUE_NEST_ELITE, ClueNestTier.ELITE) + .put(ItemID.CLUE_NEST_HARD, ClueNestTier.HARD) + .put(ItemID.CLUE_NEST_MEDIUM, ClueNestTier.MEDIUM) + .put(ItemID.CLUE_NEST_EASY, ClueNestTier.EASY) + .put(ItemID.CLUE_NEST_BEGINNER, ClueNestTier.BEGINNER) + .build(); + + static public ClueNestTier getTierFromItem(int itemId) + { + return CLUE_NEST_ID_TO_TIER.get(itemId); + } +} diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/woodcutting/WoodcuttingPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/woodcutting/WoodcuttingPluginTest.java index 82111a8cbc..8cd4d20b59 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/woodcutting/WoodcuttingPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/woodcutting/WoodcuttingPluginTest.java @@ -31,15 +31,22 @@ import com.google.inject.testing.fieldbinder.BoundFieldModule; import javax.inject.Inject; import net.runelite.api.ChatMessageType; import net.runelite.api.Client; +import net.runelite.api.ItemID; +import net.runelite.api.Tile; +import net.runelite.api.TileItem; import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.ItemSpawned; import net.runelite.client.Notifier; +import net.runelite.client.plugins.woodcutting.config.ClueNestTier; import net.runelite.client.ui.overlay.OverlayManager; import static org.junit.Assert.assertNotNull; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import org.mockito.junit.MockitoJUnitRunner; @@ -127,4 +134,119 @@ public class WoodcuttingPluginTest woodcuttingPlugin.onChatMessage(chatMessage); verifyNoMoreInteractions(notifier); } + + @Test + public void testClueNestConfigSameAsSpawn() + { + ChatMessage nestChatMessage = new ChatMessage(null, ChatMessageType.GAMEMESSAGE, "", BIRDS_NEST_MESSAGE, "", 0); + Tile tile = mock(Tile.class); + TileItem beginnerTileItem = mock(TileItem.class); + when(beginnerTileItem.getId()).thenReturn(ItemID.CLUE_NEST_BEGINNER); + ItemSpawned beginnerClueSpawned = new ItemSpawned(tile, beginnerTileItem); + + when(woodcuttingConfig.showNestNotification()).thenReturn(true); + when(woodcuttingConfig.clueNestNotifyTier()).thenReturn(ClueNestTier.BEGINNER); + woodcuttingPlugin.onItemSpawned(beginnerClueSpawned); + woodcuttingPlugin.onChatMessage(nestChatMessage); + woodcuttingPlugin.onGameTick(null); + verify(notifier).notify("A bird nest has spawned!"); + } + + @Test + public void testClueNestConfigSmallerThanSpawn() + { + ChatMessage nestChatMessage = new ChatMessage(null, ChatMessageType.GAMEMESSAGE, "", BIRDS_NEST_MESSAGE, "", 0); + Tile tile = mock(Tile.class); + TileItem eliteTileItem = mock(TileItem.class); + when(eliteTileItem.getId()).thenReturn(ItemID.CLUE_NEST_ELITE); + ItemSpawned eliteClueSpawned = new ItemSpawned(tile, eliteTileItem); + + when(woodcuttingConfig.showNestNotification()).thenReturn(true); + when(woodcuttingConfig.clueNestNotifyTier()).thenReturn(ClueNestTier.BEGINNER); + woodcuttingPlugin.onItemSpawned(eliteClueSpawned); + woodcuttingPlugin.onChatMessage(nestChatMessage); + woodcuttingPlugin.onGameTick(null); + verify(notifier).notify("A bird nest has spawned!"); + } + + @Test + public void testClueNestDisabledConfig() + { + ChatMessage nestChatMessage = new ChatMessage(null, ChatMessageType.GAMEMESSAGE, "", BIRDS_NEST_MESSAGE, "", 0); + Tile tile = mock(Tile.class); + TileItem eliteTileItem = mock(TileItem.class); + when(eliteTileItem.getId()).thenReturn(ItemID.CLUE_NEST_ELITE); + ItemSpawned eliteClueSpawned = new ItemSpawned(tile, eliteTileItem); + + when(woodcuttingConfig.showNestNotification()).thenReturn(true); + when(woodcuttingConfig.clueNestNotifyTier()).thenReturn(ClueNestTier.DISABLED); + woodcuttingPlugin.onItemSpawned(eliteClueSpawned); + woodcuttingPlugin.onChatMessage(nestChatMessage); + woodcuttingPlugin.onGameTick(null); + verifyNoInteractions(notifier); + } + + @Test + public void testClueNestConfigLargerThanSpawn() + { + ChatMessage nestChatMessage = new ChatMessage(null, ChatMessageType.GAMEMESSAGE, "", BIRDS_NEST_MESSAGE, "", 0); + Tile tile = mock(Tile.class); + TileItem beginnerTileItem = mock(TileItem.class); + when(beginnerTileItem.getId()).thenReturn(ItemID.CLUE_NEST_BEGINNER); + ItemSpawned beginnerClueSpawned = new ItemSpawned(tile, beginnerTileItem); + + when(woodcuttingConfig.showNestNotification()).thenReturn(true); + when(woodcuttingConfig.clueNestNotifyTier()).thenReturn(ClueNestTier.HARD); + woodcuttingPlugin.onItemSpawned(beginnerClueSpawned); + woodcuttingPlugin.onChatMessage(nestChatMessage); + woodcuttingPlugin.onGameTick(null); + verifyNoInteractions(notifier); + } + + @Test + public void testClueNestPlayerDrop() + { + ChatMessage nestChatMessage = new ChatMessage(null, ChatMessageType.GAMEMESSAGE, "", BIRDS_NEST_MESSAGE, "", 0); + Tile tile = mock(Tile.class); + TileItem beginnerTileItem = mock(TileItem.class); + when(beginnerTileItem.getId()).thenReturn(ItemID.CLUE_NEST_BEGINNER); + ItemSpawned beginnerClueSpawned = new ItemSpawned(tile, beginnerTileItem); + TileItem nestTileItem = mock(TileItem.class); + when(nestTileItem.getId()).thenReturn(ItemID.BIRD_NEST_22798); + ItemSpawned regularNestSpawned = new ItemSpawned(tile, nestTileItem); + + when(woodcuttingConfig.showNestNotification()).thenReturn(true); + + // Player drops clue nest + woodcuttingPlugin.onItemSpawned(beginnerClueSpawned); + woodcuttingPlugin.onGameTick(null); + verifyNoInteractions(notifier); + // A regular nest has spawned + woodcuttingPlugin.onItemSpawned(regularNestSpawned); + woodcuttingPlugin.onChatMessage(nestChatMessage); + woodcuttingPlugin.onGameTick(null); + verify(notifier).notify("A bird nest has spawned!"); + } + + @Test + public void testClueNestOtherItemSpawn() + { + ChatMessage nestChatMessage = new ChatMessage(null, ChatMessageType.GAMEMESSAGE, "", BIRDS_NEST_MESSAGE, "", 0); + Tile tile = mock(Tile.class); + TileItem beginnerTileItem = mock(TileItem.class); + when(beginnerTileItem.getId()).thenReturn(ItemID.CLUE_NEST_BEGINNER); + ItemSpawned beginnerClueSpawned = new ItemSpawned(tile, beginnerTileItem); + TileItem anotherItemTileItem = mock(TileItem.class); + ItemSpawned anotherItemSpawned = new ItemSpawned(tile, anotherItemTileItem); + + when(woodcuttingConfig.showNestNotification()).thenReturn(true); + when(woodcuttingConfig.clueNestNotifyTier()).thenReturn(ClueNestTier.BEGINNER); + + woodcuttingPlugin.onItemSpawned(beginnerClueSpawned); + woodcuttingPlugin.onItemSpawned(anotherItemSpawned); + + woodcuttingPlugin.onChatMessage(nestChatMessage); + woodcuttingPlugin.onGameTick(null); + verify(notifier).notify("A bird nest has spawned!"); + } } \ No newline at end of file