diff --git a/src/main/java/com/gmail/nossr50/datatypes/player/PlayerProfile.java b/src/main/java/com/gmail/nossr50/datatypes/player/PlayerProfile.java index 24eb86ada..9a514e277 100644 --- a/src/main/java/com/gmail/nossr50/datatypes/player/PlayerProfile.java +++ b/src/main/java/com/gmail/nossr50/datatypes/player/PlayerProfile.java @@ -1,7 +1,10 @@ package com.gmail.nossr50.datatypes.player; import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.UUID; @@ -12,6 +15,7 @@ import com.gmail.nossr50.datatypes.MobHealthbarType; import com.gmail.nossr50.datatypes.experience.FormulaType; import com.gmail.nossr50.datatypes.skills.AbilityType; import com.gmail.nossr50.datatypes.skills.SkillType; +import com.gmail.nossr50.datatypes.skills.SkillXpGain; import com.gmail.nossr50.runnables.player.PlayerProfileSaveTask; import com.gmail.nossr50.skills.child.FamilyTree; import com.gmail.nossr50.util.player.UserManager; @@ -33,7 +37,8 @@ public class PlayerProfile { private final Map abilityDATS = new HashMap(); // Ability & Cooldown // Store previous XP gains for diminished returns - private Map gainedSkillsXp = new HashMap(); + private HashMap> gainedSkillsXp = new HashMap>(); + private HashMap rollingSkillsXp = new HashMap(); @Deprecated public PlayerProfile(String playerName) { @@ -283,29 +288,15 @@ public class PlayerProfile { * @return xp Experience amount registered */ public float getRegisteredXpGain(SkillType skillType) { - float xp; - - if (gainedSkillsXp.get(skillType) == null) { - xp = 0F; - } - else { - xp = gainedSkillsXp.get(skillType); + float xp = 0F; + + if (rollingSkillsXp.get(skillType) != null) { + xp = rollingSkillsXp.get(skillType); } return xp; } - /** - * Set registered experience gains - * This is used for diminished XP returns - * - * @param skillType Skill being used - * @param xp Experience amount to set - */ - public void setRegisteredXpGain(SkillType skillType, float xp) { - gainedSkillsXp.put(skillType, xp); - } - /** * Register an experience gain * This is used for diminished XP returns @@ -314,7 +305,46 @@ public class PlayerProfile { * @param xp Experience amount to add */ public void registeredXpGain(SkillType skillType, float xp) { - gainedSkillsXp.put(skillType, getRegisteredXpGain(skillType) + xp); + LinkedList gains = gainedSkillsXp.get(skillType); + + if (gains == null) { + gains = new LinkedList(); // Maybe add an initial capacity? + } + gains.addLast(new SkillXpGain(System.currentTimeMillis(), xp)); + + gainedSkillsXp.put(skillType, gains); + rollingSkillsXp.put(skillType, getRegisteredXpGain(skillType) + xp); + } + + /** + * Remove experience gains older than a given time + * This is used for diminished XP returns + * + * @param age Age in milliseconds that gains older than should be removed + */ + public void removeXpGainsOlderThan(long age) { + long now = System.currentTimeMillis(); + + Iterator>> iterator = gainedSkillsXp.entrySet().iterator(); + while (iterator.hasNext()) { + Entry> skillGains = iterator.next(); + + float xp = 0; + // Because we are using a LinkedList and addLast ordering is guaranteed, so we loop through and remove things that are too old, and stop immediately once we find a young'n + Iterator gainsIterator = skillGains.getValue().iterator(); + while (gainsIterator.hasNext()) { + SkillXpGain gain = gainsIterator.next(); + + if (now - gain.getTime() >= age) { + gainsIterator.remove(); + xp += gain.getXp(); + } + else { + break; + } + } + rollingSkillsXp.put(skillGains.getKey(), rollingSkillsXp.get(skillGains.getKey()) - xp); + } } /** diff --git a/src/main/java/com/gmail/nossr50/datatypes/skills/SkillXpGain.java b/src/main/java/com/gmail/nossr50/datatypes/skills/SkillXpGain.java new file mode 100644 index 000000000..37bd51739 --- /dev/null +++ b/src/main/java/com/gmail/nossr50/datatypes/skills/SkillXpGain.java @@ -0,0 +1,19 @@ +package com.gmail.nossr50.datatypes.skills; + +public class SkillXpGain { + private final long time; + private final float xp; + + public SkillXpGain(long time, float xp) { + this.time = time; + this.xp = xp; + } + + public long getTime() { + return time; + } + + public float getXp() { + return xp; + } +} diff --git a/src/main/java/com/gmail/nossr50/listeners/SelfListener.java b/src/main/java/com/gmail/nossr50/listeners/SelfListener.java index 7b9c3e975..660a4c13c 100644 --- a/src/main/java/com/gmail/nossr50/listeners/SelfListener.java +++ b/src/main/java/com/gmail/nossr50/listeners/SelfListener.java @@ -47,9 +47,15 @@ public class SelfListener implements Listener { int threshold = ExperienceConfig.getInstance().getDeminishedReturnsThreshold(); if (threshold <= 0) { + // Diminished returns is turned off return; } + final float rawXp = event.getRawXpGained(); + if (rawXp < 0) { + return; // Don't calculate for XP subtraction + } + Player player = event.getPlayer(); McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player); SkillType skillType = event.getSkill(); @@ -58,18 +64,22 @@ public class SelfListener implements Listener { return; } - float difference = (mcMMOPlayer.getProfile().getRegisteredXpGain(skillType) - threshold) / threshold; + float modifiedThreshold = (float) (threshold / skillType.getXpModifier() * ExperienceConfig.getInstance().getExperienceGainsGlobalMultiplier()); + float difference = (mcMMOPlayer.getProfile().getRegisteredXpGain(skillType) - modifiedThreshold) / modifiedThreshold; if (difference > 0) { // System.out.println("Total XP Earned: " + mcMMOPlayer.getProfile().getRegisteredXpGain(skillType) + " / Threshold value: " + threshold); // System.out.println(difference * 100 + "% over the threshold!"); // System.out.println("Previous: " + event.getRawXpGained()); // System.out.println("Adjusted XP " + (event.getRawXpGained() - (event.getRawXpGained() * difference))); - float newValue = event.getRawXpGained() - (event.getRawXpGained() * difference); + float newValue = rawXp - (rawXp * difference); - event.setRawXpGained(newValue); + if (newValue > 0) { + event.setRawXpGained(newValue); + } + else { + event.setCancelled(true); + } } - - mcMMOPlayer.getProfile().registeredXpGain(skillType, event.getRawXpGained()); } } diff --git a/src/main/java/com/gmail/nossr50/mcMMO.java b/src/main/java/com/gmail/nossr50/mcMMO.java index ff201eaa0..3fab09ce2 100644 --- a/src/main/java/com/gmail/nossr50/mcMMO.java +++ b/src/main/java/com/gmail/nossr50/mcMMO.java @@ -496,10 +496,8 @@ public class mcMMO extends JavaPlugin { } // Clear the registered XP data so players can earn XP again - long clearRegisteredXPGainInterval = ExperienceConfig.getInstance().getDeminishedReturnsTimeInterval() * 60 * 20; - - if (clearRegisteredXPGainInterval > 0 && ExperienceConfig.getInstance().getDeminishedReturnsThreshold() > 0) { - new ClearRegisteredXPGainTask().runTaskTimer(this, clearRegisteredXPGainInterval, clearRegisteredXPGainInterval); + if (ExperienceConfig.getInstance().getDeminishedReturnsThreshold() > 0) { + new ClearRegisteredXPGainTask().runTaskTimer(this, 60 * 20, 60 * 20); } } diff --git a/src/main/java/com/gmail/nossr50/runnables/player/ClearRegisteredXPGainTask.java b/src/main/java/com/gmail/nossr50/runnables/player/ClearRegisteredXPGainTask.java index f1d2d73d0..637b534d6 100644 --- a/src/main/java/com/gmail/nossr50/runnables/player/ClearRegisteredXPGainTask.java +++ b/src/main/java/com/gmail/nossr50/runnables/player/ClearRegisteredXPGainTask.java @@ -2,20 +2,15 @@ package com.gmail.nossr50.runnables.player; import org.bukkit.scheduler.BukkitRunnable; +import com.gmail.nossr50.config.experience.ExperienceConfig; import com.gmail.nossr50.datatypes.player.McMMOPlayer; -import com.gmail.nossr50.datatypes.skills.SkillType; import com.gmail.nossr50.util.player.UserManager; public class ClearRegisteredXPGainTask extends BukkitRunnable { @Override public void run() { for (McMMOPlayer mcMMOPlayer : UserManager.getPlayers()) { - for (SkillType skillType : SkillType.values()) { - if (skillType.isChildSkill()) { - continue; - } - mcMMOPlayer.getProfile().setRegisteredXpGain(skillType, 0F); - } + mcMMOPlayer.getProfile().removeXpGainsOlderThan(ExperienceConfig.getInstance().getDeminishedReturnsTimeInterval() * 60 * 1000); } } } diff --git a/src/main/java/com/gmail/nossr50/util/EventUtils.java b/src/main/java/com/gmail/nossr50/util/EventUtils.java index a3cf68253..288ced48e 100644 --- a/src/main/java/com/gmail/nossr50/util/EventUtils.java +++ b/src/main/java/com/gmail/nossr50/util/EventUtils.java @@ -161,6 +161,7 @@ public class EventUtils { if (!isCancelled) { UserManager.getPlayer(player).addXp(skill, event.getRawXpGained()); + UserManager.getPlayer(player).getProfile().registeredXpGain(skill, event.getRawXpGained()); } return !isCancelled;