001package net.tnemc.plugincore.core.utils;
002
003
004import net.tnemc.plugincore.core.compatibility.PlayerProvider;
005
006import java.math.BigDecimal;
007
008public class Experience {
009
010  /**
011   * Calculates a player's total exp based on level and progress to next.
012   *
013   * @param player the Player
014   *
015   * @return the amount of exp the Player has
016   */
017  public static BigDecimal getExperienceAsDecimal(PlayerProvider player) {
018    return new BigDecimal(getExperienceFromLevel(player.getExpLevel()))
019        .add(new BigDecimal(getExperienceToNext(player.getExpLevel()) * player.getExp()));
020  }
021
022  /**
023   * Calculates a player's total exp based on level and progress to next.
024   *
025   * @param player the Player
026   *
027   * @return the amount of exp the Player has
028   */
029  public static int getExperience(PlayerProvider player) {
030    return getExperienceFromLevel(player.getExpLevel())
031        + Math.round(getExperienceToNext(player.getExpLevel()) * player.getExp());
032  }
033
034  /**
035   * Calculates total experience based on level.
036   *
037   * "One can determine how much experience has been collected to reach a level using the equations:
038   *  Total Experience = [Level]2 + 6[Level] (at levels 0-15)
039   *                     2.5[Level]2 - 40.5[Level] + 360 (at levels 16-30)
040   *                     4.5[Level]2 - 162.5[Level] + 2220 (at level 31+)"
041   *
042   * @param level the level
043   *
044   * @return the total experience calculated
045   */
046  public static int getExperienceFromLevel(int level) {
047    if (level > 30) {
048      return (int) (4.5 * level * level - 162.5 * level + 2220);
049    }
050    if (level > 15) {
051      return (int) (2.5 * level * level - 40.5 * level + 360);
052    }
053    return level * level + 6 * level;
054  }
055
056  /**
057   * Calculates level based on total experience.
058   *
059   * @param exp the total experience
060   *
061   * @return the level calculated
062   */
063  public static double getLevelFromExperience(long exp) {
064    if (exp > 1395) {
065      return (Math.sqrt(72 * exp - 54215) + 325) / 18;
066    }
067    if (exp > 315) {
068      return Math.sqrt(40 * exp - 7839) / 10 + 8.1;
069    }
070    if (exp > 0) {
071      return Math.sqrt(exp + 9) - 3;
072    }
073    return 0;
074  }
075
076  /**
077   *
078   * "The formulas for figuring out how many experience orbs you need to get to the next level are as follows:
079   *  Experience Required = 2[Current Level] + 7 (at levels 0-15)
080   *                        5[Current Level] - 38 (at levels 16-30)
081   *                        9[Current Level] - 158 (at level 31+)"
082   */
083  private static int getExperienceToNext(int level) {
084    if (level > 30) {
085      return 9 * level - 158;
086    }
087    if (level > 15) {
088      return 5 * level - 38;
089    }
090    return 2 * level + 7;
091  }
092
093  /**
094   * Change a Player's exp.
095   * <p>
096   * This method should be used in place of built-in player experience methods, which does not properly
097   * account for different levels requiring different amounts of experience.
098   *
099   * @param player the Player affected
100   * @param exp the amount of experience to add or remove
101   */
102  public static void setExperience(PlayerProvider player, int exp) {
103    final int playerExperience = getExperience(player);
104    final boolean remove = exp < playerExperience;
105
106    final int change = (remove)? playerExperience - exp : exp - playerExperience;
107
108    changeExperience(player, change, remove);
109  }
110
111  /**
112   * Change a player's exp level.
113   * @param player The Player affected.
114   * @param level the new level
115   */
116  public static void setLevel(PlayerProvider player, int level) {
117    player.setExpLevel(level);
118  }
119
120  /**
121   * Change a player's exp level.
122   * @param player The Player affected.
123   * @param level the new level
124   * @param remove should this be a removal?
125   */
126  public static void changeLevel(PlayerProvider player, int level, boolean remove) {
127    final int newLevel = (remove)? player.getExpLevel() - level : player.getExpLevel() + level;
128    player.setExpLevel(newLevel);
129  }
130
131  /**
132   * Change a Player's exp.
133   * <p>
134   * This method should be used in place of built-in player experience methods, which does not properly
135   * account for different levels requiring different amounts of experience.
136   *
137   * @param player the Player affected
138   * @param exp the amount of experience to add or remove
139   */
140  public static void changeExperience(PlayerProvider player, int exp, boolean remove) {
141    int xp = ((remove)? getExperience(player) - exp : getExperience(player) + exp);
142
143    if (xp < 0) {
144      xp = 0;
145    }
146
147    double levelAndExp = getLevelFromExperience(xp);
148
149    int level = (int) levelAndExp;
150    player.setExpLevel(level);
151    player.setExp((int)(levelAndExp - level));
152  }
153}