Added xp tracker to overlay (#147)

* Added a split component to be able to put layout elements above eachother or next to eachother easily

* Modified xp tracker plugin to allow infoboxes to be added to canvas

* Formatted experience numbers using StackFormatter

* Cleaned up swing code as a suggestion in discord to use string states

* Forgot to initialize the menu item with the ADD_STATE

* Added final back to popupMenu

* Extracted duplicate enum Orientation from PanelComponent and SplitComponent into a seperate class named ComponentOrientation. Also changed the plugins that used the previous PanelComponent.Orientation to use ComponentOrientation

* Syntax and code convention fixes from deathbeams review

* Fixed a bug where logging into an other account did not reset the tracker overlay

* Removed useless methods and fixed some code convention issues

* fix

* fix

* fix2

* fix3
This commit is contained in:
James
2019-04-29 13:23:52 -07:00
committed by Tyler Bochard
parent be531a6eb4
commit bed6c919b0
16 changed files with 322 additions and 30 deletions

View File

@@ -34,6 +34,7 @@ import lombok.Getter;
@Getter
public enum VarClientStr
{
DUEL_OPPONENT_NAME(357),
CHATBOX_TYPED_TEXT(335),
INPUT_TEXT(359),
PRIVATE_MESSAGE_TARGET(360),

View File

@@ -34,6 +34,7 @@ import lombok.Getter;
@Getter
public enum VarPlayer
{
DUEL_PENDING(286),
ATTACK_STYLE(43),
QUEST_POINTS(101),
IS_POISONED(102),

View File

@@ -36,9 +36,9 @@ import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.game.ItemManager;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayPosition;
import static net.runelite.client.ui.overlay.OverlayManager.OPTION_CONFIGURE;
import net.runelite.client.ui.overlay.OverlayMenuEntry;
import net.runelite.client.ui.overlay.OverlayPosition;
import net.runelite.client.ui.overlay.components.ImageComponent;
import net.runelite.client.ui.overlay.components.PanelComponent;
@@ -70,7 +70,7 @@ class BlastMineOreCountOverlay extends Overlay
{
return null;
}
panelComponent.getChildren().clear();
if (config.showOreOverlay())

View File

@@ -62,13 +62,13 @@ public class CerberusOverlay extends Overlay
// Ghosts are already sorted
plugin.getGhosts().stream()
// Iterate only through the correct amount of ghosts
.limit(CerberusGhost.values().length)
.forEach(npc -> CerberusGhost
.fromNPC(npc)
.ifPresent(ghost -> panelComponent
.getChildren()
.add(new ImageComponent(iconManager.getSkillImage(ghost.getType())))));
// Iterate only through the correct amount of ghosts
.limit(CerberusGhost.values().length)
.forEach(npc -> CerberusGhost
.fromNPC(npc)
.ifPresent(ghost -> panelComponent
.getChildren()
.add(new ImageComponent(iconManager.getSkillImage(ghost.getType())))));
return panelComponent.render(graphics);

View File

@@ -72,18 +72,18 @@ class InventoryViewerOverlay extends Overlay
inventoryComponent.setOrientation(PanelComponent.Orientation.HORIZONTAL);
inventoryComponent.setBackgroundColor(null);
inventoryComponent.setBorder(new Rectangle(
0,
ComponentConstants.STANDARD_BORDER,
0,
ComponentConstants.STANDARD_BORDER));
0,
ComponentConstants.STANDARD_BORDER,
0,
ComponentConstants.STANDARD_BORDER));
wrapperComponent.setOrientation(PanelComponent.Orientation.VERTICAL);
wrapperComponent.setWrapping(2);
wrapperComponent.setBorder(new Rectangle(
ComponentConstants.STANDARD_BORDER * 2,
ComponentConstants.STANDARD_BORDER,
ComponentConstants.STANDARD_BORDER * 2,
ComponentConstants.STANDARD_BORDER));
ComponentConstants.STANDARD_BORDER * 2,
ComponentConstants.STANDARD_BORDER,
ComponentConstants.STANDARD_BORDER * 2,
ComponentConstants.STANDARD_BORDER));
this.itemManager = itemManager;
this.client = client;
@@ -94,7 +94,7 @@ class InventoryViewerOverlay extends Overlay
public Dimension render(Graphics2D graphics)
{
if (config.hideWhenInvOpen()
&& client.getVar(VarClientInt.PLAYER_INVENTORY_OPENED) == 3)
&& client.getVar(VarClientInt.PLAYER_INVENTORY_OPENED) == 3)
{
return null;
}
@@ -188,4 +188,4 @@ class InventoryViewerOverlay extends Overlay
ItemComposition itemComposition = itemManager.getItemComposition(item.getId());
return itemManager.getImage(item.getId(), item.getQuantity(), itemComposition.isStackable());
}
}
}

View File

@@ -57,6 +57,8 @@ import net.runelite.client.util.StackFormatter;
class XpInfoBox extends JPanel
{
private static final String REMOVE_STATE = "Remove from canvas";
private static final String ADD_STATE = "Add to canvas";
private static final DecimalFormat TWO_DECIMAL_FORMAT = new DecimalFormat("0.00");
// Templates
@@ -91,6 +93,7 @@ class XpInfoBox extends JPanel
private final JMenuItem pauseSkill = new JMenuItem("Pause");
private final XpTrackerConfig xpTrackerConfig;
private final JMenuItem canvasItem = new JMenuItem(ADD_STATE);
private boolean paused = false;
@@ -128,6 +131,21 @@ class XpInfoBox extends JPanel
popupMenu.add(reset);
popupMenu.add(resetOthers);
popupMenu.add(pauseSkill);
popupMenu.add(canvasItem);
canvasItem.addActionListener(e ->
{
if (canvasItem.getText().equals(REMOVE_STATE))
{
xpTrackerPlugin.removeOverlay(skill);
canvasItem.setText(ADD_STATE);
}
else
{
xpTrackerPlugin.addOverlay(skill);
canvasItem.setText(REMOVE_STATE);
}
});
JLabel skillIcon = new JLabel(new ImageIcon(iconManager.getSkillImage(skill)));
skillIcon.setHorizontalAlignment(SwingConstants.CENTER);
@@ -177,6 +195,7 @@ class XpInfoBox extends JPanel
void reset()
{
canvasItem.setText(ADD_STATE);
container.remove(statsPanel);
panel.remove(this);
panel.revalidate();

View File

@@ -0,0 +1,109 @@
/*
* Copyright (c) 2018, Jasper Ketelaar <Jasper0781@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.xptracker;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import lombok.AccessLevel;
import lombok.Getter;
import net.runelite.api.Skill;
import net.runelite.client.ui.FontManager;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.components.ComponentOrientation;
import net.runelite.client.ui.overlay.components.ImageComponent;
import net.runelite.client.ui.overlay.components.LineComponent;
import net.runelite.client.ui.overlay.components.PanelComponent;
import net.runelite.client.ui.overlay.components.ProgressBarComponent;
import net.runelite.client.ui.overlay.components.SplitComponent;
import net.runelite.client.util.StackFormatter;
class XpInfoBoxOverlay extends Overlay
{
private static final int PANEL_PREFERRED_WIDTH = 155;
private static final int BORDER_SIZE = 7;
private static final int GAP_SIZE = 5;
private final PanelComponent panel = new PanelComponent();
private final XpTrackerPlugin plugin;
@Getter(AccessLevel.PACKAGE)
private final Skill skill;
private final BufferedImage icon;
XpInfoBoxOverlay(XpTrackerPlugin plugin, Skill skill, BufferedImage icon)
{
this.plugin = plugin;
this.skill = skill;
this.icon = icon;
panel.setBorder(new Rectangle(BORDER_SIZE, BORDER_SIZE, BORDER_SIZE, BORDER_SIZE));
panel.setGap(new Point(0, GAP_SIZE));
panel.setPreferredSize(new Dimension(PANEL_PREFERRED_WIDTH, 0));
}
@Override
public Dimension render(Graphics2D graphics)
{
//Setting the font to rs small font so that the overlay isn't huge
graphics.setFont(FontManager.getRunescapeSmallFont());
final XpSnapshotSingle snapshot = plugin.getSkillSnapshot(skill);
panel.getChildren().clear();
final LineComponent xpLeft = LineComponent.builder()
.left("Xp Gained:")
.right(StackFormatter.quantityToRSDecimalStack(snapshot.getXpGainedInSession()))
.build();
final LineComponent xpHour = LineComponent.builder()
.left("Xp/Hour:")
.right(StackFormatter.quantityToRSDecimalStack(snapshot.getXpPerHour()))
.build();
final SplitComponent xpSplit = SplitComponent.builder()
.first(xpLeft)
.second(xpHour)
.orientation(ComponentOrientation.VERTICAL)
.build();
final ImageComponent imageComponent = new ImageComponent(icon);
final SplitComponent iconSplit = SplitComponent.builder()
.first(imageComponent)
.second(xpSplit)
.orientation(ComponentOrientation.HORIZONTAL)
.gap(new Point(GAP_SIZE, 0))
.build();
final ProgressBarComponent progressBarComponent = new ProgressBarComponent();
progressBarComponent.setValue(snapshot.getSkillProgressToGoal());
panel.getChildren().add(iconSplit);
panel.getChildren().add(progressBarComponent);
return panel.render(graphics);
}
}

View File

@@ -60,6 +60,10 @@ import net.runelite.client.task.Schedule;
import net.runelite.client.ui.ClientToolbar;
import net.runelite.client.ui.NavigationButton;
import net.runelite.client.util.ImageUtil;
import net.runelite.client.ui.overlay.OverlayManager;
import net.runelite.http.api.worlds.World;
import net.runelite.http.api.worlds.WorldClient;
import net.runelite.http.api.worlds.WorldResult;
import net.runelite.http.api.xp.XpClient;
@PluginDescriptor(
@@ -83,6 +87,9 @@ public class XpTrackerPlugin extends Plugin
Skill.HITPOINTS,
Skill.MAGIC);
private final XpState xpState = new XpState();
private final XpClient xpClient = new XpClient();
@Inject
private ClientToolbar clientToolbar;
@@ -98,16 +105,18 @@ public class XpTrackerPlugin extends Plugin
@Inject
private NPCManager npcManager;
@Inject
private OverlayManager overlayManager;
private NavigationButton navButton;
private XpPanel xpPanel;
private WorldResult worlds;
private XpWorldType lastWorldType;
private String lastUsername;
private long lastTickMillis = 0;
private boolean fetchXp;
private long lastXp = 0;
private final XpClient xpClient = new XpClient();
private final XpState xpState = new XpState();
private final XpPauseState xpPauseState = new XpPauseState();
@Provides
@@ -208,6 +217,27 @@ public class XpTrackerPlugin extends Plugin
return xpType;
}
/**
* Adds an overlay to the canvas for tracking a specific skill.
*
* @param skill the skill for which the overlay should be added
*/
void addOverlay(Skill skill)
{
removeOverlay(skill);
overlayManager.add(new XpInfoBoxOverlay(this, skill, skillIconManager.getSkillImage(skill)));
}
/**
* Removes an overlay from the overlayManager if it's present.
*
* @param skill the skill for which the overlay should be removed.
*/
void removeOverlay(Skill skill)
{
overlayManager.removeIf(e -> e instanceof XpInfoBoxOverlay && ((XpInfoBoxOverlay) e).getSkill() == skill);
}
/**
* Reset internal state and re-initialize all skills with XP currently cached by the RS client
* This is called by the user manually clicking resetSkillState in the UI.
@@ -230,6 +260,7 @@ public class XpTrackerPlugin extends Plugin
}
xpState.initializeSkill(skill, currentXp);
removeOverlay(skill);
}
}
@@ -242,6 +273,7 @@ public class XpTrackerPlugin extends Plugin
xpState.reset();
xpPanel.resetAllInfoBoxes();
xpPanel.updateTotal(new XpSnapshotSingle.XpSnapshotSingleBuilder().build());
overlayManager.removeIf(e -> e instanceof XpInfoBoxOverlay);
}
/**
@@ -254,6 +286,8 @@ public class XpTrackerPlugin extends Plugin
int currentXp = client.getSkillExperience(skill);
xpState.resetSkill(skill, currentXp);
xpPanel.resetSkill(skill);
xpPanel.updateTotal(xpState.getTotalSnapshot());
removeOverlay(skill);
}
/**
@@ -268,6 +302,7 @@ public class XpTrackerPlugin extends Plugin
if (skill != s && s != Skill.OVERALL)
{
resetSkillState(s);
removeOverlay(s);
}
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright (c) 2018, Jasper Ketelaar <Jasper0781@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.ui.overlay.components;
public enum ComponentOrientation
{
HORIZONTAL,
VERTICAL
}

View File

@@ -67,10 +67,10 @@ public class PanelComponent implements LayoutableRenderableEntity
@Setter
private Rectangle border = new Rectangle(
ComponentConstants.STANDARD_BORDER,
ComponentConstants.STANDARD_BORDER,
ComponentConstants.STANDARD_BORDER,
ComponentConstants.STANDARD_BORDER);
ComponentConstants.STANDARD_BORDER,
ComponentConstants.STANDARD_BORDER,
ComponentConstants.STANDARD_BORDER,
ComponentConstants.STANDARD_BORDER);
@Setter
private Point gap = new Point(0, 0);
@@ -87,8 +87,8 @@ public class PanelComponent implements LayoutableRenderableEntity
// Calculate panel dimension
final Dimension dimension = new Dimension(
border.x + childDimensions.width + border.width,
border.y + childDimensions.height + border.height);
border.x + childDimensions.width + border.width,
border.y + childDimensions.height + border.height);
// Render background
if (backgroundColor != null)
@@ -109,8 +109,8 @@ public class PanelComponent implements LayoutableRenderableEntity
// Create child preferred size
final Dimension childPreferredSize = new Dimension(
preferredSize.width - border.x - border.width,
preferredSize.height - border.y - border.height);
preferredSize.width - border.x - border.width,
preferredSize.height - border.y - border.height);
// Calculate max width/height for infoboxes
int totalHeight = 0;
@@ -180,4 +180,4 @@ public class PanelComponent implements LayoutableRenderableEntity
bounds.setSize(dimension);
return dimension;
}
}
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright (c) 2018, Jasper Ketelaar <jasper0781@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.ui.overlay.components;
import java.awt.*;
import lombok.Builder;
import lombok.Setter;
@Builder
@Setter
public class SplitComponent implements LayoutableRenderableEntity
{
@Builder.Default
private Point preferredLocation = new Point();
@Builder.Default
private Dimension preferredSize = new Dimension(ComponentConstants.STANDARD_WIDTH, 0);
@Builder.Default
private ComponentOrientation orientation = ComponentOrientation.VERTICAL;
@Builder.Default
private Point gap = new Point(0, 0);
private LayoutableRenderableEntity first;
private LayoutableRenderableEntity second;
@Override
public Dimension render(Graphics2D graphics)
{
graphics.translate(preferredLocation.x, preferredLocation.y);
first.setPreferredSize(preferredSize);
first.setPreferredLocation(new Point(0, 0));
final Dimension firstDimenson = first.render(graphics);
int x = 0, y = 0;
if (orientation == ComponentOrientation.VERTICAL)
{
y = firstDimenson.height + gap.y;
}
else
{
x = firstDimenson.width + gap.x;
}
second.setPreferredLocation(new Point(x, y));
// Make the second component fit to whatever size is left after the first component is rendered
second.setPreferredSize(new Dimension(preferredSize.width - x, preferredSize.height - y));
// The total width/height need to be determined as they are now always the same as the
// individual width/height (for example image width/height will just be the height of the image
// and not the height of the area the image is in
final Dimension secondDimension = second.render(graphics);
int totalWidth, totalHeight;
if (orientation == ComponentOrientation.VERTICAL)
{
totalWidth = Math.max(firstDimenson.width, secondDimension.width);
totalHeight = y + secondDimension.height;
}
else
{
totalHeight = Math.max(firstDimenson.height, secondDimension.height);
totalWidth = x + secondDimension.width;
}
graphics.translate(-preferredLocation.x, -preferredLocation.y);
return new Dimension(totalWidth, totalHeight);
}
@Override
public Rectangle getBounds() {
return null;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB