package net.aufdemrand.denizen.scripts.commands.player;
import net.aufdemrand.denizen.BukkitScriptEntryData;
import net.aufdemrand.denizen.utilities.debugging.dB;
import net.aufdemrand.denizencore.exceptions.CommandExecutionException;
import net.aufdemrand.denizencore.exceptions.InvalidArgumentsException;
import net.aufdemrand.denizencore.objects.aH;
import net.aufdemrand.denizencore.scripts.ScriptEntry;
import net.aufdemrand.denizencore.scripts.commands.AbstractCommand;
import org.bukkit.entity.Player;
public class ExperienceCommand extends AbstractCommand {
private enum Type {SET, GIVE, TAKE}
/**
* with help from author: alkarin
* https://github.com/alkarinv/BattleArena/blob/master/src/mc/alk/arena/util/ExpUtil.java
*/
public static int getTotalExperience(Player p) {
return getTotalExperience(p.getLevel(), p.getExp());
}
public static int getTotalExperience(int level, double bar) {
return getTotalExpToLevel(level) + (int) (getExpToLevel(level + 1) * bar + 0.5);
}
public static int getExpToLevel(int level) {
if (level < 16) {
return 17;
}
else if (level < 31) {
return 3 * level - 31;
}
else {
return 7 * level - 155;
}
}
public static int getTotalExpToLevel(int level) {
if (level < 16) {
return 17 * level;
}
else if (level < 31) {
return (int) (1.5 * level * level - 29.5 * level + 360);
}
else {
return (int) (3.5 * level * level - 151.5 * level + 2220);
}
}
public static void resetExperience(Player player) {
player.setTotalExperience(0);
player.setLevel(0);
player.setExp(0);
}
public static void setTotalExperience(Player player, int exp) {
resetExperience(player);
if (exp > 0) {
player.giveExp(exp);
}
}
public static void setLevel(Player player, int level) {
resetExperience(player);
if (level > 0) {
player.giveExp(getExpToLevel(level));
}
}
public static void giveExperience(Player player, int exp) {
final int currentExp = getTotalExperience(player);
resetExperience(player);
final int newexp = currentExp + exp;
if (newexp > 0) {
player.giveExp(newexp);
}
}
/* Tail recursive way to count the level for the given exp, maybe better with iteration */
public static int countLevel(int exp, int toLevel, int level) {
if (exp < toLevel) {
return level;
}
else {
return countLevel(exp - toLevel, getTotalExpToLevel(level + 2) - getTotalExpToLevel(level + 1), ++level);
}
}
/* Setting the new level and exp using the setExp and setLevel methods, should be soundless (not yet tested) */
public static void setSilentTotalExperience(Player player, int exp) {
resetExperience(player);
if (exp > 0) {
final int level = countLevel(exp, 17, 0);
player.setLevel(level);
final int expToLvl = exp - getTotalExpToLevel(level);
player.setExp(expToLvl < 0 ? 0 : expToLvl / getExpToLevel(level + 1));
}
}
/* Adding experience using the setExp and setLevel methods, should be soundless (not tested) */
public static void giveSilentExperience(Player player, int exp) {
final int currentExp = getTotalExperience(player);
resetExperience(player);
final int newexp = currentExp + exp;
if (newexp > 0) {
final int level = countLevel(newexp, 17, 0);
player.setLevel(level);
final int expToLvl = newexp - getTotalExpToLevel(level);
player.setExp(expToLvl < 0 ? 0.0f : (float) expToLvl / (float) getExpToLevel(level + 1));
}
}
@Override
public void parseArgs(ScriptEntry scriptEntry) throws InvalidArgumentsException {
// TODO: UPDATE COMMAND PARSING
int amount = 0;
Type type = Type.SET;
boolean level = false;
boolean silent = false;
for (aH.Argument arg : aH.interpret(scriptEntry.getArguments())) {
if (arg.matchesPrimitive(aH.PrimitiveType.Integer)) {
amount = arg.asElement().asInt();
}
else if (arg.matches("SET", "GIVE", "TAKE")) {
type = Type.valueOf(arg.asElement().asString().toUpperCase());
}
else if (arg.matches("LEVEL")) {
level = true;
}
else if (arg.matches("SILENT")) {
silent = true;
}
else {
arg.reportUnhandled();
}
}
scriptEntry.addObject("quantity", amount)
.addObject("type", type)
.addObject("level", level)
.addObject("silent", silent);
}
@Override
public void execute(ScriptEntry scriptEntry) throws CommandExecutionException {
Type type = (Type) scriptEntry.getObject("type");
Integer quantity = (Integer) scriptEntry.getObject("quantity");
Boolean level = (Boolean) scriptEntry.getObject("level");
Boolean silent = (Boolean) scriptEntry.getObject("silent");
dB.report(scriptEntry, name, aH.debugObj("Type", type.toString())
+ aH.debugObj("Quantity", level ? quantity.toString() + " levels" : quantity.toString())
+ aH.debugObj("Player", ((BukkitScriptEntryData) scriptEntry.entryData).getPlayer().getName()));
Player player = ((BukkitScriptEntryData) scriptEntry.entryData).getPlayer().getPlayerEntity();
switch (type) {
case SET:
if (level) {
((BukkitScriptEntryData) scriptEntry.entryData).getPlayer().setLevel(quantity);
}
else if (!silent) {
player.setExp(quantity);
//setTotalExperience(player, quantity);
}
else {
setSilentTotalExperience(player, quantity);
}
break;
case GIVE:
if (level) {
player.setLevel(player.getLevel() + quantity);
}
else if (!silent) {
//giveExperience(player, quantity);
player.giveExp(quantity);
}
else {
giveSilentExperience(player, quantity);
}
break;
case TAKE:
if (level) {
int value = ((BukkitScriptEntryData) scriptEntry.entryData).getPlayer().getPlayerEntity().getLevel() - quantity;
((BukkitScriptEntryData) scriptEntry.entryData).getPlayer().setLevel(value <= 0 ? 0 : value);
}
else if (!silent) {
player.setExp(player.getExp() - quantity);
//giveExperience(player, -quantity);
}
else {
giveSilentExperience(player, -quantity);
}
break;
}
}
}