From 7204a7b570b0bd8b87aae197578fd997f8f23a68 Mon Sep 17 00:00:00 2001 From: Tomas Slusny Date: Mon, 21 May 2018 11:47:27 +0200 Subject: [PATCH] Add support for player experience goals to tracker - Add new fields for start and end goal XP to xp tracker state - Use Varps for determining start and end goal Closes: #1168 Signed-off-by: Tomas Slusny --- .../client/plugins/xptracker/XpInfoBox.java | 6 +- .../plugins/xptracker/XpSnapshotSingle.java | 3 +- .../client/plugins/xptracker/XpState.java | 6 +- .../plugins/xptracker/XpStateSingle.java | 81 ++++++++---- .../plugins/xptracker/XpTrackerPlugin.java | 122 +++++++++++++++++- 5 files changed, 180 insertions(+), 38 deletions(-) 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 b2d536e02d..329e75eedb 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 @@ -186,15 +186,15 @@ class XpInfoBox extends JPanel // Update progress bar progressBar.setValue(xpSnapshotSingle.getSkillProgressToGoal()); progressBar.setCenterLabel(xpSnapshotSingle.getSkillProgressToGoal() + "%"); - progressBar.setLeftLabel("Lvl. " + xpSnapshotSingle.getCurrentLevel()); - progressBar.setRightLabel("Lvl. " + (xpSnapshotSingle.getCurrentLevel() + 1)); + progressBar.setLeftLabel("Lvl. " + xpSnapshotSingle.getStartLevel()); + progressBar.setRightLabel("Lvl. " + (xpSnapshotSingle.getEndLevel())); progressBar.setToolTipText("" + xpSnapshotSingle.getActionsInSession() + " actions done" + "
" + xpSnapshotSingle.getActionsPerHour() + " actions/hr" + "
" - + xpSnapshotSingle.getTimeTillGoal() + " till next lvl" + + xpSnapshotSingle.getTimeTillGoal() + " till goal lvl" + ""); progressBar.repaint(); 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 350a501f47..0745078a98 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 @@ -31,7 +31,8 @@ import lombok.Value; @Value class XpSnapshotSingle { - private int currentLevel; + private int startLevel; + private int endLevel; private int xpGainedInSession; private int xpRemainingToGoal; private int xpPerHour; 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 1b0adfb17c..c70f460a3d 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 @@ -82,9 +82,11 @@ class XpState * and also first-login when the skills are not initialized (the start XP will be -1 in this case). * @param skill Skill to update * @param currentXp Current known XP for this skill + * @param goalStartXp Possible XP start goal + * @param goalEndXp Possible XP end goal * @return Whether or not the skill has been initialized, there was no change, or it has been updated */ - XpUpdateResult updateSkill(Skill skill, int currentXp) + XpUpdateResult updateSkill(Skill skill, int currentXp, int goalStartXp, int goalEndXp) { XpStateSingle state = getSkill(skill); @@ -113,7 +115,7 @@ class XpState } else { - return state.update(currentXp) ? XpUpdateResult.UPDATED : XpUpdateResult.NO_CHANGE; + return state.update(currentXp, goalStartXp, goalEndXp) ? XpUpdateResult.UPDATED : XpUpdateResult.NO_CHANGE; } } } 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 b8ddf5f61b..a04318f3ad 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 @@ -27,30 +27,35 @@ package net.runelite.client.plugins.xptracker; import java.time.Duration; import java.time.Instant; -import lombok.Data; +import lombok.Getter; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import net.runelite.api.Experience; import net.runelite.api.Skill; -@Data @Slf4j +@RequiredArgsConstructor class XpStateSingle { private final Skill skill; + + @Getter private final int startXp; - private Instant skillTimeStart = null; + + @Getter private int xpGained = 0; + + private Instant skillTimeStart = null; private int actions = 0; - private int nextLevelExp = 0; private int startLevelExp = 0; - private int level = 0; + private int endLevelExp = 0; private boolean actionsHistoryInitialized = false; private int[] actionExps = new int[10]; private int actionExpIndex = 0; - int getXpHr() + private int getCurrentXp() { - return toHourly(xpGained); + return startXp + xpGained; } private int getActionsHr() @@ -84,7 +89,7 @@ class XpStateSingle private int getXpRemaining() { - return nextLevelExp - (startXp + xpGained); + return endLevelExp - getCurrentXp(); } private int getActionsRemaining() @@ -92,19 +97,19 @@ class XpStateSingle if (actionsHistoryInitialized) { long xpRemaining = getXpRemaining() * actionExps.length; - long actionExp = 0; + long totalActionXp = 0; - for (int i = 0; i < actionExps.length; i++) + for (int actionXp : actionExps) { - actionExp += actionExps[i]; + totalActionXp += actionXp; } // Let's not divide by zero (or negative) - if (actionExp > 0) + if (totalActionXp > 0) { // Make sure to account for the very last action at the end - long remainder = xpRemaining % actionExp; - long quotient = xpRemaining / actionExp; + long remainder = xpRemaining % totalActionXp; + long quotient = xpRemaining / totalActionXp; return Math.toIntExact(quotient + (remainder > 0 ? 1 : 0)); } } @@ -114,10 +119,8 @@ class XpStateSingle private int getSkillProgress() { - int currentXp = startXp + xpGained; - - double xpGained = currentXp - startLevelExp; - double xpGoal = nextLevelExp - startLevelExp; + double xpGained = getCurrentXp() - startLevelExp; + double xpGoal = endLevelExp - startLevelExp; return (int) ((xpGained / xpGoal) * 100); } @@ -163,7 +166,12 @@ class XpStateSingle } - boolean update(int currentXp) + int getXpHr() + { + return toHourly(xpGained); + } + + boolean update(int currentXp, int goalStartXp, int goalEndXp) { if (startXp == -1) { @@ -174,6 +182,7 @@ class XpStateSingle int originalXp = xpGained + startXp; int actionExp = currentXp - originalXp; + // No experience gained if (actionExp == 0) { return false; @@ -195,17 +204,32 @@ class XpStateSingle } actionExpIndex = (actionExpIndex + 1) % actionExps.length; - actions++; + + // Calculate experience gained xpGained = currentXp - startXp; - startLevelExp = Experience.getXpForLevel(Experience.getLevelForXp(currentXp)); - int currentLevel = Experience.getLevelForXp(currentXp); + // Determine XP goals + if (goalStartXp <= 0) + { + startLevelExp = Experience.getXpForLevel(Experience.getLevelForXp(currentXp)); + } + else + { + startLevelExp = goalStartXp; + } - level = currentLevel; - - nextLevelExp = currentLevel + 1 <= Experience.MAX_VIRT_LEVEL ? Experience.getXpForLevel(currentLevel + 1) : -1; + if (goalEndXp <= 0 || currentXp > goalEndXp) + { + int currentLevel = Experience.getLevelForXp(currentXp); + endLevelExp = currentLevel + 1 <= Experience.MAX_VIRT_LEVEL ? Experience.getXpForLevel(currentLevel + 1) : -1; + } + else + { + endLevelExp = goalEndXp; + } + // If this is first time we are updating, we just started tracking if (skillTimeStart == null) { skillTimeStart = Instant.now(); @@ -217,12 +241,13 @@ class XpStateSingle XpSnapshotSingle snapshot() { return XpSnapshotSingle.builder() - .currentLevel(getLevel()) - .xpGainedInSession(getXpGained()) + .startLevel(Experience.getLevelForXp(startLevelExp)) + .endLevel(Experience.getLevelForXp(endLevelExp)) + .xpGainedInSession(xpGained) .xpRemainingToGoal(getXpRemaining()) .xpPerHour(getXpHr()) .skillProgressToGoal(getSkillProgress()) - .actionsInSession(getActions()) + .actionsInSession(actions) .actionsRemainingToGoal(getActionsRemaining()) .actionsPerHour(getActionsHr()) .timeTillGoal(getTimeTillLevel()) 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 9443f36935..981bf63393 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 @@ -40,6 +40,7 @@ import net.runelite.api.Client; import net.runelite.api.GameState; import net.runelite.api.Player; import net.runelite.api.Skill; +import net.runelite.api.VarPlayer; import net.runelite.api.events.ExperienceChanged; import net.runelite.api.events.GameStateChanged; import net.runelite.api.events.GameTick; @@ -259,12 +260,15 @@ public class XpTrackerPlugin extends Plugin public void onXpChanged(ExperienceChanged event) { final Skill skill = event.getSkill(); - int currentXp = client.getSkillExperience(skill); + final int currentXp = client.getSkillExperience(skill); + final VarPlayer startGoal = startGoalVarpForSkill(skill); + final VarPlayer endGoal = endGoalVarpForSkill(skill); + final int startGoalXp = startGoal != null ? client.getVar(startGoal) : -1; + final int endGoalXp = endGoal != null ? client.getVar(endGoal) : -1; - XpUpdateResult updateResult = xpState.updateSkill(skill, currentXp); - - boolean updated = XpUpdateResult.UPDATED.equals(updateResult); + final XpUpdateResult updateResult = xpState.updateSkill(skill, currentXp, startGoalXp, endGoalXp); + final boolean updated = XpUpdateResult.UPDATED.equals(updateResult); xpPanel.updateSkillExperience(updated, skill, xpState.getSkillSnapshot(skill)); xpState.recalculateTotal(); xpPanel.updateTotal(xpState.getTotalSnapshot()); @@ -287,4 +291,114 @@ public class XpTrackerPlugin extends Plugin { return xpState.getSkillSnapshot(skill); } + + private static VarPlayer startGoalVarpForSkill(final Skill skill) + { + switch (skill) + { + case ATTACK: + return VarPlayer.ATTACK_GOAL_START; + case MINING: + return VarPlayer.MINING_GOAL_START; + case WOODCUTTING: + return VarPlayer.WOODCUTTING_GOAL_START; + case DEFENCE: + return VarPlayer.DEFENCE_GOAL_START; + case MAGIC: + return VarPlayer.MAGIC_GOAL_START; + case RANGED: + return VarPlayer.RANGED_GOAL_START; + case HITPOINTS: + return VarPlayer.HITPOINTS_GOAL_START; + case AGILITY: + return VarPlayer.AGILITY_GOAL_START; + case STRENGTH: + return VarPlayer.STRENGTH_GOAL_START; + case PRAYER: + return VarPlayer.PRAYER_GOAL_START; + case SLAYER: + return VarPlayer.SLAYER_GOAL_START; + case FISHING: + return VarPlayer.FISHING_GOAL_START; + case RUNECRAFT: + return VarPlayer.RUNECRAFT_GOAL_START; + case HERBLORE: + return VarPlayer.HERBLORE_GOAL_START; + case FIREMAKING: + return VarPlayer.FIREMAKING_GOAL_START; + case CONSTRUCTION: + return VarPlayer.CONSTRUCTION_GOAL_START; + case HUNTER: + return VarPlayer.HUNTER_GOAL_START; + case COOKING: + return VarPlayer.COOKING_GOAL_START; + case FARMING: + return VarPlayer.FARMING_GOAL_START; + case CRAFTING: + return VarPlayer.CRAFTING_GOAL_START; + case SMITHING: + return VarPlayer.SMITHING_GOAL_START; + case THIEVING: + return VarPlayer.THIEVING_GOAL_START; + case FLETCHING: + return VarPlayer.FLETCHING_GOAL_START; + default: + return null; + } + } + + private static VarPlayer endGoalVarpForSkill(final Skill skill) + { + switch (skill) + { + case ATTACK: + return VarPlayer.ATTACK_GOAL_END; + case MINING: + return VarPlayer.MINING_GOAL_END; + case WOODCUTTING: + return VarPlayer.WOODCUTTING_GOAL_END; + case DEFENCE: + return VarPlayer.DEFENCE_GOAL_END; + case MAGIC: + return VarPlayer.MAGIC_GOAL_END; + case RANGED: + return VarPlayer.RANGED_GOAL_END; + case HITPOINTS: + return VarPlayer.HITPOINTS_GOAL_END; + case AGILITY: + return VarPlayer.AGILITY_GOAL_END; + case STRENGTH: + return VarPlayer.STRENGTH_GOAL_END; + case PRAYER: + return VarPlayer.PRAYER_GOAL_END; + case SLAYER: + return VarPlayer.SLAYER_GOAL_END; + case FISHING: + return VarPlayer.FISHING_GOAL_END; + case RUNECRAFT: + return VarPlayer.RUNECRAFT_GOAL_END; + case HERBLORE: + return VarPlayer.HERBLORE_GOAL_END; + case FIREMAKING: + return VarPlayer.FIREMAKING_GOAL_END; + case CONSTRUCTION: + return VarPlayer.CONSTRUCTION_GOAL_END; + case HUNTER: + return VarPlayer.HUNTER_GOAL_END; + case COOKING: + return VarPlayer.COOKING_GOAL_END; + case FARMING: + return VarPlayer.FARMING_GOAL_END; + case CRAFTING: + return VarPlayer.CRAFTING_GOAL_END; + case SMITHING: + return VarPlayer.SMITHING_GOAL_END; + case THIEVING: + return VarPlayer.THIEVING_GOAL_END; + case FLETCHING: + return VarPlayer.FLETCHING_GOAL_END; + default: + return null; + } + } }