From a2435605dfcef8467baeea8da8d8036c01b27adc Mon Sep 17 00:00:00 2001 From: ShaunDreclin Date: Sat, 14 Apr 2018 19:40:17 -0400 Subject: [PATCH] slayer plugin: add slayer target highlighting --- .../client/plugins/slayer/SlayerConfig.java | 28 ++++++ .../client/plugins/slayer/SlayerPlugin.java | 93 +++++++++++++++++-- .../plugins/slayer/TargetClickboxOverlay.java | 84 +++++++++++++++++ .../plugins/slayer/TargetMinimapOverlay.java | 75 +++++++++++++++ .../runelite/client/plugins/slayer/Task.java | 54 ++++++----- 5 files changed, 301 insertions(+), 33 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/slayer/TargetClickboxOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/slayer/TargetMinimapOverlay.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerConfig.java index 7ef39e61a4..11a66081ec 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerConfig.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, Seth + * Copyright (c) 2018, Shaun Dreclin * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,6 +25,7 @@ */ package net.runelite.client.plugins.slayer; +import java.awt.Color; import net.runelite.client.config.Config; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; @@ -36,6 +38,7 @@ import net.runelite.client.config.ConfigItem; public interface SlayerConfig extends Config { @ConfigItem( + position = 1, keyName = "infobox", name = "Task InfoBox", description = "Display task information in an InfoBox" @@ -46,6 +49,7 @@ public interface SlayerConfig extends Config } @ConfigItem( + position = 2, keyName = "itemoverlay", name = "Count on Items", description = "Display task count remaining on slayer items" @@ -56,6 +60,7 @@ public interface SlayerConfig extends Config } @ConfigItem( + position = 3, keyName = "superiornotification", name = "Superior foe notification", description = "Toggles notifications on superior foe encounters" @@ -66,6 +71,7 @@ public interface SlayerConfig extends Config } @ConfigItem( + position = 4, keyName = "statTimeout", name = "InfoBox Expiry (minutes)", description = "Set the time until the InfoBox expires" @@ -75,6 +81,28 @@ public interface SlayerConfig extends Config return 5; } + @ConfigItem( + position = 5, + keyName = "highlightTargets", + name = "Highlight Targets", + description = "Highlight monsters you can kill for your current slayer assignment" + ) + default boolean highlightTargets() + { + return false; + } + + @ConfigItem( + position = 6, + keyName = "targetColor", + name = "Target Color", + description = "Color of the highlighted targets" + ) + default Color getTargetColor() + { + return Color.RED; + } + // Stored data @ConfigItem( keyName = "taskName", diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java index 60eb7c59bd..ab4de683d9 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, Tyler + * Copyright (c) 2018, Shaun Dreclin * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,25 +28,32 @@ package net.runelite.client.plugins.slayer; import com.google.common.eventbus.Subscribe; import com.google.inject.Provides; import java.awt.image.BufferedImage; -import java.time.Instant; import java.time.Duration; -import java.time.temporal.ChronoUnit; +import java.time.Instant; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.inject.Inject; import joptsimple.internal.Strings; +import lombok.AccessLevel; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import net.runelite.api.ChatMessageType; import net.runelite.api.Client; import net.runelite.api.GameState; import net.runelite.api.ItemID; +import net.runelite.api.NPC; +import net.runelite.api.NPCComposition; import static net.runelite.api.Skill.SLAYER; import net.runelite.api.events.ChatMessage; import net.runelite.api.events.ConfigChanged; import net.runelite.api.events.ExperienceChanged; import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.GameTick; import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetInfo; import net.runelite.client.Notifier; @@ -54,7 +62,6 @@ import net.runelite.client.config.ConfigManager; import net.runelite.client.game.ItemManager; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; -import net.runelite.client.task.Schedule; import net.runelite.client.ui.overlay.Overlay; import net.runelite.client.ui.overlay.infobox.InfoBoxManager; import net.runelite.client.util.Text; @@ -100,6 +107,15 @@ public class SlayerPlugin extends Plugin @Inject private ClientThread clientThread; + @Inject + private TargetClickboxOverlay targetClickboxOverlay; + + @Inject + private TargetMinimapOverlay targetMinimapOverlay; + + @Getter(AccessLevel.PACKAGE) + private List highlightedTargets = new ArrayList<>(); + private String taskName; private int amount; private TaskCounter counter; @@ -168,11 +184,8 @@ public class SlayerPlugin extends Plugin config.streak(streak); } - @Schedule( - period = 600, - unit = ChronoUnit.MILLIS - ) - public void scheduledChecks() + @Subscribe + public void onGameTick(GameTick tick) { Widget NPCDialog = client.getWidget(WidgetInfo.DIALOG_NPC_TEXT); if (NPCDialog != null) @@ -216,6 +229,15 @@ public class SlayerPlugin extends Plugin removeCounter(); } } + + if (config.highlightTargets()) + { + highlightedTargets = buildTargetsToHighlight(); + } + else + { + highlightedTargets.clear(); + } } @Subscribe @@ -387,11 +409,62 @@ public class SlayerPlugin extends Plugin counter = null; } + private List buildTargetsToHighlight() + { + if (Strings.isNullOrEmpty(taskName)) + return Collections.EMPTY_LIST; + + List npcs = new ArrayList<>(); + List highlightedNpcs = Arrays.asList(Task.getTask(taskName).getTargetNames()); + highlightedNpcs.add(taskName.replaceAll("s$", "")); + + for (NPC npc : client.getNpcs()) + { + NPCComposition composition = getComposition(npc); + + if (composition == null || composition.getName() == null) + continue; + + String name = composition.getName().replace('\u00A0', ' '); + for (String highlight : highlightedNpcs) + { + if (name.toLowerCase().contains(highlight.toLowerCase()) + && Arrays.asList(composition.getActions()).contains("Attack")) + { + npcs.add(npc); + break; + } + } + } + + return npcs; + } + + /** + * Get npc composition, account for imposters + * + * @param npc + * @return + */ + private static NPCComposition getComposition(NPC npc) + { + if (npc == null) + return null; + + NPCComposition composition = npc.getComposition(); + if (composition != null && composition.getConfigs() != null) + { + composition = composition.transform(); + } + + return composition; + } + //Getters @Override - public Overlay getOverlay() + public Collection getOverlays() { - return overlay; + return Arrays.asList(overlay, targetClickboxOverlay, targetMinimapOverlay); } public String getTaskName() diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TargetClickboxOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TargetClickboxOverlay.java new file mode 100644 index 0000000000..cc52cf9ac1 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TargetClickboxOverlay.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2018, James Swindle + * Copyright (c) 2018, Adam + * Copyright (c) 2018, Shaun Dreclin + * 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.slayer; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Polygon; +import java.util.List; +import javax.inject.Inject; +import net.runelite.api.NPC; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayLayer; +import net.runelite.client.ui.overlay.OverlayPosition; + +public class TargetClickboxOverlay extends Overlay +{ + private final SlayerConfig config; + private final SlayerPlugin plugin; + + @Inject + TargetClickboxOverlay(SlayerConfig config, SlayerPlugin plugin) + { + this.config = config; + this.plugin = plugin; + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ABOVE_SCENE); + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (!config.highlightTargets()) + { + return null; + } + + List targets = plugin.getHighlightedTargets(); + for (NPC target : targets) + { + renderTargetOverlay(graphics, target, config.getTargetColor()); + } + + return null; + } + + private void renderTargetOverlay(Graphics2D graphics, NPC actor, Color color) + { + Polygon objectClickbox = actor.getConvexHull(); + if (objectClickbox != null) + { + graphics.setColor(color); + graphics.setStroke(new BasicStroke(2)); + graphics.draw(objectClickbox); + graphics.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), 20)); + graphics.fill(objectClickbox); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TargetMinimapOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TargetMinimapOverlay.java new file mode 100644 index 0000000000..cdc5c80016 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TargetMinimapOverlay.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2018, James Swindle + * Copyright (c) 2018, Adam + * Copyright (c) 2018, Shaun Dreclin + * 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.slayer; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.util.List; +import javax.inject.Inject; +import net.runelite.api.NPC; +import net.runelite.api.Point; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayLayer; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.OverlayUtil; + +public class TargetMinimapOverlay extends Overlay +{ + private final SlayerConfig config; + private final SlayerPlugin plugin; + + @Inject + TargetMinimapOverlay(SlayerConfig config, SlayerPlugin plugin) + { + this.config = config; + this.plugin = plugin; + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ABOVE_WIDGETS); + } + + @Override + public Dimension render(Graphics2D graphics) + { + List targets = plugin.getHighlightedTargets(); + for (NPC target : targets) + { + renderTargetOverlay(graphics, target, config.getTargetColor()); + } + + return null; + } + + private void renderTargetOverlay(Graphics2D graphics, NPC actor, Color color) + { + Point minimapLocation = actor.getMinimapLocation(); + if (minimapLocation != null) + { + OverlayUtil.renderMinimapLocation(graphics, minimapLocation, color); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/Task.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/Task.java index 27fc4c49f8..768a4c1ec0 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/Task.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/Task.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, Tyler + * Copyright (c) 2018, Shaun Dreclin * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,7 +33,7 @@ import net.runelite.api.ItemID; enum Task { // - ABERRANT_SPECTRES("aberrant spectres", ItemID.ABERRANT_SPECTRE), + ABERRANT_SPECTRES("aberrant spectres", ItemID.ABERRANT_SPECTRE, "spectre"), ABYSSAL_DEMONS("abyssal demons", ItemID.ABYSSAL_DEMON), ANKOU("ankou", ItemID.ANKOU_MASK), AVIANSIES("aviansies", ItemID.ENSOULED_AVIANSIE_HEAD), @@ -40,7 +41,7 @@ enum Task BASILISKS("basilisks", ItemID.BASILISK), BATS("bats", ItemID.GIRAL_BAT_2), BEARS("bears", ItemID.ENSOULED_BEAR_HEAD), - BIRDS("birds", ItemID.FEATHER), + BIRDS("birds", ItemID.FEATHER, "chicken", "rooster", "terrorbird"), BLACK_DEMONS("black demons", ItemID.BLACK_DEMON_MASK), BLACK_DRAGONS("black dragons", ItemID.BLACK_DRAGON_MASK), BLOODVELD("bloodveld", ItemID.BLOODVELD), @@ -49,28 +50,28 @@ enum Task BRONZE_DRAGONS("bronze dragons", ItemID.BRONZE_DRAGON_MASK), CATABLEPON("catablepon", ItemID.LEFT_SKULL_HALF), CAVE_BUGS("cave bugs", ItemID.SWAMP_CAVE_BUG), - CAVE_CRAWLERS("cave crawlers", ItemID.CAVE_CRAWLER), - CAVE_HORRORS("cave horrors", ItemID.CAVE_HORROR), + CAVE_CRAWLERS("cave crawlers", ItemID.CAVE_CRAWLER, "chasm crawler"), + CAVE_HORRORS("cave horrors", ItemID.CAVE_HORROR, "cave abomination"), CAVE_KRAKEN("cave kraken", ItemID.CAVE_KRAKEN), CAVE_SLIMES("cave slimes", ItemID.SWAMP_CAVE_SLIME), - COCKATRICE("cockatrice", ItemID.COCKATRICE), + COCKATRICE("cockatrice", ItemID.COCKATRICE, "cockathrice"), COWS("cows", ItemID.COW_MASK), - CRAWLING_HANDS("crawling hands", ItemID.CRAWLING_HAND), + CRAWLING_HANDS("crawling hands", ItemID.CRAWLING_HAND, "crushing hand"), CROCODILES("crocodiles", ItemID.SWAMP_LIZARD), DAGANNOTH("dagannoth", ItemID.DAGANNOTH), - DARK_BEASTS("dark beasts", ItemID.DARK_BEAST), + DARK_BEASTS("dark beasts", ItemID.DARK_BEAST, "night beast"), DESERT_LIZARDS("desert lizards", ItemID.DESERT_LIZARD), - DOGS("dogs", ItemID.GUARD_DOG), - DUST_DEVILS("dust devils", ItemID.DUST_DEVIL), - DWARVES("dwarves", ItemID.DWARVEN_HELMET), + DOGS("dogs", ItemID.GUARD_DOG, "jackal"), + DUST_DEVILS("dust devils", ItemID.DUST_DEVIL, "choke devil"), + DWARVES("dwarves", ItemID.DWARVEN_HELMET, "dwarf"), EARTH_WARRIORS("earth warriors", ItemID.BRONZE_FULL_HELM_T), - ELVES("elves", ItemID.ELF), + ELVES("elves", ItemID.ELF, "elf"), FEVER_SPIDERS("fever spiders", ItemID.FEVER_SPIDER), FIRE_GIANTS("fire giants", ItemID.FIRE_BATTLESTAFF), FLESH_CRAWLERS("flesh crawlers", ItemID.ENSOULED_SCORPION_HEAD), - FOSSIL_ISLAND_WYVERNS("fossil island wyverns", ItemID.FOSSIL_ISLAND_WYVERN), + FOSSIL_ISLAND_WYVERNS("fossil island wyverns", ItemID.FOSSIL_ISLAND_WYVERN, "ancient wyvern", "long-tailed wyvern", "spitting wyvern", "taloned wyvern"), GARGOYLES("gargoyles", ItemID.GARGOYLE), - GHOSTS("ghosts", ItemID.GHOSTSPEAK_AMULET), + GHOSTS("ghosts", ItemID.GHOSTSPEAK_AMULET, "tortured soul"), GHOULS("ghouls", ItemID.ZOMBIE_HEAD), GOBLINS("goblins", ItemID.ENSOULED_GOBLIN_HEAD), GREATER_DEMONS("greater demons", ItemID.GREATER_DEMON_MASK), @@ -82,16 +83,16 @@ enum Task ICE_GIANTS("ice giants", ItemID.ICE_DIAMOND), ICE_WARRIORS("ice warriors", ItemID.MITHRIL_FULL_HELM_T), ICEFIENDS("icefiends", ItemID.ICE_DIAMOND), - INFERNAL_MAGES("infernal mages", ItemID.INFERNAL_MAGE), + INFERNAL_MAGES("infernal mages", ItemID.INFERNAL_MAGE, "malevolent mage"), IRON_DRAGONS("iron dragons", ItemID.IRON_DRAGON_MASK), - JELLIES("jellies", ItemID.JELLY), + JELLIES("jellies", ItemID.JELLY, "jelly"), JUNGLE_HORROR("jungle horrors", ItemID.ENSOULED_HORROR_HEAD), KALPHITE("kalphite", ItemID.KALPHITE_SOLDIER), KILLERWATTS("killerwatts", ItemID.KILLERWATT), KURASK("kurask", ItemID.KURASK), LESSER_DEMONS("lesser demons", ItemID.LESSER_DEMON_MASK), - LIZARDMEN("lizardmen", ItemID.LIZARDMAN_FANG), - MINIONS_OF_SCABARAS("minions of scabaras", ItemID.GOLDEN_SCARAB), + LIZARDMEN("lizardmen", ItemID.LIZARDMAN_FANG, "lizardman"), + MINIONS_OF_SCABARAS("minions of scabaras", ItemID.GOLDEN_SCARAB, "scarab swarm", "locust rider", "scarab mage"), MINOTAURS("minotaurs", ItemID.ENSOULED_MINOTAUR_HEAD), MITHRIL_DRAGONS("mithril dragons", ItemID.MITHRIL_DRAGON_MASK), MOGRES("mogres", ItemID.MOGRE), @@ -99,10 +100,10 @@ enum Task MONKEYS("monkeys", ItemID.ENSOULED_MONKEY_HEAD), MOSS_GIANTS("moss giants", ItemID.HILL_GIANT_CLUB), MUTATED_ZYGOMITES("mutated zygomites", ItemID.MUTATED_ZYGOMITE), - NECHRYAEL("nechryael", ItemID.NECHRYAEL), + NECHRYAEL("nechryael", ItemID.NECHRYAEL, "nechryarch"), OGRES("ogres", ItemID.ENSOULED_OGRE_HEAD), OTHERWORLDLY_BEING("otherworldly being", ItemID.GHOSTLY_HOOD), - PYREFIENDS("pyrefiends", ItemID.PYREFIEND), + PYREFIENDS("pyrefiends", ItemID.PYREFIEND, "flaming pyrelord"), RATS("rats", ItemID.RATS_TAIL), RED_DRAGONS("red dragons", ItemID.BABY_RED_DRAGON), ROCKSLUGS("rockslugs", ItemID.ROCKSLUG), @@ -123,15 +124,16 @@ enum Task VAMPIRES("vampires", ItemID.STAKE), WALL_BEASTS("wall beasts", ItemID.SWAMP_WALLBEAST), WATERFIENDS("waterfiends", ItemID.WATER_ORB), - WEREWOLVES("werewolves", ItemID.WOLFBANE), - WOLVES("wolves", ItemID.GREY_WOLF_FUR), - ZOMBIES("zombies", ItemID.ZOMBIE_HEAD); + WEREWOLVES("werewolves", ItemID.WOLFBANE, "werewolf"), + WOLVES("wolves", ItemID.GREY_WOLF_FUR, "wolf"), + ZOMBIES("zombies", ItemID.ZOMBIE_HEAD, "undead"); // private static final Map tasks = new HashMap<>(); private final String name; private final int itemSpriteId; + private final String[] targetNames; static { @@ -141,11 +143,12 @@ enum Task } } - Task(String name, int itemSpriteId) + Task(String name, int itemSpriteId, String... targetNames) { Preconditions.checkArgument(itemSpriteId >= 0); this.name = name; this.itemSpriteId = itemSpriteId; + this.targetNames = targetNames; } public static Task getTask(String taskName) @@ -162,4 +165,9 @@ enum Task { return itemSpriteId; } + + public String[] getTargetNames() + { + return this.targetNames; + } }