From 68e433b3b798d08a3ce741e28de0c96522aaf2e7 Mon Sep 17 00:00:00 2001 From: TfT_02 Date: Mon, 20 May 2013 15:38:30 +0200 Subject: [PATCH] Added new ExperienceFormula config and /mcconvert command --- Changelog.txt | 3 + .../commands/database/McconvertCommand.java | 69 +++++++ .../java/com/gmail/nossr50/config/Config.java | 9 - .../config/experience/ExperienceConfig.java | 44 ++++ .../datatypes/experience/FormulaType.java | 16 ++ .../datatypes/player/PlayerProfile.java | 13 +- .../nossr50/datatypes/skills/SkillType.java | 3 +- src/main/java/com/gmail/nossr50/mcMMO.java | 9 + .../gmail/nossr50/metrics/MetricsManager.java | 17 +- .../database/FormulaConversionTask.java | 96 +++++++++ .../commands/CommandRegistrationManager.java | 11 + .../util/experience/FormulaManager.java | 192 ++++++++++++++++++ src/main/resources/config.yml | 21 -- src/main/resources/experienceFormula.yml | 38 ++++ .../resources/locale/locale_en_US.properties | 4 + src/main/resources/plugin.yml | 2 + 16 files changed, 507 insertions(+), 40 deletions(-) create mode 100644 src/main/java/com/gmail/nossr50/commands/database/McconvertCommand.java create mode 100644 src/main/java/com/gmail/nossr50/config/experience/ExperienceConfig.java create mode 100644 src/main/java/com/gmail/nossr50/datatypes/experience/FormulaType.java create mode 100644 src/main/java/com/gmail/nossr50/runnables/database/FormulaConversionTask.java create mode 100644 src/main/java/com/gmail/nossr50/util/experience/FormulaManager.java create mode 100644 src/main/resources/experienceFormula.yml diff --git a/Changelog.txt b/Changelog.txt index 4cbd4fc9b..16c24bf4e 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -8,6 +8,8 @@ Key: - Removal Version 1.4.07-dev + + Added new experienceFormula config file! Has support for EXPONENTIAL formula curves. + + Added new /mcconvert command to convert players levels and experience from one formula curve to another. + Added snow to excavation + Added new experience curve option. Cumulative curve, calculates experience needed for next level using power level. + Added extra settings to config.yml for Call of the Wild (Taming) @@ -26,6 +28,7 @@ Version 1.4.07-dev ! Changed Swords "Counter Attack" ability from passive to active. Blocking is required to activate. ! Admin and Party chat prefixes are now customizable ! Changed the color of party leader names in Party chat + ! Moved all experience formula related settings from config.yml to experienceFormula.yml (This includes skill modifiers and curve modifiers) ! Improved profile saving ! Improved partial name matcher ! Slightly improved update checker feedback diff --git a/src/main/java/com/gmail/nossr50/commands/database/McconvertCommand.java b/src/main/java/com/gmail/nossr50/commands/database/McconvertCommand.java new file mode 100644 index 000000000..d6f27257c --- /dev/null +++ b/src/main/java/com/gmail/nossr50/commands/database/McconvertCommand.java @@ -0,0 +1,69 @@ +package com.gmail.nossr50.commands.database; + +import java.util.List; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabExecutor; +import org.bukkit.entity.Player; + +import com.gmail.nossr50.locale.LocaleLoader; +import com.gmail.nossr50.datatypes.experience.FormulaType; +import com.gmail.nossr50.mcMMO; +import com.gmail.nossr50.runnables.database.FormulaConversionTask; +import com.gmail.nossr50.util.player.UserManager; +import com.google.common.collect.ImmutableList; + +public class McconvertCommand implements TabExecutor { + + /* + * Do this later; Use mcconvert instead of mmoupdate: + * OLD : + * /mmoupdate flatfile / mysql + * + * NEW : + * /mcconvert + * /mcconvert + * */ + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + switch (args.length) { + case 0: + sender.sendMessage("Usage is /mcconvert "); + return true; + + case 1: + FormulaType previousType = mcMMO.getFormulaManager().getPreviousFormulaType(); + FormulaType newType = FormulaType.getFormulaType(args[0].toUpperCase()); + + if (newType == FormulaType.UNKNOWN) { + sender.sendMessage("Unknown formula type! Valid types are: LINEAR and EXPONENTIAL"); + return true; + } + + if (previousType == newType) { + sender.sendMessage(LocaleLoader.getString("Commands.mcconvert.Same", newType)); + return true; + } + + sender.sendMessage(LocaleLoader.getString("Commands.mcconvert.Start", previousType.toString(), newType.toString())); + UserManager.saveAll(); + UserManager.clearAll(); + new FormulaConversionTask(sender, newType).runTaskLater(mcMMO.p, 1); + + for (Player player : mcMMO.p.getServer().getOnlinePlayers()) { + UserManager.addUser(player); + } + return true; + + default: + break; + } + return false; + } + + @Override + public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { + return ImmutableList.of(); + } +} diff --git a/src/main/java/com/gmail/nossr50/config/Config.java b/src/main/java/com/gmail/nossr50/config/Config.java index add4b200a..0b2e2dc29 100644 --- a/src/main/java/com/gmail/nossr50/config/Config.java +++ b/src/main/java/com/gmail/nossr50/config/Config.java @@ -192,10 +192,6 @@ public class Config extends AutoUpdateConfigLoader { reason.add("Experience.Gains.Mobspawners.Multiplier should be at least 0!"); } - if (getFormulaMultiplierCurve() < 0) { - reason.add("Experience.Formula.Curve_Modifier should be at least 0!"); - } - return noErrorsInConfig(reason); } @@ -484,9 +480,4 @@ public class Config extends AutoUpdateConfigLoader { public double getWitherSkeletonXP() { return config.getDouble("Experience.Combat.Multiplier.Wither_Skeleton", 4.0); } public double getSpawnedMobXpMultiplier() { return config.getDouble("Experience.Gains.Mobspawners.Multiplier", 0.0); } - - /* XP Formula Multiplier */ - public int getFormulaMultiplierCurve() { return config.getInt("Experience.Formula.Curve_Modifier", 20); } - public boolean getCumulativeCurveEnabled() { return config.getBoolean("Experience.Formula.Cumulative_Curve", false); } - public double getFormulaSkillModifier(SkillType skill) { return config.getDouble("Experience.Formula.Modifier." + StringUtils.getCapitalized(skill.toString())); } } diff --git a/src/main/java/com/gmail/nossr50/config/experience/ExperienceConfig.java b/src/main/java/com/gmail/nossr50/config/experience/ExperienceConfig.java new file mode 100644 index 000000000..82e0d444e --- /dev/null +++ b/src/main/java/com/gmail/nossr50/config/experience/ExperienceConfig.java @@ -0,0 +1,44 @@ +package com.gmail.nossr50.config.experience; + +import com.gmail.nossr50.config.AutoUpdateConfigLoader; +import com.gmail.nossr50.datatypes.experience.FormulaType; +import com.gmail.nossr50.datatypes.skills.SkillType; +import com.gmail.nossr50.util.StringUtils; + +public class ExperienceConfig extends AutoUpdateConfigLoader { + private static ExperienceConfig instance; + + private ExperienceConfig() { + super("experienceFormula.yml"); + } + + public static ExperienceConfig getInstance() { + if (instance == null) { + instance = new ExperienceConfig(); + } + + return instance; + } + + @Override + protected void loadKeys() {} + + /* XP Formula Multiplier */ + public FormulaType getFormulaType() { return FormulaType.getFormulaType(config.getString("Experience_Formula.Curve")); } + public boolean getCumulativeCurveEnabled() { return config.getBoolean("Experience_Formula.Cumulative_Curve", false); } + + /* Linear curve values */ + public int getLinearBase() { return config.getInt("Experience_Formula.Linear_Values.base", 1020); } + public double getLinearMultiplier() { return config.getDouble("Experience_Formula.Linear_Values.multiplier", 20); } + + /* Exponential curve values */ + public double getExponentialMultiplier() { return config.getDouble("Experience_Formula.Exponential_Values.multiplier", 0.1); } + public double getExponentialExponent() { return config.getDouble("Experience_Formula.Exponential_Values.exponent", 1.80); } + public int getExponentialBase() { return config.getInt("Experience_Formula.Exponential_Values.base", 2000); } + + /* Skill modifiers */ + public double getFormulaSkillModifier(SkillType skill) { return config.getDouble("Experience_Formula.Modifier." + StringUtils.getCapitalized(skill.toString())); } + + /* Conversion */ + public double getExpModifier() { return config.getDouble("Conversion.Exp_Modifier", 1); } +} diff --git a/src/main/java/com/gmail/nossr50/datatypes/experience/FormulaType.java b/src/main/java/com/gmail/nossr50/datatypes/experience/FormulaType.java new file mode 100644 index 000000000..09b602ec9 --- /dev/null +++ b/src/main/java/com/gmail/nossr50/datatypes/experience/FormulaType.java @@ -0,0 +1,16 @@ +package com.gmail.nossr50.datatypes.experience; + +public enum FormulaType { + LINEAR, + EXPONENTIAL, + UNKNOWN; + + public static FormulaType getFormulaType(String string) { + try { + return valueOf(string); + } + catch (IllegalArgumentException ex) { + return UNKNOWN; + } + } +}; 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 4cecb4d46..b0f1a7d22 100644 --- a/src/main/java/com/gmail/nossr50/datatypes/player/PlayerProfile.java +++ b/src/main/java/com/gmail/nossr50/datatypes/player/PlayerProfile.java @@ -8,8 +8,10 @@ import org.bukkit.scoreboard.Scoreboard; import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.config.Config; +import com.gmail.nossr50.config.experience.ExperienceConfig; import com.gmail.nossr50.config.spout.SpoutConfig; 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.spout.huds.HudType; @@ -271,17 +273,16 @@ public class PlayerProfile { } /** - * Get the amount of Xp remaining before the next level. + * Get the total amount of Xp before the next level. * * @param skillType Type of skill to check - * @return the Xp remaining until next level + * @return the total amount of Xp until next level */ public int getXpToLevel(SkillType skillType) { - if (Config.getInstance().getCumulativeCurveEnabled()) { - return 1020 + (UserManager.getPlayer(playerName).getPowerLevel() * Config.getInstance().getFormulaMultiplierCurve()); - } + int level = (ExperienceConfig.getInstance().getCumulativeCurveEnabled()) ? UserManager.getPlayer(playerName).getPowerLevel() : skills.get(skillType); + FormulaType formulaType = ExperienceConfig.getInstance().getFormulaType(); - return 1020 + (skills.get(skillType) * Config.getInstance().getFormulaMultiplierCurve()); + return mcMMO.getFormulaManager().getCachedXpToLevel(level, formulaType); } private int getChildSkillLevel(SkillType skillType) { diff --git a/src/main/java/com/gmail/nossr50/datatypes/skills/SkillType.java b/src/main/java/com/gmail/nossr50/datatypes/skills/SkillType.java index 3b3a64615..12e8bb74a 100644 --- a/src/main/java/com/gmail/nossr50/datatypes/skills/SkillType.java +++ b/src/main/java/com/gmail/nossr50/datatypes/skills/SkillType.java @@ -8,6 +8,7 @@ import org.bukkit.Color; import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.config.Config; +import com.gmail.nossr50.config.experience.ExperienceConfig; import com.gmail.nossr50.locale.LocaleLoader; import com.gmail.nossr50.skills.SkillManager; import com.gmail.nossr50.skills.acrobatics.AcrobaticsManager; @@ -108,7 +109,7 @@ public enum SkillType { } public double getXpModifier() { - return Config.getInstance().getFormulaSkillModifier(this); + return ExperienceConfig.getInstance().getFormulaSkillModifier(this); } public static SkillType getSkill(String skillName) { diff --git a/src/main/java/com/gmail/nossr50/mcMMO.java b/src/main/java/com/gmail/nossr50/mcMMO.java index 53c8d67d7..59b4228b7 100644 --- a/src/main/java/com/gmail/nossr50/mcMMO.java +++ b/src/main/java/com/gmail/nossr50/mcMMO.java @@ -5,6 +5,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; +import com.gmail.nossr50.util.experience.FormulaManager; import net.shatteredlands.shatt.backup.ZipLibrary; import org.bukkit.entity.Player; @@ -59,6 +60,7 @@ public class mcMMO extends JavaPlugin { private static ChunkManager placeStore; private static RepairableManager repairableManager; private static DatabaseManager databaseManager; + private static FormulaManager formulaManager; /* File Paths */ private static String mainDirectory; @@ -125,6 +127,8 @@ public class mcMMO extends JavaPlugin { PartyManager.loadParties(); + formulaManager = new FormulaManager(this); + for (Player player : getServer().getOnlinePlayers()) { UserManager.addUser(player); // In case of reload add all users back into UserManager } @@ -166,6 +170,7 @@ public class mcMMO extends JavaPlugin { try { UserManager.saveAll(); // Make sure to save player information if the server shuts down PartyManager.saveParties(); // Save our parties + formulaManager.saveFormula(); placeStore.saveAll(); // Save our metadata placeStore.cleanUp(); // Cleanup empty metadata stores } @@ -232,6 +237,10 @@ public class mcMMO extends JavaPlugin { getLogger().info("[Debug] " + message); } + public static FormulaManager getFormulaManager() { + return formulaManager; + } + public static ChunkManager getPlaceStore() { return placeStore; } diff --git a/src/main/java/com/gmail/nossr50/metrics/MetricsManager.java b/src/main/java/com/gmail/nossr50/metrics/MetricsManager.java index a8db9f42d..3abc93a48 100644 --- a/src/main/java/com/gmail/nossr50/metrics/MetricsManager.java +++ b/src/main/java/com/gmail/nossr50/metrics/MetricsManager.java @@ -8,6 +8,7 @@ import java.util.Locale; import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.config.Config; import com.gmail.nossr50.config.HiddenConfig; +import com.gmail.nossr50.config.experience.ExperienceConfig; import com.gmail.nossr50.locale.LocaleLoader; import com.turt2live.metrics.EMetrics; import com.turt2live.metrics.Metrics; @@ -176,6 +177,16 @@ public class MetricsManager { } }); + // ExperienceFormulaShape Graph + Graph experienceFormulaShapeGraph = metrics.createGraph("Experience Formula Shape Graph"); + + experienceFormulaShapeGraph.addPlotter(new Metrics.Plotter(ExperienceConfig.getInstance().getFormulaType().toString()) { + @Override + public int getValue() { + return 1; + } + }); + // GlobalMultiplier Graph Graph globalMultiplierGraph = metrics.createGraph("Global Multiplier Graph"); @@ -189,7 +200,7 @@ public class MetricsManager { // GlobalCurveModifier Graph Graph globalCurveModifierGraph = metrics.createGraph("Global Curve Modifier Graph"); - globalCurveModifierGraph.addPlotter(new Metrics.Plotter(Config.getInstance().getFormulaMultiplierCurve() + "") { + globalCurveModifierGraph.addPlotter(new Metrics.Plotter(ExperienceConfig.getInstance().getLinearMultiplier() + "") { @Override public int getValue() { return 1; @@ -227,7 +238,7 @@ public class MetricsManager { // GlobalCurveModifier Fuzzy Logic Numbers Graph globalCurveMultiplierGraphFuzzy = metrics.createGraph("Global Curve Multiplier Fuzz"); - if (Config.getInstance().getFormulaMultiplierCurve() > 20.0) { + if (ExperienceConfig.getInstance().getLinearMultiplier() > 20.0) { globalCurveMultiplierGraphFuzzy.addPlotter(new Metrics.Plotter("Higher") { @Override public int getValue() { @@ -235,7 +246,7 @@ public class MetricsManager { } }); } - else if (Config.getInstance().getFormulaMultiplierCurve() < 20.0) { + else if (ExperienceConfig.getInstance().getLinearMultiplier() < 20.0) { globalCurveMultiplierGraphFuzzy.addPlotter(new Metrics.Plotter("Lower") { @Override public int getValue() { diff --git a/src/main/java/com/gmail/nossr50/runnables/database/FormulaConversionTask.java b/src/main/java/com/gmail/nossr50/runnables/database/FormulaConversionTask.java new file mode 100644 index 000000000..c5d6ca4aa --- /dev/null +++ b/src/main/java/com/gmail/nossr50/runnables/database/FormulaConversionTask.java @@ -0,0 +1,96 @@ +package com.gmail.nossr50.runnables.database; + +import com.gmail.nossr50.datatypes.experience.FormulaType; + +import org.bukkit.command.CommandSender; +import org.bukkit.scheduler.BukkitRunnable; + +import com.gmail.nossr50.locale.LocaleLoader; +import com.gmail.nossr50.mcMMO; +import com.gmail.nossr50.config.experience.ExperienceConfig; +import com.gmail.nossr50.datatypes.player.McMMOPlayer; +import com.gmail.nossr50.datatypes.player.PlayerProfile; +import com.gmail.nossr50.datatypes.skills.SkillType; +import com.gmail.nossr50.util.player.UserManager; + +public class FormulaConversionTask extends BukkitRunnable { + private CommandSender sender; + private FormulaType formulaType; + + public FormulaConversionTask(CommandSender sender, FormulaType formulaType) { + this.sender = sender; + this.formulaType = formulaType; + } + + @Override + public void run() { + for (String playerName : mcMMO.getDatabaseManager().getStoredUsers()) { + McMMOPlayer mcMMOPlayer = UserManager.getPlayer(playerName); + PlayerProfile profile; + + // If the mcMMOPlayer doesn't exist, create a temporary profile and check if it's present in the database. If it's not, abort the process. + if (mcMMOPlayer == null) { + profile = mcMMO.getDatabaseManager().loadPlayerProfile(playerName, false); + + if (!profile.isLoaded()) { + mcMMO.p.debug("Profile not loaded"); + continue; + } + + editValues(profile); + profile.save(); // Since this is a temporary profile, we save it here. + } + else { + profile = mcMMOPlayer.getProfile(); + editValues(profile); + } + } + mcMMO.getFormulaManager().setPreviousFormulaType(formulaType); + + sender.sendMessage(LocaleLoader.getString("Commands.mcconvert.Finish", formulaType.toString())); + } + + private void editValues(PlayerProfile profile) { + mcMMO.p.debug("========================================================================"); + mcMMO.p.debug("Conversion report for " + profile.getPlayerName() + ":"); + for (SkillType skillType : SkillType.values()) { + if (skillType.isChildSkill()) { + continue; + } + + int[] oldExperienceValues = new int[2]; + oldExperienceValues[0] = profile.getSkillLevel(skillType); + oldExperienceValues[1] = profile.getSkillXpLevel(skillType); + + int totalOldXP = mcMMO.getFormulaManager().calculateTotalExperience(oldExperienceValues); + if (totalOldXP == 0) { + continue; + } + + double modifier = ExperienceConfig.getInstance().getExpModifier(); + if (modifier <= 0) { + modifier = 1; + mcMMO.p.getLogger().warning("Invalid value found for Conversion.Exp_Modifier! Skipping using the modifier..."); + } + + int[] newExperienceValues = mcMMO.getFormulaManager().calculateNewLevel(skillType, (int) Math.floor(totalOldXP / modifier), formulaType); + int newLevel = newExperienceValues[0]; + int newXPlevel = newExperienceValues[1]; + + mcMMO.p.debug(" Skill: " + skillType.toString()); + + mcMMO.p.debug(" OLD:"); + mcMMO.p.debug(" Level: " + oldExperienceValues[0]); + mcMMO.p.debug(" XP " + oldExperienceValues[1]); + mcMMO.p.debug(" Total XP " + totalOldXP); + + mcMMO.p.debug(" NEW:"); + mcMMO.p.debug(" Level " + newLevel); + mcMMO.p.debug(" XP " + newXPlevel); + mcMMO.p.debug("------------------------------------------------------------------------"); + + profile.modifySkill(skillType, newLevel); + profile.setSkillXpLevel(skillType, newXPlevel); + } + } +} diff --git a/src/main/java/com/gmail/nossr50/util/commands/CommandRegistrationManager.java b/src/main/java/com/gmail/nossr50/util/commands/CommandRegistrationManager.java index 971fccbf3..5e0d89b2f 100644 --- a/src/main/java/com/gmail/nossr50/util/commands/CommandRegistrationManager.java +++ b/src/main/java/com/gmail/nossr50/util/commands/CommandRegistrationManager.java @@ -21,6 +21,7 @@ import com.gmail.nossr50.commands.database.McpurgeCommand; import com.gmail.nossr50.commands.database.McremoveCommand; import com.gmail.nossr50.commands.database.MmoshowdbCommand; import com.gmail.nossr50.commands.database.MmoupdateCommand; +import com.gmail.nossr50.commands.database.McconvertCommand; import com.gmail.nossr50.commands.experience.AddlevelsCommand; import com.gmail.nossr50.commands.experience.AddxpCommand; import com.gmail.nossr50.commands.experience.MmoeditCommand; @@ -291,6 +292,15 @@ public final class CommandRegistrationManager { command.setExecutor(new MmoshowdbCommand()); } + private static void registerMcconvertCommand() { + PluginCommand command = mcMMO.p.getCommand("mcconvert"); + command.setDescription(LocaleLoader.getString("Commands.Description.mcconvert")); + command.setPermission("mcmmo.commands.mcconvert"); + command.setPermissionMessage(permissionsMessage); + command.setUsage(LocaleLoader.getString("Commands.Usage.0", "mcconvert")); + command.setExecutor(new McconvertCommand()); + } + private static void registerAdminChatCommand() { PluginCommand command = mcMMO.p.getCommand("adminchat"); command.setDescription(LocaleLoader.getString("Commands.Description.adminchat")); @@ -432,6 +442,7 @@ public final class CommandRegistrationManager { registerMcremoveCommand(); registerMmoupdateCommand(); registerMmoshowdbCommand(); + registerMcconvertCommand(); // Experience Commands registerAddlevelsCommand(); diff --git a/src/main/java/com/gmail/nossr50/util/experience/FormulaManager.java b/src/main/java/com/gmail/nossr50/util/experience/FormulaManager.java new file mode 100644 index 000000000..b3a241845 --- /dev/null +++ b/src/main/java/com/gmail/nossr50/util/experience/FormulaManager.java @@ -0,0 +1,192 @@ +package com.gmail.nossr50.util.experience; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +import org.bukkit.configuration.file.YamlConfiguration; + +import com.gmail.nossr50.config.Config; +import com.gmail.nossr50.config.experience.ExperienceConfig; +import com.gmail.nossr50.datatypes.experience.FormulaType; +import com.gmail.nossr50.datatypes.skills.SkillType; +import com.gmail.nossr50.mcMMO; + +public class FormulaManager { + private final mcMMO plugin; + private static String formulaFilePath = mcMMO.getFlatFileDirectory() + "formula.yml"; + private static File formulaFile = new File(formulaFilePath); + + // Experience needed to reach a level, cached values to improve conversion speed + private final Map experienceNeededLinear = new HashMap(); + private final Map experienceNeededExponential = new HashMap(); + + private FormulaType previousFormula; + + public FormulaManager(final mcMMO plugin) { + this.plugin = plugin; + + loadFormula(); + } + + /** + * Get the formula type that was used before converting + * + * @return previously used formula type + */ + public FormulaType getPreviousFormulaType() { + return previousFormula; + } + + /** + * Set the formula type that was used before converting + * + * @param previousFormulaType The {@link FormulaType} previously used + */ + public void setPreviousFormulaType(FormulaType previousFormulaType) { + this.previousFormula = previousFormulaType; + } + + /** + * Calculate the total amount of experience earned based on + * the amount of levels and experience, using the previously + * used formula type. + * + * @param oldExperienceValues level and experience amount + * @return The total amount of experience + */ + public int calculateTotalExperience(int[] oldExperienceValues) { + int totalXP = 0; + + int skillLevel = oldExperienceValues[0]; + int skillXPLevel = oldExperienceValues[1]; + + for (int level = 0; level < skillLevel; level++) { + totalXP += getCachedXpToLevel(level, previousFormula); + } + totalXP += skillXPLevel; + + return totalXP; + } + + /** + * Calculate how many levels a player should have using + * the new formula type. + * + * @param skillType skill where new levels and experience are calculated for + * @param experience total amount of experience + * @param formulaType The new {@link FormulaType} + * @return the amount of levels and experience + */ + public int[] calculateNewLevel(SkillType skillType, int experience, FormulaType formulaType) { + int[] newExperienceValues = new int[2]; + int newLevel = 0; + int remainder = 0; + int maxLevel = Config.getInstance().getLevelCap(skillType); + + while (experience > 0 && newLevel < maxLevel) { + int experienceToNextLevel = getCachedXpToLevel(newLevel, formulaType); + if (experience - experienceToNextLevel >= 0) { + newLevel++; + experience -= experienceToNextLevel; + } + else { + remainder = experience; + break; + } + } + newExperienceValues[0] = newLevel; + newExperienceValues[1] = remainder; + return newExperienceValues; + } + + /** + * Get the cached amount of experience needed to reach the next level, + * if cache doesn't contain the given value it is calculated and added + * to the cached data. + * + * @param level level to check + * @param formulaType The {@link FormulaType} used + * @return amount of experience needed to reach next level + */ + public int getCachedXpToLevel(int level, FormulaType formulaType) { + int experience; + + switch (formulaType) { + case UNKNOWN: + case LINEAR: + if (experienceNeededLinear.containsKey(level)) { + experience = experienceNeededLinear.get(level); + return experience; + } + + double multiplier = ExperienceConfig.getInstance().getLinearMultiplier(); + if (multiplier <= 0) { + multiplier = 20; + } + + experience = (int) Math.floor(ExperienceConfig.getInstance().getLinearBase() + level * multiplier); + experienceNeededLinear.put(level, experience); + return experience; + + case EXPONENTIAL: + if (experienceNeededExponential.containsKey(level)) { + experience = experienceNeededExponential.get(level); + return experience; + } + + multiplier = ExperienceConfig.getInstance().getExponentialMultiplier(); + double exponent = ExperienceConfig.getInstance().getExponentialExponent(); + int base = ExperienceConfig.getInstance().getExponentialBase(); + + if (multiplier <= 0) { + multiplier = 0.1; + } + + if (exponent <= 0) { + exponent = 1.80; + } + + experience = (int) Math.floor(multiplier * Math.pow(level, exponent) + base); + experienceNeededExponential.put(level, experience); + return experience; + + default: + return 0; + } + } + + /** + * Load formula file. + */ + public void loadFormula() { + if (!formulaFile.exists()) { + previousFormula = FormulaType.UNKNOWN; + return; + } + + YamlConfiguration formulasFile = YamlConfiguration.loadConfiguration(formulaFile); + + previousFormula = FormulaType.getFormulaType(formulasFile.getString("Previous_Formula", "UNKNOWN")); + } + + /** + * Save formula file. + */ + public void saveFormula() { + if (formulaFile.exists()) { + formulaFile.delete(); + } + + YamlConfiguration formulasFile = new YamlConfiguration(); + + formulasFile.set("Previous_Formula", previousFormula.toString()); + + try { + formulasFile.save(formulaFile); + } + catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 3d6f05d01..03af9d530 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -294,27 +294,6 @@ Experience: Global: 1.0 PVP: Rewards: true - Formula: - Curve_Modifier: 20 - - # Cumulative experience curves will use a players power level instead of their skill level, - # players with high power levels will have to gain a lot more experience to reach the next level in every skill. - Cumulative_Curve: false - - # Experience gained will get divided by these values. 1.0 by default, 2.0 means two times lower - Modifier: - Swords: 1.0 - Taming: 1.0 - Acrobatics: 1.0 - Excavation: 1.0 - Herbalism: 1.0 - Unarmed: 1.0 - Woodcutting: 1.0 - Mining: 1.0 - Archery: 1.0 - Axes: 1.0 - Repair: 1.0 - Fishing: 1.0 Fishing: Base: 800 Excavation: diff --git a/src/main/resources/experienceFormula.yml b/src/main/resources/experienceFormula.yml new file mode 100644 index 000000000..216d01ad3 --- /dev/null +++ b/src/main/resources/experienceFormula.yml @@ -0,0 +1,38 @@ +Experience_Formula: + # Valid values are: LINEAR and EXPONENTIAL + # If an invalid value is entered, this will reset to the default setting, which is LINEAR + # LINEAR: base + (level * multiplier) + # EXPONENTIAL: multiplier * level ^ exponent + base + Curve: LINEAR + + # If invalid values are entered mcMMO will use the default values instead and print an error in the console + Linear_Values: + base: 1020 + multiplier: 20 + Exponential_Values: + multiplier: 0.1 + exponent: 1.80 + base: 2000 + + # Cumulative experience curves will use a players power level instead of their skill level, + # players with high power levels will have to gain a lot more experience to reach the next level in every skill. + Cumulative_Curve: false + + # Experience gained will get divided by these values. 1.0 by default, 2.0 means two times lower + Modifier: + Swords: 1.0 + Taming: 1.0 + Acrobatics: 1.0 + Excavation: 1.0 + Herbalism: 1.0 + Unarmed: 1.0 + Woodcutting: 1.0 + Mining: 1.0 + Archery: 1.0 + Axes: 1.0 + Repair: 1.0 + Fishing: 1.0 + +Conversion: + # Old experience will get divided by this modifier + Exp_Modifier: 1 diff --git a/src/main/resources/locale/locale_en_US.properties b/src/main/resources/locale/locale_en_US.properties index 258cff51f..941ed6a0f 100644 --- a/src/main/resources/locale/locale_en_US.properties +++ b/src/main/resources/locale/locale_en_US.properties @@ -447,6 +447,9 @@ Commands.mmoupdate.InvalidType=[[RED]]{0} is not a valid database type. Commands.mmoupdate.Start=[[GRAY]]Starting conversion from {0} to {1}... Commands.mmoupdate.Finish=[[GRAY]]Database migration complete; the {1} database now has all data from the {0} database. Commands.mmoshowdb=[[YELLOW]]The currently used database is [[GREEN]]{0} +Commands.mcconvert.Same=[[RED]]Already using formula type {0} +Commands.mcconvert.Start=[[GRAY]]Starting conversion from {0} to {1} curve +Commands.mcconvert.Finish=[[GRAY]]Formula conversion complete; now using an {0} XP curve. Commands.ModDescription=[[RED]]- Read brief mod description Commands.NoConsole=This command does not support console usage. Commands.Notifications.Off=Ability notifications toggled [[RED]]off @@ -755,6 +758,7 @@ Commands.Description.mcstats=Show your mcMMO levels and XP Commands.Description.mctop=Show mcMMO leader boards Commands.Description.mmoedit=Edit mcMMO levels for a user Commands.Description.mmoupdate=Migrate mcMMO database from an old database into the current one +Commands.Description.mcconvert=Converts formula types Commands.Description.mmoshowdb=Show the name of the current database type (for later use with /mmoupdate) Commands.Description.party=Control various mcMMO party settings Commands.Description.partychat=Toggle mcMMO party chat on/off or send party chat messages diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 210eeae8a..780dd5d89 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -61,6 +61,8 @@ commands: description: Migrate mcMMO database from an old database type to the current mmoshowdb: description: Show the name of the current database type (for later use with /mmoupdate) + mcconvert: + description: Convert from linear to exponential formula partychat: aliases: [pc, p] description: Toggle Party chat or send party chat messages