diff --git a/runelite-api/src/main/java/net/runelite/api/Actor.java b/runelite-api/src/main/java/net/runelite/api/Actor.java index 39f275cf6a..88dff72d94 100644 --- a/runelite-api/src/main/java/net/runelite/api/Actor.java +++ b/runelite-api/src/main/java/net/runelite/api/Actor.java @@ -26,6 +26,7 @@ package net.runelite.api; import java.awt.Graphics2D; import java.awt.Polygon; +import java.awt.image.BufferedImage; import java.util.Objects; import net.runelite.rs.api.CombatInfo1; import net.runelite.rs.api.CombatInfo2; @@ -185,6 +186,11 @@ public abstract class Actor extends Renderable return Perspective.getCanvasTextLocation(client, graphics, getLocalLocation(), text, zOffset); } + public Point getCanvasImageLocation(Graphics2D graphics, BufferedImage image, int zOffset) + { + return Perspective.getCanvasImageLocation(client, graphics, getLocalLocation(), image, zOffset); + } + public Point getMinimapLocation() { return Perspective.worldToMiniMap(client, getX(), getY()); diff --git a/runelite-api/src/main/java/net/runelite/api/Perspective.java b/runelite-api/src/main/java/net/runelite/api/Perspective.java index f58f70a8f9..d6fa1f05ef 100644 --- a/runelite-api/src/main/java/net/runelite/api/Perspective.java +++ b/runelite-api/src/main/java/net/runelite/api/Perspective.java @@ -28,6 +28,7 @@ import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.Polygon; import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; public class Perspective { @@ -281,4 +282,32 @@ public class Perspective return new Point(xOffset, p.getY()); } + /** + * Calculates image position and centers depending on image size. + * + * @param client + * @param graphics + * @param localLocation local location of the tile + * @param image image for size measurement + * @param zOffset offset from ground plane + * @return a {@link Point} on screen corresponding to the given + * localLocation. + */ + public static Point getCanvasImageLocation(Client client, Graphics2D graphics, Point localLocation, BufferedImage image, int zOffset) + { + int plane = client.getPlane(); + + Point p = Perspective.worldToCanvas(client, localLocation.getX(), localLocation.getY(), plane, zOffset); + + if (p == null) + { + return null; + } + + int xOffset = p.getX() - image.getWidth() / 2; + int yOffset = p.getY() - image.getHeight() / 2; + + return new Point(xOffset, yOffset); + } + } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java index 94e5503508..de920620e8 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java @@ -40,6 +40,7 @@ import net.runelite.client.plugins.combatnotifier.CombatNotifier; import net.runelite.client.plugins.config.ConfigPlugin; import net.runelite.client.plugins.devtools.DevTools; import net.runelite.client.plugins.examine.ExaminePlugin; +import net.runelite.client.plugins.fishing.FishingPlugin; import net.runelite.client.plugins.fpsinfo.FPS; import net.runelite.client.plugins.grounditems.GroundItems; import net.runelite.client.plugins.hiscore.Hiscore; @@ -93,6 +94,7 @@ public class PluginManager plugins.add(new JewelryCount()); plugins.add(new XPTracker()); plugins.add(new ExaminePlugin()); + plugins.add(new FishingPlugin()); if (RuneLite.getOptions().has("developer-mode")) { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingConfig.java new file mode 100644 index 0000000000..744353c290 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingConfig.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2017, Seth + * 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.fishing; + +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup( + keyName = "fishing", + name = "Fishing", + description = "Configuration for the fishing plugin" +) +public interface FishingConfig +{ + @ConfigItem( + keyName = "enabled", + name = "Enable", + description = "Configures whether or not the fishing plugin is displayed" + ) + default boolean enabled() + { + return true; + } + + @ConfigItem( + keyName = "showIcons", + name = "Display Fish icons", + description = "Configures whether icons or text is displayed" + ) + default boolean showIcons() + { + return true; + } + + @ConfigItem( + keyName = "statTimeout", + name = "Reset stats (minutes)", + description = "Configures the time until statistic is reset" + ) + default int statTimeout() + { + return 5; + } + + @ConfigItem( + keyName = "showShrimp", + name = "Show Shrimp/Anchovies", + description = "Configures whether shrimp/anchovies is displayed" + ) + default boolean showShrimp() + { + return true; + } + + @ConfigItem( + keyName = "showLobster", + name = "Show Lobster/Swordfish/Tuna", + description = "Configures whether lobster/swordfish/tuna is displayed" + ) + default boolean showLobster() + { + return true; + } + + @ConfigItem( + keyName = "showShark", + name = "Show Shark", + description = "Configures whether shark is displayed" + ) + default boolean showShark() + { + return true; + } + + @ConfigItem( + keyName = "showMonkfish", + name = "Show Monkfish", + description = "Configures whether monkfish displayed" + ) + default boolean showMonkfish() + { + return true; + } + + @ConfigItem( + keyName = "showSalmon", + name = "Show Salmon/Trout", + description = "Configures whether salmon/trout is displayed" + ) + default boolean showSalmon() + { + return true; + } + + @ConfigItem( + keyName = "showBarb", + name = "Show Barbarian fish", + description = "Configures whether barbarian fish is displayed" + ) + default boolean showBarb() + { + return true; + } + + @ConfigItem( + keyName = "showAngler", + name = "Show Anglerfish", + description = "Configures whether anglerfish is displayed" + ) + default boolean showAngler() + { + return true; + } + + @ConfigItem( + keyName = "showMinnow", + name = "Show Minnow fish", + description = "Configures whether minnow fish is displayed" + ) + default boolean showMinnow() + { + return true; + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingOverlay.java new file mode 100644 index 0000000000..be38c45243 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingOverlay.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2017, Seth + * 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.fishing; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.time.Duration; +import java.time.Instant; +import net.runelite.api.Client; +import net.runelite.api.GameState; +import net.runelite.client.RuneLite; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.OverlayPriority; + +class FishingOverlay extends Overlay +{ + private static final int WIDTH = 140; + + private static final int TOP_BORDER = 2; + private static final int LEFT_BORDER = 2; + private static final int RIGHT_BORDER = 2; + private static final int BOTTOM_BORDER = 2; + private static final int SEPARATOR = 2; + + private static final Color BACKGROUND = new Color(Color.gray.getRed(), Color.gray.getGreen(), Color.gray.getBlue(), 127); + private static final String FISHING_SPOT = "Fishing spot"; + + private final Client client = RuneLite.getClient(); + + private final FishingPlugin plugin; + private final FishingConfig config; + + public FishingOverlay(FishingPlugin plugin) + { + super(OverlayPosition.TOP_LEFT, OverlayPriority.LOW); + this.plugin = plugin; + this.config = plugin.getConfig(); + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (client.getGameState() != GameState.LOGGED_IN || !config.enabled()) + { + return null; + } + + FishingSession session = plugin.getSession(); + + if (session.getLastFishCaught() == null) + { + return null; + } + + Duration statTimeout = Duration.ofMinutes(config.statTimeout()); + Duration sinceCaught = Duration.between(session.getLastFishCaught(), Instant.now()); + + if (sinceCaught.compareTo(statTimeout) >= 0) + { + return null; + } + + FontMetrics metrics = graphics.getFontMetrics(); + + int height = TOP_BORDER + (metrics.getHeight() * 3) + SEPARATOR * 3 + BOTTOM_BORDER; + int y = TOP_BORDER + metrics.getHeight(); + + graphics.setColor(BACKGROUND); + graphics.fillRect(0, 0, WIDTH, height); + + if (client.getLocalPlayer().getInteracting() != null && client.getLocalPlayer().getInteracting().getName().equals(FISHING_SPOT)) + { + graphics.setColor(Color.green); + String str = "You are fishing"; + graphics.drawString(str, (WIDTH - metrics.stringWidth(str)) / 2, y); + } + else + { + graphics.setColor(Color.red); + String str = "You are NOT fishing"; + graphics.drawString(str, (WIDTH - metrics.stringWidth(str)) / 2, y); + } + + y += metrics.getHeight() + SEPARATOR; + + graphics.setColor(Color.white); + graphics.drawString("Caught Fish:", LEFT_BORDER, y); + + String totalFishedStr = Integer.toString(session.getTotalFished()); + graphics.drawString(totalFishedStr, WIDTH - RIGHT_BORDER - metrics.stringWidth(totalFishedStr), y); + + y += metrics.getHeight() + SEPARATOR; + + graphics.drawString("Fish/hr:", LEFT_BORDER, y); + + String perHourStr; + if (session.getRecentFished() > 2) + { + perHourStr = Integer.toString(session.getPerHour()); + } + else + { + perHourStr = "~"; + } + graphics.drawString(perHourStr, WIDTH - RIGHT_BORDER - metrics.stringWidth(perHourStr), y); + + return new Dimension(WIDTH, height); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingPlugin.java new file mode 100644 index 0000000000..c217671aa0 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingPlugin.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2017, Seth + * 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.fishing; + +import com.google.common.eventbus.Subscribe; +import net.runelite.api.ChatMessageType; +import net.runelite.client.RuneLite; +import net.runelite.client.events.ChatMessage; +import net.runelite.client.events.ConfigChanged; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.ui.overlay.Overlay; + +import java.time.Duration; +import java.time.Instant; +import java.util.Arrays; +import java.util.Collection; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +public class FishingPlugin extends Plugin +{ + private static final int CHECK_INTERVAL = 1; + + private final RuneLite runelite = RuneLite.getRunelite(); + private final FishingConfig config = runelite.getConfigManager().getConfig(FishingConfig.class); + private final FishingOverlay overlay = new FishingOverlay(this); + private final FishingSpotOverlay spotOverlay = new FishingSpotOverlay(this); + + private FishingSession session = new FishingSession(); + private ScheduledFuture future; + + @Override + public Collection getOverlays() + { + return Arrays.asList(overlay, spotOverlay); + } + + @Override + protected void startUp() throws Exception + { + ScheduledExecutorService executor = runelite.getExecutor(); + future = executor.scheduleAtFixedRate(this::checkFishing, CHECK_INTERVAL, CHECK_INTERVAL, TimeUnit.SECONDS); + } + + @Override + protected void shutDown() throws Exception + { + future.cancel(true); + } + + public FishingConfig getConfig() + { + return config; + } + + public FishingSession getSession() + { + return session; + } + + @Subscribe + public void onChatMessage(ChatMessage event) + { + if (event.getType() != ChatMessageType.FILTERED) + { + return; + } + + if (event.getMessage().contains("You catch a") || event.getMessage().contains("You catch some")) + { + session.incrementFishCaught(); + } + } + + @Subscribe + public void updateConfig(ConfigChanged event) + { + spotOverlay.updateConfig(); + } + + private void checkFishing() + { + Instant lastFishCaught = session.getLastFishCaught(); + if (lastFishCaught == null) + { + return; + } + + // reset recentcaught if you haven't caught anything recently + Duration statTimeout = Duration.ofMinutes(config.statTimeout()); + Duration sinceCaught = Duration.between(lastFishCaught, Instant.now()); + + if (sinceCaught.compareTo(statTimeout) >= 0) + { + session.resetRecent(); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingSession.java b/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingSession.java new file mode 100644 index 0000000000..a995cb82dd --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingSession.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2017, Adam + * 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.fishing; + +import java.time.Duration; +import java.time.Instant; + +public class FishingSession +{ + private static final Duration HOUR = Duration.ofHours(1); + + private int perHour; + + private Instant lastFishCaught; + private int totalFished; + + private Instant recentFishCaught; + private int recentFished; + + public void incrementFishCaught() + { + Instant now = Instant.now(); + + lastFishCaught = now; + ++totalFished; + + if (recentFished == 0) + { + recentFishCaught = now; + } + ++recentFished; + + Duration timeSinceStart = Duration.between(recentFishCaught, now); + if (!timeSinceStart.isZero()) + { + perHour = (int) ((double) recentFished * (double) HOUR.toMillis() / (double) timeSinceStart.toMillis()); + } + } + + public void resetRecent() + { + recentFishCaught = null; + recentFished = 0; + } + + public int getPerHour() + { + return perHour; + } + + public Instant getLastFishCaught() + { + return lastFishCaught; + } + + public int getTotalFished() + { + return totalFished; + } + + public Instant getRecentFishCaught() + { + return recentFishCaught; + } + + public int getRecentFished() + { + return recentFished; + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingSpot.java b/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingSpot.java new file mode 100644 index 0000000000..692a53c134 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingSpot.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2017, Seth + * 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.fishing; + +import java.util.HashMap; +import java.util.Map; +import static net.runelite.api.NpcID.FISHING_SPOT_1506; +import static net.runelite.api.NpcID.FISHING_SPOT_1508; +import static net.runelite.api.NpcID.FISHING_SPOT_1510; +import static net.runelite.api.NpcID.FISHING_SPOT_1511; +import static net.runelite.api.NpcID.FISHING_SPOT_1515; +import static net.runelite.api.NpcID.FISHING_SPOT_1518; +import static net.runelite.api.NpcID.FISHING_SPOT_1519; +import static net.runelite.api.NpcID.FISHING_SPOT_1520; +import static net.runelite.api.NpcID.FISHING_SPOT_1521; +import static net.runelite.api.NpcID.FISHING_SPOT_1522; +import static net.runelite.api.NpcID.FISHING_SPOT_1525; +import static net.runelite.api.NpcID.FISHING_SPOT_1526; +import static net.runelite.api.NpcID.FISHING_SPOT_1527; +import static net.runelite.api.NpcID.FISHING_SPOT_1528; +import static net.runelite.api.NpcID.FISHING_SPOT_1530; +import static net.runelite.api.NpcID.FISHING_SPOT_1542; +import static net.runelite.api.NpcID.FISHING_SPOT_1544; +import static net.runelite.api.NpcID.FISHING_SPOT_4316; +import static net.runelite.api.NpcID.FISHING_SPOT_6825; +import static net.runelite.api.NpcID.FISHING_SPOT_7155; +import static net.runelite.api.NpcID.FISHING_SPOT_7199; +import static net.runelite.api.NpcID.FISHING_SPOT_7200; +import static net.runelite.api.NpcID.FISHING_SPOT_7469; +import static net.runelite.api.NpcID.FISHING_SPOT_7470; +import static net.runelite.api.NpcID.FISHING_SPOT_7730; +import static net.runelite.api.NpcID.FISHING_SPOT_7731; +import static net.runelite.api.NpcID.FISHING_SPOT_7732; +import static net.runelite.api.NpcID.FISHING_SPOT_7733; +import static net.runelite.api.NpcID.FISHING_SPOT_7734; + +public enum FishingSpot +{ + SHRIMP("Shrimp, Anchovies", "shrimp", + FISHING_SPOT_1518, FISHING_SPOT_1525, FISHING_SPOT_1528, + FISHING_SPOT_1530, FISHING_SPOT_1544, FISHING_SPOT_7155, + FISHING_SPOT_7469 + ), + LOBSTER("Lobster, Swordfish, Tuna", "lobster", + FISHING_SPOT_1510, FISHING_SPOT_1519, FISHING_SPOT_1521, + FISHING_SPOT_1522, FISHING_SPOT_7199, FISHING_SPOT_7470 + ), + SHARK("Shark, Bass", "shark", + FISHING_SPOT_1511, FISHING_SPOT_1520, FISHING_SPOT_7200 + ), + MONKFISH("Monkfish", "monkfish", + FISHING_SPOT_4316 + ), + SALMON("Salmon, Trout", "salmon", + FISHING_SPOT_1506, FISHING_SPOT_1508, FISHING_SPOT_1515, + FISHING_SPOT_1526, FISHING_SPOT_1527 + ), + BARB_FISH("Sturgeon, Salmon, Trout", "barb", + FISHING_SPOT_1542 + ), + ANGLERFISH("Anglerfish", "anglerfish", + FISHING_SPOT_6825 + ), + MINNOW("Minnow", "minnow", + FISHING_SPOT_7730, FISHING_SPOT_7731, FISHING_SPOT_7732, FISHING_SPOT_7733, FISHING_SPOT_7734 + ); + + private static final Map fishingSpots = new HashMap<>(); + + private final String name; + private final String image; + private final int[] spots; + + static + { + FishingSpot[] spots = values(); + + for (FishingSpot spot : spots) + { + for (int spotId : spot.getIds()) + { + fishingSpots.put(spotId, spot); + } + } + } + + FishingSpot(String spot, String image, int... spots) + { + this.name = spot; + this.image = image; + this.spots = spots; + } + + public String getName() + { + return name; + } + + public String getImage() + { + return image; + } + + public int[] getIds() + { + return spots; + } + + public static FishingSpot getSpot(int npcId) + { + return fishingSpots.get(npcId); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingSpotOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingSpotOverlay.java new file mode 100644 index 0000000000..23256b6df5 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingSpotOverlay.java @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2017, Seth + * 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.fishing; + +import com.google.common.primitives.Ints; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import javax.imageio.ImageIO; +import net.runelite.api.Client; +import net.runelite.api.GameState; +import net.runelite.api.NPC; +import net.runelite.api.queries.NPCQuery; +import net.runelite.client.RuneLite; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.OverlayUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +class FishingSpotOverlay extends Overlay +{ + private static final Logger logger = LoggerFactory.getLogger(FishingSpotOverlay.class); + + private final BufferedImage[] imgCache = new BufferedImage[FishingSpot.values().length]; + + private final List ids = new ArrayList<>(); + + private final FishingConfig config; + private final static Client client = RuneLite.getClient(); + + public FishingSpotOverlay(FishingPlugin plugin) + { + super(OverlayPosition.DYNAMIC); + this.config = plugin.getConfig(); + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (client.getGameState() != GameState.LOGGED_IN || !config.enabled()) + { + return null; + } + + NPCQuery query = new NPCQuery() + .idEquals(Ints.toArray(ids)); + NPC[] npcs = client.runQuery(query); + + for (NPC npc : npcs) + { + FishingSpot spot = FishingSpot.getSpot(npc.getId()); + + if (spot == null) + { + continue; + } + + if (config.showIcons()) + { + BufferedImage fishImage = getFishImage(spot); + if (fishImage != null) + { + OverlayUtil.renderActorOverlayImage(graphics, npc, fishImage, Color.cyan.darker()); + } + } + else + { + String text = spot.getName(); + OverlayUtil.renderActorOverlay(graphics, npc, text, Color.cyan.darker()); + } + } + + return null; + } + + private BufferedImage getFishImage(FishingSpot spot) + { + int fishIdx = spot.ordinal(); + BufferedImage fishImage = null; + + if (imgCache[fishIdx] != null) + { + return imgCache[fishIdx]; + } + + try + { + InputStream in = FishingSpotOverlay.class.getResourceAsStream(spot.getImage() + ".png"); + fishImage = ImageIO.read(in); + imgCache[fishIdx] = fishImage; + } + catch (IOException e) + { + logger.warn("Error Loading fish icon", e); + } + + return fishImage; + } + + public void updateConfig() + { + ids.clear(); + if (config.showShrimp()) + { + ids.addAll(Ints.asList(FishingSpot.SHRIMP.getIds())); + } + if (config.showLobster()) + { + ids.addAll(Ints.asList(FishingSpot.LOBSTER.getIds())); + } + if (config.showShark()) + { + ids.addAll(Ints.asList(FishingSpot.SHARK.getIds())); + } + if (config.showMonkfish()) + { + ids.addAll(Ints.asList(FishingSpot.MONKFISH.getIds())); + } + if (config.showSalmon()) + { + ids.addAll(Ints.asList(FishingSpot.SALMON.getIds())); + } + if (config.showBarb()) + { + ids.addAll(Ints.asList(FishingSpot.BARB_FISH.getIds())); + } + if (config.showAngler()) + { + ids.addAll(Ints.asList(FishingSpot.ANGLERFISH.getIds())); + } + if (config.showMinnow()) + { + ids.addAll(Ints.asList(FishingSpot.MINNOW.getIds())); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayUtil.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayUtil.java index 51854aa445..f9ca92fa6e 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayUtil.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayUtil.java @@ -28,6 +28,8 @@ import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Polygon; +import java.awt.image.BufferedImage; + import net.runelite.api.Actor; import net.runelite.api.Point; import net.runelite.api.TileObject; @@ -68,6 +70,14 @@ public class OverlayUtil graphics.drawString(text, x, y); } + public static void renderImageLocation(Graphics2D graphics, Point imgLoc, BufferedImage image) + { + int x = imgLoc.getX(); + int y = imgLoc.getY(); + + graphics.drawImage(image, x, y, null); + } + public static void renderActorOverlay(Graphics2D graphics, Actor actor, String text, Color color) { Polygon poly = actor.getCanvasTilePoly(); @@ -89,6 +99,27 @@ public class OverlayUtil } } + public static void renderActorOverlayImage(Graphics2D graphics, Actor actor, BufferedImage image, Color color) + { + Polygon poly = actor.getCanvasTilePoly(); + if (poly != null) + { + renderPolygon(graphics, poly, color); + } + + Point minimapLocation = actor.getMinimapLocation(); + if (minimapLocation != null) + { + renderMinimapLocation(graphics, minimapLocation, color); + } + + Point imageLocation = actor.getCanvasImageLocation(graphics, image, actor.getModelHeight()); + if (imageLocation != null) + { + renderImageLocation(graphics, imageLocation, image); + } + } + public static void renderTileOverlay(Graphics2D graphics, TileObject tileObject, String text, Color color) { Polygon poly = tileObject.getCanvasTilePoly(); diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/fishing/anglerfish.png b/runelite-client/src/main/resources/net/runelite/client/plugins/fishing/anglerfish.png new file mode 100644 index 0000000000..dfa4666ebf Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/fishing/anglerfish.png differ diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/fishing/barb.png b/runelite-client/src/main/resources/net/runelite/client/plugins/fishing/barb.png new file mode 100644 index 0000000000..1fb97cde71 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/fishing/barb.png differ diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/fishing/lobster.png b/runelite-client/src/main/resources/net/runelite/client/plugins/fishing/lobster.png new file mode 100644 index 0000000000..8c76b053c0 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/fishing/lobster.png differ diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/fishing/minnow.png b/runelite-client/src/main/resources/net/runelite/client/plugins/fishing/minnow.png new file mode 100644 index 0000000000..7829433de1 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/fishing/minnow.png differ diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/fishing/monkfish.png b/runelite-client/src/main/resources/net/runelite/client/plugins/fishing/monkfish.png new file mode 100644 index 0000000000..0b0f0faf28 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/fishing/monkfish.png differ diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/fishing/salmon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/fishing/salmon.png new file mode 100644 index 0000000000..d59b4494c7 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/fishing/salmon.png differ diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/fishing/shark.png b/runelite-client/src/main/resources/net/runelite/client/plugins/fishing/shark.png new file mode 100644 index 0000000000..2624fb4e45 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/fishing/shark.png differ diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/fishing/shrimp.png b/runelite-client/src/main/resources/net/runelite/client/plugins/fishing/shrimp.png new file mode 100644 index 0000000000..54cf89512b Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/fishing/shrimp.png differ