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 8f0390b927..d41abdb55b 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
@@ -54,6 +54,15 @@ import net.runelite.client.util.SwingUtil;
@Slf4j
class XpInfoBox extends JPanel
{
+ // Templates
+ private static final String HTML_TOOL_TIP_TEMPLATE =
+ "%s actions done
"
+ + "%s actions/hr
"
+ + "%s till goal lvl";
+ private static final String HTML_LABEL_TEMPLATE =
+ "
%s%s";
+
+ // Instance members
private final JPanel panel;
@Getter(AccessLevel.PACKAGE)
@@ -74,6 +83,9 @@ class XpInfoBox extends JPanel
private final JLabel expHour = new JLabel();
private final JLabel expLeft = new JLabel();
private final JLabel actionsLeft = new JLabel();
+ private final JMenuItem pauseSkill = new JMenuItem("Pause");
+
+ private boolean paused = false;
XpInfoBox(XpTrackerPlugin xpTrackerPlugin, Client client, JPanel panel, Skill skill, SkillIconManager iconManager) throws IOException
{
@@ -98,12 +110,16 @@ class XpInfoBox extends JPanel
final JMenuItem resetOthers = new JMenuItem("Reset others");
resetOthers.addActionListener(e -> xpTrackerPlugin.resetOtherSkillState(skill));
+ // Create reset others menu
+ pauseSkill.addActionListener(e -> xpTrackerPlugin.pauseSkill(skill, !paused));
+
// Create popup menu
final JPopupMenu popupMenu = new JPopupMenu();
popupMenu.setBorder(new EmptyBorder(5, 5, 5, 5));
popupMenu.add(openXpTracker);
popupMenu.add(reset);
popupMenu.add(resetOthers);
+ popupMenu.add(pauseSkill);
JLabel skillIcon = new JLabel(new ImageIcon(iconManager.getSkillImage(skill)));
skillIcon.setHorizontalAlignment(SwingConstants.CENTER);
@@ -138,6 +154,7 @@ class XpInfoBox extends JPanel
progressBar.setMaximumValue(100);
progressBar.setBackground(new Color(61, 56, 49));
progressBar.setForeground(SkillColor.values()[skill.ordinal()].getColor());
+ progressBar.setDimmedText("Paused");
progressWrapper.add(progressBar, BorderLayout.NORTH);
@@ -157,12 +174,12 @@ class XpInfoBox extends JPanel
panel.revalidate();
}
- void update(boolean updated, XpSnapshotSingle xpSnapshotSingle)
+ void update(boolean updated, boolean paused, XpSnapshotSingle xpSnapshotSingle)
{
- SwingUtilities.invokeLater(() -> rebuildAsync(updated, xpSnapshotSingle));
+ SwingUtilities.invokeLater(() -> rebuildAsync(updated, paused, xpSnapshotSingle));
}
- private void rebuildAsync(boolean updated, XpSnapshotSingle xpSnapshotSingle)
+ private void rebuildAsync(boolean updated, boolean skillPaused, XpSnapshotSingle xpSnapshotSingle)
{
if (updated)
{
@@ -172,6 +189,8 @@ class XpInfoBox extends JPanel
panel.revalidate();
}
+ paused = skillPaused;
+
// Update information labels
expGained.setText(htmlLabel("XP Gained: ", xpSnapshotSingle.getXpGainedInSession()));
expLeft.setText(htmlLabel("XP Left: ", xpSnapshotSingle.getXpRemainingToGoal()));
@@ -181,28 +200,42 @@ class XpInfoBox extends JPanel
progressBar.setValue(xpSnapshotSingle.getSkillProgressToGoal());
progressBar.setCenterLabel(xpSnapshotSingle.getSkillProgressToGoal() + "%");
progressBar.setLeftLabel("Lvl. " + xpSnapshotSingle.getStartLevel());
- progressBar.setRightLabel("Lvl. " + (xpSnapshotSingle.getEndLevel()));
+ progressBar.setRightLabel("Lvl. " + xpSnapshotSingle.getEndLevel());
- progressBar.setToolTipText(""
- + xpSnapshotSingle.getActionsInSession() + " actions done"
- + "
"
- + xpSnapshotSingle.getActionsPerHour() + " actions/hr"
- + "
"
- + xpSnapshotSingle.getTimeTillGoal() + " till goal lvl"
- + "");
+ progressBar.setToolTipText(String.format(
+ HTML_TOOL_TIP_TEMPLATE,
+ xpSnapshotSingle.getActionsInSession(),
+ xpSnapshotSingle.getActionsPerHour(),
+ xpSnapshotSingle.getTimeTillGoal()));
+
+ progressBar.setDimmed(skillPaused);
progressBar.repaint();
}
+ else if (!paused && skillPaused)
+ {
+ // React to the skill state now being paused
+ progressBar.setDimmed(true);
+ progressBar.repaint();
+ paused = true;
+ pauseSkill.setText("Unpause");
+ }
+ else if (paused && !skillPaused)
+ {
+ // React to the skill being unpaused (without update)
+ progressBar.setDimmed(false);
+ progressBar.repaint();
+ paused = false;
+ pauseSkill.setText("Pause");
+ }
- // Update exp per hour seperately, everytime (not only when there's an update)
+ // Update exp per hour separately, every time (not only when there's an update)
expHour.setText(htmlLabel("XP/Hour: ", xpSnapshotSingle.getXpPerHour()));
}
static String htmlLabel(String key, int value)
{
String valueStr = StackFormatter.quantityToRSDecimalStack(value);
-
- return "" + key + "" + valueStr + "";
+ return String.format(HTML_LABEL_TEMPLATE, SwingUtil.toHexColor(ColorScheme.LIGHT_GRAY_COLOR), key, valueStr);
}
-
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpPanel.java
index 5a87715b13..4226e4009d 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpPanel.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpPanel.java
@@ -172,13 +172,13 @@ class XpPanel extends PluginPanel
}
}
- void updateSkillExperience(boolean updated, Skill skill, XpSnapshotSingle xpSnapshotSingle)
+ void updateSkillExperience(boolean updated, boolean paused, Skill skill, XpSnapshotSingle xpSnapshotSingle)
{
final XpInfoBox xpInfoBox = infoBoxes.get(skill);
if (xpInfoBox != null)
{
- xpInfoBox.update(updated, xpSnapshotSingle);
+ xpInfoBox.update(updated, paused, xpSnapshotSingle);
}
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpPauseState.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpPauseState.java
new file mode 100644
index 0000000000..9f57de71c3
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpPauseState.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2018, Levi
+ * 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.EnumMap;
+import java.util.Map;
+import net.runelite.api.Skill;
+
+class XpPauseState
+{
+ // Internal state
+ private final Map skillPauses = new EnumMap<>(Skill.class);
+ private boolean cachedIsLoggedIn = false;
+
+ boolean pauseSkill(Skill skill)
+ {
+ return findPauseState(skill).manualPause();
+ }
+
+ boolean unpauseSkill(Skill skill)
+ {
+ return findPauseState(skill).unpause();
+ }
+
+ boolean isPaused(Skill skill)
+ {
+ return findPauseState(skill).isPaused();
+ }
+
+ void tickXp(Skill skill, int currentXp, int pauseAfterMinutes)
+ {
+ final XpPauseStateSingle state = findPauseState(skill);
+
+ if (state.getXp() != currentXp)
+ {
+ state.xpChanged(currentXp);
+ }
+ else if (pauseAfterMinutes > 0)
+ {
+ final long now = System.currentTimeMillis();
+ final int pauseAfterMillis = pauseAfterMinutes * 60 * 1000;
+ final long lastChangeMillis = state.getLastChangeMillis();
+ // When config.pauseSkillAfter is 0, it is effectively disabled
+ if (lastChangeMillis != 0 && (now - lastChangeMillis) >= pauseAfterMillis)
+ {
+ state.timeout();
+ }
+ }
+ }
+
+ void tickLogout(boolean pauseOnLogout, boolean loggedIn)
+ {
+ // Deduplicated login and logout calls
+ if (!cachedIsLoggedIn && loggedIn)
+ {
+ cachedIsLoggedIn = true;
+
+ for (Skill skill : Skill.values())
+ {
+ findPauseState(skill).login();
+ }
+ }
+ else if (cachedIsLoggedIn && !loggedIn)
+ {
+ cachedIsLoggedIn = false;
+
+ // If configured, then let the pause state know to pause with reason: logout
+ if (pauseOnLogout)
+ {
+ for (Skill skill : Skill.values())
+ {
+ findPauseState(skill).logout();
+ }
+ }
+ }
+ }
+
+ private XpPauseStateSingle findPauseState(Skill skill)
+ {
+ return skillPauses.computeIfAbsent(skill, XpPauseStateSingle::new);
+ }
+}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpPauseStateSingle.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpPauseStateSingle.java
new file mode 100644
index 0000000000..d74d498bd1
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpPauseStateSingle.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2018, Levi
+ * 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.EnumSet;
+import java.util.Set;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import net.runelite.api.Skill;
+
+@RequiredArgsConstructor
+class XpPauseStateSingle
+{
+ @Getter
+ private final Skill skill;
+ private final Set pauseReasons = EnumSet.noneOf(XpPauseReason.class);
+ @Getter
+ private long lastChangeMillis;
+ @Getter
+ private int xp;
+
+ boolean isPaused()
+ {
+ return !pauseReasons.isEmpty();
+ }
+
+ boolean login()
+ {
+ return pauseReasons.remove(XpPauseReason.PAUSED_LOGOUT);
+ }
+
+ boolean logout()
+ {
+ return pauseReasons.add(XpPauseReason.PAUSED_LOGOUT);
+ }
+
+ boolean timeout()
+ {
+ return pauseReasons.add(XpPauseReason.PAUSED_TIMEOUT);
+ }
+
+ boolean manualPause()
+ {
+ return pauseReasons.add(XpPauseReason.PAUSE_MANUAL);
+ }
+
+ boolean xpChanged(int xp)
+ {
+ this.xp = xp;
+ this.lastChangeMillis = System.currentTimeMillis();
+ return clearAll();
+ }
+
+ boolean unpause()
+ {
+ this.lastChangeMillis = System.currentTimeMillis();
+ return clearAll();
+ }
+
+ private boolean clearAll()
+ {
+ if (pauseReasons.isEmpty())
+ {
+ return false;
+ }
+
+ pauseReasons.clear();
+ return true;
+ }
+
+ private enum XpPauseReason
+ {
+ PAUSE_MANUAL,
+ PAUSED_LOGOUT,
+ PAUSED_TIMEOUT
+ }
+}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpState.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpState.java
index c70f460a3d..da981077ef 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpState.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpState.java
@@ -120,6 +120,11 @@ class XpState
}
}
+ void tick(Skill skill, long delta)
+ {
+ getSkill(skill).tick(delta);
+ }
+
/**
* Forcefully initialize a skill with a known start XP from the current XP.
* This is used in resetAndInitState by the plugin. It should not result in showing the XP in the UI.
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 153c53fc3b..82a8849b0c 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
@@ -25,8 +25,6 @@
*/
package net.runelite.client.plugins.xptracker;
-import java.time.Duration;
-import java.time.Instant;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -45,7 +43,7 @@ class XpStateSingle
@Getter
private int xpGained = 0;
- private Instant skillTimeStart = null;
+ private long skillTime = 0;
private int actions = 0;
private int startLevelExp = 0;
private int endLevelExp = 0;
@@ -65,7 +63,7 @@ class XpStateSingle
private int toHourly(int value)
{
- if (skillTimeStart == null)
+ if (skillTime == 0)
{
return 0;
}
@@ -75,7 +73,7 @@ class XpStateSingle
private long getTimeElapsedInSeconds()
{
- if (skillTimeStart == null)
+ if (skillTime == 0)
{
return 0;
}
@@ -84,7 +82,7 @@ class XpStateSingle
// To prevent that, pretend the skill has been active for a minute (60 seconds)
// This will create a lower estimate for the first minute,
// but it isn't ridiculous like saying 2 billion XP per hour.
- return Math.max(60, Duration.between(skillTimeStart, Instant.now()).getSeconds());
+ return Math.max(60, skillTime / 1000);
}
private int getXpRemaining()
@@ -229,15 +227,14 @@ class XpStateSingle
endLevelExp = goalEndXp;
}
- // If this is first time we are updating, we just started tracking
- if (skillTimeStart == null)
- {
- skillTimeStart = Instant.now();
- }
-
return true;
}
+ public void tick(long delta)
+ {
+ skillTime += delta;
+ }
+
XpSnapshotSingle snapshot()
{
return XpSnapshotSingle.builder()
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
new file mode 100644
index 0000000000..f74721c48d
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerConfig.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2018, Levi
+ * 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 net.runelite.client.config.Config;
+import net.runelite.client.config.ConfigGroup;
+import net.runelite.client.config.ConfigItem;
+
+@ConfigGroup("xpTracker")
+public interface XpTrackerConfig extends Config
+{
+ @ConfigItem(
+ position = 0,
+ keyName = "logoutPausing",
+ name = "Pause on Logout",
+ description = "Configures whether skills should pause on logout"
+ )
+ default boolean pauseOnLogout()
+ {
+ return false;
+ }
+
+ @ConfigItem(
+ position = 1,
+ keyName = "pauseSkillAfter",
+ name = "Auto pause after",
+ description = "Configures how many minutes passes before pausing a skill while in game and there's no XP, 0 means disabled"
+ )
+ default int pauseSkillAfter()
+ {
+ return 0;
+ }
+}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java
index 1261c19dfd..de27a3edba 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java
@@ -28,7 +28,9 @@ package net.runelite.client.plugins.xptracker;
import static com.google.common.base.MoreObjects.firstNonNull;
import com.google.common.eventbus.Subscribe;
import com.google.inject.Binder;
+import com.google.inject.Provides;
import java.awt.image.BufferedImage;
+import java.time.temporal.ChronoUnit;
import java.util.EnumSet;
import java.util.Objects;
import javax.imageio.ImageIO;
@@ -43,10 +45,12 @@ import net.runelite.api.WorldType;
import net.runelite.api.events.ExperienceChanged;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.GameTick;
+import net.runelite.client.config.ConfigManager;
import net.runelite.client.game.SkillIconManager;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import static net.runelite.client.plugins.xptracker.XpWorldType.NORMAL;
+import net.runelite.client.task.Schedule;
import net.runelite.client.ui.NavigationButton;
import net.runelite.client.ui.PluginToolbar;
import net.runelite.http.api.xp.XpClient;
@@ -68,15 +72,24 @@ public class XpTrackerPlugin extends Plugin
@Inject
private SkillIconManager skillIconManager;
+ @Inject
+ private XpTrackerConfig xpTrackerConfig;
+
private NavigationButton navButton;
private XpPanel xpPanel;
-
- private final XpState xpState = new XpState();
-
private XpWorldType lastWorldType;
private String lastUsername;
+ private long lastTickMillis = 0;
private final XpClient xpClient = new XpClient();
+ private final XpState xpState = new XpState();
+ private final XpPauseState xpPauseState = new XpPauseState();
+
+ @Provides
+ XpTrackerConfig provideConfig(ConfigManager configManager)
+ {
+ return configManager.getConfig(XpTrackerConfig.class);
+ }
@Override
public void configure(Binder binder)
@@ -229,7 +242,7 @@ public class XpTrackerPlugin extends Plugin
final XpUpdateResult updateResult = xpState.updateSkill(skill, currentXp, startGoalXp, endGoalXp);
final boolean updated = XpUpdateResult.UPDATED.equals(updateResult);
- xpPanel.updateSkillExperience(updated, skill, xpState.getSkillSnapshot(skill));
+ xpPanel.updateSkillExperience(updated, xpPauseState.isPaused(skill), skill, xpState.getSkillSnapshot(skill));
xpState.recalculateTotal();
xpPanel.updateTotal(xpState.getTotalSnapshot());
}
@@ -237,14 +250,7 @@ public class XpTrackerPlugin extends Plugin
@Subscribe
public void onGameTick(GameTick event)
{
- // Rebuild calculated values like xp/hr in panel
- for (Skill skill : Skill.values())
- {
- xpPanel.updateSkillExperience(false, skill, xpState.getSkillSnapshot(skill));
- }
-
- xpState.recalculateTotal();
- xpPanel.updateTotal(xpState.getTotalSnapshot());
+ rebuildSkills();
}
XpSnapshotSingle getSkillSnapshot(Skill skill)
@@ -361,4 +367,59 @@ public class XpTrackerPlugin extends Plugin
return null;
}
}
+
+ @Schedule(
+ period = 1,
+ unit = ChronoUnit.SECONDS
+ )
+ public void tickSkillTimes()
+ {
+ // Adjust unpause states
+ for (Skill skill : Skill.values())
+ {
+ xpPauseState.tickXp(skill, client.getSkillExperience(skill), xpTrackerConfig.pauseSkillAfter());
+ }
+
+ xpPauseState.tickLogout(xpTrackerConfig.pauseOnLogout(), !GameState.LOGIN_SCREEN.equals(client.getGameState()));
+
+ if (lastTickMillis == 0)
+ {
+ lastTickMillis = System.currentTimeMillis();
+ return;
+ }
+
+ final long nowMillis = System.currentTimeMillis();
+ final long tickDelta = nowMillis - lastTickMillis;
+ lastTickMillis = nowMillis;
+
+ for (Skill skill : Skill.values())
+ {
+ if (!xpPauseState.isPaused(skill))
+ {
+ xpState.tick(skill, tickDelta);
+ }
+ }
+
+ rebuildSkills();
+ }
+
+ private void rebuildSkills()
+ {
+ // Rebuild calculated values like xp/hr in panel
+ for (Skill skill : Skill.values())
+ {
+ xpPanel.updateSkillExperience(false, xpPauseState.isPaused(skill), skill, xpState.getSkillSnapshot(skill));
+ }
+
+ xpState.recalculateTotal();
+ xpPanel.updateTotal(xpState.getTotalSnapshot());
+ }
+
+ void pauseSkill(Skill skill, boolean pause)
+ {
+ if (pause ? xpPauseState.pauseSkill(skill) : xpPauseState.unpauseSkill(skill))
+ {
+ xpPanel.updateSkillExperience(false, xpPauseState.isPaused(skill), skill, xpState.getSkillSnapshot(skill));
+ }
+ }
}
diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/DimmableJPanel.java b/runelite-client/src/main/java/net/runelite/client/ui/components/DimmableJPanel.java
new file mode 100644
index 0000000000..c36dd72abe
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/ui/components/DimmableJPanel.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2018, Levi
+ * 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.components;
+
+import java.awt.Color;
+import javax.swing.JPanel;
+import lombok.Getter;
+
+public class DimmableJPanel extends JPanel
+{
+ // Dimming state, allows for restoring original colors before dimming
+ @Getter
+ private boolean dimmed = false;
+ private Color dimmedForeground = null;
+ private Color dimmedBackground = null;
+ private Color undimmedForeground = null;
+ private Color undimmedBackground = null;
+
+ @Override
+ public void setForeground(Color color)
+ {
+ undimmedForeground = color;
+ dimmedForeground = color.darker();
+ super.setForeground(color);
+ }
+
+ @Override
+ public void setBackground(Color color)
+ {
+ undimmedBackground = color;
+ dimmedBackground = color.darker();
+ super.setBackground(color);
+ }
+
+ @Override
+ public Color getForeground()
+ {
+ return dimmed ? dimmedForeground : undimmedForeground;
+ }
+
+ @Override
+ public Color getBackground()
+ {
+ return dimmed ? dimmedBackground : undimmedBackground;
+ }
+
+ /**
+ * Dimming sets all parts of this component with darker colors except for the central label
+ * This is useful for showing that progress is paused
+ * Setting dim to false will restore the original colors from before the component was dimmed.
+ * @param dimmed
+ */
+ public void setDimmed(boolean dimmed)
+ {
+ this.dimmed = dimmed;
+
+ if (dimmed)
+ {
+ super.setBackground(dimmedBackground);
+ super.setForeground(dimmedForeground);
+ }
+ else
+ {
+ super.setBackground(undimmedBackground);
+ super.setForeground(undimmedForeground);
+ }
+ }
+}
diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/ProgressBar.java b/runelite-client/src/main/java/net/runelite/client/ui/components/ProgressBar.java
index c94845ee1e..0c36df1161 100644
--- a/runelite-client/src/main/java/net/runelite/client/ui/components/ProgressBar.java
+++ b/runelite-client/src/main/java/net/runelite/client/ui/components/ProgressBar.java
@@ -29,7 +29,6 @@ import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JLabel;
-import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.border.EmptyBorder;
import lombok.Setter;
@@ -39,7 +38,7 @@ import net.runelite.client.ui.components.shadowlabel.JShadowedLabel;
/**
* A progress bar to be displayed underneath the GE offer item panels
*/
-public class ProgressBar extends JPanel
+public class ProgressBar extends DimmableJPanel
{
@Setter
private int maximumValue;
@@ -50,11 +49,16 @@ public class ProgressBar extends JPanel
private final JLabel leftLabel = new JShadowedLabel();
private final JLabel rightLabel = new JShadowedLabel();
private final JLabel centerLabel = new JShadowedLabel();
+ private String centerLabelText = "";
+ private String dimmedText = "";
public ProgressBar()
{
setLayout(new BorderLayout());
+ // The background color should be overridden
setBackground(Color.GREEN.darker());
+ setForeground(Color.GREEN.brighter());
+
setPreferredSize(new Dimension(100, 16));
leftLabel.setFont(FontManager.getRunescapeSmallFont());
@@ -70,10 +74,10 @@ public class ProgressBar extends JPanel
centerLabel.setHorizontalAlignment(SwingConstants.CENTER);
centerLabel.setBorder(new EmptyBorder(2, 0, 0, 0));
+ // Adds components to be automatically redrawn when paintComponents is called
add(leftLabel, BorderLayout.WEST);
add(centerLabel, BorderLayout.CENTER);
add(rightLabel, BorderLayout.EAST);
-
}
@Override
@@ -88,20 +92,45 @@ public class ProgressBar extends JPanel
super.paintComponents(g);
}
+ @Override
+ public void setDimmed(boolean dimmed)
+ {
+ super.setDimmed(dimmed);
+
+ if (dimmed)
+ {
+ leftLabel.setForeground(Color.GRAY);
+ rightLabel.setForeground(Color.GRAY);
+ centerLabel.setText(dimmedText);
+ }
+ else
+ {
+ leftLabel.setForeground(Color.WHITE);
+ rightLabel.setForeground(Color.WHITE);
+ centerLabel.setText(centerLabelText);
+ }
+ }
public void setLeftLabel(String txt)
{
- this.leftLabel.setText(txt);
+ leftLabel.setText(txt);
}
public void setRightLabel(String txt)
{
- this.rightLabel.setText(txt);
+ rightLabel.setText(txt);
}
public void setCenterLabel(String txt)
{
- this.centerLabel.setText(txt);
+ centerLabelText = txt;
+ centerLabel.setText(isDimmed() ? dimmedText : txt);
+ }
+
+ public void setDimmedText(String txt)
+ {
+ dimmedText = txt;
+ centerLabel.setText(isDimmed() ? txt : centerLabelText);
}
public double getPercentage()