/*
* This file is part of NucleusFramework for Bukkit, licensed under the MIT License (MIT).
*
* Copyright (c) JCThePants (www.jcwhatever.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.jcwhatever.nucleus.utils.player;
import com.jcwhatever.nucleus.utils.ArrayUtils;
import com.jcwhatever.nucleus.utils.PreCon;
import org.bukkit.entity.Player;
import java.util.Arrays;
/**
* Static player exp utilities.
*/
public final class Exp {
private Exp() {}
private static final int MAX_LEVELS = 21863;
private static final int[] EXP_LEVEL_LOOKUP = new int[MAX_LEVELS / 4];
static {
for (int i=0; i < EXP_LEVEL_LOOKUP.length; i++) {
EXP_LEVEL_LOOKUP[i] = calculateExpInLevel(i);
}
}
/**
* Get a players total exp.
*
* @param player The player.
*/
public static double get(Player player) {
PreCon.notNull(player);
int level = player.getLevel();
float exp = player.getExp();
return get(level) + exp;
}
/**
* Set a players total experience.
*
* @param player The player.
* @param exp The experience.
*/
public static void set(Player player, double exp) {
PreCon.notNull(player);
PreCon.positiveNumber(exp);
double current = get(player);
int level = getLevel(exp);
double lvlExp = get(level);
player.setLevel(level);
if (exp > current) {
player.setTotalExperience((int) (player.getTotalExperience() + (exp - current)));
}
else {
// causes client update
player.setTotalExperience(player.getTotalExperience());
}
double capacity = getBarCapacity(level);
double expPercent = (exp - lvlExp) / capacity;
player.setExp((float) expPercent);
}
/**
* Increment a players exp by the specified amount.
*
* <p>Negative numbers are allowed.</p>
*
* @param player The player.
* @param exp The amount of experience to add or subtract.
*/
public static void increment(Player player, double exp) {
PreCon.notNull(player);
if (Double.compare(exp, 0.0D) == 0)
return;
double current = get(player);
current += exp;
set(player, Math.max(0.0D, current));
}
/**
* Get the total Exp required to reach the given level.
*
* @param level The exp level.
*/
public static double get(int level) {
PreCon.positiveNumber(level);
if (level >= EXP_LEVEL_LOOKUP.length)
return calculateExpInLevel(level);
return EXP_LEVEL_LOOKUP[level];
}
/**
* Get the exp level of the specified amount of experience.
*
* @param exp The experience.
*/
public static int getLevel(double exp) {
PreCon.positiveNumber(exp);
if (Double.compare(exp, 0.0D) == 0)
return 0;
if (exp > ArrayUtils.last(EXP_LEVEL_LOOKUP)) {
return calculateLevel((int)exp);
}
int insertion = Arrays.binarySearch(EXP_LEVEL_LOOKUP, (int) exp);
return insertion < 0 ? -(insertion) - 2 : insertion;
}
/**
* Set a players experience to the amount required for the
* specified level.
*
* @param player The player.
* @param level The level.
*/
public static void setLevel(Player player, int level) {
PreCon.notNull(player);
PreCon.positiveNumber(level);
double exp = get(level);
set(player, exp);
}
/**
* Increment a players level by the specified amount.
*
* <p>Negative numbers are allowed.</p>
*
* @param player The player.
* @param amount The amount to add or subtract.
*/
public static void incrementLevel(Player player, int amount) {
PreCon.notNull(player);
if (amount == 0)
return;
int level = player.getLevel();
double currLevelExp = get(level);
level += amount;
level = Math.max(0, level);
double newLevelExp = get(level);
double deltaExp = newLevelExp - currLevelExp;
increment(player, deltaExp);
}
/**
* Get the amount of Exp that can be held in the players experience
* bar at the given level.
*
* @param level The exp level.
*/
public static double getBarCapacity(int level) {
PreCon.positiveNumber(level);
// algorithm found at NMS: EntityHuman.getExpToLevel()
if (level >= 30) {
return 112 + (level - 30) * 9;
}
else if (level >= 15) {
return 37 + (level - 15) * 5;
}
else {
return 7 + level * 2;
}
}
private static int calculateExpInLevel(int level) {
int current = 0;
int exp = 0;
while (current < level) {
exp += getBarCapacity(current);
current++;
}
return exp;
}
private static int calculateLevel(int exp) {
int level = 0;
int current = getLevel(1);
while (current <= exp) {
level++;
current = getLevel(level);
}
return level;
}
}