mangos/src/game/Object/Formulas.h

355 lines
12 KiB
C++

/**
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2019 MaNGOS project <https://getmangos.eu>
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
#ifndef MANGOS_FORMULAS_H
#define MANGOS_FORMULAS_H
#include "World.h"
struct HonorScores
{
float FX[15];
float FY[15];
float BRK[14];
};
namespace MaNGOS
{
namespace Honor
{
inline float hk_honor_at_level(uint32 level, uint32 count = 1)
{
return (float)ceil(count * (-0.53177f + 0.59357f * exp((level + 23.54042f) / 26.07859f)));
}
// set passed rank info to default
inline void InitRankInfo(HonorRankInfo& prk)
{
prk.positive = true;
prk.rank = 0;
prk.visualRank = 0;
prk.maxRP = 2000.00f;
prk.minRP = 0.00f;
}
inline HonorRankInfo CalculateRankInfo(HonorRankInfo prk)
{
if (prk.rank != 0)
{
int8 rank = prk.positive ? prk.rank - NEGATIVE_HONOR_RANK_COUNT - 1 : prk.rank - NEGATIVE_HONOR_RANK_COUNT;
prk.maxRP = (rank) * 5000.00f;
if (prk.maxRP < 0) // in negative rank case
{ prk.maxRP *= -1; }
prk.minRP = prk.maxRP > 5000.0f ? prk.maxRP - 5000.00f : 2000.00f;
prk.visualRank = prk.rank > NEGATIVE_HONOR_RANK_COUNT ? prk.rank - NEGATIVE_HONOR_RANK_COUNT : prk.rank * -1;
}
else
{ InitRankInfo(prk); }
return prk;
}
// What is Player's rank... private, scout...
inline HonorRankInfo CalculateHonorRank(float honor_points)
{
HonorRankInfo prk;
InitRankInfo(prk);
// rank none
if (honor_points == 0)
{ return prk; }
prk.positive = honor_points > 0;
if (!prk.positive)
{ honor_points *= -1; }
uint8 rCount = prk.positive ? POSITIVE_HONOR_RANK_COUNT - 2 : NEGATIVE_HONOR_RANK_COUNT;
uint8 firstRank = prk.positive ? NEGATIVE_HONOR_RANK_COUNT + 1 : 1;
if (honor_points < 2000.00f)
{ prk.rank = prk.positive ? firstRank : NEGATIVE_HONOR_RANK_COUNT; }
else
{
if (honor_points > (rCount - 1) * 5000.00f)
{ prk.rank = prk.positive ? HONOR_RANK_COUNT - 1 : firstRank; }
else
{
prk.rank = uint32(honor_points / 5000.00f) + firstRank;
prk.rank = (prk.positive ? prk.rank + 1 : NEGATIVE_HONOR_RANK_COUNT - prk.rank);
}
}
prk = CalculateRankInfo(prk);
return prk;
}
inline HonorScores GenerateScores(HonorStandingList standingList, uint32 team)
{
HonorScores sc;
// initialize the breakpoint values
sc.BRK[13] = 0.002f;
sc.BRK[12] = 0.007f;
sc.BRK[11] = 0.017f;
sc.BRK[10] = 0.037f;
sc.BRK[ 9] = 0.077f;
sc.BRK[ 8] = 0.137f;
sc.BRK[ 7] = 0.207f;
sc.BRK[ 6] = 0.287f;
sc.BRK[ 5] = 0.377f;
sc.BRK[ 4] = 0.477f;
sc.BRK[ 3] = 0.587f;
sc.BRK[ 2] = 0.715f;
sc.BRK[ 1] = 0.858f;
sc.BRK[ 0] = 1.000f;
// get the WS scores at the top of each break point
for (uint8 group = 0; group < 14; group++)
{ sc.BRK[group] = floor((sc.BRK[group] * standingList.size()) + 0.5f); }
// initialize RP array
// set the low point
sc.FY[ 0] = 0;
// the Y values for each breakpoint are fixed
sc.FY[ 1] = 400;
for (uint8 i = 2; i <= 13; i++)
{
sc.FY[i] = (i - 1) * 1000;
}
// and finally
sc.FY[14] = 13000; // ... gets 13000 RP
// the X values for each breakpoint are found from the CP scores
// of the players around that point in the WS scores
HonorStanding* tempSt;
float honor;
// initialize CP array
sc.FX[ 0] = 0;
for (uint8 i = 1; i <= 13; i++)
{
honor = 0.0f;
tempSt = sObjectMgr.GetHonorStandingByPosition(sc.BRK[i], team);
if (tempSt)
{
honor += tempSt->honorPoints;
tempSt = sObjectMgr.GetHonorStandingByPosition(sc.BRK[i] + 1, team);
if (tempSt)
{ honor += tempSt->honorPoints; }
}
sc.FX[i] = honor ? honor / 2 : 0;
}
// set the high point if FX full filled before
sc.FX[14] = sc.FX[13] ? standingList.begin()->honorPoints : 0; // top scorer
return sc;
}
inline float CalculateRpEarning(float CP, HonorScores sc)
{
// search the function for the two points that bound the given CP
uint8 i = 0;
while (i < 14 && sc.BRK[i] > 0 && sc.FX[i] <= CP)
{ i++; }
// we now have i such that FX[i] > CP >= FX[i-1]
// so interpolate
return (sc.FY[i] - sc.FY[i - 1]) * (CP - sc.FX[i - 1]) / (sc.FX[i] - sc.FX[i - 1]) + sc.FY[i - 1];
}
inline float CalculateRpDecay(float rpEarning, float RP)
{
float Decay = floor((0.2f * RP) + 0.5f);
float Delta = rpEarning - Decay;
if (Delta < 0)
{
Delta = Delta / 2;
}
if (Delta < -2500)
{
Delta = -2500;
}
return RP + Delta;
}
inline float DishonorableKillPoints(int level)
{
float result = 10;
if (level >= 30 && level <= 35)
{ result = result + 1.5 * (level - 29); }
if (level >= 36 && level <= 41)
{ result = result + 9 + 2 * (level - 35); }
if (level >= 42 && level <= 50)
{ result = result + 21 + 3.2 * (level - 41); }
if (level >= 51)
{ result = result + 50 + 4 * (level - 50); }
if (result > 100)
{ return 100.0; }
else
{ return result; }
}
inline float HonorableKillPoints(Player* killer, Player* victim, uint32 groupsize)
{
if (!killer || !victim || !groupsize)
{ return 0.0; }
uint32 today = sWorld.GetDateToday();
int total_kills = killer->CalculateTotalKills(victim, today, today);
// int k_rank = killer->CalculateHonorRank( killer->GetTotalHonor() );
uint32 v_rank = victim->GetHonorRankInfo().visualRank;
uint32 k_level = killer->getLevel();
// int v_level = victim->getLevel();
float diff_honor = (victim->GetRankPoints() / (killer->GetRankPoints() + 1)) + 1;
float diff_level = (victim->getLevel() * (1.0 / (killer->getLevel())));
int f = (10 - total_kills) >= 0 ? (10 - total_kills) : 0;
int honor_points = int(((float)(f * 0.25) * (float)((k_level + (v_rank * 5 + 1)) * (1 + 0.05 * diff_honor) * diff_level)));
return (honor_points <= 400 ? honor_points : 400) / groupsize;
}
}
namespace XP
{
enum XPColorChar { RED, ORANGE, YELLOW, GREEN, GRAY };
inline uint32 GetGrayLevel(uint32 pl_level)
{
if (pl_level <= 5)
{ return 0; }
else if (pl_level <= 39)
{ return pl_level - 5 - pl_level / 10; }
else if (pl_level == 60)
{ return 51; }
else
{ return pl_level - 1 - pl_level / 5; }
}
inline XPColorChar GetColorCode(uint32 pl_level, uint32 mob_level)
{
if (mob_level >= pl_level + 5)
{ return RED; }
else if (mob_level >= pl_level + 3)
{ return ORANGE; }
else if (mob_level >= pl_level - 2)
{ return YELLOW; }
else if (mob_level > GetGrayLevel(pl_level))
{ return GREEN; }
else
{ return GRAY; }
}
inline uint32 GetZeroDifference(uint32 pl_level)
{
if (pl_level < 8) { return 5; }
if (pl_level < 10) { return 6; }
if (pl_level < 12) { return 7; }
if (pl_level < 16) { return 8; }
if (pl_level < 20) { return 9; }
if (pl_level < 30) { return 11; }
if (pl_level < 40) { return 12; }
if (pl_level < 45) { return 13; }
if (pl_level < 50) { return 14; }
if (pl_level < 55) { return 15; }
if (pl_level < 60) { return 16; }
return 17;
}
inline uint32 BaseGain(uint32 pl_level, uint32 mob_level)
{
const uint32 nBaseExp = 45;
if (mob_level >= pl_level)
{
uint32 nLevelDiff = mob_level - pl_level;
if (nLevelDiff > 4)
{ nLevelDiff = 4; }
return ((pl_level * 5 + nBaseExp) * (20 + nLevelDiff) / 10 + 1) / 2;
}
else
{
uint32 gray_level = GetGrayLevel(pl_level);
if (mob_level > gray_level)
{
uint32 ZD = GetZeroDifference(pl_level);
return (pl_level * 5 + nBaseExp) * (ZD + mob_level - pl_level) / ZD;
}
return 0;
}
}
inline uint32 Gain(Player* pl, Unit* u)
{
if (u->GetTypeId() == TYPEID_UNIT && (
((Creature*)u)->IsTotem() || ((Creature*)u)->IsPet() ||
(((Creature*)u)->GetCreatureInfo()->ExtraFlags & CREATURE_EXTRA_FLAG_NO_XP_AT_KILL)))
{ return 0; }
uint32 xp_gain = BaseGain(pl->getLevel(), u->getLevel());
if (xp_gain == 0)
{ return 0; }
if (u->GetTypeId() == TYPEID_UNIT && ((Creature*)u)->IsElite())
{ xp_gain *= 2; }
return (uint32)(xp_gain * sWorld.getConfig(CONFIG_FLOAT_RATE_XP_KILL));
}
inline float xp_in_group_rate(uint32 count, bool isRaid)
{
if (isRaid)
{
// FIX ME: must apply decrease modifiers dependent from raid size
return 1.0f;
}
else
{
switch (count)
{
case 0:
case 1:
case 2:
return 1.0f;
case 3:
return 1.166f;
case 4:
return 1.3f;
case 5:
default:
return 1.4f;
}
}
}
}
}
#endif