Added xp globes plugin (#86)

This commit is contained in:
SteveOSRS
2017-06-18 01:54:35 +01:00
committed by Adam
parent 204f4a9685
commit a3d3f95ca9
5 changed files with 518 additions and 0 deletions

View File

@@ -47,6 +47,7 @@ import net.runelite.client.plugins.mousehighlight.MouseHighlight;
import net.runelite.client.plugins.opponentinfo.OpponentInfo;
import net.runelite.client.plugins.pestcontrol.PestControl;
import net.runelite.client.plugins.runecraft.Runecraft;
import net.runelite.client.plugins.xpglobes.XpGlobes;
import net.runelite.client.plugins.xtea.Xtea;
import net.runelite.client.plugins.zulrah.Zulrah;
import org.slf4j.Logger;
@@ -83,6 +84,7 @@ public class PluginManager
plugins.add(new ConfigPlugin());
plugins.add(new GroundItems());
plugins.add(new Implings());
plugins.add(new XpGlobes());
if (RuneLite.getOptions().has("developer-mode"))
{

View File

@@ -0,0 +1,121 @@
/*
* Copyright (c) 2017, Steve <steve.rs.dev@gmail.com>
* 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.xpglobes;
import net.runelite.api.Skill;
import java.time.Instant;
public class XpGlobe
{
private Skill skill;
private int currentXp;
private int currentLevel;
private int goalXp;
private Instant time;
private double skillProgressRadius;
public XpGlobe(Skill skill, int currentXp, int currentLevel, int goalXp, Instant time)
{
this.skill = skill;
this.currentXp = currentXp;
this.currentLevel = currentLevel;
this.goalXp = goalXp;
this.time = time;
}
public Skill getSkill()
{
return skill;
}
public void setSkill(Skill skill)
{
this.skill = skill;
}
public int getCurrentXp()
{
return currentXp;
}
public void setCurrentXp(int currentXp)
{
this.currentXp = currentXp;
}
public int getCurrentLevel()
{
return currentLevel;
}
public void setCurrentLevel(int currentLevel)
{
this.currentLevel = currentLevel;
}
public int getGoalXp()
{
return goalXp;
}
public void setGoalXp(int goalXp)
{
this.goalXp = goalXp;
}
public int getGoalLevel()
{
return this.currentLevel++;
}
public String getSkillName()
{
return skill.getName();
}
public double getSkillProgressRadius()
{
return skillProgressRadius;
}
public void setSkillProgressRadius(int startXp, int currentXp, int goalXp)
{
double xpGained = currentXp - startXp;
double xpGoal = goalXp - startXp;
this.skillProgressRadius = -(3.6 * ((xpGained / xpGoal) * 100)); //arc goes backwards
}
public Instant getTime()
{
return time;
}
public void setTime(Instant time)
{
this.time = time;
}
}

View File

@@ -0,0 +1,157 @@
/*
* Copyright (c) 2017, Steve <steve.rs.dev@gmail.com>
* 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.xpglobes;
import com.google.common.eventbus.Subscribe;
import net.runelite.api.Client;
import net.runelite.api.Experience;
import net.runelite.api.Skill;
import net.runelite.client.RuneLite;
import net.runelite.client.events.ExperienceChanged;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.ui.overlay.Overlay;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
public class XpGlobes extends Plugin
{
private final XpGlobesConfig config = RuneLite.getRunelite().getConfigManager()
.getConfig(XpGlobesConfig.class);
private final Overlay overlay = new XpGlobesOverlay(this);
private final Client client = RuneLite.getClient();
private final XpGlobe[] globeCache = new XpGlobe[Skill.values().length - 1]; //overall does not trigger xp change event
private final List<XpGlobe> xpGlobes = new ArrayList<>();
private static final int SECONDS_TO_SHOW_GLOBE = 10;
private static final int MAXIMUM_SHOWN_GLOBES = 5;
@Override
protected void startUp() throws Exception
{
}
@Override
protected void shutDown() throws Exception
{
}
@Override
public Overlay getOverlay()
{
return overlay;
}
@Subscribe
private void onExperienceChanged(ExperienceChanged event)
{
if (!config.enabled())
{
return;
}
Skill skill = event.getSkill();
int currentXp = client.getSkillExperience(skill);
int currentLevel = Experience.getLevelForXp(currentXp);
int startingXp = 0;
if (currentLevel > 1)
{
startingXp = Experience.getXpForLevel(currentLevel);
}
int goalXp = Experience.getXpForLevel(currentLevel+1);
int skillIdx = skill.ordinal();
XpGlobe cachedGlobe = globeCache[skillIdx];
if (cachedGlobe != null)
{
cachedGlobe.setSkill(skill);
cachedGlobe.setCurrentXp(currentXp);
cachedGlobe.setCurrentLevel(currentLevel);
cachedGlobe.setGoalXp(goalXp);
cachedGlobe.setTime(Instant.now());
cachedGlobe.setSkillProgressRadius(startingXp, currentXp, goalXp);
}
else
{
globeCache[skillIdx] = new XpGlobe(skill, currentXp, currentLevel, goalXp, Instant.now());
}
this.addXpGlobe(globeCache[skillIdx], MAXIMUM_SHOWN_GLOBES);
}
public List<XpGlobe> getXpGlobes()
{
return xpGlobes;
}
public void addXpGlobe(XpGlobe xpGlobe, int maxLength)
{
if (xpGlobes.contains(xpGlobe))
{
//remove the old globe, allowing it to be readded as the most recent (right) side when drawn
xpGlobes.remove(xpGlobe);
}
if (getXpGlobesSize() >= maxLength)
{
xpGlobes.remove(0);
}
xpGlobes.add(xpGlobe);
}
public int getXpGlobesSize()
{
return xpGlobes.size();
}
public void removeExpiredXpGlobes()
{
if (!xpGlobes.isEmpty())
{
Instant currentTime = Instant.now();
for (Iterator<XpGlobe> it = xpGlobes.iterator(); it.hasNext();)
{
XpGlobe globe = it.next();
Instant globeCreationTime = globe.getTime();
if (currentTime.isBefore(globeCreationTime.plusSeconds(SECONDS_TO_SHOW_GLOBE)))
{
//if a globe is not expired, stop checking newer globes
return;
}
it.remove();
}
}
}
public XpGlobesConfig getConfig()
{
return config;
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (c) 2017, Steve <steve.rs.dev@gmail.com>
* 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.xpglobes;
import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigItem;
@ConfigGroup(
keyName = "xpglobes",
name = "XP Globes",
description = "Configuration for the xp globes plugin"
)
public interface XpGlobesConfig
{
@ConfigItem(
keyName = "enabled",
name = "Enabled",
description = "Configures whether or not xp globes are displayed"
)
default boolean enabled()
{
return true;
}
}

View File

@@ -0,0 +1,192 @@
/*
* Copyright (c) 2017, Steve <steve.rs.dev@gmail.com>
* 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.xpglobes;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.Skill;
import net.runelite.client.RuneLite;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayPosition;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import javax.imageio.ImageIO;
import java.awt.geom.Arc2D;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class XpGlobesOverlay extends Overlay
{
private final XpGlobes plugin;
private final Client client = RuneLite.getClient();
private final XpGlobesConfig config;
private static final Logger logger = LoggerFactory.getLogger(XpGlobesOverlay.class);
private static final int DEFAULT_CIRCLE_WIDTH = 40;
private static final int DEFAULT_CIRCLE_HEIGHT = 40;
private static final int MINIMUM_STEP_WIDTH = DEFAULT_CIRCLE_WIDTH + 10;
private static final int PROGRESS_RADIUS_START = 270;
private static final int PROGRESS_RADIUS_REMAINDER = 0;
private static final Color DEFAULT_XPGLOBE_BACKGROUND_COLOR = new Color(Color.gray.getRed(), Color.gray.getGreen(), Color.gray.getBlue(), 127);
private static final Color DEFAULT_PROGRESS_ARC_COLOR = Color.ORANGE;
private static final Color DEFAULT_PROGRESS_REMAINDER_ARC_COLOR = Color.BLACK;
private static final int DEFAULT_START_Y = 10;
private final BufferedImage[] imgCache = new BufferedImage[Skill.values().length - 1];
public XpGlobesOverlay(XpGlobes plugin)
{
super(OverlayPosition.DYNAMIC);
this.config = plugin.getConfig();
this.plugin = plugin;
}
@Override
public Dimension render(Graphics2D graphics)
{
// won't draw if not logged in or not enabled
if (client.getGameState() != GameState.LOGGED_IN || !config.enabled())
{
return null;
}
int queueSize = plugin.getXpGlobesSize();
if (queueSize > 0)
{
List<XpGlobe> xpChangedQueue = plugin.getXpGlobes();
int markersLength = (queueSize * (DEFAULT_CIRCLE_WIDTH)) + ((MINIMUM_STEP_WIDTH - DEFAULT_CIRCLE_WIDTH) * (queueSize - 1));
int startDrawX;
if (client.isResized())
{
startDrawX = (client.getClientWidth() - markersLength) / 2;
}
else
{
startDrawX = (client.getViewportHeight() - markersLength) / 2;
}
for (XpGlobe xpGlobe : xpChangedQueue)
{
renderProgressCircle(graphics, xpGlobe, startDrawX, DEFAULT_START_Y);
startDrawX += MINIMUM_STEP_WIDTH;
}
plugin.removeExpiredXpGlobes();
}
return null;
}
private void renderProgressCircle(Graphics2D graphics, XpGlobe skillToDraw, int x, int y)
{
double radiusCurrentXp = skillToDraw.getSkillProgressRadius();
double radiusToGoalXp = 360; //draw a circle
drawEllipse(graphics, x, y);
drawProgressArc(
graphics,
x, y,
DEFAULT_CIRCLE_WIDTH, DEFAULT_CIRCLE_HEIGHT,
PROGRESS_RADIUS_REMAINDER, radiusToGoalXp,
5,
DEFAULT_PROGRESS_REMAINDER_ARC_COLOR
);
drawProgressArc(
graphics,
x, y,
DEFAULT_CIRCLE_WIDTH, DEFAULT_CIRCLE_HEIGHT,
PROGRESS_RADIUS_START, radiusCurrentXp,
2,
DEFAULT_PROGRESS_ARC_COLOR);
drawSkillImage(graphics, skillToDraw, x, y);
}
private void drawProgressArc(Graphics2D graphics, int x, int y, int w, int h, double radiusStart, double radiusEnd, int strokeWidth, Color color)
{
graphics.setStroke(new BasicStroke(strokeWidth));
graphics.setColor(color);
graphics.draw(new Arc2D.Double(
x, y,
w, h,
radiusStart, radiusEnd,
Arc2D.OPEN));
}
private void drawEllipse(Graphics2D graphics, int x, int y)
{
graphics.setColor(DEFAULT_XPGLOBE_BACKGROUND_COLOR);
Ellipse2D backgroundCircle = new Ellipse2D.Double(x, y, DEFAULT_CIRCLE_WIDTH, DEFAULT_CIRCLE_HEIGHT);
graphics.fill(backgroundCircle);
graphics.draw(backgroundCircle);
}
private void drawSkillImage(Graphics2D graphics, XpGlobe xpGlobe, int x, int y)
{
BufferedImage skillImage = getSkillImage(xpGlobe);
graphics.drawImage(
getSkillImage(xpGlobe),
x + (DEFAULT_CIRCLE_WIDTH / 2) - (skillImage.getWidth() / 2),
y + (DEFAULT_CIRCLE_HEIGHT / 2) - (skillImage.getHeight() / 2),
null
);
}
private BufferedImage getSkillImage(XpGlobe xpGlobe)
{
int skillIdx = xpGlobe.getSkill().ordinal();
BufferedImage skillImage = null;
if (imgCache[skillIdx] != null)
{
return imgCache[skillIdx];
}
try
{
String skillIconPath = "/skill_icons/" + xpGlobe.getSkillName().toLowerCase() + ".png";
logger.debug("Loading skill icon from {}", skillIconPath);
skillImage = ImageIO.read(XpGlobesOverlay.class.getResourceAsStream(skillIconPath));
imgCache[skillIdx] = skillImage;
}
catch (IOException e)
{
logger.debug("Error Loading skill icons {}", e);
}
return skillImage;
}
}