diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java index 3bb39c4487..8c18ddfea6 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java @@ -48,6 +48,7 @@ class WidgetID static final int PRIVATE_CHAT = 163; static final int CHATBOX_GROUP_ID = 162; static final int WORLD_MAP_MENU_GROUP_ID = 160; + static final int VOLCANIC_MINE_GROUP_ID = 611; static class WorldMap { @@ -188,4 +189,20 @@ class WidgetID static final int WEAPON_NAME = 1; static final int LEVEL = 2; } + + static class VolcanicMine + { + static final int GENERAL_INFOBOX_GROUP_ID = 4; + static final int TIME_LEFT = 8; + static final int POINTS = 10; + static final int STABILITY = 12; + static final int PLAYER_COUNT = 14; + static final int VENTS_INFOBOX_GROUP_ID = 15; + static final int VENT_A_PERCENTAGE = 19; + static final int VENT_B_PERCENTAGE = 20; + static final int VENT_C_PERCENTAGE = 21; + static final int VENT_A_STATUS = 23; + static final int VENT_B_STATUS = 24; + static final int VENT_C_STATUS = 25; + } } diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java index ba6fe27ed5..deb6f3a9f5 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java @@ -61,6 +61,19 @@ public enum WidgetInfo PESTCONTROL_ACTIVITY_BAR(WidgetID.PESTRCONTROL_GROUP_ID, WidgetID.PestControl.ACTIVITY_BAR), PESTCONTROL_ACTIVITY_PROGRESS(WidgetID.PESTRCONTROL_GROUP_ID, WidgetID.PestControl.ACTIVITY_PROGRESS), + VOLCANICMINE_GENERAL_INFOBOX_GROUP(WidgetID.VOLCANIC_MINE_GROUP_ID, WidgetID.VolcanicMine.GENERAL_INFOBOX_GROUP_ID), + VOLCANICMINE_TIME_LEFT(WidgetID.VOLCANIC_MINE_GROUP_ID, WidgetID.VolcanicMine.TIME_LEFT), + VOLCANICMINE_POINTS(WidgetID.VOLCANIC_MINE_GROUP_ID, WidgetID.VolcanicMine.POINTS), + VOLCANICMINE_STABILITY(WidgetID.VOLCANIC_MINE_GROUP_ID, WidgetID.VolcanicMine.STABILITY), + VOLCANICMINE_PLAYER_COUNT(WidgetID.VOLCANIC_MINE_GROUP_ID, WidgetID.VolcanicMine.PLAYER_COUNT), + VOLCANICMINE_VENTS_INFOBOX_GROUP(WidgetID.VOLCANIC_MINE_GROUP_ID, WidgetID.VolcanicMine.VENTS_INFOBOX_GROUP_ID), + VOLCANICMINE_VENT_A_PERCENTAGE(WidgetID.VOLCANIC_MINE_GROUP_ID, WidgetID.VolcanicMine.VENT_A_PERCENTAGE), + VOLCANICMINE_VENT_B_PERCENTAGE(WidgetID.VOLCANIC_MINE_GROUP_ID, WidgetID.VolcanicMine.VENT_B_PERCENTAGE), + VOLCANICMINE_VENT_C_PERCENTAGE(WidgetID.VOLCANIC_MINE_GROUP_ID, WidgetID.VolcanicMine.VENT_C_PERCENTAGE), + VOLCANICMINE_VENT_A_STATUS(WidgetID.VOLCANIC_MINE_GROUP_ID, WidgetID.VolcanicMine.VENT_A_STATUS), + VOLCANICMINE_VENT_B_STATUS(WidgetID.VOLCANIC_MINE_GROUP_ID, WidgetID.VolcanicMine.VENT_B_STATUS), + VOLCANICMINE_VENT_C_STATUS(WidgetID.VOLCANIC_MINE_GROUP_ID, WidgetID.VolcanicMine.VENT_C_STATUS), + CLAN_CHAT_TITLE(WidgetID.CLAN_CHAT_GROUP_ID, WidgetID.ClanChat.TITLE), CLAN_CHAT_NAME(WidgetID.CLAN_CHAT_GROUP_ID, WidgetID.ClanChat.NAME), CLAN_CHAT_OWNER(WidgetID.CLAN_CHAT_GROUP_ID, WidgetID.ClanChat.OWNER), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/volcanicminehelper/LavaPlatform.java b/runelite-client/src/main/java/net/runelite/client/plugins/volcanicminehelper/LavaPlatform.java new file mode 100644 index 0000000000..30dcc1ddbd --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/volcanicminehelper/LavaPlatform.java @@ -0,0 +1,65 @@ +/* + * 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.volcanicminehelper; + +import java.time.Duration; + +public enum LavaPlatform +{ + STAGE_1(30998, 90), + STAGE_2(30999, 45), + STAGE_3(31000, 5); + + private final int objectId; + private final Duration time; + + LavaPlatform(int objectId, int time) + { + this.objectId = objectId; + this.time = Duration.ofSeconds(time); + } + + public int getObjectId() + { + return objectId; + } + + public Duration getTime() + { + return time; + } + + public static LavaPlatform fromId(int id) + { + for (LavaPlatform lavaPlatform : values()) + { + if (lavaPlatform.objectId == id) + { + return lavaPlatform; + } + } + return null; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/volcanicminehelper/VolcanicMineConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/volcanicminehelper/VolcanicMineConfig.java new file mode 100644 index 0000000000..e55604d27e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/volcanicminehelper/VolcanicMineConfig.java @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2017. l2- + * + * 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.volcanicminehelper; + +import java.awt.Color; +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup( + keyName = "VolcanicMine", + name = "Volcanic mine", + description = "Configuration for the volcanic mine plugin" +) +public interface VolcanicMineConfig extends Config +{ + @ConfigItem( + position = 0, + keyName = "enabled", + name = "Enable", + description = "Configures whether the volcanic mine plugin is displayed" + ) + default boolean enabled() + { + return true; + } + + @ConfigItem( + position = 1, + keyName = "tray", + name = "Send Tray Notification", + description = "Toggles tray notifications" + ) + default boolean sendTrayNotification() + { + return true; + } + + @ConfigItem( + position = 2, + keyName = "focused", + name = "Alert When Focused", + description = "Toggles idle notifications for when the client is focused" + ) + default boolean alertWhenFocused() + { + return true; + } + + @ConfigItem( + position = 3, + keyName = "request", + name = "Request Window Focus", + description = "Toggles window focus request" + ) + default boolean requestFocus() + { + return true; + } + + @ConfigItem( + position = 4, + keyName = "prayerHelper", + name = "shows prayer", + description = "Shows when you need to use protection prayers" + ) + default boolean prayerHelperEnabled() + { + return true; + } + + @ConfigItem( + position = 5, + keyName = "platformColorLow", + name = "PlatformColor low risk", + description = "Configures the color for the platformOverlay" + ) + default Color platformColorLow() + { + return Color.GREEN; + } + + @ConfigItem( + position = 6, + keyName = "platformColorMed", + name = "PlatformColor med risk", + description = "Configures the color for the platformOverlay" + ) + default Color platformColorMed() + { + return Color.YELLOW; + } + + @ConfigItem( + position = 7, + keyName = "platformColorHigh", + name = "PlatformColor high risk", + description = "Configures the color for the platformOverlay" + ) + default Color platformColorHigh() + { + return Color.decode("#FF3333"); + } + + @ConfigItem( + position = 8, + keyName = "platformTimerThreshold", + name = "Platform timer notification threshold", + description = "At what time does the plugin notify (in seconds), set to -1 to disable" + ) + default int platformTimerThreshold() + { + return 20; + } + + @ConfigItem( + position = 9, + keyName = "timeLeftThreshold", + name = "Time left threshold", + description = "At what time does the plugin notify (in seconds), set to -1 to disable" + ) + default int timerThreshold() + { + return 40; + } + + @ConfigItem( + position = 9, + keyName = "stabilityThreshold", + name = "Stability threshold", + description = "At what stability does the plugin notify (in percentage), set to -1 to disable" + ) + default int stabilityPercentageThreshold() + { + return 30; + } + + @ConfigItem( + position = 10, + keyName = "prayer", + name = "Prayer warning", + description = "Protect prayer warning setting" + ) + default WarningMode prayerWarning() + { + return WarningMode.INGAME_AND_NOTIFICATION; + } + + @ConfigItem( + position = 11, + keyName = "hotTiles", + name = "Hot Tile warning", + description = "Hot tile warning setting" + ) + default WarningMode hotTileWarning() + { + return WarningMode.INGAME_AND_NOTIFICATION; + } + + @ConfigItem( + position = 12, + keyName = "timerWarning", + name = "timer warning", + description = "timer low warning setting" + ) + default WarningMode timerWarning() + { + return WarningMode.INGAME_AND_NOTIFICATION; + } + + @ConfigItem( + position = 13, + keyName = "stabilityWarning", + name = "stability warning", + description = "stability low warning setting" + ) + default WarningMode stabilityWarning() + { + return WarningMode.INGAME_AND_NOTIFICATION; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/volcanicminehelper/VolcanicMineHelperPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/volcanicminehelper/VolcanicMineHelperPlugin.java new file mode 100644 index 0000000000..8ae928e563 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/volcanicminehelper/VolcanicMineHelperPlugin.java @@ -0,0 +1,406 @@ +/* + * Copyright (c) 2017. l2- + * + * 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.volcanicminehelper; + +import com.google.common.eventbus.Subscribe; +import com.google.inject.Binder; +import com.google.inject.Provides; +import java.awt.Color; +import static java.lang.Math.abs; +import net.runelite.api.Client; +import net.runelite.api.GameObject; +import net.runelite.api.GameState; +import net.runelite.api.NPC; +import net.runelite.api.Player; +import net.runelite.api.Point; +import net.runelite.api.Region; +import net.runelite.api.Tile; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.RuneLite; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.events.ConfigChanged; +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 org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import javax.annotation.Nullable; +import javax.inject.Inject; +import java.time.Duration; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import net.runelite.api.Prayer; +import net.runelite.api.Query; +import net.runelite.api.queries.NPCQuery; + +@PluginDescriptor( + name = "Volcanic mine helper" +) +public class VolcanicMineHelperPlugin extends Plugin +{ + private static final Logger logger = LoggerFactory.getLogger(VolcanicMineHelperPlugin.class); + private static final int REGION_SIZE = 104; + private static final int MAX_DISTANCE = 2400; + private static final int LAVA_ID = 30997; + private static final int LAVA_BEAST_ATTACK_RANGE = 1400; + private static final String LAVA_BEAST = "Lava beast"; + private static final Pattern coltagPattern = Pattern.compile("(()|(<\\/col>))"); + + @Inject + @Nullable + Client client; + + @Inject + RuneLite runeLite; + + @Inject + VolcanicMineConfig config; + + @Inject + VolcanicMineOverlay overlay; + + private Map objectTimerMap = new HashMap<>(); + private boolean drawRangePray = false; + private boolean isAboutToFall = false; + private boolean isAboutToCollapse = false; + private boolean timerRunningLow = false; + private boolean isInside = false; + private int stabilityPercentageThreshold; + + @Override + public void configure(Binder binder) + { + binder.bind(VolcanicMineOverlay.class); + } + + @Provides + VolcanicMineConfig provideConfig(ConfigManager configManager) + { + return configManager.getConfig(VolcanicMineConfig.class); + } + + @Override + protected void startUp() throws Exception + { + if (config.stabilityPercentageThreshold() >= -1 && config.stabilityPercentageThreshold() <= 100) + { + stabilityPercentageThreshold = config.stabilityPercentageThreshold(); + } + } + + public Boolean getInside() + { + return isInside; + } + + public VolcanicMineConfig getConfig() + { + return config; + } + + public Map getObjectTimerMap() + { + return objectTimerMap; + } + + public Boolean getDrawRangePray() + { + return drawRangePray; + } + + @Override + public Overlay getOverlay() + { + return overlay; + } + + @Subscribe + public void onConfigChanged(ConfigChanged event) + { + if (config.stabilityPercentageThreshold() >= -1 && config.stabilityPercentageThreshold() <= 100) + { + stabilityPercentageThreshold = config.stabilityPercentageThreshold(); + } + } + + @Schedule( + period = 600, + unit = ChronoUnit.MILLIS + ) + public void update() + { + if (client.getGameState() != GameState.LOGGED_IN || !config.enabled()) + { + return; + } + isInside = isInside(); + if (!isInside) + { + return; + } + lookForGameObjects(); + cleanMap(); + //-- it is unlikely more than 1 of the following notifications will be triggered in 1 update call. + //-- it is possible so maybe send 1 notification per update max which contains the message(s)? + //check if in range of a lava beast + boolean lastRangePray = drawRangePray; + drawRangePray = lavaBeastInRange(client.getLocalPlayer()); + if (!client.isPrayerActive(Prayer.PROTECT_FROM_MISSILES) && drawRangePray && !lastRangePray) + { + warnPlayer(config.prayerWarning(), "Turn on prayer!"); + } + //check if about to fall into lava + boolean lastIsAboutToFall = isAboutToFall; + isAboutToFall = isAboutToFall(client.getLocalPlayer().getLocalLocation()); + if (isAboutToFall && !lastIsAboutToFall) + { + warnPlayer(config.hotTileWarning(), "Watch out for the lava!"); + } + //check if timer is about to run out + boolean lastTimerRunningLow = timerRunningLow; + timerRunningLow = isTimerRunningLow(); + if (timerRunningLow && !lastTimerRunningLow) + { + warnPlayer(config.timerWarning(), "Time to bail!"); + } + //check if stability is low + boolean lastIsAboutToCollapse = isAboutToCollapse; + isAboutToCollapse = isAboutToCollapse(); + if (isAboutToCollapse && !lastIsAboutToCollapse) + { + warnPlayer(config.stabilityWarning(), "Stability is low!"); + } + } + + private boolean isAboutToCollapse() + { + Widget widget = client.getWidget(WidgetInfo.VOLCANICMINE_STABILITY); + if (widget != null && !widget.isHidden()) + { + try + { + if (Integer.parseInt(removeColTags(widget.getText().replace("%", ""))) < stabilityPercentageThreshold) //sanitize of col tags + { + return true; + } + } + catch (NumberFormatException ex) + { + logger.debug("Failed to retrieve stability percentage", ex); + } + } + return false; + } + + private boolean isTimerRunningLow() + { + Widget widget = client.getWidget(WidgetInfo.VOLCANICMINE_TIME_LEFT); + if (widget != null && !widget.isHidden()) + { + try + { + if (timeToSeconds(widget.getText()) < config.timerThreshold()) + { + return true; + } + } + catch (NumberFormatException ex) + { + logger.debug("Failed to retrieve time left", ex); + } + } + return false; + } + + private boolean isAboutToFall(Point point) + { + Instant now = Instant.now(); + for (Tile tile : objectTimerMap.keySet()) + { + if (tile.getGameObjects() != null + && tile.getGameObjects()[0] != null + && tile.getGameObjects()[0].getLocalLocation().equals(point) + && objectTimerMap.get(tile).isAfter(now) //in case the entry hasn't been removed from the hashmap, ignore. + && Duration.between(now, objectTimerMap.get(tile)).getSeconds() < config.platformTimerThreshold()) + { + return true; + } + } + return false; + } + + private boolean isInside() + { + Widget widget = client.getWidget(WidgetInfo.VOLCANICMINE_GENERAL_INFOBOX_GROUP); + return widget != null && !widget.isHidden(); + } + + private boolean lavaBeastInRange(Player player) + { + Query query = new NPCQuery() + .nameEquals(LAVA_BEAST) + .isWithinArea(player.getLocalLocation(), LAVA_BEAST_ATTACK_RANGE); + NPC[] npcs = runeLite.runQuery(query); + return npcs.length > 0; + } + + private void warnPlayer(WarningMode warningMode, String warning) + { + if (warningMode == WarningMode.INGAME || warningMode == WarningMode.INGAME_AND_NOTIFICATION) + { + client.sendGameMessage(getColTag(config.platformColorHigh()) + "Volcanic Mine helper: " + warning); + } + if (warningMode == WarningMode.NOTIFICATION || warningMode == WarningMode.INGAME_AND_NOTIFICATION) + { + sendNotification(warning); + } + } + + private void sendNotification(String message) + { + if (!config.alertWhenFocused() && runeLite.getGui().isFocused()) + { + return; + } + if (config.requestFocus()) + { + runeLite.getGui().requestFocus(); + } + if (config.sendTrayNotification()) + { + runeLite.notify(message); + } + } + + private void lookForGameObjects() + { + Player player = client.getLocalPlayer(); + Region region = client.getRegion(); + Tile[][][] tiles = region.getTiles(); + + int z = client.getPlane(); + + for (int x = 0; x < REGION_SIZE; ++x) + { + for (int y = 0; y < REGION_SIZE; ++y) + { + Tile tile = tiles[z][x][y]; + + if (tile == null) + { + continue; + } + + lookForPlatforms(tile, player); + } + } + } + + private void lookForPlatforms(Tile tile, Player player) + { + Point playerLocation = player.getLocalLocation(); + GameObject[] gameObjects = tile.getGameObjects(); + if (gameObjects == null) + { + return; + } + + for (GameObject gameObject : gameObjects) + { + if (gameObject == null) + { + continue; + } + + LavaPlatform lavaPlatform = LavaPlatform.fromId(gameObject.getId()); + Point objectLocation = gameObject.getLocalLocation(); + + if (lavaPlatform == null + || abs(playerLocation.getX() - objectLocation.getX()) > MAX_DISTANCE + || abs(playerLocation.getY() - objectLocation.getY()) > MAX_DISTANCE) + { + continue; + } + + Instant now = Instant.now(); + Instant vanishTime = now.plus(lavaPlatform.getTime()); + + Instant returnInstant = objectTimerMap.putIfAbsent(tile, vanishTime); + if (returnInstant != null) + { + if (returnInstant.isBefore(now) + || vanishTime.isBefore(returnInstant)) + { + objectTimerMap.replace(tile, vanishTime); + } + } + } + } + + private void cleanMap() + { + //remove the timers which hit 0 + objectTimerMap = objectTimerMap.entrySet().stream() + .filter(entry -> Instant.now().isBefore(entry.getValue())) + .filter(v -> v.getKey().getGameObjects() != null && v.getKey().getGameObjects()[0] != null && v.getKey().getGameObjects()[0].getId() != LAVA_ID) + .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); + } + + public static int timeToSeconds(String s) throws NumberFormatException + { + String[] time = s.split(":", 2); + if (time.length < 2) + { + return Integer.parseInt(time[0]); + } + return Integer.parseInt(time[0]) * 60 + Integer.parseInt(time[1]); + } + + public static String getColTag(Color color) + { + return color == null ? "" : ""; + } + + public static String removeColTags(String s) + { + StringBuffer result = new StringBuffer(); + Matcher m = coltagPattern.matcher(s); + while (m.find()) + { + m.appendReplacement(result, ""); + } + m.appendTail(result); + return result.toString(); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/volcanicminehelper/VolcanicMineOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/volcanicminehelper/VolcanicMineOverlay.java new file mode 100644 index 0000000000..d1e6460f5e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/volcanicminehelper/VolcanicMineOverlay.java @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2017. l2- + * + * 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.volcanicminehelper; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Rectangle; +import java.io.IOException; +import java.io.InputStream; +import java.time.Duration; +import java.time.Instant; +import javax.annotation.Nullable; +import javax.imageio.ImageIO; +import javax.inject.Inject; +import net.runelite.api.Client; +import net.runelite.api.GameObject; +import net.runelite.api.GameState; +import net.runelite.api.Point; +import net.runelite.api.Prayer; +import net.runelite.api.Region; +import net.runelite.api.Tile; +import net.runelite.client.ui.FontManager; +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; + +public class VolcanicMineOverlay extends Overlay +{ + private static final Logger logger = LoggerFactory.getLogger(VolcanicMineHelperPlugin.class); + + private static final int THRESH_LOW = 45; + private static final int THRESH_MED = 5; + + private static final int REGION_SIZE = 104; + private static final int Z_OFFSET_TIMER = 25; + private static final String PROTECT_MESSAGE = "Protect!"; + + private final VolcanicMineHelperPlugin plugin; + private final VolcanicMineConfig config; + + private final Client client; + private Image protectFromMissilesImg; + + @Inject + VolcanicMineOverlay(@Nullable Client client, VolcanicMineHelperPlugin plugin, VolcanicMineConfig config) + { + super(OverlayPosition.DYNAMIC); + this.client = client; + this.plugin = plugin; + this.config = config; + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (client == null || client.getGameState() != GameState.LOGGED_IN || !plugin.getInside() || !config.enabled()) + { + return null; + } + + Font font = FontManager.getRunescapeFont(); + if (font != null) + { + graphics.setFont(font); + } + renderTileObjects(graphics); + renderRangePrayer(graphics); + return null; + } + + private void renderRangePrayer(Graphics2D graphics) + { + if (client.getViewportWidget() == null || !plugin.getDrawRangePray() || !config.prayerHelperEnabled()) + { + return; + } + + Image protectFromMissiles = getProtectFromMissilesImage(); + if (protectFromMissiles == null) + { + return; + } + + Rectangle viewport = client.getViewportWidget().getBounds(); + Rectangle stringBounds = graphics.getFontMetrics().getStringBounds(PROTECT_MESSAGE, graphics).getBounds(); + int x = (int) (viewport.getX() + viewport.getWidth()); + int y = (int) (viewport.getY() + viewport.getHeight()); + int width = (int) stringBounds.getWidth() + 6; + int height = (int) (protectFromMissiles.getHeight(null) + stringBounds.getHeight()) + 5; + Rectangle rectangle = new Rectangle(x - width, y - height, width, height); + graphics.setColor(new Color(255, 255, 255, 150)); + graphics.fillRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height); + graphics.setColor(client.isPrayerActive(Prayer.PROTECT_FROM_MISSILES) ? Color.GREEN : Color.WHITE); + graphics.drawRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height); + graphics.drawImage(protectFromMissiles, x - protectFromMissiles.getWidth(null) - 10, y - protectFromMissiles.getHeight(null) - 15, null); + graphics.drawString(PROTECT_MESSAGE, x - (int) stringBounds.getWidth() - 2, y); + } + + //this is less efficient than drawing from the map however this is more accurate as it doesn't draw false positives (tiles which are not in the current region). + private void renderTileObjects(Graphics2D graphics) + { + Region region = client.getRegion(); + Tile[][][] tiles = region.getTiles(); + + int z = client.getPlane(); + + for (int x = 0; x < REGION_SIZE; ++x) + { + for (int y = 0; y < REGION_SIZE; ++y) + { + Tile tile = tiles[z][x][y]; + + if (tile == null) + { + continue; + } + + renderGameObjects(graphics, tile); + } + } + } + + private void renderGameObjects(Graphics2D graphics, Tile tile) + { + GameObject[] gameObjects = tile.getGameObjects(); + if (gameObjects == null) + { + return; + } + + for (GameObject gameObject : gameObjects) + { + if (gameObject != null && plugin.getObjectTimerMap().containsKey(tile)) + { + Duration duration = Duration.between(Instant.now(), plugin.getObjectTimerMap().get(tile)); + if (!duration.isNegative()) + { + long seconds = duration.getSeconds(); + int minutes = (int) seconds / 60; + String text = String.format("%d:%02d", minutes, seconds % 60); // format as m:ss + Color color; + if (seconds > THRESH_LOW) + { + color = config.platformColorLow();; + } + else if (seconds > THRESH_MED) + { + color = config.platformColorMed(); + } + else + { + color = config.platformColorHigh(); + } + + Point location = gameObject.getCanvasTextLocation(graphics, text, Z_OFFSET_TIMER); + if (location != null) + { + OverlayUtil.renderTextLocation(graphics, location, text, color); + return; //only 1 timer per tile + } + } + } + } + } + + public Image getProtectFromMissilesImage() + { + if (protectFromMissilesImg == null) + { + String path = "/prayers/protect_from_missiles.png"; + protectFromMissilesImg = getImage(path); + } + return protectFromMissilesImg; + } + + private Image getImage(String path) + { + Image image = null; + try + { + InputStream in = VolcanicMineOverlay.class.getResourceAsStream(path); + image = ImageIO.read(in); + } + catch (IOException e) + { + logger.warn("Error loading image", e); + } + return image; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/volcanicminehelper/WarningMode.java b/runelite-client/src/main/java/net/runelite/client/plugins/volcanicminehelper/WarningMode.java new file mode 100644 index 0000000000..a8759c32e3 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/volcanicminehelper/WarningMode.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2017. l2- + * + * 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.volcanicminehelper; + +public enum WarningMode +{ + OFF("Off"), + INGAME("Ingame message"), + NOTIFICATION("Windows notification"), + INGAME_AND_NOTIFICATION("Ingame & notification"); + + private final String name; + + WarningMode(String name) + { + this.name = name; + } + + @Override + public String toString() + { + return name; + } +}