Add run energy plugin

This commit is contained in:
Sean Dewar
2018-08-09 02:36:36 +01:00
committed by Adam
parent 9c4c4c0092
commit cc698c98a3
8 changed files with 403 additions and 0 deletions

View File

@@ -456,6 +456,13 @@ public interface Client extends GameEngine
*/
int getEnergy();
/**
* Gets the current weight of the logged in player.
*
* @return the weight
*/
int getWeight();
/**
* Gets an array of options that can currently be used on other players.
* <p>

View File

@@ -39,6 +39,11 @@ public enum Varbits
*/
TRANSPARENT_CHATBOX(4608),
/*
* If the player has an active stamina potion effect or not
*/
RUN_SLOWED_DEPLETION_ACTIVE(25),
/**
* If scrollbar in resizable mode chat is on the left
*/

View File

@@ -254,6 +254,8 @@ public class WidgetID
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 TOGGLE_RUN_ORB = 22; // Has the "Toggle run" name
static final int RUN_ORB_TEXT = 23;
static final int SPEC_ORB = 28;
}

View File

@@ -141,6 +141,8 @@ public enum WidgetInfo
MINIMAP_PRAYER_ORB(WidgetID.MINIMAP_GROUP_ID, WidgetID.Minimap.PRAYER_ORB),
MINIMAP_QUICK_PRAYER_ORB(WidgetID.MINIMAP_GROUP_ID, WidgetID.Minimap.QUICK_PRAYER_ORB),
MINIMAP_RUN_ORB(WidgetID.MINIMAP_GROUP_ID, WidgetID.Minimap.RUN_ORB),
MINIMAP_TOGGLE_RUN_ORB(WidgetID.MINIMAP_GROUP_ID, WidgetID.Minimap.TOGGLE_RUN_ORB),
MINIMAP_RUN_ORB_TEXT(WidgetID.MINIMAP_GROUP_ID, WidgetID.Minimap.RUN_ORB_TEXT),
MINIMAP_HEALTH_ORB(WidgetID.MINIMAP_GROUP_ID, WidgetID.Minimap.HEALTH_ORB),
MINIMAP_SPEC_ORB(WidgetID.MINIMAP_GROUP_ID, WidgetID.Minimap.SPEC_ORB),

View File

@@ -0,0 +1,43 @@
/*
* 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;
}
}

View File

@@ -0,0 +1,97 @@
/*
* 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 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;
class RunEnergyOverlay extends Overlay
{
private final RunEnergyPlugin plugin;
private final Client client;
private final TooltipManager tooltipManager;
@Inject
private RunEnergyOverlay(final RunEnergyPlugin plugin, final Client client, final TooltipManager tooltipManager)
{
this.plugin = plugin;
this.client = client;
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)
{
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>")
.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;
}
}

View File

@@ -0,0 +1,246 @@
/*
* 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.common.eventbus.Subscribe;
import com.google.inject.Provides;
import javax.inject.Inject;
import net.runelite.api.Client;
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.*;
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.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"}
)
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 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 Integer.toString((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 Integer.toString(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
final double secondsLeft = (100 - client.getEnergy()) / recoverRate;
return (int) secondsLeft;
}
}

View File

@@ -111,6 +111,7 @@ public interface RSClient extends RSGameEngine, Client
int getEnergy();
@Import("weight")
@Override
int getWeight();
@Import("baseX")