diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBox.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBox.java index 53bad6208a..cbc0be4520 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBox.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBox.java @@ -1,6 +1,7 @@ /* * Copyright (c) 2018, Adam * Copyright (c) 2018, Psikoi + * Copyright (c) 2020, Anthony * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -60,7 +61,7 @@ import net.runelite.client.util.QuantityFormatter; class XpInfoBox extends JPanel { - private static final DecimalFormat TWO_DECIMAL_FORMAT = new DecimalFormat("0.00"); + static final DecimalFormat TWO_DECIMAL_FORMAT = new DecimalFormat("0.00"); static { @@ -95,10 +96,10 @@ class XpInfoBox extends JPanel private final ProgressBar progressBar = new ProgressBar(); - private final JLabel expGained = new JLabel(); - private final JLabel expHour = new JLabel(); - private final JLabel expLeft = new JLabel(); - private final JLabel actionsLeft = new JLabel(); + private final JLabel topLeftStat = new JLabel(); + private final JLabel bottomLeftStat = new JLabel(); + private final JLabel topRightStat = new JLabel(); + private final JLabel bottomRightStat = new JLabel(); private final JMenuItem pauseSkill = new JMenuItem("Pause"); private final JMenuItem canvasItem = new JMenuItem(ADD_STATE); @@ -184,15 +185,16 @@ class XpInfoBox extends JPanel statsPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR); statsPanel.setBorder(new EmptyBorder(9, 2, 9, 2)); - expGained.setFont(FontManager.getRunescapeSmallFont()); - expHour.setFont(FontManager.getRunescapeSmallFont()); - expLeft.setFont(FontManager.getRunescapeSmallFont()); - actionsLeft.setFont(FontManager.getRunescapeSmallFont()); - statsPanel.add(expGained); - statsPanel.add(expLeft); - statsPanel.add(expHour); - statsPanel.add(actionsLeft); + topLeftStat.setFont(FontManager.getRunescapeSmallFont()); + bottomLeftStat.setFont(FontManager.getRunescapeSmallFont()); + topRightStat.setFont(FontManager.getRunescapeSmallFont()); + bottomRightStat.setFont(FontManager.getRunescapeSmallFont()); + + statsPanel.add(topLeftStat); // top left + statsPanel.add(topRightStat); // top right + statsPanel.add(bottomLeftStat); // bottom left + statsPanel.add(bottomRightStat); // bottom right headerPanel.add(skillIcon, BorderLayout.WEST); headerPanel.add(statsPanel, BorderLayout.CENTER); @@ -243,14 +245,9 @@ class XpInfoBox extends JPanel paused = skillPaused; - // Update information labels - expGained.setText(htmlLabel("XP Gained: ", xpSnapshotSingle.getXpGainedInSession())); - expLeft.setText(htmlLabel("XP Left: ", xpSnapshotSingle.getXpRemainingToGoal())); - actionsLeft.setText(htmlLabel(xpSnapshotSingle.getActionType().getLabel() + ": ", xpSnapshotSingle.getActionsRemainingToGoal())); - // Update progress bar progressBar.setValue((int) xpSnapshotSingle.getSkillProgressToGoal()); - progressBar.setCenterLabel(TWO_DECIMAL_FORMAT.format(xpSnapshotSingle.getSkillProgressToGoal()) + "%"); + progressBar.setCenterLabel(xpTrackerConfig.progressBarLabel().getValueFunc().apply(xpSnapshotSingle)); progressBar.setLeftLabel("Lvl. " + xpSnapshotSingle.getStartLevel()); progressBar.setRightLabel(xpSnapshotSingle.getEndGoalXp() == Experience.MAX_SKILL_XP ? "200M" @@ -304,13 +301,29 @@ class XpInfoBox extends JPanel pauseSkill.setText("Pause"); } + // Update information labels // Update exp per hour separately, every time (not only when there's an update) - expHour.setText(htmlLabel("XP/Hour: ", xpSnapshotSingle.getXpPerHour())); + topLeftStat.setText(htmlLabel(xpTrackerConfig.xpPanelLabel1(), xpSnapshotSingle)); + topRightStat.setText(htmlLabel(xpTrackerConfig.xpPanelLabel2(), xpSnapshotSingle)); + bottomLeftStat.setText(htmlLabel(xpTrackerConfig.xpPanelLabel3(), xpSnapshotSingle)); + bottomRightStat.setText(htmlLabel(xpTrackerConfig.xpPanelLabel4(), xpSnapshotSingle)); + } + + static String htmlLabel(XpPanelLabel panelLabel, XpSnapshotSingle xpSnapshotSingle) + { + String key = panelLabel.getActionKey(xpSnapshotSingle) + ": "; + String value = panelLabel.getValueFunc().apply(xpSnapshotSingle); + return htmlLabel(key, value); } static String htmlLabel(String key, int value) { String valueStr = QuantityFormatter.quantityToRSDecimalStack(value, true); + return htmlLabel(key, valueStr); + } + + static String htmlLabel(String key, String valueStr) + { return String.format(HTML_LABEL_TEMPLATE, ColorUtil.toHexColor(ColorScheme.LIGHT_GRAY_COLOR), key, valueStr); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBoxOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBoxOverlay.java index ec9ab50b71..95f9beefbd 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBoxOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBoxOverlay.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, Jasper Ketelaar + * Copyright (c) 2020, Anthony * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -46,7 +47,6 @@ 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.QuantityFormatter; class XpInfoBoxOverlay extends OverlayPanel { @@ -91,54 +91,20 @@ class XpInfoBoxOverlay extends OverlayPanel final XpSnapshotSingle snapshot = plugin.getSkillSnapshot(skill); - final String leftStr; - final int rightNum; - - switch (config.onScreenDisplayMode()) - { - case ACTIONS_DONE: - leftStr = snapshot.getActionType().getLabel() + " Done"; - rightNum = snapshot.getActionsInSession(); - break; - case ACTIONS_LEFT: - leftStr = snapshot.getActionType().getLabel() + " Left"; - rightNum = snapshot.getActionsRemainingToGoal(); - break; - case XP_LEFT: - leftStr = "XP Left"; - rightNum = snapshot.getXpRemainingToGoal(); - break; - case XP_GAINED: - default: - leftStr = "XP Gained"; - rightNum = snapshot.getXpGainedInSession(); - break; - } + final String leftStr = config.onScreenDisplayMode().getActionKey(snapshot); + final String rightNum = config.onScreenDisplayMode().getValueFunc().apply(snapshot); final LineComponent xpLine = LineComponent.builder() .left(leftStr + ":") - .right(QuantityFormatter.quantityToRSDecimalStack(rightNum, true)) + .right(rightNum) .build(); - final String bottemLeftStr; - final int bottomRightNum; - - switch (config.onScreenDisplayModeBottom()) - { - case ACTIONS_HOUR: - bottemLeftStr = snapshot.getActionType().getLabel() + "/Hour"; - bottomRightNum = snapshot.getActionsPerHour(); - break; - case XP_HOUR: - default: - bottemLeftStr = "XP/Hour"; - bottomRightNum = snapshot.getXpPerHour(); - break; - } + final String bottomLeftStr = config.onScreenDisplayModeBottom().getActionKey(snapshot); + final String bottomRightNum = config.onScreenDisplayModeBottom().getValueFunc().apply(snapshot); final LineComponent xpLineBottom = LineComponent.builder() - .left(bottemLeftStr + ":") - .right(QuantityFormatter.quantityToRSDecimalStack(bottomRightNum, true)) + .left(bottomLeftStr + ":") + .right(bottomRightNum) .build(); final SplitComponent xpSplit = SplitComponent.builder() diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpPanelLabel.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpPanelLabel.java new file mode 100644 index 0000000000..d17c31872e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpPanelLabel.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2020, Anthony + * 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.util.function.Function; +import lombok.AllArgsConstructor; +import lombok.Getter; +import net.runelite.client.util.QuantityFormatter; + +@Getter +@AllArgsConstructor +public enum XpPanelLabel +{ + TIME_TO_LEVEL("TTL", XpSnapshotSingle::getTimeTillGoalShort), + + XP_GAINED("XP Gained", snap -> format(snap.getXpGainedInSession())), + XP_HOUR("XP/hr", snap -> format(snap.getXpPerHour())), + XP_LEFT("XP Left", snap -> format(snap.getXpRemainingToGoal())), + + ACTIONS_LEFT("Actions", snap -> format(snap.getActionsRemainingToGoal())), + ACTIONS_HOUR("Actions/hr", snap -> format(snap.getActionsPerHour())), + ACTIONS_DONE("Actions Done", snap -> format(snap.getActionsInSession())), + ; + + private final String key; + private final Function valueFunc; + + /** + * Get the action key label based on if the Action type is an xp drop or kill + * + * @param snapshot + * @return + */ + public String getActionKey(XpSnapshotSingle snapshot) + { + String actionKey = key; + if (snapshot.getActionType() == XpActionType.ACTOR_HEALTH) + { + return actionKey.replace("Action", "Kill"); + } + + return actionKey; + } + + private static String format(int val) + { + return QuantityFormatter.quantityToRSDecimalStack(val, true); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpProgressBarLabel.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpProgressBarLabel.java new file mode 100644 index 0000000000..bdcbb91fc3 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpProgressBarLabel.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2020, Anthony + * 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 lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.function.Function; + +import static net.runelite.client.plugins.xptracker.XpInfoBox.TWO_DECIMAL_FORMAT; + +@Getter +@AllArgsConstructor +public enum XpProgressBarLabel +{ + PERCENTAGE((snap) -> TWO_DECIMAL_FORMAT.format(snap.getSkillProgressToGoal()) + "%"), + TIME_TO_LEVEL(XpSnapshotSingle::getTimeTillGoal), + ; + + private final Function valueFunc; +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpSnapshotSingle.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpSnapshotSingle.java index bb7f8a5ef7..6767ab6066 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpSnapshotSingle.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpSnapshotSingle.java @@ -44,4 +44,5 @@ class XpSnapshotSingle private int actionsRemainingToGoal; private int actionsPerHour; private String timeTillGoal; + private String timeTillGoalShort; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpStateSingle.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpStateSingle.java index 608accc239..82182ae54c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpStateSingle.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpStateSingle.java @@ -1,6 +1,7 @@ /* * Copyright (c) 2017, Cameron * Copyright (c) 2018, Levi + * Copyright (c) 2020, Anthony * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -128,24 +129,31 @@ class XpStateSingle return (xpGained / xpGoal) * 100; } - private String getTimeTillLevel() + private long getSecondsTillLevel() { + // Java 8 doesn't have good duration / period objects to represent spans of time that can be formatted + // Rather than importing another dependency like joda time (which is practically built into java 10) + // below will be a custom formatter that handles spans larger than 1 day long seconds = getTimeElapsedInSeconds(); if (seconds <= 0 || xpGained <= 0) { - // Infinity symbol - return "\u221e"; + return -1; } // formula is xpRemaining / xpPerSecond // xpPerSecond being xpGained / seconds // This can be simplified so division is only done once and we can work in whole numbers! - long remainingSeconds = (getXpRemaining() * seconds) / xpGained; + return (getXpRemaining() * seconds) / xpGained; + } - // Java 8 doesn't have good duration / period objects to represent spans of time that can be formatted - // Rather than importing another dependency like joda time (which is practically built into java 10) - // below will be a custom formatter that handles spans larger than 1 day + private String getTimeTillLevel() + { + long remainingSeconds = getSecondsTillLevel(); + if (remainingSeconds < 0) + { + return "\u221e"; + } long durationDays = remainingSeconds / (24 * 60 * 60); long durationHours = (remainingSeconds % (24 * 60 * 60)) / (60 * 60); @@ -160,7 +168,29 @@ class XpStateSingle { return String.format("1 day %02d:%02d:%02d", durationHours, durationMinutes, durationSeconds); } - else if (durationHours > 0) + + // durationDays = 0 if we got here. + // return time remaining in hh:mm:ss or mm:ss format + return getTimeTillLevelShort(); + } + + /** + * Get time to level in `hh:mm:ss` or `mm:ss` format, + * where `hh` can be > 24. + * @return + */ + private String getTimeTillLevelShort() + { + long remainingSeconds = getSecondsTillLevel(); + if (remainingSeconds < 0) + { + return "\u221e"; + } + + long durationHours = remainingSeconds / (60 * 60); + long durationMinutes = (remainingSeconds % (60 * 60)) / 60; + long durationSeconds = remainingSeconds % 60; + if (durationHours > 0) { return String.format("%02d:%02d:%02d", durationHours, durationMinutes, durationSeconds); } @@ -269,6 +299,7 @@ class XpStateSingle .actionsRemainingToGoal(getActionsRemaining()) .actionsPerHour(getActionsHr()) .timeTillGoal(getTimeTillLevel()) + .timeTillGoalShort(getTimeTillLevelShort()) .startGoalXp(startLevelExp) .endGoalXp(endLevelExp) .build(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerConfig.java index 7a4ccf8406..cc41bb5585 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerConfig.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, Levi + * Copyright (c) 2020, Anthony * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,7 +25,6 @@ */ package net.runelite.client.plugins.xptracker; -import lombok.AllArgsConstructor; import net.runelite.client.config.Config; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; @@ -33,22 +33,6 @@ import net.runelite.client.config.Units; @ConfigGroup("xpTracker") public interface XpTrackerConfig extends Config { - @AllArgsConstructor - enum OnScreenDisplayMode - { - XP_GAINED, - XP_LEFT, - ACTIONS_DONE, - ACTIONS_LEFT - } - - @AllArgsConstructor - enum OnScreenDisplayModeBottom - { - XP_HOUR, - ACTIONS_HOUR, - } - @ConfigItem( position = 0, keyName = "hideMaxed", @@ -111,9 +95,9 @@ public interface XpTrackerConfig extends Config name = "On-screen tracker display mode (top)", description = "Configures the information displayed in the first line of on-screen XP overlays" ) - default OnScreenDisplayMode onScreenDisplayMode() + default XpPanelLabel onScreenDisplayMode() { - return OnScreenDisplayMode.XP_GAINED; + return XpPanelLabel.XP_GAINED; } @ConfigItem( @@ -122,8 +106,64 @@ public interface XpTrackerConfig extends Config name = "On-screen tracker display mode (bottom)", description = "Configures the information displayed in the second line of on-screen XP overlays" ) - default OnScreenDisplayModeBottom onScreenDisplayModeBottom() + default XpPanelLabel onScreenDisplayModeBottom() { - return OnScreenDisplayModeBottom.XP_HOUR; + return XpPanelLabel.XP_HOUR; + } + + @ConfigItem( + position = 7, + keyName = "xpPanelLabel1", + name = "Top-left XP info label", + description = "Configures the information displayed in the top-left of XP info box" + ) + default XpPanelLabel xpPanelLabel1() + { + return XpPanelLabel.XP_GAINED; + } + + @ConfigItem( + position = 8, + keyName = "xpPanelLabel2", + name = "Top-right XP info label", + description = "Configures the information displayed in the top-right of XP info box" + ) + + default XpPanelLabel xpPanelLabel2() + { + return XpPanelLabel.XP_LEFT; + } + + @ConfigItem( + position = 9, + keyName = "xpPanelLabel3", + name = "Bottom-left XP info label", + description = "Configures the information displayed in the bottom-left of XP info box" + ) + default XpPanelLabel xpPanelLabel3() + { + return XpPanelLabel.XP_HOUR; + } + + @ConfigItem( + position = 10, + keyName = "xpPanelLabel4", + name = "Bottom-right XP info label", + description = "Configures the information displayed in the bottom-right of XP info box" + ) + default XpPanelLabel xpPanelLabel4() + { + return XpPanelLabel.ACTIONS_LEFT; + } + + @ConfigItem( + position = 11, + keyName = "progressBarLabel", + name = "Progress bar label", + description = "Configures the info box progress bar to show Time to goal or percentage complete" + ) + default XpProgressBarLabel progressBarLabel() + { + return XpProgressBarLabel.PERCENTAGE; } }