You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
441 lines
11 KiB
441 lines
11 KiB
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . |
|
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· |
|
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ |
|
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ |
|
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ |
|
// Magicbane Emulator Project © 2013 - 2022 |
|
// www.magicbane.com |
|
|
|
|
|
package engine.objects; |
|
|
|
import engine.Enum.TargetColor; |
|
import engine.gameManager.ZoneManager; |
|
import engine.math.Vector3fImmutable; |
|
import engine.server.MBServerStatics; |
|
|
|
import java.util.ArrayList; |
|
import java.util.TreeMap; |
|
|
|
public class Experience { |
|
|
|
private static final TreeMap<Integer, Integer> ExpToLevel; |
|
private static final int[] LevelToExp = { Integer.MIN_VALUE, // Pad |
|
// everything |
|
// over 1 |
|
|
|
// R0 |
|
0, // Level 1 |
|
150, // Level 2 |
|
1200, // Level 3 |
|
4050, // Level 4 |
|
9600, // Level 5 |
|
18750, // Level 6 |
|
32400, // Level 7 |
|
51450, // Level 8 |
|
76800, // Level 9 |
|
|
|
// R1 |
|
109350, // Level 10 |
|
150000, // Level 11 |
|
199650, // Level 12 |
|
259200, // Level 13 |
|
329550, // Level 14 |
|
411600, // Level 15 |
|
506250, // Level 16 |
|
614400, // Level 17 |
|
736950, // Level 18 |
|
874800, // Level 19 |
|
|
|
// R2 |
|
1028850, // Level 20 |
|
1200000, // Level 21 |
|
1389150, // Level 22 |
|
1597200, // Level 23 |
|
1825050, // Level 24 |
|
2073600, // Level 25 |
|
2343750, // Level 26 |
|
2636400, // Level 27 |
|
2952450, // Level 28 |
|
3292800, // Level 29 |
|
|
|
// R3 |
|
3658350, // Level 30 |
|
4050000, // Level 31 |
|
4468650, // Level 32 |
|
4915200, // Level 33 |
|
5390550, // Level 34 |
|
5895600, // Level 35 |
|
6431250, // Level 36 |
|
6998400, // Level 37 |
|
7597950, // Level 38 |
|
8230800, // Level 39 |
|
|
|
// R4 |
|
8897850, // Level 40 |
|
10091520, // Level 41 |
|
11396777, // Level 42 |
|
12820187, // Level 43 |
|
14368505, // Level 44 |
|
16048666, // Level 45 |
|
17867790, // Level 46 |
|
19833183, // Level 47 |
|
21952335, // Level 48 |
|
24232919, // Level 49 |
|
|
|
// R5 |
|
26682793, // Level 50 |
|
29310000, // Level 51 |
|
32122766, // Level 52 |
|
35129502, // Level 53 |
|
38338805, // Level 54 |
|
41759452, // Level 55 |
|
45400409, // Level 56 |
|
49270824, // Level 57 |
|
53380030, // Level 58 |
|
57737542, // Level 59 |
|
|
|
// R6 |
|
62353064, // Level 60 |
|
67236479, // Level 61 |
|
72397859, // Level 62 |
|
77847457, // Level 63 |
|
83595712, // Level 64 |
|
89653247, // Level 65 |
|
96030869, // Level 66 |
|
102739569, // Level 67 |
|
109790524, // Level 68 |
|
117195093, // Level 69 |
|
|
|
// R7 |
|
124964822, // Level 70 |
|
133111438, // Level 71 |
|
141646855, // Level 72 |
|
150583171, // Level 73 |
|
159932666, // Level 74 |
|
169707808, // Level 75 |
|
179921247, // Level 76 |
|
|
|
}; |
|
|
|
private static final float[] MaxExpPerLevel = { Float.MIN_VALUE, // Pad |
|
// everything |
|
// over |
|
// 1 |
|
|
|
// R0 |
|
15, // Level 1 |
|
105, // Level 2 |
|
285, // Level 3 |
|
555, // Level 4 |
|
610, // Level 5 |
|
682.5f, // Level 6 |
|
730, // Level 7 |
|
975, // Level 8 |
|
1251.92f, // Level 9 |
|
|
|
// R1 |
|
1563.46f, // Level 10 |
|
1909.62f, // Level 11 |
|
2290.38f, // Level 12 |
|
2705.77f, // Level 13 |
|
3155.77f, // Level 14 |
|
3640.38f, // Level 15 |
|
4159.62f, // Level 16 |
|
4713.46f, // Level 17 |
|
5301.92f, // Level 18 |
|
5925, // Level 19 |
|
|
|
// R2 |
|
6582.69f, // Level 20 |
|
7275, // Level 21 |
|
8001.92f, // Level 22 |
|
8763.46f, // Level 23 |
|
9559.62f, // Level 24 |
|
10390.38f, // Level 25 |
|
11255.77f, // Level 26 |
|
12155.77f, // Level 27 |
|
13090.38f, // Level 28 |
|
14059.62f, // Level 29 |
|
|
|
// R3 |
|
15063.46f, // Level 30 |
|
16101.92f, // Level 31 |
|
17175, // Level 32 |
|
18282.69f, // Level 33 |
|
19425, // Level 34 |
|
20601.92f, // Level 35 |
|
21813.46f, // Level 36 |
|
23059.62f, // Level 37 |
|
24340.38f, // Level 38 |
|
25655.77f, // Level 39 |
|
|
|
// R4 |
|
45910.38f, // Level 40 |
|
34348.87f, // Level 41 |
|
37458.16f, // Level 42 |
|
40745.21f, // Level 43 |
|
44214.76f, // Level 44 |
|
47871.68f, // Level 45 |
|
51720.87f, // Level 46 |
|
55767.16f, // Level 47 |
|
60015.37f, // Level 48 |
|
64470.37f, // Level 49 |
|
|
|
// R5 |
|
69137.03f, // Level 50 |
|
74020.16f, // Level 51 |
|
79124.63f, // Level 52 |
|
84455.34f, // Level 53 |
|
90017.03f, // Level 54 |
|
95814.66f, // Level 55 |
|
101853.03f, // Level 56 |
|
108137, // Level 57 |
|
114671.37f, // Level 58 |
|
121461.11f, // Level 59 |
|
|
|
// R6 |
|
128510.92f, // Level 60 |
|
135825.79f, // Level 61 |
|
143410.47f, // Level 62 |
|
151269.87f, // Level 63 |
|
159408.82f, // Level 64 |
|
167832.16f, // Level 65 |
|
176544.74f, // Level 66 |
|
185551.45f, // Level 67 |
|
194857.08f, // Level 68 |
|
204466.55f, // Level 69 |
|
|
|
// R7 |
|
214384.63f, // Level 70 |
|
224616.24f, // Level 71 |
|
235166.21f, // Level 72 |
|
246039.34f, // Level 73 |
|
257240.58f, // Level 74 |
|
1 // 268774.71 //Level 75 |
|
|
|
}; |
|
|
|
static { |
|
ExpToLevel = new TreeMap<>(); |
|
|
|
// flip keys and values for other Map |
|
for (int i = 1; i < LevelToExp.length; i++) { |
|
ExpToLevel.put(LevelToExp[i], i); |
|
} |
|
} // end Static block |
|
|
|
// Used to calcuate the amount of experience a monster grants in the |
|
// following formula |
|
// expGranted = a(moblevel)^2 + b(moblevel) + c |
|
private static final float EXPQUADA = 10.0f; |
|
private static final float EXPQUADB = 6.0f; |
|
private static final float EXPQUADC = -10.0f; |
|
|
|
// Adds addtional exp per addtional member of a group using the following |
|
// (expGranted / group.size()) * (groupBonus * (group.size()-1) +1) |
|
private static final float GROUP_BONUS = 0.5f; // 0.2 grants (20%) addtional |
|
// exp per group member |
|
|
|
// called to determine current level based on xp |
|
public static int getLevel(int experience) { |
|
int expKey = ExpToLevel.floorKey(experience); |
|
int level = ExpToLevel.get(expKey); |
|
if (level > MBServerStatics.LEVELCAP) { |
|
level = MBServerStatics.LEVELCAP; |
|
} |
|
return level; |
|
} |
|
|
|
// Get the base xp for a level |
|
public static int getBaseExperience(int level) { |
|
if (level < LevelToExp.length) { |
|
return LevelToExp[level]; |
|
} |
|
|
|
int fLevel = level - 1; |
|
int baseXP = fLevel * fLevel * fLevel; |
|
return (int) ((fLevel < 40) ? (baseXP * 150) |
|
: (baseXP * (150 + (7.6799998 * (level - 40))))); |
|
} |
|
|
|
// Get XP needed for the next level |
|
public static int getExpForNextLevel(int experience, int currentLevel) { |
|
return (getBaseExperience(currentLevel + 1) - experience); |
|
} |
|
|
|
// Max XP granted for killing a blue, yellow or orange mob |
|
public static float maxXPPerKill(int level) { |
|
if (level < 1) |
|
level = 1; |
|
if (level > 75) |
|
level = 75; |
|
return MaxExpPerLevel[level]; |
|
// return (LevelToExp[level + 1] - LevelToExp[level])/(11 + level/2); |
|
// return ((((level * level)-level)*50)+16); |
|
} |
|
|
|
// Returns a penalty modifier depending on mob color |
|
public static double getConMod(AbstractCharacter pc, AbstractCharacter mob) { |
|
switch (TargetColor.getCon(pc, mob)) { |
|
case Red: |
|
return 1.25; |
|
case Orange: |
|
return 1.15; |
|
case Yellow: |
|
return 1.05; |
|
case Blue: |
|
return 1; |
|
case Cyan: |
|
return 0.8; |
|
case Green: |
|
return 0.5; |
|
default: |
|
return 0; |
|
} |
|
} |
|
|
|
public static double getGroupMemberPenalty(double leadership, |
|
PlayerCharacter currPlayer, ArrayList<PlayerCharacter> players, |
|
int highestLevel) { |
|
|
|
double penalty = 0.0; |
|
int adjustedGroupSize = 0; |
|
int totalLevels = 0; |
|
int level = currPlayer.getLevel(); |
|
|
|
// Group Size Penalty |
|
if (players.size() > 2) |
|
penalty = (players.size() - 2) * 1.5; |
|
|
|
// Calculate Penalty For Highest level -> Current Player difference, != |
|
// check to prevent divide by zero error |
|
if (highestLevel != level) |
|
penalty += ((highestLevel - level) * .5); |
|
|
|
// double avgLevels = totalLevels / adjustedGroupSize; |
|
// if (adjustedGroupSize >= 1) |
|
// if (level < avgLevels) |
|
// penalty += ((avgLevels - level) * .5); |
|
|
|
// Extra noob penalty |
|
if ((highestLevel - level) > 25) |
|
penalty += (highestLevel - level - 25); |
|
|
|
return penalty; |
|
} |
|
|
|
public static void doExperience(PlayerCharacter killer, AbstractCharacter mob, Group g) { |
|
// Check for some failure conditions |
|
if (killer == null || mob == null) |
|
return; |
|
|
|
double xp = 0.0; |
|
|
|
//Get the xp modifier for the world |
|
float xpMod = MBServerStatics.EXP_RATE_MOD; |
|
|
|
|
|
|
|
if (g != null) { // Do group EXP stuff |
|
|
|
int leadership = 0; |
|
int highestLevel = 0; |
|
double penalty = 0.0; |
|
|
|
ArrayList<PlayerCharacter> giveEXPTo = new ArrayList<>(); |
|
|
|
// Check if leader is within range of kill and then get leadership |
|
// skill |
|
Vector3fImmutable killLoc = mob.getLoc(); |
|
if (killLoc.distanceSquared2D(g.getGroupLead().getLoc()) < (MBServerStatics.EXP_RANGE * MBServerStatics.EXP_RANGE)) { |
|
CharacterSkill leaderskill = g.getGroupLead().skills |
|
.get("Leadership"); |
|
if (leaderskill != null) |
|
leadership = leaderskill.getNumTrains(); |
|
if (leadership > 90) |
|
leadership = 90; // leadership caps at 90% |
|
} |
|
|
|
// Check every group member for distance to see if they get xp |
|
for (PlayerCharacter pc : g.getMembers()) { |
|
if (pc.isAlive()) { // Skip if the player is dead. |
|
|
|
// Check within range |
|
if (killLoc.distanceSquared2D(pc.getLoc()) < (MBServerStatics.EXP_RANGE * MBServerStatics.EXP_RANGE)) { |
|
giveEXPTo.add(pc); |
|
// Track highest level character |
|
if (pc.getLevel() > highestLevel) |
|
highestLevel = pc.getLevel(); |
|
} |
|
} |
|
} |
|
|
|
// Process every player in the group getting XP |
|
for (PlayerCharacter pc : giveEXPTo) { |
|
if (pc.getLevel() >= MBServerStatics.LEVELCAP) |
|
continue; |
|
|
|
// Sets Max XP with server exp mod taken into account. |
|
xp = (double) xpMod * maxXPPerKill(pc.getLevel()); |
|
|
|
// Adjust XP for Mob Level |
|
xp *= getConMod(pc, mob); |
|
|
|
// Process XP for this member |
|
penalty = getGroupMemberPenalty(leadership, pc, giveEXPTo, |
|
highestLevel); |
|
|
|
// Leadership Penalty Reduction |
|
if (leadership > 0) |
|
penalty -= ((leadership) * 0.01) * penalty; |
|
|
|
// Modify for hotzone |
|
if (xp != 0) |
|
if (ZoneManager.inHotZone(mob.getLoc())) |
|
xp *= MBServerStatics.HOT_EXP_RATE_MOD; |
|
|
|
// Check for 0 XP due to white mob, otherwise subtract penalty |
|
// xp |
|
if (xp == 0) { |
|
xp = 1; |
|
} else { |
|
xp -= (penalty * 0.01) * xp; |
|
|
|
// Errant Penalty Calculation |
|
if (pc.getGuild().isErrant()) |
|
xp *= 0.6; |
|
} |
|
|
|
if (xp == 0) |
|
xp = 1; |
|
|
|
// Grant the player the EXP |
|
pc.grantXP((int) Math.floor(xp)); |
|
} |
|
|
|
} else { // Give EXP to a single character |
|
if (!killer.isAlive()) // Skip if the player is dead. |
|
return; |
|
|
|
if (killer.getLevel() >= MBServerStatics.LEVELCAP) |
|
return; |
|
|
|
// Get XP and adjust for Mob Level with world xp modifier taken into account |
|
xp = (double) xpMod * maxXPPerKill(killer.getLevel()); |
|
xp *= getConMod(killer, mob); |
|
|
|
// Modify for hotzone |
|
if (ZoneManager.inHotZone(mob.getLoc())) |
|
xp *= MBServerStatics.HOT_EXP_RATE_MOD; |
|
|
|
// Errant penalty |
|
if (xp != 1) |
|
if (killer.getGuild().isErrant()) |
|
xp *= .6; |
|
|
|
// Grant XP |
|
killer.grantXP((int) Math.floor(xp)); |
|
} |
|
} |
|
}
|
|
|