slayer: Fix name matching
The Slayer plugin highlights target monsters based on their name rather than NPC ID, as many common monsters (skeletons, zombies, etc.) have nearly endless variations for different models and combat levels. Previously, this name matching was done via a simple `String#contains()`, which led to some incorrect matches such as pirates being highlighted while on rat tasks and Jonny the beard being highlighted while on bear tasks. This commit changes matching to use regex to match string boundaries or whitespace at either end of the task string, ensuring these substring matches can only happen when word breaks occur. The only known existing case where this would apply is for baby dragons and brutal dragons, which are valid alternatives for their respective chromatic dragon tasks.
This commit is contained in:
@@ -203,7 +203,7 @@ public class SlayerPlugin extends Plugin
|
||||
private int cachedXp = -1;
|
||||
private Instant infoTimer;
|
||||
private boolean loginFlag;
|
||||
private final List<String> targetNames = new ArrayList<>();
|
||||
private final List<Pattern> targetNames = new ArrayList<>();
|
||||
|
||||
public final Function<NPC, HighlightedNpc> isTarget = (n) ->
|
||||
{
|
||||
@@ -664,7 +664,8 @@ public class SlayerPlugin extends Plugin
|
||||
SlayerUnlock.GROTESQUE_GUARDIAN_DOUBLE_COUNT.isEnabled(client);
|
||||
}
|
||||
|
||||
private boolean isTarget(NPC npc)
|
||||
@VisibleForTesting
|
||||
boolean isTarget(NPC npc)
|
||||
{
|
||||
if (targetNames.isEmpty())
|
||||
{
|
||||
@@ -681,16 +682,15 @@ public class SlayerPlugin extends Plugin
|
||||
.replace('\u00A0', ' ')
|
||||
.toLowerCase();
|
||||
|
||||
for (String target : targetNames)
|
||||
for (Pattern target : targetNames)
|
||||
{
|
||||
if (name.contains(target))
|
||||
{
|
||||
if (ArrayUtils.contains(composition.getActions(), "Attack")
|
||||
final Matcher targetMatcher = target.matcher(name);
|
||||
if (targetMatcher.find()
|
||||
&& (ArrayUtils.contains(composition.getActions(), "Attack")
|
||||
// Pick action is for zygomite-fungi
|
||||
|| ArrayUtils.contains(composition.getActions(), "Pick"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|| ArrayUtils.contains(composition.getActions(), "Pick")))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -703,13 +703,18 @@ public class SlayerPlugin extends Plugin
|
||||
if (task != null)
|
||||
{
|
||||
Arrays.stream(task.getTargetNames())
|
||||
.map(String::toLowerCase)
|
||||
.map(SlayerPlugin::targetNamePattern)
|
||||
.forEach(targetNames::add);
|
||||
|
||||
targetNames.add(taskName.toLowerCase().replaceAll("s$", ""));
|
||||
targetNames.add(targetNamePattern(taskName.replaceAll("s$", "")));
|
||||
}
|
||||
}
|
||||
|
||||
private static Pattern targetNamePattern(final String targetName)
|
||||
{
|
||||
return Pattern.compile("(?:\\s|^)" + targetName + "(?:\\s|$)", Pattern.CASE_INSENSITIVE);
|
||||
}
|
||||
|
||||
private void rebuildTargetList()
|
||||
{
|
||||
targets.clear();
|
||||
@@ -723,7 +728,8 @@ public class SlayerPlugin extends Plugin
|
||||
}
|
||||
}
|
||||
|
||||
private void setTask(String name, int amt, int initAmt)
|
||||
@VisibleForTesting
|
||||
void setTask(String name, int amt, int initAmt)
|
||||
{
|
||||
setTask(name, amt, initAmt, null);
|
||||
}
|
||||
|
||||
@@ -60,6 +60,8 @@ import net.runelite.client.game.npcoverlay.NpcOverlayService;
|
||||
import net.runelite.client.ui.overlay.OverlayManager;
|
||||
import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -926,4 +928,30 @@ public class SlayerPluginTest
|
||||
assertEquals("Suqahs", slayerPlugin.getTaskName());
|
||||
assertEquals(229, slayerPlugin.getAmount()); // 2 kills
|
||||
}
|
||||
|
||||
@Test
|
||||
public void npcMatching()
|
||||
{
|
||||
assertTrue(matches("Abyssal demon", Task.ABYSSAL_DEMONS));
|
||||
assertTrue(matches("Baby blue dragon", Task.BLUE_DRAGONS));
|
||||
assertTrue(matches("Duck", Task.BIRDS));
|
||||
assertTrue(matches("Donny the Lad", Task.BANDITS));
|
||||
|
||||
assertFalse(matches("Rat", Task.PIRATES));
|
||||
assertFalse(matches("Wolf", Task.WEREWOLVES));
|
||||
assertFalse(matches("Scorpia's offspring", Task.SCORPIA));
|
||||
assertFalse(matches("Jonny the beard", Task.BEARS));
|
||||
}
|
||||
|
||||
private boolean matches(final String npcName, final Task task)
|
||||
{
|
||||
final NPC npc = mock(NPC.class);
|
||||
final NPCComposition comp = mock(NPCComposition.class);
|
||||
when(npc.getTransformedComposition()).thenReturn(comp);
|
||||
when(comp.getName()).thenReturn(npcName);
|
||||
when(comp.getActions()).thenReturn(new String[] { "Attack" });
|
||||
|
||||
slayerPlugin.setTask(task.getName(), 0, 0);
|
||||
return slayerPlugin.isTarget(npc);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user