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 <slusnucky@gmail.com>
This commit is contained in:
Tomas Slusny
2018-05-21 11:47:27 +02:00
parent 570584310b
commit 7204a7b570
5 changed files with 180 additions and 38 deletions

View File

@@ -186,15 +186,15 @@ class XpInfoBox extends JPanel
// Update progress bar // Update progress bar
progressBar.setValue(xpSnapshotSingle.getSkillProgressToGoal()); progressBar.setValue(xpSnapshotSingle.getSkillProgressToGoal());
progressBar.setCenterLabel(xpSnapshotSingle.getSkillProgressToGoal() + "%"); progressBar.setCenterLabel(xpSnapshotSingle.getSkillProgressToGoal() + "%");
progressBar.setLeftLabel("Lvl. " + xpSnapshotSingle.getCurrentLevel()); progressBar.setLeftLabel("Lvl. " + xpSnapshotSingle.getStartLevel());
progressBar.setRightLabel("Lvl. " + (xpSnapshotSingle.getCurrentLevel() + 1)); progressBar.setRightLabel("Lvl. " + (xpSnapshotSingle.getEndLevel()));
progressBar.setToolTipText("<html>" progressBar.setToolTipText("<html>"
+ xpSnapshotSingle.getActionsInSession() + " actions done" + xpSnapshotSingle.getActionsInSession() + " actions done"
+ "<br/>" + "<br/>"
+ xpSnapshotSingle.getActionsPerHour() + " actions/hr" + xpSnapshotSingle.getActionsPerHour() + " actions/hr"
+ "<br/>" + "<br/>"
+ xpSnapshotSingle.getTimeTillGoal() + " till next lvl" + xpSnapshotSingle.getTimeTillGoal() + " till goal lvl"
+ "</html>"); + "</html>");
progressBar.repaint(); progressBar.repaint();

View File

@@ -31,7 +31,8 @@ import lombok.Value;
@Value @Value
class XpSnapshotSingle class XpSnapshotSingle
{ {
private int currentLevel; private int startLevel;
private int endLevel;
private int xpGainedInSession; private int xpGainedInSession;
private int xpRemainingToGoal; private int xpRemainingToGoal;
private int xpPerHour; private int xpPerHour;

View File

@@ -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). * 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 skill Skill to update
* @param currentXp Current known XP for this skill * @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 * @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); XpStateSingle state = getSkill(skill);
@@ -113,7 +115,7 @@ class XpState
} }
else else
{ {
return state.update(currentXp) ? XpUpdateResult.UPDATED : XpUpdateResult.NO_CHANGE; return state.update(currentXp, goalStartXp, goalEndXp) ? XpUpdateResult.UPDATED : XpUpdateResult.NO_CHANGE;
} }
} }
} }

View File

@@ -27,30 +27,35 @@ package net.runelite.client.plugins.xptracker;
import java.time.Duration; import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import lombok.Data; import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Experience; import net.runelite.api.Experience;
import net.runelite.api.Skill; import net.runelite.api.Skill;
@Data
@Slf4j @Slf4j
@RequiredArgsConstructor
class XpStateSingle class XpStateSingle
{ {
private final Skill skill; private final Skill skill;
@Getter
private final int startXp; private final int startXp;
private Instant skillTimeStart = null;
@Getter
private int xpGained = 0; private int xpGained = 0;
private Instant skillTimeStart = null;
private int actions = 0; private int actions = 0;
private int nextLevelExp = 0;
private int startLevelExp = 0; private int startLevelExp = 0;
private int level = 0; private int endLevelExp = 0;
private boolean actionsHistoryInitialized = false; private boolean actionsHistoryInitialized = false;
private int[] actionExps = new int[10]; private int[] actionExps = new int[10];
private int actionExpIndex = 0; private int actionExpIndex = 0;
int getXpHr() private int getCurrentXp()
{ {
return toHourly(xpGained); return startXp + xpGained;
} }
private int getActionsHr() private int getActionsHr()
@@ -84,7 +89,7 @@ class XpStateSingle
private int getXpRemaining() private int getXpRemaining()
{ {
return nextLevelExp - (startXp + xpGained); return endLevelExp - getCurrentXp();
} }
private int getActionsRemaining() private int getActionsRemaining()
@@ -92,19 +97,19 @@ class XpStateSingle
if (actionsHistoryInitialized) if (actionsHistoryInitialized)
{ {
long xpRemaining = getXpRemaining() * actionExps.length; 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) // 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 // Make sure to account for the very last action at the end
long remainder = xpRemaining % actionExp; long remainder = xpRemaining % totalActionXp;
long quotient = xpRemaining / actionExp; long quotient = xpRemaining / totalActionXp;
return Math.toIntExact(quotient + (remainder > 0 ? 1 : 0)); return Math.toIntExact(quotient + (remainder > 0 ? 1 : 0));
} }
} }
@@ -114,10 +119,8 @@ class XpStateSingle
private int getSkillProgress() private int getSkillProgress()
{ {
int currentXp = startXp + xpGained; double xpGained = getCurrentXp() - startLevelExp;
double xpGoal = endLevelExp - startLevelExp;
double xpGained = currentXp - startLevelExp;
double xpGoal = nextLevelExp - startLevelExp;
return (int) ((xpGained / xpGoal) * 100); 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) if (startXp == -1)
{ {
@@ -174,6 +182,7 @@ class XpStateSingle
int originalXp = xpGained + startXp; int originalXp = xpGained + startXp;
int actionExp = currentXp - originalXp; int actionExp = currentXp - originalXp;
// No experience gained
if (actionExp == 0) if (actionExp == 0)
{ {
return false; return false;
@@ -195,17 +204,32 @@ class XpStateSingle
} }
actionExpIndex = (actionExpIndex + 1) % actionExps.length; actionExpIndex = (actionExpIndex + 1) % actionExps.length;
actions++; actions++;
// Calculate experience gained
xpGained = currentXp - startXp; 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; if (goalEndXp <= 0 || currentXp > goalEndXp)
{
nextLevelExp = currentLevel + 1 <= Experience.MAX_VIRT_LEVEL ? Experience.getXpForLevel(currentLevel + 1) : -1; 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) if (skillTimeStart == null)
{ {
skillTimeStart = Instant.now(); skillTimeStart = Instant.now();
@@ -217,12 +241,13 @@ class XpStateSingle
XpSnapshotSingle snapshot() XpSnapshotSingle snapshot()
{ {
return XpSnapshotSingle.builder() return XpSnapshotSingle.builder()
.currentLevel(getLevel()) .startLevel(Experience.getLevelForXp(startLevelExp))
.xpGainedInSession(getXpGained()) .endLevel(Experience.getLevelForXp(endLevelExp))
.xpGainedInSession(xpGained)
.xpRemainingToGoal(getXpRemaining()) .xpRemainingToGoal(getXpRemaining())
.xpPerHour(getXpHr()) .xpPerHour(getXpHr())
.skillProgressToGoal(getSkillProgress()) .skillProgressToGoal(getSkillProgress())
.actionsInSession(getActions()) .actionsInSession(actions)
.actionsRemainingToGoal(getActionsRemaining()) .actionsRemainingToGoal(getActionsRemaining())
.actionsPerHour(getActionsHr()) .actionsPerHour(getActionsHr())
.timeTillGoal(getTimeTillLevel()) .timeTillGoal(getTimeTillLevel())

View File

@@ -40,6 +40,7 @@ import net.runelite.api.Client;
import net.runelite.api.GameState; import net.runelite.api.GameState;
import net.runelite.api.Player; import net.runelite.api.Player;
import net.runelite.api.Skill; import net.runelite.api.Skill;
import net.runelite.api.VarPlayer;
import net.runelite.api.events.ExperienceChanged; import net.runelite.api.events.ExperienceChanged;
import net.runelite.api.events.GameStateChanged; import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.GameTick; import net.runelite.api.events.GameTick;
@@ -259,12 +260,15 @@ public class XpTrackerPlugin extends Plugin
public void onXpChanged(ExperienceChanged event) public void onXpChanged(ExperienceChanged event)
{ {
final Skill skill = event.getSkill(); 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); final XpUpdateResult updateResult = xpState.updateSkill(skill, currentXp, startGoalXp, endGoalXp);
boolean updated = XpUpdateResult.UPDATED.equals(updateResult);
final boolean updated = XpUpdateResult.UPDATED.equals(updateResult);
xpPanel.updateSkillExperience(updated, skill, xpState.getSkillSnapshot(skill)); xpPanel.updateSkillExperience(updated, skill, xpState.getSkillSnapshot(skill));
xpState.recalculateTotal(); xpState.recalculateTotal();
xpPanel.updateTotal(xpState.getTotalSnapshot()); xpPanel.updateTotal(xpState.getTotalSnapshot());
@@ -287,4 +291,114 @@ public class XpTrackerPlugin extends Plugin
{ {
return xpState.getSkillSnapshot(skill); 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;
}
}
} }