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

This commit is contained in:
nossr50 2019-05-27 17:37:54 -07:00
parent 33dd34931c
commit 50e4e971d9
7 changed files with 130 additions and 37 deletions

View File

@ -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 Version 2.1.63
Fixed Armor Impact not scaling by skill rank Fixed Armor Impact not scaling by skill rank
Significantly Buffed the amount of durability damage incurred by a successful Armor Impact Significantly Buffed the amount of durability damage incurred by a successful Armor Impact

View File

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

View File

@ -649,11 +649,28 @@ public final class ExperienceAPI {
* @return the level of a given skill * @return the level of a given skill
* *
* @throws InvalidSkillException if the given skill is not valid * @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) { public static int getLevel(Player player, String skillType) {
return getPlayer(player).getSkillLevel(getSkillType(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. * Get the level an offline player has in a specific skill.
* </br> * </br>
@ -995,7 +1012,7 @@ public final class ExperienceAPI {
* @throws InvalidFormulaTypeException if the given formulaType is not valid * @throws InvalidFormulaTypeException if the given formulaType is not valid
*/ */
public static int getXpNeededToLevel(int level) { 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 * @throws InvalidFormulaTypeException if the given formulaType is not valid
*/ */
public static int getXpNeededToLevel(int level, String formulaType) { public static int getXpNeededToLevel(int level, String formulaType) {
return mcMMO.getFormulaManager().getCachedXpToLevel(level, getFormulaType(formulaType)); return mcMMO.getFormulaManager().getXPtoNextLevel(level, getFormulaType(formulaType));
} }
/** /**

View File

@ -203,7 +203,7 @@ public class Party {
public int getXpToLevel() { public int getXpToLevel() {
FormulaType formulaType = ExperienceConfig.getInstance().getFormulaType(); 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() { public String getXpToLevelPercentage() {

View File

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

View File

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

View File

@ -15,8 +15,10 @@ public class FormulaManager {
private static File formulaFile = new File(mcMMO.getFlatFileDirectory() + "formula.yml"); private static File formulaFile = new File(mcMMO.getFlatFileDirectory() + "formula.yml");
// Experience needed to reach a level, cached values to improve conversion speed // Experience needed to reach a level, cached values to improve conversion speed
private final Map<Integer, Integer> experienceNeededLinear = new HashMap<Integer, Integer>(); private Map<Integer, Integer> experienceNeededRetroLinear;
private final Map<Integer, Integer> experienceNeededExponential = new HashMap<Integer, Integer>(); private Map<Integer, Integer> experienceNeededStandardLinear;
private Map<Integer, Integer> experienceNeededRetroExponential;
private Map<Integer, Integer> experienceNeededStandardExponential;
private FormulaType previousFormula; private FormulaType previousFormula;
@ -26,9 +28,20 @@ public class FormulaManager {
public FormulaManager() { public FormulaManager() {
/* Setting for Classic Mode (Scales a lot of stuff up by * 10) */ /* Setting for Classic Mode (Scales a lot of stuff up by * 10) */
retroModeEnabled = Config.getInstance().getIsRetroMode(); retroModeEnabled = Config.getInstance().getIsRetroMode();
initExperienceNeededMaps();
loadFormula(); 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 * Get the formula type that was used before converting
* *
@ -60,7 +73,7 @@ public class FormulaManager {
int totalXP = 0; int totalXP = 0;
for (int level = 0; level < skillLevel; level++) { for (int level = 0; level < skillLevel; level++) {
totalXP += getCachedXpToLevel(level, previousFormula); totalXP += getXPtoNextLevel(level, previousFormula);
} }
totalXP += skillXPLevel; totalXP += skillXPLevel;
@ -83,7 +96,7 @@ public class FormulaManager {
int maxLevel = Config.getInstance().getLevelCap(primarySkillType); int maxLevel = Config.getInstance().getLevelCap(primarySkillType);
while (experience > 0 && newLevel < maxLevel) { while (experience > 0 && newLevel < maxLevel) {
int experienceToNextLevel = getCachedXpToLevel(newLevel, formulaType); int experienceToNextLevel = getXPtoNextLevel(newLevel, formulaType);
if (experience - experienceToNextLevel < 0) { if (experience - experienceToNextLevel < 0) {
remainder = experience; remainder = experience;
@ -106,42 +119,97 @@ public class FormulaManager {
* @param formulaType The {@link FormulaType} used * @param formulaType The {@link FormulaType} used
* @return amount of experience needed to reach next level * @return amount of experience needed to reach next level
*/ */
public int getCachedXpToLevel(int level, FormulaType formulaType) { public int getXPtoNextLevel(int level, FormulaType formulaType) {
int experience;
/** /**
* Retro mode XP requirements are the default requirements * Retro mode XP requirements are the default requirements
* Standard mode XP requirements are multiplied by a factor of 10 * 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) { if (formulaType == FormulaType.UNKNOWN) {
formulaType = FormulaType.LINEAR; formulaType = FormulaType.LINEAR;
} }
return processXPToNextLevel(level, formulaType);
}
/**
* 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);
}
}
/**
* 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;
if(!experienceMapRef.containsKey(level)) {
int experienceSum = 0;
int retroIndex = (level * 10) + 1;
//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); int base = ExperienceConfig.getInstance().getBase(formulaType);
double multiplier = ExperienceConfig.getInstance().getMultiplier(formulaType); double multiplier = ExperienceConfig.getInstance().getMultiplier(formulaType);
double exponent = ExperienceConfig.getInstance().getExponent(formulaType);
switch (formulaType) { switch(formulaType) {
case LINEAR: case LINEAR:
if (!experienceNeededLinear.containsKey(level)) { return (int) Math.floor(base + level * multiplier);
experience = (int) Math.floor( xpNeededMultiplier * (base + level * multiplier));
experienceNeededLinear.put(level, experience);
}
return experienceNeededLinear.get(level);
case EXPONENTIAL: case EXPONENTIAL:
if (!experienceNeededExponential.containsKey(level)) { double exponent = ExperienceConfig.getInstance().getExponent(formulaType);
experience = (int) Math.floor( xpNeededMultiplier * (multiplier * Math.pow(level, exponent) + base)); return (int) Math.floor(multiplier * Math.pow(level, exponent) + base);
experienceNeededExponential.put(level, experience);
}
return experienceNeededExponential.get(level);
default: 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);
} }
} }