Public Repository for the Magicbane Shadowbane Emulator
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.

442 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().isEmptyGuild())
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().isEmptyGuild())
xp *= .6;
// Grant XP
killer.grantXP((int) Math.floor(xp));
}
}
}