generated from taggernation/TaggerNationLib
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
140 additions
and
0 deletions.
There are no files selected for viewing
140 changes: 140 additions & 0 deletions
140
src/main/java/in/arcadelabs/labaide/experience/ExperienceManager.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
/* | ||
* LabAide - Common utility library for our products. | ||
* Copyright (C) 2022 ArcadeLabs Production. | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
package in.arcadelabs.labaide.experience; | ||
|
||
import org.bukkit.entity.Player; | ||
|
||
/** | ||
* Good job Mojang with messed up XP maths that I don't understand. | ||
* Thanks to <a href="https://gist.github.com/Jikoo/30ec040443a4701b8980">Jikoo</a> for this utility class that made my life easier. | ||
*/ | ||
public class ExperienceManager { | ||
/** | ||
* Calculate a player's total experience based on level and progress to next. | ||
* | ||
* @param player the Player | ||
* @return the amount of experience the Player has | ||
* @see <a href=http://minecraft.gamepedia.com/Experience#Leveling_up>Experience#Leveling_up</a> | ||
*/ | ||
public static int getExp(final Player player) { | ||
return getExpFromLevel(player.getLevel()) | ||
+ Math.round(getExpToNext(player.getLevel()) * player.getExp()); | ||
} | ||
|
||
/** | ||
* Calculate total experience based on level. | ||
* | ||
* @param level the level | ||
* @return the total experience calculated | ||
* @see <a href=http://minecraft.gamepedia.com/Experience#Leveling_up>Experience#Leveling_up</a> | ||
*/ | ||
public static int getExpFromLevel(final int level) { | ||
if (level > 30) { | ||
return (int) (4.5 * level * level - 162.5 * level + 2220); | ||
} | ||
if (level > 15) { | ||
return (int) (2.5 * level * level - 40.5 * level + 360); | ||
} | ||
return level * level + 6 * level; | ||
} | ||
|
||
/** | ||
* Calculate level (including progress to next level) based on total experience. | ||
* | ||
* @param exp the total experience | ||
* @return the level calculated | ||
*/ | ||
public static double getLevelFromExp(final long exp) { | ||
int level = getIntLevelFromExp(exp); | ||
|
||
// Get remaining exp progressing towards next level. Cast to float for next bit of math. | ||
float remainder = exp - (float) getExpFromLevel(level); | ||
|
||
// Get level progress with float precision. | ||
float progress = remainder / getExpToNext(level); | ||
|
||
// Slap both numbers together and call it a day. While it shouldn't be possible for progress | ||
// to be an invalid value (value < 0 || 1 <= value) | ||
return ((double) level) + progress; | ||
} | ||
|
||
/** | ||
* Calculate level based on total experience. | ||
* | ||
* @param exp the total experience | ||
* @return the level calculated | ||
*/ | ||
public static int getIntLevelFromExp(final long exp) { | ||
if (exp > 1395) { | ||
return (int) ((Math.sqrt(72 * exp - 54215D) + 325) / 18); | ||
} | ||
if (exp > 315) { | ||
return (int) (Math.sqrt(40 * exp - 7839D) / 10 + 8.1); | ||
} | ||
if (exp > 0) { | ||
return (int) (Math.sqrt(exp + 9D) - 3); | ||
} | ||
return 0; | ||
} | ||
|
||
/** | ||
* Get the total amount of experience required to progress to the next level. | ||
* | ||
* @param level the current level | ||
* @see <a href=http://minecraft.gamepedia.com/Experience#Leveling_up>Experience#Leveling_up</a> | ||
*/ | ||
private static int getExpToNext(final int level) { | ||
if (level >= 30) { | ||
// Simplified formula. Internal: 112 + (level - 30) * 9 | ||
return level * 9 - 158; | ||
} | ||
if (level >= 15) { | ||
// Simplified formula. Internal: 37 + (level - 15) * 5 | ||
return level * 5 - 38; | ||
} | ||
// Internal: 7 + level * 2 | ||
return level * 2 + 7; | ||
} | ||
|
||
/** | ||
* Change a Player's experience. | ||
* | ||
* <p>This method is preferred over {@link Player#giveExp(int)}. | ||
* <br>In older versions the method does not take differences in exp per level into account. | ||
* This leads to overlevelling when granting players large amounts of experience. | ||
* <br>In modern versions, while differing amounts of experience per level are accounted for, the | ||
* approach used is loop-heavy and requires an excessive number of calculations, which makes it | ||
* quite slow. | ||
* | ||
* @param player the Player affected | ||
* @param exp the amount of experience to add or remove | ||
*/ | ||
public static void changeExp(final Player player, int exp) { | ||
exp += getExp(player); | ||
|
||
if (exp < 0) { | ||
exp = 0; | ||
} | ||
|
||
double levelAndExp = getLevelFromExp(exp); | ||
int level = (int) levelAndExp; | ||
player.setLevel(level); | ||
player.setExp((float) (levelAndExp - level)); | ||
} | ||
} |