From 3ef90957718198742ac8d30b7bad1a30b67b88db Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 10 Jun 2022 12:25:10 -0400 Subject: [PATCH] cannon: use varp for cannonball count Co-authored-by: Bonfire <5704760+Bonfire@users.noreply.github.com> --- .../main/java/net/runelite/api/VarPlayer.java | 1 + .../client/plugins/cannon/CannonPlugin.java | 131 ++---------------- .../plugins/cannon/CannonPluginTest.java | 125 +++++++++-------- 3 files changed, 79 insertions(+), 178 deletions(-) diff --git a/runelite-api/src/main/java/net/runelite/api/VarPlayer.java b/runelite-api/src/main/java/net/runelite/api/VarPlayer.java index ce6ff390f7..d0ae07ade7 100644 --- a/runelite-api/src/main/java/net/runelite/api/VarPlayer.java +++ b/runelite-api/src/main/java/net/runelite/api/VarPlayer.java @@ -39,6 +39,7 @@ import lombok.Getter; @Getter public enum VarPlayer { + CANNON_AMMO(3), ATTACK_STYLE(43), QUEST_POINTS(101), IS_POISONED(102), 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 2c69cef83d..19f5712810 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 @@ -24,14 +24,10 @@ */ package net.runelite.client.plugins.cannon; -import com.google.common.collect.ImmutableSet; import com.google.inject.Provides; import java.awt.Color; import java.util.ArrayList; import java.util.List; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import javax.inject.Inject; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -40,25 +36,22 @@ import net.runelite.api.ChatMessageType; import net.runelite.api.Client; import net.runelite.api.GameObject; import net.runelite.api.GameState; -import net.runelite.api.GraphicID; import net.runelite.api.InventoryID; import net.runelite.api.Item; -import net.runelite.api.ItemContainer; import net.runelite.api.ItemID; import net.runelite.api.MenuAction; import net.runelite.api.ObjectID; import net.runelite.api.Player; -import net.runelite.api.Projectile; +import net.runelite.api.VarPlayer; import net.runelite.api.coords.LocalPoint; import net.runelite.api.coords.WorldArea; import net.runelite.api.coords.WorldPoint; import net.runelite.api.events.ChatMessage; import net.runelite.api.events.GameObjectSpawned; import net.runelite.api.events.GameStateChanged; -import net.runelite.api.events.GameTick; import net.runelite.api.events.ItemContainerChanged; import net.runelite.api.events.MenuOptionClicked; -import net.runelite.api.events.ProjectileMoved; +import net.runelite.api.events.VarbitChanged; import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetInfo; import net.runelite.client.Notifier; @@ -80,17 +73,10 @@ import net.runelite.client.ui.overlay.infobox.InfoBoxManager; @Slf4j public class CannonPlugin extends Plugin { - private static final Pattern NUMBER_PATTERN = Pattern.compile("([0-9]+)"); static final int MAX_OVERLAY_DISTANCE = 4100; static final int MAX_CBALLS = 30; - private static final Set CANNONBALL_PROJECTILE_IDS = ImmutableSet.of( - GraphicID.CANNONBALL, GraphicID.GRANITE_CANNONBALL, - GraphicID.CANNONBALL_OR, GraphicID.GRANITE_CANNONBALL_OR - ); - private CannonCounter counter; - private boolean skipProjectileCheckThisTick; private boolean cannonBallNotificationSent; private WorldPoint clickedCannonLocation; private boolean firstCannonLoad; @@ -151,6 +137,7 @@ public class CannonPlugin extends Plugin { overlayManager.add(cannonOverlay); overlayManager.add(cannonSpotOverlay); + clientThread.invoke(() -> cballsLeft = client.getVar(VarPlayer.CANNON_AMMO)); } @Override @@ -165,7 +152,6 @@ public class CannonPlugin extends Plugin cannonBallNotificationSent = false; cballsLeft = 0; removeCounter(); - skipProjectileCheckThisTick = false; spotPoints.clear(); } @@ -278,7 +264,7 @@ public class CannonPlugin extends Plugin } } } - + @Subscribe public void onMenuOptionClicked(MenuOptionClicked event) { @@ -309,32 +295,16 @@ public class CannonPlugin extends Plugin } @Subscribe - public void onProjectileMoved(ProjectileMoved event) + public void onVarbitChanged(VarbitChanged varbitChanged) { - Projectile projectile = event.getProjectile(); - - if (CANNONBALL_PROJECTILE_IDS.contains(projectile.getId()) && cannonPosition != null && cannonWorld == client.getWorld()) + if (varbitChanged.getIndex() == VarPlayer.CANNON_AMMO.getId()) { - WorldPoint projectileLoc = WorldPoint.fromLocal(client, projectile.getX1(), projectile.getY1(), client.getPlane()); + cballsLeft = client.getVar(VarPlayer.CANNON_AMMO); - //Check to see if projectile x,y is 0 else it will continuously decrease while ball is flying. - if (cannonPosition.contains(projectileLoc) && projectile.getX() == 0 && projectile.getY() == 0) + if (config.showCannonNotifications() && !cannonBallNotificationSent && cballsLeft > 0 && config.lowWarningThreshold() >= cballsLeft) { - // When there's a chat message about cannon reloaded/unloaded/out of ammo, - // the message event runs before the projectile event. However they run - // in the opposite order on the server. So if both fires in the same tick, - // we don't want to update the cannonball counter if it was set to a specific - // amount. - if (!skipProjectileCheckThisTick) - { - cballsLeft--; - - if (config.showCannonNotifications() && !cannonBallNotificationSent && cballsLeft > 0 && config.lowWarningThreshold() >= cballsLeft) - { - notifier.notify(String.format("Your cannon has %d cannon balls remaining!", cballsLeft)); - cannonBallNotificationSent = true; - } - } + notifier.notify(String.format("Your cannon has %d cannon balls remaining!", cballsLeft)); + cannonBallNotificationSent = true; } } } @@ -351,32 +321,13 @@ public class CannonPlugin extends Plugin { cannonPlaced = true; addCounter(); - cballsLeft = 0; firstCannonLoad = true; - - final ItemContainer inventory = client.getItemContainer(InventoryID.INVENTORY); - if (inventory != null) - { - int invCballs = inventory.count(ItemID.GRANITE_CANNONBALL) > 0 - ? inventory.count(ItemID.GRANITE_CANNONBALL) - : inventory.count(ItemID.CANNONBALL); - // Cannonballs are always forcibly loaded after the furnace is added. If the player has more than - // the max number of cannon balls in their inventory, the cannon will always be fully filled. - // This is preferable to using the proceeding "You load the cannon with x cannon balls" message - // since it will show a lower number of cannon balls if the cannon is already partially-filled - // prior to being placed. - if (invCballs >= MAX_CBALLS) - { - cballsLeft = MAX_CBALLS; - } - } } else if (event.getMessage().contains("You pick up the cannon") || event.getMessage().contains("Your cannon has decayed. Speak to Nulodion to get a new one!") || event.getMessage().contains("Your cannon has been destroyed!")) { cannonPlaced = false; - cballsLeft = 0; removeCounter(); cannonPosition = null; } @@ -410,83 +361,21 @@ public class CannonPlugin extends Plugin clickedCannonLocation = null; } - Matcher m = NUMBER_PATTERN.matcher(event.getMessage()); - if (m.find()) - { - // The cannon will usually refill to MAX_CBALLS, but if the - // player didn't have enough cannonballs in their inventory, - // it could fill up less than that. Filling the cannon to - // cballsLeft + amt is not always accurate though because our - // counter doesn't decrease if the player has been too far away - // from the cannon due to the projectiels not being in memory, - // so our counter can be higher than it is supposed to be. - int amt = Integer.valueOf(m.group()); - if (cballsLeft + amt >= MAX_CBALLS) - { - skipProjectileCheckThisTick = true; - cballsLeft = MAX_CBALLS; - } - else - { - cballsLeft += amt; - } - } - else if (event.getMessage().equals("You load the cannon with one cannonball.")) - { - if (cballsLeft + 1 >= MAX_CBALLS) - { - skipProjectileCheckThisTick = true; - cballsLeft = MAX_CBALLS; - } - else - { - cballsLeft++; - } - } - cannonBallNotificationSent = false; } else if (event.getMessage().contains("Your cannon is out of ammo!")) { - skipProjectileCheckThisTick = true; - - // If the player was out of range of the cannon, some cannonballs - // may have been used without the client knowing, so having this - // extra check is a good idea. - cballsLeft = 0; - if (config.showCannonNotifications()) { notifier.notify("Your cannon is out of ammo!"); } } - else if (event.getMessage().startsWith("Your cannon contains")) - { - Matcher m = NUMBER_PATTERN.matcher(event.getMessage()); - if (m.find()) - { - cballsLeft = Integer.parseInt(m.group()); - } - } - else if (event.getMessage().startsWith("You unload your cannon and receive Cannonball") - || event.getMessage().startsWith("You unload your cannon and receive Granite cannonball")) - { - skipProjectileCheckThisTick = true; - - cballsLeft = 0; - } else if (event.getMessage().equals("This isn't your cannon!") || event.getMessage().equals("This is not your cannon.")) { clickedCannonLocation = null; } } - @Subscribe - public void onGameTick(GameTick event) - { - skipProjectileCheckThisTick = false; - } - Color getStateColor() { if (cballsLeft > 15) diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/cannon/CannonPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/cannon/CannonPluginTest.java index 12bc19ead8..a1f602c26d 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/cannon/CannonPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/cannon/CannonPluginTest.java @@ -31,10 +31,9 @@ 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.InventoryID; -import net.runelite.api.ItemContainer; -import net.runelite.api.ItemID; +import net.runelite.api.VarPlayer; import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.VarbitChanged; import net.runelite.client.Notifier; import net.runelite.client.game.ItemManager; import net.runelite.client.ui.overlay.OverlayManager; @@ -45,8 +44,11 @@ import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; +import static org.mockito.ArgumentMatchers.any; import org.mockito.Mock; -import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import org.mockito.junit.MockitoJUnitRunner; @@ -89,13 +91,12 @@ public class CannonPluginTest @Bind private OverlayManager overlayManager; - private static final ChatMessage ADD_FURNACE = new ChatMessage(); + private static final VarbitChanged cannonAmmoChanged = new VarbitChanged(); @BeforeClass - public static void chatMessageSetup() + public static void cannonVarpSetup() { - ADD_FURNACE.setType(ChatMessageType.SPAM); - ADD_FURNACE.setMessage("You add the furnace."); + cannonAmmoChanged.setIndex(VarPlayer.CANNON_AMMO.getId()); } @Before @@ -105,82 +106,92 @@ public class CannonPluginTest } @Test - public void addWrongTypeOfCannonballs() + public void testAmmoCountOnPlace() { - final ChatMessage message = new ChatMessage(); - message.setType(ChatMessageType.GAMEMESSAGE); - message.setMessage("Your cannon contains 20 x Cannonball.
You can only add cannonballs of the same kind."); + ChatMessage chatMessage = new ChatMessage(); + chatMessage.setType(ChatMessageType.SPAM); + chatMessage.setMessage("You add the furnace."); - plugin.onChatMessage(message); - - assertEquals(20, plugin.getCballsLeft()); - } - - @Test - public void addMaxCannonballs() - { - final ItemContainer inventory = mock(ItemContainer.class); - when(client.getItemContainer(InventoryID.INVENTORY)).thenReturn(inventory); - when(inventory.count(ItemID.CANNONBALL)).thenReturn(100); - when(inventory.count(ItemID.GRANITE_CANNONBALL)).thenReturn(0); - - plugin.onChatMessage(ADD_FURNACE); + plugin.onChatMessage(chatMessage); assertTrue(plugin.isCannonPlaced()); - assertEquals(30, plugin.getCballsLeft()); + plugin.onVarbitChanged(cannonAmmoChanged); + assertEquals(0, plugin.getCballsLeft()); - plugin.onChatMessage(loadCannonballs(30)); + // Some time passes... + + when(client.getVar(VarPlayer.CANNON_AMMO)).thenReturn(30); + plugin.onVarbitChanged(cannonAmmoChanged); assertEquals(30, plugin.getCballsLeft()); } @Test - public void addNotMaxCannonballs() + public void testCannonInfoBox() { - final ItemContainer inventory = mock(ItemContainer.class); - when(client.getItemContainer(InventoryID.INVENTORY)).thenReturn(inventory); - when(inventory.count(ItemID.GRANITE_CANNONBALL)).thenReturn(12); + when(config.showInfobox()).thenReturn(true); - plugin.onChatMessage(ADD_FURNACE); + ChatMessage chatMessage = new ChatMessage(); + chatMessage.setType(ChatMessageType.SPAM); + chatMessage.setMessage("You add the furnace."); + + plugin.onChatMessage(chatMessage); assertTrue(plugin.isCannonPlaced()); assertEquals(0, plugin.getCballsLeft()); - - plugin.onChatMessage(loadCannonballs(12)); - assertEquals(12, plugin.getCballsLeft()); + verify(infoBoxManager).addInfoBox(any(CannonCounter.class)); } @Test - public void addReclaimedCannonballs() + public void testThresholdNotificationShouldNotify() { - final ItemContainer inventory = mock(ItemContainer.class); - when(client.getItemContainer(InventoryID.INVENTORY)).thenReturn(inventory); - when(inventory.count(ItemID.CANNONBALL)).thenReturn(1250); + when(config.showCannonNotifications()).thenReturn(true); + when(config.lowWarningThreshold()).thenReturn(10); - plugin.onChatMessage(ADD_FURNACE); - assertTrue(plugin.isCannonPlaced()); + when(client.getVar(VarPlayer.CANNON_AMMO)).thenReturn(30); + plugin.onVarbitChanged(cannonAmmoChanged); + when(client.getVar(VarPlayer.CANNON_AMMO)).thenReturn(10); + plugin.onVarbitChanged(cannonAmmoChanged); - assertEquals(30, plugin.getCballsLeft()); - - plugin.onChatMessage(loadCannonballs(18)); - assertEquals(30, plugin.getCballsLeft()); + verify(notifier, times(1)).notify("Your cannon has 10 cannon balls remaining!"); } - private static ChatMessage loadCannonballs(final int numCannonballs) + @Test + public void testThresholdNotificationShouldNotifyOnce() { - final ChatMessage message = new ChatMessage(); - message.setType(ChatMessageType.GAMEMESSAGE); + when(config.showCannonNotifications()).thenReturn(true); + when(config.lowWarningThreshold()).thenReturn(10); - // Cannons use the same chat message for loading cannonballs regardless of whether they're normal or granite. - if (numCannonballs == 1) + for (int cballs = 15; cballs >= 8; --cballs) { - message.setMessage("You load the cannon with one cannonball."); - } - else - { - message.setMessage(String.format("You load the cannon with %s cannonballs.", numCannonballs)); + when(client.getVar(VarPlayer.CANNON_AMMO)).thenReturn(cballs); + plugin.onVarbitChanged(cannonAmmoChanged); } - return message; + verify(notifier, times(1)).notify("Your cannon has 10 cannon balls remaining!"); } + @Test + public void testThresholdNotificationsShouldNotNotify() + { + when(config.showCannonNotifications()).thenReturn(true); + when(config.lowWarningThreshold()).thenReturn(0); + + when(client.getVar(VarPlayer.CANNON_AMMO)).thenReturn(30); + plugin.onVarbitChanged(cannonAmmoChanged); + when(client.getVar(VarPlayer.CANNON_AMMO)).thenReturn(10); + plugin.onVarbitChanged(cannonAmmoChanged); + + verify(notifier, never()).notify("Your cannon has 10 cannon balls remaining!"); + } + + @Test + public void testCannonOutOfAmmo() + { + when(config.showCannonNotifications()).thenReturn(true); + ChatMessage cannonOutOfAmmo = new ChatMessage(null, ChatMessageType.GAMEMESSAGE, "", "Your cannon is out of ammo!", "", 0); + + plugin.onChatMessage(cannonOutOfAmmo); + + verify(notifier, times(1)).notify("Your cannon is out of ammo!"); + } }