Răsfoiți Sursa

Standard level scaling now mirrors RetroMode in a true 1:10 parity

nossr50 6 ani în urmă
părinte
comite
50e4e971d9

+ 8 - 0
Changelog.txt

@@ -1,3 +1,11 @@
+Version 2.1.64
+    (API) method to get XP in FormulaManager has been renamed to getXPtoNextLevel(...), this shouldn't break anything as plugins should be using our Experience API methods instead of this
+    (API) Added method getLevel(Player player, PrimarySkillType primarySkillType) to ExperienceAPI.java
+    Corrected how Standard mode (1-100 scaling) XP to next level was calculated, it is now a true 1:10 ratio with Retro (1-1000) scale, which is how it was intended to be to begin with
+
+    NOTE: The net result of this change is it will take a bit longer to level with Standard, but it should not be a drastic change. You might not even notice it.
+    Standard is meant to take the same amount of time to level from levels 1-100 as it takes Retro to do 1-1000, this change corrects from errors in the code that made Standard actually take less XP than Retro despite intending for it to be a cosmetic difference in progression.
+
 Version 2.1.63
     Fixed Armor Impact not scaling by skill rank
     Significantly Buffed the amount of durability damage incurred by a successful Armor Impact

+ 2 - 2
pom.xml

@@ -2,7 +2,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>com.gmail.nossr50.mcMMO</groupId>
     <artifactId>mcMMO</artifactId>
-    <version>2.1.63</version>
+    <version>2.1.64-SNAPSHOT</version>
     <name>mcMMO</name>
     <url>https://github.com/mcMMO-Dev/mcMMO</url>
     <scm>
@@ -167,7 +167,7 @@
         <dependency>
             <groupId>org.spigotmc</groupId>
             <artifactId>spigot-api</artifactId>
-            <version>1.14.1-R0.1-SNAPSHOT</version>
+            <version>1.14.2-R0.1-SNAPSHOT</version>
             <scope>provided</scope>
         </dependency>
         <dependency>

+ 19 - 2
src/main/java/com/gmail/nossr50/api/ExperienceAPI.java

@@ -649,11 +649,28 @@ public final class ExperienceAPI {
      * @return the level of a given skill
      *
      * @throws InvalidSkillException if the given skill is not valid
+     * @deprecated Use getLevel(Player player, PrimarySkillType skillType) instead
      */
+    @Deprecated
     public static int getLevel(Player player, String skillType) {
         return getPlayer(player).getSkillLevel(getSkillType(skillType));
     }
 
+    /**
+     * Get the level a player has in a specific skill.
+     * </br>
+     * This function is designed for API usage.
+     *
+     * @param player The player to get the level for
+     * @param skillType The skill to get the level for
+     * @return the level of a given skill
+     *
+     * @throws InvalidSkillException if the given skill is not valid
+     */
+    public static int getLevel(Player player, PrimarySkillType skillType) {
+        return getPlayer(player).getSkillLevel(skillType);
+    }
+
     /**
      * Get the level an offline player has in a specific skill.
      * </br>
@@ -995,7 +1012,7 @@ public final class ExperienceAPI {
      * @throws InvalidFormulaTypeException if the given formulaType is not valid
      */
     public static int getXpNeededToLevel(int level) {
-        return mcMMO.getFormulaManager().getCachedXpToLevel(level, ExperienceConfig.getInstance().getFormulaType());
+        return mcMMO.getFormulaManager().getXPtoNextLevel(level, ExperienceConfig.getInstance().getFormulaType());
     }
 
     /**
@@ -1009,7 +1026,7 @@ public final class ExperienceAPI {
      * @throws InvalidFormulaTypeException if the given formulaType is not valid
      */
     public static int getXpNeededToLevel(int level, String formulaType) {
-        return mcMMO.getFormulaManager().getCachedXpToLevel(level, getFormulaType(formulaType));
+        return mcMMO.getFormulaManager().getXPtoNextLevel(level, getFormulaType(formulaType));
     }
 
     /**

+ 1 - 1
src/main/java/com/gmail/nossr50/datatypes/party/Party.java

@@ -203,7 +203,7 @@ public class Party {
 
     public int getXpToLevel() {
         FormulaType formulaType = ExperienceConfig.getInstance().getFormulaType();
-        return (mcMMO.getFormulaManager().getCachedXpToLevel(level, formulaType)) * (getOnlineMembers().size() + Config.getInstance().getPartyXpCurveMultiplier());
+        return (mcMMO.getFormulaManager().getXPtoNextLevel(level, formulaType)) * (getOnlineMembers().size() + Config.getInstance().getPartyXpCurveMultiplier());
     }
 
     public String getXpToLevelPercentage() {

+ 1 - 1
src/main/java/com/gmail/nossr50/datatypes/player/PlayerProfile.java

@@ -397,7 +397,7 @@ public class PlayerProfile {
         int level = (ExperienceConfig.getInstance().getCumulativeCurveEnabled()) ? UserManager.getPlayer(playerName).getPowerLevel() : skills.get(primarySkillType);
         FormulaType formulaType = ExperienceConfig.getInstance().getFormulaType();
 
-        return mcMMO.getFormulaManager().getCachedXpToLevel(level, formulaType);
+        return mcMMO.getFormulaManager().getXPtoNextLevel(level, formulaType);
     }
 
     private int getChildSkillLevel(PrimarySkillType primarySkillType) {

+ 6 - 6
src/main/java/com/gmail/nossr50/datatypes/skills/PrimarySkillType.java

@@ -66,7 +66,7 @@ public enum PrimarySkillType {
             ImmutableList.of(SubSkillType.WOODCUTTING_LEAF_BLOWER, SubSkillType.WOODCUTTING_TREE_FELLER, SubSkillType.WOODCUTTING_HARVEST_LUMBER));
 
     private Class<? extends SkillManager> managerClass;
-    private Color runescapeColor;
+    private Color skillColor;
     private SuperAbilityType ability;
     private ToolType tool;
     private List<SubSkillType> subSkillTypes;
@@ -110,13 +110,13 @@ public enum PrimarySkillType {
         NON_CHILD_SKILLS = ImmutableList.copyOf(nonChildSkills);
     }
 
-    private PrimarySkillType(Class<? extends SkillManager> managerClass, Color runescapeColor, List<SubSkillType> subSkillTypes) {
-        this(managerClass, runescapeColor, null, null, subSkillTypes);
+    private PrimarySkillType(Class<? extends SkillManager> managerClass, Color skillColor, List<SubSkillType> subSkillTypes) {
+        this(managerClass, skillColor, null, null, subSkillTypes);
     }
 
-    private PrimarySkillType(Class<? extends SkillManager> managerClass, Color runescapeColor, SuperAbilityType ability, ToolType tool, List<SubSkillType> subSkillTypes) {
+    private PrimarySkillType(Class<? extends SkillManager> managerClass, Color skillColor, SuperAbilityType ability, ToolType tool, List<SubSkillType> subSkillTypes) {
         this.managerClass = managerClass;
-        this.runescapeColor = runescapeColor;
+        this.skillColor = skillColor;
         this.ability = ability;
         this.tool = tool;
         this.subSkillTypes = subSkillTypes;
@@ -243,7 +243,7 @@ public enum PrimarySkillType {
     }
 
 /*    public void celebrateLevelUp(Player player) {
-        ParticleEffectUtils.fireworkParticleShower(player, runescapeColor);
+        ParticleEffectUtils.fireworkParticleShower(player, skillColor);
     }*/
 
     public boolean shouldProcess(Entity target) {

+ 93 - 25
src/main/java/com/gmail/nossr50/util/experience/FormulaManager.java

@@ -15,8 +15,10 @@ public class FormulaManager {
     private static File formulaFile = new File(mcMMO.getFlatFileDirectory() + "formula.yml");
 
     // Experience needed to reach a level, cached values to improve conversion speed
-    private final Map<Integer, Integer> experienceNeededLinear = new HashMap<Integer, Integer>();
-    private final Map<Integer, Integer> experienceNeededExponential = new HashMap<Integer, Integer>();
+    private Map<Integer, Integer> experienceNeededRetroLinear;
+    private Map<Integer, Integer> experienceNeededStandardLinear;
+    private Map<Integer, Integer> experienceNeededRetroExponential;
+    private Map<Integer, Integer> experienceNeededStandardExponential;
 
     private FormulaType previousFormula;
 
@@ -26,9 +28,20 @@ public class FormulaManager {
     public FormulaManager() {
         /* Setting for Classic Mode (Scales a lot of stuff up by * 10) */
         retroModeEnabled = Config.getInstance().getIsRetroMode();
+        initExperienceNeededMaps();
         loadFormula();
     }
 
+    /**
+     * Initialize maps used for XP to next level
+     */
+    private void initExperienceNeededMaps() {
+        experienceNeededRetroLinear = new HashMap<>();
+        experienceNeededRetroExponential = new HashMap<>();
+        experienceNeededStandardLinear = new HashMap<>();
+        experienceNeededStandardExponential = new HashMap<>();
+    }
+
     /**
      * Get the formula type that was used before converting
      *
@@ -60,7 +73,7 @@ public class FormulaManager {
         int totalXP = 0;
 
         for (int level = 0; level < skillLevel; level++) {
-            totalXP += getCachedXpToLevel(level, previousFormula);
+            totalXP += getXPtoNextLevel(level, previousFormula);
         }
 
         totalXP += skillXPLevel;
@@ -83,7 +96,7 @@ public class FormulaManager {
         int maxLevel = Config.getInstance().getLevelCap(primarySkillType);
 
         while (experience > 0 && newLevel < maxLevel) {
-            int experienceToNextLevel = getCachedXpToLevel(newLevel, formulaType);
+            int experienceToNextLevel = getXPtoNextLevel(newLevel, formulaType);
 
             if (experience - experienceToNextLevel < 0) {
                 remainder = experience;
@@ -106,42 +119,97 @@ public class FormulaManager {
      * @param formulaType The {@link FormulaType} used
      * @return amount of experience needed to reach next level
      */
-    public int getCachedXpToLevel(int level, FormulaType formulaType) {
-        int experience;
-
+    public int getXPtoNextLevel(int level, FormulaType formulaType) {
         /**
          * Retro mode XP requirements are the default requirements
          * Standard mode XP requirements are multiplied by a factor of 10
          */
-        int xpNeededMultiplier = retroModeEnabled ? 1 : 10;
 
+        //TODO: When the heck is Unknown used?
         if (formulaType == FormulaType.UNKNOWN) {
             formulaType = FormulaType.LINEAR;
         }
 
-        int base = ExperienceConfig.getInstance().getBase(formulaType);
-        double multiplier = ExperienceConfig.getInstance().getMultiplier(formulaType);
-        double exponent = ExperienceConfig.getInstance().getExponent(formulaType);
+        return processXPToNextLevel(level, formulaType);
+    }
 
-        switch (formulaType) {
-            case LINEAR:
-                if (!experienceNeededLinear.containsKey(level)) {
-                    experience = (int) Math.floor( xpNeededMultiplier * (base + level * multiplier));
-                    experienceNeededLinear.put(level, experience);
-                }
+    /**
+     * Gets the value of XP needed for the next level based on the level Scaling, the level, and the formula type
+     * @param level target level
+     * @param formulaType target formulaType
+     */
+    private int processXPToNextLevel(int level, FormulaType formulaType) {
+        if(mcMMO.isRetroModeEnabled())
+        {
+            return processXPRetroToNextLevel(level, formulaType);
+        } else {
+            return processStandardXPToNextLevel(level, formulaType);
+        }
+    }
 
-                return experienceNeededLinear.get(level);
+    /**
+     * Calculate the XP needed for the next level for the linear formula for Standard scaling (1-100)
+     * @param level target level
+     * @return raw xp needed to reach the next level
+     */
+    private int processStandardXPToNextLevel(int level, FormulaType formulaType) {
+        Map<Integer, Integer> experienceMapRef = formulaType == FormulaType.LINEAR ? experienceNeededStandardLinear : experienceNeededStandardExponential;
 
-            case EXPONENTIAL:
-                if (!experienceNeededExponential.containsKey(level)) {
-                    experience = (int) Math.floor( xpNeededMultiplier * (multiplier * Math.pow(level, exponent) + base));
-                    experienceNeededExponential.put(level, experience);
-                }
+        if(!experienceMapRef.containsKey(level)) {
+            int experienceSum = 0;
+            int retroIndex = (level * 10) + 1;
 
-                return experienceNeededExponential.get(level);
+            //Sum the range of levels in Retro that this Standard level would represent
+            for(int x = retroIndex; x < (retroIndex + 10); x++) {
+                //calculateXPNeeded doesn't cache results so we use that instead of invoking the Retro XP methods to avoid memory bloat
+                experienceSum += calculateXPNeeded(x, formulaType);
+            }
 
+            experienceMapRef.put(level, experienceSum);
+        }
+
+        return experienceMapRef.get(level);
+    }
+
+    /**
+     * Calculates the XP to next level for Retro Mode scaling
+     * Results are cached to reduce needless operations
+     * @param level target level
+     * @param formulaType target formula type
+     * @return raw xp needed to reach the next level based on formula type
+     */
+    private int processXPRetroToNextLevel(int level, FormulaType formulaType) {
+        Map<Integer, Integer> experienceMapRef = formulaType == FormulaType.LINEAR ? experienceNeededRetroLinear : experienceNeededRetroExponential;
+
+        if (!experienceMapRef.containsKey(level)) {
+            int experience = calculateXPNeeded(level, FormulaType.LINEAR);
+            experienceMapRef.put(level, experience);
+        }
+
+        return experienceMapRef.get(level);
+    }
+
+    /**
+     * Does the actual math to get the XP needed for a level in RetroMode scaling
+     * Standard uses a sum of RetroMode XP needed levels for its own thing, so it uses this too
+     * @param level target level
+     * @param formulaType target formulatype
+     * @return the raw XP needed for the next level based on formula type
+     */
+    private int calculateXPNeeded(int level, FormulaType formulaType) {
+        int base = ExperienceConfig.getInstance().getBase(formulaType);
+        double multiplier = ExperienceConfig.getInstance().getMultiplier(formulaType);
+
+        switch(formulaType) {
+            case LINEAR:
+                return (int) Math.floor(base + level * multiplier);
+            case EXPONENTIAL:
+                double exponent = ExperienceConfig.getInstance().getExponent(formulaType);
+                return (int) Math.floor(multiplier * Math.pow(level, exponent) + base);
             default:
-                return 0;
+                //TODO: Should never be called
+                mcMMO.p.getLogger().severe("Invalid formula specified for calculation, defaulting to Linear");
+                return calculateXPNeeded(level, FormulaType.LINEAR);
         }
     }