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 dd8b4e4449..9ce0e4678b 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 @@ -179,9 +179,11 @@ public class WidgetID static class Minimap { static final int XP_ORB = 1; + static final int HEALTH_ORB = 2; static final int PRAYER_ORB = 12; static final int QUICK_PRAYER_ORB = 14; // Has the "Quick-prayers" name static final int RUN_ORB = 20; + static final int SPEC_ORB = 28; } static class Viewport 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 ab05fdd6b5..77d911193e 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 @@ -103,6 +103,8 @@ public enum WidgetInfo MINIMAP_XP_ORB(WidgetID.MINIMAP_GROUP_ID, WidgetID.Minimap.XP_ORB), MINIMAP_PRAYER_ORB(WidgetID.MINIMAP_GROUP_ID, WidgetID.Minimap.PRAYER_ORB), MINIMAP_RUN_ORB(WidgetID.MINIMAP_GROUP_ID, WidgetID.Minimap.RUN_ORB), + MINIMAP_HEALTH_ORB(WidgetID.MINIMAP_GROUP_ID, WidgetID.Minimap.HEALTH_ORB), + MINIMAP_SPEC_ORB(WidgetID.MINIMAP_GROUP_ID, WidgetID.Minimap.SPEC_ORB), LOGIN_CLICK_TO_PLAY_SCREEN(WidgetID.LOGIN_CLICK_TO_PLAY_GROUP_ID, 0), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterConfig.java new file mode 100644 index 0000000000..da1dd73d3f --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterConfig.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018 Abex + * 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.regenmeter; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup( + keyName = "regenmeter", + name = "Regeneration Meter", + description = "Configuration for the data orb regeneration meters" +) +public interface RegenMeterConfig extends Config +{ + @ConfigItem( + keyName = "showHitpoints", + name = "Show hitpoints regen", + description = "Show a ring around the hitpoints orb") + default boolean showHitpoints() + { + return true; + } + + @ConfigItem( + keyName = "showSpecial", + name = "Show Spec. Attack regen", + description = "Show a ring around the Special Attack orb") + default boolean showSpecial() + { + return true; + } + + @ConfigItem( + keyName = "showWhenNoChange", + name = "Show hitpoints regen at full hitpoints", + description = "Always show the hitpoints regen orb, even if there will be no stat change") + default boolean showWhenNoChange() + { + return false; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterOverlay.java new file mode 100644 index 0000000000..456c165e5d --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterOverlay.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2018 Abex + * 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.regenmeter; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.Stroke; +import java.awt.geom.Arc2D; +import javax.inject.Inject; +import net.runelite.api.Client; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayLayer; +import net.runelite.client.ui.overlay.OverlayPosition; + +public class RegenMeterOverlay extends Overlay +{ + private static final Color HITPOINTS_COLOR = brighter(0x9B0703); + private static final Color SPECIAL_COLOR = brighter(0x1E95B0); + + private final Client client; + private RegenMeterPlugin plugin; + private RegenMeterConfig config; + + private static Color brighter(int color) + { + float[] hsv = new float[3]; + Color.RGBtoHSB(color >>> 16, (color >> 8) & 0xFF, color & 0xFF, hsv); + return Color.getHSBColor(hsv[0], 1.f, 1.f); + } + + @Inject + public RegenMeterOverlay(Client client, RegenMeterPlugin plugin, RegenMeterConfig config) + { + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ABOVE_WIDGETS); + this.client = client; + this.plugin = plugin; + this.config = config; + } + + @Override + public Dimension render(Graphics2D g, Point point) + { + g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); + + if (config.showHitpoints()) + { + renderRegen(g, WidgetInfo.MINIMAP_HEALTH_ORB, plugin.getHitpointsPercentage(), HITPOINTS_COLOR); + } + + if (config.showSpecial()) + { + renderRegen(g, WidgetInfo.MINIMAP_SPEC_ORB, plugin.getSpecialPercentage(), SPECIAL_COLOR); + } + + return null; + } + + private void renderRegen(Graphics2D g, WidgetInfo widgetInfo, double percent, Color color) + { + Widget widget = client.getWidget(widgetInfo); + if (widget == null) + { + return; + } + Rectangle bounds = widget.getBounds(); + + Arc2D.Double arc = new Arc2D.Double(bounds.x + 26d, bounds.y + 3.d, 27d, 27d, 90.d, -360.d * percent, Arc2D.OPEN); + final Stroke STROKE = new BasicStroke(2f); + g.setStroke(STROKE); + g.setColor(color); + g.draw(arc); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterPlugin.java new file mode 100644 index 0000000000..3a1313c8c6 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterPlugin.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2018 Abex + * Copyright (c) 2018, Zimaya + * 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.regenmeter; + +import com.google.common.eventbus.Subscribe; +import com.google.inject.Provides; +import javax.inject.Inject; +import lombok.Getter; +import net.runelite.api.Client; +import net.runelite.api.GameState; +import net.runelite.api.Prayer; +import net.runelite.api.Setting; +import net.runelite.api.Skill; +import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.GameTick; +import net.runelite.api.events.VarbitChanged; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.overlay.Overlay; + +@PluginDescriptor(name = "Regeneration Meter") +public class RegenMeterPlugin extends Plugin +{ + private static final int SPEC_REGEN_TICKS = 51; + private static final int NORMAL_HP_REGEN_TICKS = 100; + + @Inject + private RegenMeterOverlay overlay; + + @Inject + private Client client; + + @Inject + RegenMeterConfig config; + + + private int ticksSinceHPRegen; + + private boolean wasRapidHeal; + + @Getter + private double hitpointsPercentage; + + + private int ticksSinceSpecRegen; + + @Getter + private double specialPercentage; + + @Override + public Overlay getOverlay() + { + return overlay; + } + + @Provides + RegenMeterConfig provideConfig(ConfigManager configManager) + { + return configManager.getConfig(RegenMeterConfig.class); + } + + @Subscribe + private void onGameStateChanged(GameStateChanged ev) + { + if (ev.getGameState() == GameState.HOPPING || ev.getGameState() == GameState.LOGIN_SCREEN) + { + ticksSinceHPRegen = -2; // For some reason this makes this accurate + ticksSinceSpecRegen = 0; + } + } + + @Subscribe + private void onVarbitChanged(VarbitChanged ev) + { + boolean isRapidHeal = client.isPrayerActive(Prayer.RAPID_HEAL); + if (wasRapidHeal != isRapidHeal) + { + ticksSinceHPRegen = 0; + } + wasRapidHeal = isRapidHeal; + } + + @Subscribe + private void onTick(GameTick event) + { + if (client.getSetting(Setting.SPECIAL_ATTACK_PERCENT) == 1000) + { + // The recharge doesn't tick when at 100% + ticksSinceSpecRegen = 0; + } + else + { + ticksSinceSpecRegen = (ticksSinceSpecRegen + 1) % SPEC_REGEN_TICKS; + } + specialPercentage = ticksSinceSpecRegen / (double) SPEC_REGEN_TICKS; + + + int ticksPerHPRegen = NORMAL_HP_REGEN_TICKS; + if (client.isPrayerActive(Prayer.RAPID_HEAL)) + { + ticksPerHPRegen /= 2; + } + + ticksSinceHPRegen = (ticksSinceHPRegen + 1) % ticksPerHPRegen; + hitpointsPercentage = ticksSinceHPRegen / (double) ticksPerHPRegen; + + int currentHP = client.getBoostedSkillLevel(Skill.HITPOINTS); + int maxHP = client.getRealSkillLevel(Skill.HITPOINTS); + if (currentHP == maxHP && !config.showWhenNoChange()) + { + hitpointsPercentage = 0; + } + else if (currentHP > maxHP) + { + // Show it going down + hitpointsPercentage = 1 - hitpointsPercentage; + } + } +}