cannon: use varp for cannonball count

Co-authored-by: Bonfire <5704760+Bonfire@users.noreply.github.com>
This commit is contained in:
Adam
2022-06-10 12:25:10 -04:00
parent f8c27b680b
commit 3ef9095771
3 changed files with 79 additions and 178 deletions

View File

@@ -39,6 +39,7 @@ import lombok.Getter;
@Getter
public enum VarPlayer
{
CANNON_AMMO(3),
ATTACK_STYLE(43),
QUEST_POINTS(101),
IS_POISONED(102),

View File

@@ -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<Integer> 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)

View File

@@ -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.<br>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!");
}
}