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