Merge pull request #970 from sdburns1998/statusorbs
statusorbsplugin: Merge regenmeter and runenergy plugins
This commit is contained in:
@@ -1,126 +0,0 @@
|
||||
/*
|
||||
* 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.Rectangle;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.Stroke;
|
||||
import java.awt.geom.Arc2D;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.VarPlayer;
|
||||
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;
|
||||
|
||||
@Singleton
|
||||
public class RegenMeterOverlay extends Overlay
|
||||
{
|
||||
private static final Color HITPOINTS_COLOR = brighter(0x9B0703);
|
||||
private static final Color SPECIAL_COLOR = brighter(0x1E95B0);
|
||||
private static final Color OVERLAY_COLOR = new Color(255, 255, 255, 60);
|
||||
private static final double DIAMETER = 26D;
|
||||
private static final int OFFSET = 27;
|
||||
private static final Stroke STROKE = new BasicStroke(2f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER);
|
||||
|
||||
private final Client client;
|
||||
private final RegenMeterPlugin plugin;
|
||||
|
||||
private Rectangle getBounds(WidgetInfo widgetInfo)
|
||||
{
|
||||
Widget widget = client.getWidget(widgetInfo);
|
||||
if (widget == null || widget.isHidden())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return widget.getBounds();
|
||||
}
|
||||
|
||||
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(final Client client, final RegenMeterPlugin plugin)
|
||||
{
|
||||
setPosition(OverlayPosition.DYNAMIC);
|
||||
setLayer(OverlayLayer.ABOVE_WIDGETS);
|
||||
this.client = client;
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension render(Graphics2D g)
|
||||
{
|
||||
g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
|
||||
|
||||
if (plugin.isShowHitpoints())
|
||||
{
|
||||
renderRegen(g, WidgetInfo.MINIMAP_HEALTH_ORB, plugin.getHitpointsPercentage(), HITPOINTS_COLOR);
|
||||
}
|
||||
|
||||
if (plugin.isShowSpecial())
|
||||
{
|
||||
if (client.getVar(VarPlayer.SPECIAL_ATTACK_ENABLED) == 1)
|
||||
{
|
||||
final Rectangle bounds = getBounds(WidgetInfo.MINIMAP_SPEC_ORB);
|
||||
if (bounds != null)
|
||||
{
|
||||
g.setColor(RegenMeterOverlay.OVERLAY_COLOR);
|
||||
g.fillOval(
|
||||
bounds.x + OFFSET,
|
||||
bounds.y + (int) (bounds.height / 2D - (DIAMETER) / 2D),
|
||||
(int) DIAMETER, (int) DIAMETER);
|
||||
}
|
||||
}
|
||||
|
||||
renderRegen(g, WidgetInfo.MINIMAP_SPEC_ORB, plugin.getSpecialPercentage(), SPECIAL_COLOR);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void renderRegen(Graphics2D g, WidgetInfo widgetInfo, double percent, Color color)
|
||||
{
|
||||
final Rectangle bounds = getBounds(widgetInfo);
|
||||
if (bounds != null)
|
||||
{
|
||||
Arc2D.Double arc = new Arc2D.Double(bounds.x + OFFSET, bounds.y + (bounds.height / 2 - DIAMETER / 2), DIAMETER, DIAMETER, 90.d, -360.d * percent, Arc2D.OPEN);
|
||||
g.setStroke(STROKE);
|
||||
g.setColor(color);
|
||||
g.draw(arc);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,201 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Sean Dewar <https://github.com/seandewar>
|
||||
* Copyright (c) 2018, Abex
|
||||
* Copyright (c) 2018, Zimaya <https://github.com/Zimaya>
|
||||
* Copyright (c) 2017, Adam <Adam@sigterm.info>
|
||||
* 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.inject.Provides;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.Constants;
|
||||
import net.runelite.api.GameState;
|
||||
import net.runelite.api.Prayer;
|
||||
import net.runelite.api.Skill;
|
||||
import net.runelite.api.VarPlayer;
|
||||
import net.runelite.api.events.ConfigChanged;
|
||||
import net.runelite.api.events.GameStateChanged;
|
||||
import net.runelite.api.events.GameTick;
|
||||
import net.runelite.api.events.VarbitChanged;
|
||||
import net.runelite.client.Notifier;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
import net.runelite.client.ui.overlay.OverlayManager;
|
||||
|
||||
@PluginDescriptor(
|
||||
name = "Regeneration Meter",
|
||||
description = "Track and show the hitpoints and special attack regeneration timers",
|
||||
tags = {"combat", "health", "hitpoints", "special", "attack", "overlay", "notifications"}
|
||||
)
|
||||
@Singleton
|
||||
public class RegenMeterPlugin extends Plugin
|
||||
{
|
||||
private static final int SPEC_REGEN_TICKS = 50;
|
||||
private static final int NORMAL_HP_REGEN_TICKS = 100;
|
||||
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
@Inject
|
||||
private OverlayManager overlayManager;
|
||||
|
||||
@Inject
|
||||
private Notifier notifier;
|
||||
|
||||
@Inject
|
||||
private RegenMeterOverlay overlay;
|
||||
|
||||
@Inject
|
||||
private RegenMeterConfig config;
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private double hitpointsPercentage;
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private double specialPercentage;
|
||||
|
||||
private int ticksSinceSpecRegen;
|
||||
private int ticksSinceHPRegen;
|
||||
private boolean wasRapidHeal;
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private boolean showHitpoints;
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private boolean showSpecial;
|
||||
private boolean showWhenNoChange;
|
||||
private int getNotifyBeforeHpRegenSeconds;
|
||||
|
||||
@Provides
|
||||
RegenMeterConfig provideConfig(ConfigManager configManager)
|
||||
{
|
||||
return configManager.getConfig(RegenMeterConfig.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startUp() throws Exception
|
||||
{
|
||||
updateConfig();
|
||||
overlayManager.add(overlay);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutDown() throws Exception
|
||||
{
|
||||
overlayManager.remove(overlay);
|
||||
}
|
||||
|
||||
@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
|
||||
public void onGameTick(GameTick event)
|
||||
{
|
||||
if (client.getVar(VarPlayer.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 && !this.showWhenNoChange)
|
||||
{
|
||||
hitpointsPercentage = 0;
|
||||
}
|
||||
else if (currentHP > maxHP)
|
||||
{
|
||||
// Show it going down
|
||||
hitpointsPercentage = 1 - hitpointsPercentage;
|
||||
}
|
||||
|
||||
if (this.getNotifyBeforeHpRegenSeconds > 0 && currentHP < maxHP && shouldNotifyHpRegenThisTick(ticksPerHPRegen))
|
||||
{
|
||||
notifier.notify("[" + client.getLocalPlayer().getName() + "] regenerates their next hitpoint soon!");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldNotifyHpRegenThisTick(int ticksPerHPRegen)
|
||||
{
|
||||
// if the configured duration lies between two ticks, choose the earlier tick
|
||||
final int ticksBeforeHPRegen = ticksPerHPRegen - ticksSinceHPRegen;
|
||||
final int notifyTick = (int) Math.ceil(this.getNotifyBeforeHpRegenSeconds * 1000d / Constants.GAME_TICK_LENGTH);
|
||||
return ticksBeforeHPRegen == notifyTick;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onConfigChanged(ConfigChanged event)
|
||||
{
|
||||
if (event.getGroup().equals("regenmeter"))
|
||||
{
|
||||
updateConfig();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateConfig()
|
||||
{
|
||||
this.showHitpoints = config.showHitpoints();
|
||||
this.showSpecial = config.showSpecial();
|
||||
this.showWhenNoChange = config.showWhenNoChange();
|
||||
this.getNotifyBeforeHpRegenSeconds = config.getNotifyBeforeHpRegenSeconds();
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Sean Dewar <https://github.com/seandewar>
|
||||
* 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.runenergy;
|
||||
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
|
||||
@ConfigGroup("runenergy")
|
||||
public interface RunEnergyConfig extends Config
|
||||
{
|
||||
@ConfigItem(
|
||||
keyName = "replaceOrbText",
|
||||
name = "Replace orb text with run time left",
|
||||
description = "Show the remaining run time (in seconds) next in the energy orb."
|
||||
)
|
||||
default boolean replaceOrbText()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Sean Dewar <https://github.com/seandewar>
|
||||
* 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.runenergy;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Rectangle;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.Point;
|
||||
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;
|
||||
import net.runelite.client.ui.overlay.tooltip.Tooltip;
|
||||
import net.runelite.client.ui.overlay.tooltip.TooltipManager;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@Singleton
|
||||
class RunEnergyOverlay extends Overlay
|
||||
{
|
||||
private final RunEnergyPlugin plugin;
|
||||
private final Client client;
|
||||
private final RunEnergyConfig config;
|
||||
private final TooltipManager tooltipManager;
|
||||
|
||||
@Inject
|
||||
private RunEnergyOverlay(final RunEnergyPlugin plugin, final Client client, final RunEnergyConfig config, final TooltipManager tooltipManager)
|
||||
{
|
||||
this.plugin = plugin;
|
||||
this.client = client;
|
||||
this.config = config;
|
||||
this.tooltipManager = tooltipManager;
|
||||
setPosition(OverlayPosition.DYNAMIC);
|
||||
setLayer(OverlayLayer.ABOVE_WIDGETS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension render(Graphics2D graphics)
|
||||
{
|
||||
final Widget runOrb = client.getWidget(WidgetInfo.MINIMAP_TOGGLE_RUN_ORB);
|
||||
|
||||
if (runOrb == null || runOrb.isHidden())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
final Rectangle bounds = runOrb.getBounds();
|
||||
|
||||
if (bounds.getX() <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
final Point mousePosition = client.getMouseCanvasPosition();
|
||||
|
||||
if (bounds.contains(mousePosition.getX(), mousePosition.getY()))
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Weight: ").append(client.getWeight()).append(" kg</br>");
|
||||
|
||||
if (config.replaceOrbText())
|
||||
{
|
||||
sb.append("Run Energy: ").append(client.getEnergy()).append("%");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append("Run Time Remaining: ").append(plugin.getEstimatedRunTimeRemaining(false));
|
||||
}
|
||||
|
||||
int secondsUntil100 = plugin.getEstimatedRecoverTimeRemaining();
|
||||
if (secondsUntil100 > 0)
|
||||
{
|
||||
final int minutes = (int) Math.floor(secondsUntil100 / 60.0);
|
||||
final int seconds = (int) Math.floor(secondsUntil100 - (minutes * 60.0));
|
||||
|
||||
sb.append("</br>").append("100% Energy In: ").append(minutes).append(':').append(StringUtils.leftPad(Integer.toString(seconds), 2, "0"));
|
||||
}
|
||||
|
||||
tooltipManager.add(new Tooltip(sb.toString()));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,340 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Sean Dewar <https://github.com/seandewar>
|
||||
* 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.runenergy;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.inject.Provides;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.Constants;
|
||||
import net.runelite.api.EquipmentInventorySlot;
|
||||
import net.runelite.api.InventoryID;
|
||||
import net.runelite.api.Item;
|
||||
import net.runelite.api.ItemContainer;
|
||||
import static net.runelite.api.ItemID.AGILITY_CAPE;
|
||||
import static net.runelite.api.ItemID.AGILITY_CAPET;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_BOOTS_11861;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13589;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13590;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13601;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13602;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13613;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13614;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13625;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13626;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13637;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13638;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13677;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_BOOTS_13678;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_BOOTS_21076;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_BOOTS_21078;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_CAPE_11853;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_CAPE_13581;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_CAPE_13582;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_CAPE_13593;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_CAPE_13594;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_CAPE_13605;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_CAPE_13606;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_CAPE_13617;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_CAPE_13618;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_CAPE_13629;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_CAPE_13630;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_CAPE_13669;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_CAPE_13670;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_CAPE_21064;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_CAPE_21066;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_GLOVES_11859;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13587;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13588;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13599;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13600;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13611;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13612;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13623;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13624;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13635;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13636;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13675;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_GLOVES_13676;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_GLOVES_21073;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_GLOVES_21075;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_HOOD_11851;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_HOOD_13579;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_HOOD_13580;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_HOOD_13591;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_HOOD_13592;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_HOOD_13603;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_HOOD_13604;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_HOOD_13615;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_HOOD_13616;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_HOOD_13627;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_HOOD_13628;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_HOOD_13667;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_HOOD_13668;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_HOOD_21061;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_HOOD_21063;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_LEGS_11857;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_LEGS_13585;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_LEGS_13586;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_LEGS_13597;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_LEGS_13598;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_LEGS_13609;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_LEGS_13610;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_LEGS_13621;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_LEGS_13622;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_LEGS_13633;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_LEGS_13634;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_LEGS_13673;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_LEGS_13674;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_LEGS_21070;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_LEGS_21072;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_TOP_11855;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_TOP_13583;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_TOP_13584;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_TOP_13595;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_TOP_13596;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_TOP_13607;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_TOP_13608;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_TOP_13619;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_TOP_13620;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_TOP_13631;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_TOP_13632;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_TOP_13671;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_TOP_13672;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_TOP_21067;
|
||||
import static net.runelite.api.ItemID.GRACEFUL_TOP_21069;
|
||||
import static net.runelite.api.ItemID.MAX_CAPE;
|
||||
import net.runelite.api.Skill;
|
||||
import net.runelite.api.Varbits;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
import net.runelite.api.events.ConfigChanged;
|
||||
import net.runelite.api.events.GameTick;
|
||||
import net.runelite.api.widgets.Widget;
|
||||
import net.runelite.api.widgets.WidgetInfo;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
import net.runelite.client.ui.overlay.OverlayManager;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@PluginDescriptor(
|
||||
name = "Run Energy",
|
||||
description = "Show various information related to run energy",
|
||||
tags = {"overlay", "stamina"}
|
||||
)
|
||||
@Singleton
|
||||
public class RunEnergyPlugin extends Plugin
|
||||
{
|
||||
// TODO It would be nice if we have the IDs for just the equipped variants of the Graceful set items.
|
||||
private static final ImmutableSet<Integer> ALL_GRACEFUL_HOODS = ImmutableSet.of(
|
||||
GRACEFUL_HOOD_11851, GRACEFUL_HOOD_13579, GRACEFUL_HOOD_13580, GRACEFUL_HOOD_13591, GRACEFUL_HOOD_13592,
|
||||
GRACEFUL_HOOD_13603, GRACEFUL_HOOD_13604, GRACEFUL_HOOD_13615, GRACEFUL_HOOD_13616, GRACEFUL_HOOD_13627,
|
||||
GRACEFUL_HOOD_13628, GRACEFUL_HOOD_13667, GRACEFUL_HOOD_13668, GRACEFUL_HOOD_21061, GRACEFUL_HOOD_21063
|
||||
);
|
||||
|
||||
private static final ImmutableSet<Integer> ALL_GRACEFUL_TOPS = ImmutableSet.of(
|
||||
GRACEFUL_TOP_11855, GRACEFUL_TOP_13583, GRACEFUL_TOP_13584, GRACEFUL_TOP_13595, GRACEFUL_TOP_13596,
|
||||
GRACEFUL_TOP_13607, GRACEFUL_TOP_13608, GRACEFUL_TOP_13619, GRACEFUL_TOP_13620, GRACEFUL_TOP_13631,
|
||||
GRACEFUL_TOP_13632, GRACEFUL_TOP_13671, GRACEFUL_TOP_13672, GRACEFUL_TOP_21067, GRACEFUL_TOP_21069
|
||||
);
|
||||
|
||||
private static final ImmutableSet<Integer> ALL_GRACEFUL_LEGS = ImmutableSet.of(
|
||||
GRACEFUL_LEGS_11857, GRACEFUL_LEGS_13585, GRACEFUL_LEGS_13586, GRACEFUL_LEGS_13597, GRACEFUL_LEGS_13598,
|
||||
GRACEFUL_LEGS_13609, GRACEFUL_LEGS_13610, GRACEFUL_LEGS_13621, GRACEFUL_LEGS_13622, GRACEFUL_LEGS_13633,
|
||||
GRACEFUL_LEGS_13634, GRACEFUL_LEGS_13673, GRACEFUL_LEGS_13674, GRACEFUL_LEGS_21070, GRACEFUL_LEGS_21072
|
||||
);
|
||||
|
||||
private static final ImmutableSet<Integer> ALL_GRACEFUL_GLOVES = ImmutableSet.of(
|
||||
GRACEFUL_GLOVES_11859, GRACEFUL_GLOVES_13587, GRACEFUL_GLOVES_13588, GRACEFUL_GLOVES_13599, GRACEFUL_GLOVES_13600,
|
||||
GRACEFUL_GLOVES_13611, GRACEFUL_GLOVES_13612, GRACEFUL_GLOVES_13623, GRACEFUL_GLOVES_13624, GRACEFUL_GLOVES_13635,
|
||||
GRACEFUL_GLOVES_13636, GRACEFUL_GLOVES_13675, GRACEFUL_GLOVES_13676, GRACEFUL_GLOVES_21073, GRACEFUL_GLOVES_21075
|
||||
);
|
||||
|
||||
private static final ImmutableSet<Integer> ALL_GRACEFUL_BOOTS = ImmutableSet.of(
|
||||
GRACEFUL_BOOTS_11861, GRACEFUL_BOOTS_13589, GRACEFUL_BOOTS_13590, GRACEFUL_BOOTS_13601, GRACEFUL_BOOTS_13602,
|
||||
GRACEFUL_BOOTS_13613, GRACEFUL_BOOTS_13614, GRACEFUL_BOOTS_13625, GRACEFUL_BOOTS_13626, GRACEFUL_BOOTS_13637,
|
||||
GRACEFUL_BOOTS_13638, GRACEFUL_BOOTS_13677, GRACEFUL_BOOTS_13678, GRACEFUL_BOOTS_21076, GRACEFUL_BOOTS_21078
|
||||
);
|
||||
|
||||
// Agility skill capes and the non-cosmetic Max capes also count for the Graceful set effect
|
||||
private static final ImmutableSet<Integer> ALL_GRACEFUL_CAPES = ImmutableSet.of(
|
||||
GRACEFUL_CAPE_11853, GRACEFUL_CAPE_13581, GRACEFUL_CAPE_13582, GRACEFUL_CAPE_13593, GRACEFUL_CAPE_13594,
|
||||
GRACEFUL_CAPE_13605, GRACEFUL_CAPE_13606, GRACEFUL_CAPE_13617, GRACEFUL_CAPE_13618, GRACEFUL_CAPE_13629,
|
||||
GRACEFUL_CAPE_13630, GRACEFUL_CAPE_13669, GRACEFUL_CAPE_13670, GRACEFUL_CAPE_21064, GRACEFUL_CAPE_21066,
|
||||
AGILITY_CAPE, AGILITY_CAPET, MAX_CAPE
|
||||
);
|
||||
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
@Inject
|
||||
private OverlayManager overlayManager;
|
||||
|
||||
@Inject
|
||||
private RunEnergyOverlay energyOverlay;
|
||||
|
||||
@Inject
|
||||
private RunEnergyConfig energyConfig;
|
||||
|
||||
private boolean localPlayerRunningToDestination;
|
||||
private WorldPoint prevLocalPlayerLocation;
|
||||
|
||||
@Provides
|
||||
RunEnergyConfig getConfig(ConfigManager configManager)
|
||||
{
|
||||
return configManager.getConfig(RunEnergyConfig.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startUp() throws Exception
|
||||
{
|
||||
overlayManager.add(energyOverlay);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutDown() throws Exception
|
||||
{
|
||||
overlayManager.remove(energyOverlay);
|
||||
localPlayerRunningToDestination = false;
|
||||
prevLocalPlayerLocation = null;
|
||||
resetRunOrbText();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGameTick(GameTick event)
|
||||
{
|
||||
localPlayerRunningToDestination =
|
||||
prevLocalPlayerLocation != null &&
|
||||
client.getLocalDestinationLocation() != null &&
|
||||
prevLocalPlayerLocation.distanceTo(client.getLocalPlayer().getWorldLocation()) > 1;
|
||||
|
||||
prevLocalPlayerLocation = client.getLocalPlayer().getWorldLocation();
|
||||
|
||||
if (energyConfig.replaceOrbText())
|
||||
{
|
||||
setRunOrbText(getEstimatedRunTimeRemaining(true));
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onConfigChanged(ConfigChanged event)
|
||||
{
|
||||
if (event.getGroup().equals("runenergy") && !energyConfig.replaceOrbText())
|
||||
{
|
||||
resetRunOrbText();
|
||||
}
|
||||
}
|
||||
|
||||
private void setRunOrbText(String text)
|
||||
{
|
||||
Widget runOrbText = client.getWidget(WidgetInfo.MINIMAP_RUN_ORB_TEXT);
|
||||
|
||||
if (runOrbText != null)
|
||||
{
|
||||
runOrbText.setText(text);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetRunOrbText()
|
||||
{
|
||||
setRunOrbText(Integer.toString(client.getEnergy()));
|
||||
}
|
||||
|
||||
String getEstimatedRunTimeRemaining(boolean inSeconds)
|
||||
{
|
||||
// Calculate the amount of energy lost every tick.
|
||||
// Negative weight has the same depletion effect as 0 kg.
|
||||
final int effectiveWeight = Math.max(client.getWeight(), 0);
|
||||
double lossRate = (Math.min(effectiveWeight, 64) / 100.0) + 0.64;
|
||||
|
||||
if (client.getVar(Varbits.RUN_SLOWED_DEPLETION_ACTIVE) != 0)
|
||||
{
|
||||
lossRate *= 0.3; // Stamina effect reduces energy depletion to 30%
|
||||
}
|
||||
|
||||
// Calculate the number of seconds left
|
||||
final double secondsLeft = (client.getEnergy() * Constants.GAME_TICK_LENGTH) / (lossRate * 1000.0);
|
||||
|
||||
// Return the text
|
||||
if (inSeconds)
|
||||
{
|
||||
return (int) Math.floor(secondsLeft) + "s";
|
||||
}
|
||||
else
|
||||
{
|
||||
final int minutes = (int) Math.floor(secondsLeft / 60.0);
|
||||
final int seconds = (int) Math.floor(secondsLeft - (minutes * 60.0));
|
||||
|
||||
return minutes + ":" + StringUtils.leftPad(Integer.toString(seconds), 2, "0");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isLocalPlayerWearingFullGraceful()
|
||||
{
|
||||
final ItemContainer equipment = client.getItemContainer(InventoryID.EQUIPMENT);
|
||||
|
||||
if (equipment == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final Item[] items = equipment.getItems();
|
||||
|
||||
// Check that the local player is wearing enough items to be using full Graceful
|
||||
// (the Graceful boots will have the highest slot index in the worn set).
|
||||
if (items == null || items.length <= EquipmentInventorySlot.BOOTS.getSlotIdx())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return (ALL_GRACEFUL_HOODS.contains(items[EquipmentInventorySlot.HEAD.getSlotIdx()].getId()) &&
|
||||
ALL_GRACEFUL_TOPS.contains(items[EquipmentInventorySlot.BODY.getSlotIdx()].getId()) &&
|
||||
ALL_GRACEFUL_LEGS.contains(items[EquipmentInventorySlot.LEGS.getSlotIdx()].getId()) &&
|
||||
ALL_GRACEFUL_GLOVES.contains(items[EquipmentInventorySlot.GLOVES.getSlotIdx()].getId()) &&
|
||||
ALL_GRACEFUL_BOOTS.contains(items[EquipmentInventorySlot.BOOTS.getSlotIdx()].getId()) &&
|
||||
ALL_GRACEFUL_CAPES.contains(items[EquipmentInventorySlot.CAPE.getSlotIdx()].getId()));
|
||||
}
|
||||
|
||||
int getEstimatedRecoverTimeRemaining()
|
||||
{
|
||||
if (localPlayerRunningToDestination)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Calculate the amount of energy recovered every second
|
||||
double recoverRate = (48 + client.getBoostedSkillLevel(Skill.AGILITY)) / 360.0;
|
||||
|
||||
if (isLocalPlayerWearingFullGraceful())
|
||||
{
|
||||
recoverRate *= 1.3; // 30% recover rate increase from Graceful set effect
|
||||
}
|
||||
|
||||
// Calculate the number of seconds left
|
||||
return (int) ((100 - client.getEnergy()) / recoverRate);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Abex
|
||||
* Copyright (c) 2018, Sean Dewar <https://github.com/seandewar>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -22,29 +23,47 @@
|
||||
* (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;
|
||||
package net.runelite.client.plugins.statusorbs;
|
||||
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
import net.runelite.client.config.Stub;
|
||||
|
||||
@ConfigGroup("regenmeter")
|
||||
public interface RegenMeterConfig extends Config
|
||||
@ConfigGroup("statusorbs")
|
||||
public interface StatusOrbsConfig extends Config
|
||||
{
|
||||
@ConfigItem(
|
||||
keyName = "showHitpoints",
|
||||
name = "Show hitpoints regen",
|
||||
description = "Show a ring around the hitpoints orb")
|
||||
default boolean showHitpoints()
|
||||
keyName = "hp",
|
||||
name = "Hitpoints",
|
||||
description = "",
|
||||
position = 0
|
||||
)
|
||||
default Stub hp()
|
||||
{
|
||||
return new Stub();
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "dynamicHpHeart",
|
||||
name = "Dynamic hitpoints heart",
|
||||
description = "Changes the HP heart color to match players current affliction",
|
||||
parent = "hp",
|
||||
position = 1
|
||||
)
|
||||
default boolean dynamicHpHeart()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "showSpecial",
|
||||
name = "Show Spec. Attack regen",
|
||||
description = "Show a ring around the Special Attack orb")
|
||||
default boolean showSpecial()
|
||||
keyName = "showHitpoints",
|
||||
name = "Show hitpoints regen",
|
||||
description = "Show a ring around the hitpoints orb",
|
||||
parent = "hp",
|
||||
position = 2
|
||||
)
|
||||
default boolean showHitpoints()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -52,7 +71,10 @@ public interface RegenMeterConfig extends Config
|
||||
@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")
|
||||
description = "Always show the hitpoints regen orb, even if there will be no stat change",
|
||||
parent = "hp",
|
||||
position = 3
|
||||
)
|
||||
default boolean showWhenNoChange()
|
||||
{
|
||||
return false;
|
||||
@@ -61,10 +83,70 @@ public interface RegenMeterConfig extends Config
|
||||
@ConfigItem(
|
||||
keyName = "notifyBeforeHpRegenDuration",
|
||||
name = "Hitpoint Regen Notification (seconds)",
|
||||
description = "Notify approximately when your next hitpoint is about to regen. A value of 0 will disable notification."
|
||||
description = "Notify approximately when your next hitpoint is about to regen. A value of 0 will disable notification.",
|
||||
parent = "hp",
|
||||
position = 4
|
||||
)
|
||||
default int getNotifyBeforeHpRegenSeconds()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "spec",
|
||||
name = "Special attack",
|
||||
description = "",
|
||||
position = 5
|
||||
)
|
||||
default Stub spec()
|
||||
{
|
||||
return new Stub();
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "showSpecial",
|
||||
name = "Show Spec. Attack regen",
|
||||
description = "Show a ring around the Special Attack orb",
|
||||
parent = "spec",
|
||||
position = 6
|
||||
)
|
||||
default boolean showSpecial()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "run",
|
||||
name = "Run energy",
|
||||
description = "",
|
||||
position = 7
|
||||
)
|
||||
default Stub run()
|
||||
{
|
||||
return new Stub();
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "showRun",
|
||||
name = "Show run energy regen",
|
||||
description = "Show a ring around the run regen orb",
|
||||
position = 8,
|
||||
parent = "run"
|
||||
)
|
||||
default boolean showRun()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "replaceOrbText",
|
||||
name = "Replace run orb text with run time left",
|
||||
description = "Show the remaining run time (in seconds) next in the energy orb",
|
||||
position = 9,
|
||||
parent = "run"
|
||||
)
|
||||
default boolean replaceOrbText()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Abex
|
||||
* Copyright (c) 2018, Sean Dewar <https://github.com/seandewar>
|
||||
* 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.statusorbs;
|
||||
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
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.InventoryID;
|
||||
import net.runelite.api.Point;
|
||||
import net.runelite.api.Skill;
|
||||
import net.runelite.api.VarPlayer;
|
||||
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;
|
||||
import net.runelite.client.ui.overlay.tooltip.Tooltip;
|
||||
import net.runelite.client.ui.overlay.tooltip.TooltipManager;
|
||||
import net.runelite.client.util.Graceful;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public class StatusOrbsOverlay extends Overlay
|
||||
{
|
||||
private static final Color HITPOINTS_COLOR = brighter(0x9B0703);
|
||||
private static final Color SPECIAL_COLOR = brighter(0x1E95B0);
|
||||
private static final Color RUN_COLOR = new Color(255, 215, 0);
|
||||
private static final Color OVERLAY_COLOR = new Color(255, 255, 255, 60);
|
||||
private static final double DIAMETER = 26D;
|
||||
private static final int OFFSET = 27;
|
||||
|
||||
private final Client client;
|
||||
private final StatusOrbsPlugin plugin;
|
||||
private final TooltipManager tooltipManager;
|
||||
|
||||
private long last = System.nanoTime();
|
||||
private double percentHp;
|
||||
private double lastHp;
|
||||
private double percentSpec;
|
||||
private double lastSpec;
|
||||
private double percentRun;
|
||||
private double lastRun;
|
||||
|
||||
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 StatusOrbsOverlay(Client client, StatusOrbsPlugin plugin, TooltipManager tooltipManager)
|
||||
{
|
||||
setPosition(OverlayPosition.DYNAMIC);
|
||||
setLayer(OverlayLayer.ABOVE_WIDGETS);
|
||||
this.client = client;
|
||||
this.plugin = plugin;
|
||||
this.tooltipManager = tooltipManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension render(Graphics2D g)
|
||||
{
|
||||
g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
|
||||
|
||||
long current = System.nanoTime();
|
||||
double ms = (current - last) / (double) 1000000;
|
||||
|
||||
if (plugin.isShowHitpoints())
|
||||
{
|
||||
if (lastHp == plugin.getHitpointsPercentage() && plugin.getHitpointsPercentage() != 0)
|
||||
{
|
||||
percentHp += ms * plugin.getHpPerMs();
|
||||
}
|
||||
else
|
||||
{
|
||||
percentHp = plugin.getHitpointsPercentage();
|
||||
lastHp = plugin.getHitpointsPercentage();
|
||||
}
|
||||
renderRegen(g, WidgetInfo.MINIMAP_HEALTH_ORB, percentHp, HITPOINTS_COLOR);
|
||||
}
|
||||
|
||||
if (plugin.isShowSpecial())
|
||||
{
|
||||
if (client.getVar(VarPlayer.SPECIAL_ATTACK_ENABLED) == 1)
|
||||
{
|
||||
final Widget widget = client.getWidget(WidgetInfo.MINIMAP_SPEC_ORB);
|
||||
|
||||
if (widget != null && !widget.isHidden())
|
||||
{
|
||||
final Rectangle bounds = widget.getBounds();
|
||||
g.setColor(OVERLAY_COLOR);
|
||||
g.fillOval(
|
||||
bounds.x + OFFSET,
|
||||
bounds.y + (int) (bounds.height / 2 - (DIAMETER) / 2),
|
||||
(int) DIAMETER, (int) DIAMETER);
|
||||
}
|
||||
}
|
||||
|
||||
if (lastSpec == plugin.getSpecialPercentage() && plugin.getSpecialPercentage() != 0)
|
||||
{
|
||||
percentSpec += ms * plugin.getSpecPerMs();
|
||||
}
|
||||
else
|
||||
{
|
||||
percentSpec = plugin.getSpecialPercentage();
|
||||
lastSpec = plugin.getSpecialPercentage();
|
||||
}
|
||||
|
||||
renderRegen(g, WidgetInfo.MINIMAP_SPEC_ORB, percentSpec, SPECIAL_COLOR);
|
||||
}
|
||||
|
||||
if (plugin.isReplaceOrbText())
|
||||
{
|
||||
final Widget runOrb = client.getWidget(WidgetInfo.MINIMAP_TOGGLE_RUN_ORB);
|
||||
|
||||
if (runOrb == null || runOrb.isHidden())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
final Rectangle bounds = runOrb.getBounds();
|
||||
|
||||
if (bounds.getX() <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
final Point mousePosition = client.getMouseCanvasPosition();
|
||||
|
||||
if (bounds.contains(mousePosition.getX(), mousePosition.getY()))
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Weight: ").append(client.getWeight()).append(" kg</br>");
|
||||
|
||||
if (plugin.isReplaceOrbText())
|
||||
{
|
||||
sb.append("Run Energy: ").append(client.getEnergy()).append("%");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append("Run Time Remaining: ").append(plugin.getEstimatedRunTimeRemaining(false));
|
||||
}
|
||||
|
||||
int secondsUntil100 = plugin.getEstimatedRecoverTimeRemaining();
|
||||
if (secondsUntil100 > 0)
|
||||
{
|
||||
final int minutes = (int) Math.floor(secondsUntil100 / 60.0);
|
||||
final int seconds = (int) Math.floor(secondsUntil100 - (minutes * 60.0));
|
||||
|
||||
sb.append("</br>").append("100% Energy In: ").append(minutes).append(':').append(StringUtils.leftPad(Integer.toString(seconds), 2, "0"));
|
||||
}
|
||||
|
||||
tooltipManager.add(new Tooltip(sb.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
if (plugin.isShowRun())
|
||||
{
|
||||
if (lastRun == plugin.getRunPercentage() && plugin.getRunPercentage() != 0)
|
||||
{
|
||||
double recoverRate = (48 + client.getBoostedSkillLevel(Skill.AGILITY)) / 360000.0;
|
||||
|
||||
if (Graceful.hasFullSet(client.getItemContainer(InventoryID.EQUIPMENT)))
|
||||
{
|
||||
recoverRate *= 1.3; // 30% recover rate increase from Graceful set effect
|
||||
}
|
||||
|
||||
percentRun += ms * recoverRate;
|
||||
}
|
||||
else
|
||||
{
|
||||
percentRun = plugin.getRunPercentage();
|
||||
lastRun = plugin.getRunPercentage();
|
||||
}
|
||||
renderRegen(g, WidgetInfo.MINIMAP_RUN_ORB, percentRun, RUN_COLOR);
|
||||
}
|
||||
|
||||
last = current;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void renderRegen(Graphics2D g, WidgetInfo widgetInfo, double percent, Color color)
|
||||
{
|
||||
Widget widget = client.getWidget(widgetInfo);
|
||||
if (widget == null || widget.isHidden())
|
||||
{
|
||||
return;
|
||||
}
|
||||
Rectangle bounds = widget.getBounds();
|
||||
|
||||
Arc2D.Double arc = new Arc2D.Double(bounds.x + OFFSET, bounds.y + (bounds.height / 2 - DIAMETER / 2), DIAMETER, DIAMETER, 90.d, -360.d * percent, Arc2D.OPEN);
|
||||
final Stroke STROKE = new BasicStroke(2f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER);
|
||||
g.setStroke(STROKE);
|
||||
g.setColor(color);
|
||||
g.draw(arc);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,490 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Owain van Brakel <https://github.com/Owain94>
|
||||
* Copyright (c) 2018, TheStonedTurtle <https://github.com/TheStonedTurtle>
|
||||
* Copyright (c) 2018 Abex
|
||||
* Copyright (c) 2018, Zimaya <https://github.com/Zimaya>
|
||||
* Copyright (c) 2017, Adam <Adam@sigterm.info>
|
||||
* 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.statusorbs;
|
||||
|
||||
import com.google.inject.Provides;
|
||||
import java.awt.image.BufferedImage;
|
||||
import javax.inject.Inject;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.Constants;
|
||||
import net.runelite.api.GameState;
|
||||
import net.runelite.api.InventoryID;
|
||||
import net.runelite.api.Prayer;
|
||||
import net.runelite.api.Skill;
|
||||
import net.runelite.api.SpriteID;
|
||||
import net.runelite.api.VarPlayer;
|
||||
import net.runelite.api.Varbits;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
import net.runelite.api.events.ConfigChanged;
|
||||
import net.runelite.api.events.GameStateChanged;
|
||||
import net.runelite.api.events.GameTick;
|
||||
import net.runelite.api.events.VarbitChanged;
|
||||
import net.runelite.api.widgets.Widget;
|
||||
import net.runelite.api.widgets.WidgetInfo;
|
||||
import net.runelite.client.Notifier;
|
||||
import net.runelite.client.callback.ClientThread;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
import net.runelite.client.ui.overlay.OverlayManager;
|
||||
import net.runelite.client.util.Graceful;
|
||||
import net.runelite.client.util.ImageUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@PluginDescriptor(
|
||||
name = "Status Orbs",
|
||||
description = "Configure settings for the Minimap orbs",
|
||||
tags = {"minimap", "orb", "regen", "energy", "special"}
|
||||
)
|
||||
public class StatusOrbsPlugin extends Plugin
|
||||
{
|
||||
private static final BufferedImage HEART_DISEASE;
|
||||
private static final BufferedImage HEART_POISON;
|
||||
private static final BufferedImage HEART_VENOM;
|
||||
|
||||
static
|
||||
{
|
||||
HEART_DISEASE = ImageUtil.resizeCanvas(ImageUtil.getResourceStreamFromClass(StatusOrbsPlugin.class, "1067-DISEASE.png"), 26, 26);
|
||||
HEART_POISON = ImageUtil.resizeCanvas(ImageUtil.getResourceStreamFromClass(StatusOrbsPlugin.class, "1067-POISON.png"), 26, 26);
|
||||
HEART_VENOM = ImageUtil.resizeCanvas(ImageUtil.getResourceStreamFromClass(StatusOrbsPlugin.class, "1067-VENOM.png"), 26, 26);
|
||||
}
|
||||
|
||||
private static final int SPEC_REGEN_TICKS = 50;
|
||||
private static final int NORMAL_HP_REGEN_TICKS = 100;
|
||||
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
@Inject
|
||||
private ClientThread clientThread;
|
||||
|
||||
@Inject
|
||||
private ConfigManager configManager;
|
||||
|
||||
@Inject
|
||||
private StatusOrbsConfig config;
|
||||
|
||||
@Inject
|
||||
private StatusOrbsOverlay overlay;
|
||||
|
||||
@Inject
|
||||
private OverlayManager overlayManager;
|
||||
|
||||
@Inject
|
||||
private Notifier notifier;
|
||||
|
||||
@Getter
|
||||
private double hitpointsPercentage;
|
||||
|
||||
@Getter
|
||||
private double specialPercentage;
|
||||
|
||||
@Getter
|
||||
private double runPercentage;
|
||||
|
||||
@Getter
|
||||
private double hpPerMs;
|
||||
|
||||
@Getter
|
||||
private double specPerMs = (double) 1 / (SPEC_REGEN_TICKS * 600);
|
||||
|
||||
// RegenMeter
|
||||
private int ticksSinceSpecRegen;
|
||||
private int ticksSinceHPRegen;
|
||||
private boolean wasRapidHeal;
|
||||
private double ticksSinceRunRegen;
|
||||
|
||||
// Run Energy
|
||||
private int lastEnergy = 0;
|
||||
private boolean localPlayerRunningToDestination;
|
||||
private WorldPoint currPoint;
|
||||
private WorldPoint prevLocalPlayerLocation;
|
||||
|
||||
private BufferedImage heart;
|
||||
|
||||
private boolean dynamicHpHeart;
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private boolean showHitpoints;
|
||||
private boolean showWhenNoChange;
|
||||
private int getNotifyBeforeHpRegenSeconds;
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private boolean showSpecial;
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private boolean showRun;
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private boolean replaceOrbText;
|
||||
|
||||
@Provides
|
||||
StatusOrbsConfig provideConfig(ConfigManager configManager)
|
||||
{
|
||||
return configManager.getConfig(StatusOrbsConfig.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startUp() throws Exception
|
||||
{
|
||||
migrateConfigs();
|
||||
updateConfig();
|
||||
overlayManager.add(overlay);
|
||||
if (this.dynamicHpHeart && client.getGameState().equals(GameState.LOGGED_IN))
|
||||
{
|
||||
clientThread.invoke(this::checkHealthIcon);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutDown() throws Exception
|
||||
{
|
||||
overlayManager.remove(overlay);
|
||||
localPlayerRunningToDestination = false;
|
||||
prevLocalPlayerLocation = null;
|
||||
resetRunOrbText();
|
||||
if (this.dynamicHpHeart)
|
||||
{
|
||||
clientThread.invoke(this::resetHealthIcon);
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onConfigChanged(ConfigChanged event)
|
||||
{
|
||||
if (event.getGroup().equals("statusorbs"))
|
||||
{
|
||||
updateConfig();
|
||||
switch (event.getKey())
|
||||
{
|
||||
case "replaceOrbText":
|
||||
if (!this.replaceOrbText)
|
||||
{
|
||||
resetRunOrbText();
|
||||
}
|
||||
break;
|
||||
case "dynamicHpHeart":
|
||||
if (this.dynamicHpHeart)
|
||||
{
|
||||
checkHealthIcon();
|
||||
}
|
||||
else
|
||||
{
|
||||
resetHealthIcon();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
private void onVarbitChanged(VarbitChanged e)
|
||||
{
|
||||
if (this.dynamicHpHeart)
|
||||
{
|
||||
checkHealthIcon();
|
||||
}
|
||||
|
||||
boolean isRapidHeal = client.isPrayerActive(Prayer.RAPID_HEAL);
|
||||
if (wasRapidHeal != isRapidHeal)
|
||||
{
|
||||
ticksSinceHPRegen = 0;
|
||||
}
|
||||
wasRapidHeal = isRapidHeal;
|
||||
}
|
||||
|
||||
@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;
|
||||
ticksSinceRunRegen = -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGameTick(GameTick event)
|
||||
{
|
||||
if (client.getVar(VarPlayer.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;
|
||||
hpPerMs = ticksPerHPRegen / (double) 6000000;
|
||||
if (client.isPrayerActive(Prayer.RAPID_HEAL))
|
||||
{
|
||||
ticksPerHPRegen /= 2;
|
||||
hpPerMs *= 2;
|
||||
}
|
||||
|
||||
ticksSinceHPRegen = (ticksSinceHPRegen + 1) % ticksPerHPRegen;
|
||||
hitpointsPercentage = ticksSinceHPRegen / (double) ticksPerHPRegen;
|
||||
|
||||
int currentHP = client.getBoostedSkillLevel(Skill.HITPOINTS);
|
||||
int maxHP = client.getRealSkillLevel(Skill.HITPOINTS);
|
||||
if (currentHP == maxHP && !this.showWhenNoChange)
|
||||
{
|
||||
hitpointsPercentage = 0;
|
||||
}
|
||||
else if (currentHP > maxHP)
|
||||
{
|
||||
// Show it going down
|
||||
hitpointsPercentage = 1 - hitpointsPercentage;
|
||||
}
|
||||
|
||||
// Run Energy
|
||||
localPlayerRunningToDestination =
|
||||
prevLocalPlayerLocation != null &&
|
||||
client.getLocalDestinationLocation() != null &&
|
||||
prevLocalPlayerLocation.distanceTo(client.getLocalPlayer().getWorldLocation()) > 1;
|
||||
|
||||
if (this.getNotifyBeforeHpRegenSeconds > 0 && currentHP < maxHP && shouldNotifyHpRegenThisTick(ticksPerHPRegen))
|
||||
{
|
||||
notifier.notify("[" + client.getLocalPlayer().getName() + "] regenerates their next hitpoint soon!");
|
||||
}
|
||||
|
||||
localPlayerRunningToDestination =
|
||||
prevLocalPlayerLocation != null &&
|
||||
client.getLocalDestinationLocation() != null &&
|
||||
prevLocalPlayerLocation.distanceTo(client.getLocalPlayer().getWorldLocation()) > 1;
|
||||
|
||||
prevLocalPlayerLocation = client.getLocalPlayer().getWorldLocation();
|
||||
|
||||
if (this.replaceOrbText)
|
||||
{
|
||||
setRunOrbText(getEstimatedRunTimeRemaining(true));
|
||||
}
|
||||
|
||||
int currEnergy = client.getEnergy();
|
||||
currPoint = client.getLocalPlayer().getWorldLocation();
|
||||
if (currEnergy == 100 || (prevLocalPlayerLocation != null && currPoint.distanceTo(prevLocalPlayerLocation) > 1) || currEnergy < lastEnergy)
|
||||
{
|
||||
ticksSinceRunRegen = 0;
|
||||
}
|
||||
else if (currEnergy > lastEnergy)
|
||||
{
|
||||
if (runPercentage < 1)
|
||||
{
|
||||
ticksSinceRunRegen = (1 - runPercentage) / runRegenPerTick();
|
||||
ticksSinceRunRegen = ticksSinceRunRegen > 1 ? 1 : ticksSinceRunRegen;
|
||||
}
|
||||
else
|
||||
{
|
||||
ticksSinceRunRegen = (runPercentage - 1) / runRegenPerTick();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ticksSinceRunRegen += 1;
|
||||
}
|
||||
runPercentage = ticksSinceRunRegen * runRegenPerTick();
|
||||
prevLocalPlayerLocation = currPoint;
|
||||
lastEnergy = currEnergy;
|
||||
}
|
||||
|
||||
private boolean shouldNotifyHpRegenThisTick(int ticksPerHPRegen)
|
||||
{
|
||||
// if the configured duration lies between two ticks, choose the earlier tick
|
||||
final int ticksBeforeHPRegen = ticksPerHPRegen - ticksSinceHPRegen;
|
||||
final int notifyTick = (int) Math.ceil(this.getNotifyBeforeHpRegenSeconds * 1000d / Constants.GAME_TICK_LENGTH);
|
||||
return ticksBeforeHPRegen == notifyTick;
|
||||
}
|
||||
|
||||
private void setRunOrbText(String text)
|
||||
{
|
||||
Widget runOrbText = client.getWidget(WidgetInfo.MINIMAP_RUN_ORB_TEXT);
|
||||
|
||||
if (runOrbText != null)
|
||||
{
|
||||
runOrbText.setText(text);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetRunOrbText()
|
||||
{
|
||||
setRunOrbText(Integer.toString(client.getEnergy()));
|
||||
}
|
||||
|
||||
String getEstimatedRunTimeRemaining(boolean inSeconds)
|
||||
{
|
||||
// Calculate the amount of energy lost every 2 ticks (0.6 seconds).
|
||||
// Negative weight has the same depletion effect as 0 kg.
|
||||
final int effectiveWeight = Math.max(client.getWeight(), 0);
|
||||
double lossRate = (Math.min(effectiveWeight, 64) / 100.0) + 0.64;
|
||||
|
||||
if (client.getVar(Varbits.RUN_SLOWED_DEPLETION_ACTIVE) != 0)
|
||||
{
|
||||
lossRate *= 0.3; // Stamina effect reduces energy depletion to 30%
|
||||
}
|
||||
|
||||
// Calculate the number of seconds left
|
||||
final double secondsLeft = (client.getEnergy() * 0.6) / lossRate;
|
||||
|
||||
// Return the text
|
||||
if (inSeconds)
|
||||
{
|
||||
return (int) Math.floor(secondsLeft) + "s";
|
||||
}
|
||||
else
|
||||
{
|
||||
final int minutes = (int) Math.floor(secondsLeft / 60.0);
|
||||
final int seconds = (int) Math.floor(secondsLeft - (minutes * 60.0));
|
||||
|
||||
return minutes + ":" + StringUtils.leftPad(Integer.toString(seconds), 2, "0");
|
||||
}
|
||||
}
|
||||
|
||||
int getEstimatedRecoverTimeRemaining()
|
||||
{
|
||||
if (localPlayerRunningToDestination)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Calculate the amount of energy recovered every second
|
||||
double recoverRate = (48 + client.getBoostedSkillLevel(Skill.AGILITY)) / 360.0;
|
||||
|
||||
if (Graceful.hasFullSet(client.getItemContainer(InventoryID.EQUIPMENT)))
|
||||
{
|
||||
recoverRate *= 1.3; // 30% recover rate increase from Graceful set effect
|
||||
}
|
||||
|
||||
// Calculate the number of seconds left
|
||||
final double secondsLeft = (100 - client.getEnergy()) / recoverRate;
|
||||
return (int) secondsLeft;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check player afflictions to determine health icon
|
||||
*/
|
||||
private void checkHealthIcon()
|
||||
{
|
||||
BufferedImage newHeart;
|
||||
|
||||
int poison = client.getVar(VarPlayer.IS_POISONED);
|
||||
if (poison >= 1000000)
|
||||
{
|
||||
newHeart = HEART_VENOM;
|
||||
}
|
||||
else if (poison > 0)
|
||||
{
|
||||
newHeart = HEART_POISON;
|
||||
}
|
||||
else if (client.getVar(VarPlayer.DISEASE_VALUE) > 0)
|
||||
{
|
||||
newHeart = HEART_DISEASE;
|
||||
}
|
||||
else
|
||||
{
|
||||
heart = null;
|
||||
resetHealthIcon();
|
||||
return;
|
||||
}
|
||||
|
||||
// Only update sprites when the heart icon actually changes
|
||||
if (newHeart != heart)
|
||||
{
|
||||
heart = newHeart;
|
||||
client.getWidgetSpriteCache().reset();
|
||||
client.getSpriteOverrides().put(SpriteID.MINIMAP_ORB_HITPOINTS_ICON, ImageUtil.getImageSprite(heart, client));
|
||||
}
|
||||
}
|
||||
|
||||
private double runRegenPerTick()
|
||||
{
|
||||
double recoverRate = (client.getBoostedSkillLevel(Skill.AGILITY) / 6d + 8) / 100;
|
||||
|
||||
if (Graceful.hasFullSet(client.getItemContainer(InventoryID.EQUIPMENT)))
|
||||
{
|
||||
return recoverRate * 1.3;
|
||||
}
|
||||
return recoverRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the HP Heart is the default Sprite
|
||||
*/
|
||||
private void resetHealthIcon()
|
||||
{
|
||||
client.getWidgetSpriteCache().reset();
|
||||
client.getSpriteOverrides().remove(SpriteID.MINIMAP_ORB_HITPOINTS_ICON);
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates configs from runenergy and regenmeter to this plugin and deletes the old config values.
|
||||
* This method should be removed after a reasonable amount of time.
|
||||
*/
|
||||
@Deprecated
|
||||
private void migrateConfigs()
|
||||
{
|
||||
migrateConfig("regenmeter", "showHitpoints");
|
||||
migrateConfig("regenmeter", "showSpecial");
|
||||
migrateConfig("regenmeter", "showWhenNoChange");
|
||||
migrateConfig("regenmeter", "notifyBeforeHpRegenDuration");
|
||||
|
||||
migrateConfig("runenergy", "replaceOrbText");
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for migrating individual config options
|
||||
* This method should be removed after a reasonable amount of time.
|
||||
*
|
||||
* @param group old group name
|
||||
* @param key key name to migrate
|
||||
*/
|
||||
@Deprecated
|
||||
private void migrateConfig(String group, String key)
|
||||
{
|
||||
String value = configManager.getConfiguration(group, key);
|
||||
if (value != null)
|
||||
{
|
||||
configManager.setConfiguration("statusorbs", key, value);
|
||||
configManager.unsetConfiguration(group, key);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateConfig()
|
||||
{
|
||||
this.dynamicHpHeart = config.dynamicHpHeart();
|
||||
this.showHitpoints = config.showHitpoints();
|
||||
this.showWhenNoChange = config.showWhenNoChange();
|
||||
this.getNotifyBeforeHpRegenSeconds = config.getNotifyBeforeHpRegenSeconds();
|
||||
this.showSpecial = config.showSpecial();
|
||||
this.showRun = config.showRun();
|
||||
this.replaceOrbText = config.replaceOrbText();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (c) 2018 raiyni <https://github.com/raiyni>
|
||||
* 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.util;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import net.runelite.api.EquipmentInventorySlot;
|
||||
import net.runelite.api.Item;
|
||||
import net.runelite.api.ItemContainer;
|
||||
import static net.runelite.api.ItemID.*;
|
||||
|
||||
public enum Graceful
|
||||
{
|
||||
// TODO: It would be nice if we have the IDs for just the equipped variants of the Graceful set items.
|
||||
HOOD(
|
||||
GRACEFUL_HOOD_11851, GRACEFUL_HOOD_13579, GRACEFUL_HOOD_13580, GRACEFUL_HOOD_13591, GRACEFUL_HOOD_13592,
|
||||
GRACEFUL_HOOD_13603, GRACEFUL_HOOD_13604, GRACEFUL_HOOD_13615, GRACEFUL_HOOD_13616, GRACEFUL_HOOD_13627,
|
||||
GRACEFUL_HOOD_13628, GRACEFUL_HOOD_13667, GRACEFUL_HOOD_13668, GRACEFUL_HOOD_21061, GRACEFUL_HOOD_21063
|
||||
),
|
||||
|
||||
TOP(
|
||||
GRACEFUL_TOP_11855, GRACEFUL_TOP_13583, GRACEFUL_TOP_13584, GRACEFUL_TOP_13595, GRACEFUL_TOP_13596,
|
||||
GRACEFUL_TOP_13607, GRACEFUL_TOP_13608, GRACEFUL_TOP_13619, GRACEFUL_TOP_13620, GRACEFUL_TOP_13631,
|
||||
GRACEFUL_TOP_13632, GRACEFUL_TOP_13671, GRACEFUL_TOP_13672, GRACEFUL_TOP_21067, GRACEFUL_TOP_21069
|
||||
),
|
||||
|
||||
LEGS(
|
||||
GRACEFUL_LEGS_11857, GRACEFUL_LEGS_13585, GRACEFUL_LEGS_13586, GRACEFUL_LEGS_13597, GRACEFUL_LEGS_13598,
|
||||
GRACEFUL_LEGS_13609, GRACEFUL_LEGS_13610, GRACEFUL_LEGS_13621, GRACEFUL_LEGS_13622, GRACEFUL_LEGS_13633,
|
||||
GRACEFUL_LEGS_13634, GRACEFUL_LEGS_13673, GRACEFUL_LEGS_13674, GRACEFUL_LEGS_21070, GRACEFUL_LEGS_21072
|
||||
),
|
||||
|
||||
GLOVES(
|
||||
GRACEFUL_GLOVES_11859, GRACEFUL_GLOVES_13587, GRACEFUL_GLOVES_13588, GRACEFUL_GLOVES_13599, GRACEFUL_GLOVES_13600,
|
||||
GRACEFUL_GLOVES_13611, GRACEFUL_GLOVES_13612, GRACEFUL_GLOVES_13623, GRACEFUL_GLOVES_13624, GRACEFUL_GLOVES_13635,
|
||||
GRACEFUL_GLOVES_13636, GRACEFUL_GLOVES_13675, GRACEFUL_GLOVES_13676, GRACEFUL_GLOVES_21073, GRACEFUL_GLOVES_21075
|
||||
),
|
||||
|
||||
BOOTS(
|
||||
GRACEFUL_BOOTS_11861, GRACEFUL_BOOTS_13589, GRACEFUL_BOOTS_13590, GRACEFUL_BOOTS_13601, GRACEFUL_BOOTS_13602,
|
||||
GRACEFUL_BOOTS_13613, GRACEFUL_BOOTS_13614, GRACEFUL_BOOTS_13625, GRACEFUL_BOOTS_13626, GRACEFUL_BOOTS_13637,
|
||||
GRACEFUL_BOOTS_13638, GRACEFUL_BOOTS_13677, GRACEFUL_BOOTS_13678, GRACEFUL_BOOTS_21076, GRACEFUL_BOOTS_21078
|
||||
),
|
||||
|
||||
// Agility skill capes and the non-cosmetic Max capes also count for the Graceful set effect
|
||||
CAPE(
|
||||
GRACEFUL_CAPE_11853, GRACEFUL_CAPE_13581, GRACEFUL_CAPE_13582, GRACEFUL_CAPE_13593, GRACEFUL_CAPE_13594,
|
||||
GRACEFUL_CAPE_13605, GRACEFUL_CAPE_13606, GRACEFUL_CAPE_13617, GRACEFUL_CAPE_13618, GRACEFUL_CAPE_13629,
|
||||
GRACEFUL_CAPE_13630, GRACEFUL_CAPE_13669, GRACEFUL_CAPE_13670, GRACEFUL_CAPE_21064, GRACEFUL_CAPE_21066,
|
||||
AGILITY_CAPE, AGILITY_CAPET, MAX_CAPE
|
||||
);
|
||||
|
||||
private final ImmutableSet<Integer> ids;
|
||||
|
||||
Graceful(Integer... ids)
|
||||
{
|
||||
this.ids = ImmutableSet.copyOf(ids);
|
||||
}
|
||||
|
||||
public static boolean hasFullSet(final ItemContainer equipment)
|
||||
{
|
||||
if (equipment == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final Item[] items = equipment.getItems();
|
||||
|
||||
if (equipment == null || items.length <= EquipmentInventorySlot.BOOTS.getSlotIdx())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return HOOD.ids.contains(items[EquipmentInventorySlot.HEAD.getSlotIdx()].getId())
|
||||
&& TOP.ids.contains(items[EquipmentInventorySlot.BODY.getSlotIdx()].getId())
|
||||
&& LEGS.ids.contains(items[EquipmentInventorySlot.LEGS.getSlotIdx()].getId())
|
||||
&& GLOVES.ids.contains(items[EquipmentInventorySlot.GLOVES.getSlotIdx()].getId())
|
||||
&& BOOTS.ids.contains(items[EquipmentInventorySlot.BOOTS.getSlotIdx()].getId())
|
||||
&& CAPE.ids.contains(items[EquipmentInventorySlot.CAPE.getSlotIdx()].getId());
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
Reference in New Issue
Block a user