// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . // ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· // ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ // ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ // ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ // 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 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 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 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)); } } }