Files
moonwell-core-vanilla/src/game/HonorMgr.cpp
T
shenna 1ae47c5b37 Make HCR logs folder configurable. (#385)
* Since honor logs are usually kept for a long time, it's better to have separate folder for that.
2020-02-27 22:47:34 +02:00

993 lines
30 KiB
C++

/*
* Copyright (C) 2016 Elysium Project <https://elysium-project.org>
*/
#include "Formulas.h"
#include "HonorMgr.h"
#include "Language.h"
#include "World.h"
#include "Creature.h"
#include "Player.h"
#include "Database/DatabaseEnv.h"
#include "Policies/SingletonImp.h"
#include "ObjectAccessor.h"
#include <fstream>
INSTANTIATE_SINGLETON_1(HonorMaintenancer);
HonorStandingList& HonorMaintenancer::GetStandingListByTeam(Team team)
{
switch (team)
{
case ALLIANCE:
return m_allianceStandingList;
case HORDE:
return m_hordeStandingList;
default:
return m_allianceStandingList;
}
}
float HonorMaintenancer::GetStandingCPByPosition(HonorStandingList& standingList, uint32 position)
{
uint32 pos = 1;
for (auto& standing : standingList)
{
if (pos == position)
return standing.cp;
pos++;
}
return 0.0f;
}
uint32 HonorMaintenancer::GetStandingPositionByGUID(uint32 guid, Team team)
{
uint32 pos = 1;
auto& standingList = GetStandingListByTeam(team);
for (auto& standing : standingList)
{
if (standing.guid == guid)
return pos;
pos++;
}
return 0;
}
void HonorMaintenancer::LoadWeeklyScores()
{
uint32 weekBeginDay = GetWeekBeginDay();
uint32 weekEndDay = GetWeekEndDay();
std::ostringstream query;
query << "SELECT `scores`.`guid`, `c`.`level`, `c`.`account`, `c`.`honorRankPoints`, `c`.`honorHighestRank`, SUM(`hk`), SUM(`dk`), SUM(`cp`) FROM"
"("
" SELECT `guid` AS `guid`, COUNT(*) AS `hk`, 0 AS `dk`, SUM(`cp`) AS `cp` FROM `character_honor_cp` WHERE `type` = " << HONORABLE <<
" AND (`date` BETWEEN " << weekBeginDay << " AND " << weekEndDay << ") GROUP BY `guid`"
" UNION"
" SELECT `guid` AS `guid`, 0 AS `hk`, COUNT(*) AS `dk`, 0 AS `cp` FROM `character_honor_cp` WHERE `type` = " << DISHONORABLE <<
" AND (`date` BETWEEN " << weekBeginDay << " AND " << weekEndDay << ") GROUP BY `guid`"
" UNION"
" SELECT `guid` AS `guid`, 0 AS `hk`, 0 AS `dk`, SUM(`cp`) AS `cp` FROM `character_honor_cp` WHERE `type` NOT IN (" << HONORABLE << ", " << DISHONORABLE << ")"
" AND (`date` BETWEEN " << weekBeginDay << " AND " << weekEndDay << ") GROUP BY `guid`"
" UNION"
" SELECT `guid` AS `guid`, 0 AS `hk`, 0 AS `dk`, 0 AS `cp` FROM `characters` WHERE `honorRankPoints` > 0"
") AS `scores` INNER JOIN `characters` AS `c` ON `scores`.`guid` = `c`.`guid` GROUP BY `guid` ORDER BY `guid` ";
QueryResult* result = CharacterDatabase.Query(query.str().c_str());
if (result)
{
do
{
Field* fields = result->Fetch();
WeeklyScore score;
score.level = fields[1].GetUInt32();
score.account = fields[2].GetUInt32();
score.oldRp = fields[3].GetFloat();
score.highestRank = fields[4].GetUInt32();
score.hk = fields[5].GetUInt32();
score.dk = fields[6].GetUInt32();
score.cp = fields[7].GetFloat();
m_weeklyScores[fields[0].GetUInt32()] = score;
}
while (result->NextRow());
delete result;
}
}
void HonorMaintenancer::LoadStandingLists()
{
uint8 minHK = sWorld.getConfig(CONFIG_UINT32_MIN_HONOR_KILLS);
for (auto& pair : m_weeklyScores)
{
auto weeklyScore = pair.second;
HonorStanding standing;
standing.guid = pair.first;
standing.cp = weeklyScore.cp;
if (weeklyScore.hk < minHK || !weeklyScore.account)
{
m_inactiveStandingList.push_back(standing);
continue;
}
auto team = sObjectMgr.GetPlayerTeamByGUID(ObjectGuid(HIGHGUID_PLAYER, pair.first));
if (team == ALLIANCE)
m_allianceStandingList.push_back(standing);
else
m_hordeStandingList.push_back(standing);
}
// Make sure all things are sorted
std::sort(m_allianceStandingList.begin(), m_allianceStandingList.end());
std::sort(m_hordeStandingList.begin(), m_hordeStandingList.end());
sLog.outHonor("[MAINTENANCE] Alliance: %u, Horde: %u, Inactive: %u",
m_allianceStandingList.size(), m_hordeStandingList.size(), m_inactiveStandingList.size());
}
void HonorMaintenancer::DistributeRankPoints(Team team)
{
HonorStandingList& standingList = GetStandingListByTeam(team);
if (standingList.empty())
return;
HonorScores scores = GenerateScores(standingList);
uint32 position = 1;
for (auto& standing : standingList)
{
auto itrWS = m_weeklyScores.find(standing.guid);
if (itrWS == m_weeklyScores.end())
continue;
auto& weeklyScore = itrWS->second;
// Calculate rank points earning
weeklyScore.earning = CalculateRpEarning(weeklyScore.cp, scores);
// Calculate rank points with decay
weeklyScore.newRp = CalculateRpDecay(weeklyScore.earning, weeklyScore.oldRp);
// Level restrictions
weeklyScore.newRp = std::min(MaximumRpAtLevel(weeklyScore.level), weeklyScore.newRp);
// Set standing to score
weeklyScore.standing = position;
++position;
}
}
void HonorMaintenancer::InactiveDecayRankPoints()
{
for (auto& standing : m_inactiveStandingList)
{
auto itrWS = m_weeklyScores.find(standing.guid);
if (itrWS == m_weeklyScores.end())
continue;
auto& weeklyScore = itrWS->second;
weeklyScore.newRp = finiteAlways(CalculateRpDecay(0, weeklyScore.oldRp));
}
}
void HonorMaintenancer::FlushRankPoints()
{
// Imediatly reset honor standing before flushing
CharacterDatabase.Execute("UPDATE `characters` SET `honorStanding` = 0 WHERE `honorStanding` > 0");
for (auto& pair : m_weeklyScores)
{
auto weeklyScore = pair.second;
HonorRankInfo currentRank = HonorMgr::CalculateRank(weeklyScore.newRp);
HonorRankInfo highestRank;
HonorMgr::InitRankInfo(highestRank);
highestRank.rank = weeklyScore.highestRank;
HonorMgr::CalculateRankInfo(highestRank);
if (currentRank.visualRank > 0 && (currentRank.visualRank > highestRank.visualRank))
highestRank = currentRank;
CharacterDatabase.PExecute("UPDATE `characters` SET `honorHighestRank` = %u, `honorRankPoints` = %.1f, `honorStanding` = %u, "
"`honorLastWeekHK` = %u, `honorStoredHK` = (`honorStoredHK` + %u), `honorStoredDK` = (`honorStoredDK` + %u), `honorLastWeekCP` = %.1f WHERE `guid` = %u",
highestRank.rank,
finiteAlways(weeklyScore.newRp), weeklyScore.standing,
weeklyScore.hk, weeklyScore.hk, weeklyScore.dk,
finiteAlways(weeklyScore.cp), pair.first);
}
// Not includes weekend day, for correct view in honor tab for group "Yesterday"
CharacterDatabase.PExecute("DELETE FROM `character_honor_cp` WHERE `date` < %u", GetWeekEndDay());
}
void HonorMaintenancer::DoMaintenance()
{
if (!m_markerToStart)
return;
sLog.outHonor("[MAINTENANCE] Honor maintenance starting.");
sLog.outHonor("[MAINTENANCE] Load weekly players scores.");
LoadWeeklyScores();
sLog.outHonor("[MAINTENANCE] Load standing lists.");
LoadStandingLists();
sLog.outHonor("[MAINTENANCE] Distribute rank points for Alliance.");
DistributeRankPoints(ALLIANCE);
sLog.outHonor("[MAINTENANCE] Distribute rank points for Horde.");
DistributeRankPoints(HORDE);
sLog.outHonor("[MAINTENANCE] Decay rank points for inactive players.");
InactiveDecayRankPoints();
sLog.outHonor("[MAINTENANCE] Flush rank points.");
FlushRankPoints();
CreateCalculationReport();
sLog.outHonor("[MAINTENANCE] Honor maintenance finished.");
ToggleMaintenanceMarker();
SetMaintenanceDays(GetNextMaintenanceDay());
}
void HonorMaintenancer::CreateCalculationReport()
{
std::string timestamp = Log::GetTimestampStr();
std::string filename = "HCR_" + timestamp + ".txt";
std::string path = sWorld.GetHonorPath() + filename;
std::ofstream ofs;
ofs.open(path.c_str());
if (!ofs.is_open())
{
sLog.outError("Can't create HCR file!");
return;
}
if (!m_allianceStandingList.empty())
{
HonorScores scores = GenerateScores(m_allianceStandingList);
ofs << "Alliance Honor Scores" << std::endl << std::endl;
ofs << "Standing size: " << m_allianceStandingList.size() << std::endl << std::endl;
ofs << std::flush;
for (auto i = 0; i < 14; ++i)
ofs << "BRK[" << i << "] = " << scores.BRK[i] << std::endl;
ofs << std::endl;
ofs << std::flush;
for (auto i = 0; i < 15; ++i)
ofs << "FX[" << i << "] = " << scores.FX[i] << std::endl;
ofs << std::endl;
ofs << std::flush;
for (auto i = 0; i < 15; ++i)
ofs << "FY[" << i << "] = " << scores.FY[i] << std::endl;
ofs << std::endl;
ofs << std::flush;
for (auto& st : m_allianceStandingList)
{
auto itrWS = m_weeklyScores.find(st.guid);
if (itrWS == m_weeklyScores.end())
continue;
auto ws = itrWS->second;
ofs << "Guid: " << st.guid
<< ", HK: " << ws.hk
<< ", DK: " << ws.dk
<< ", CP: " << ws.cp
<< ", oldRp: " << ws.oldRp
<< ", earning: " << ws.earning
<< ", newRp: " << ws.newRp
<< ", capRp: " << MaximumRpAtLevel(ws.level)
<< ", standing: " << ws.standing << std::endl << std::flush;
}
}
ofs << "--------------------------------------------------" << std::endl << std::endl << std::flush;
if (!m_hordeStandingList.empty())
{
HonorScores scores = GenerateScores(m_hordeStandingList);
ofs << "Horde Honor Scores" << std::endl << std::endl;
ofs << "Standing size: " << m_hordeStandingList.size() << std::endl << std::endl;
ofs << std::flush;
for (auto i = 0; i < 14; ++i)
ofs << "BRK[" << i << "] = " << scores.BRK[i] << std::endl;
ofs << std::endl;
ofs << std::flush;
for (auto i = 0; i < 15; ++i)
ofs << "FX[" << i << "] = " << scores.FX[i] << std::endl;
ofs << std::endl;
ofs << std::flush;
for (auto i = 0; i < 15; ++i)
ofs << "FY[" << i << "] = " << scores.FY[i] << std::endl;
ofs << std::endl;
ofs << std::flush;
for (auto& st : m_hordeStandingList)
{
auto itrWS = m_weeklyScores.find(st.guid);
if (itrWS == m_weeklyScores.end())
continue;
auto ws = itrWS->second;
ofs << "Guid: " << st.guid
<< ", HK: " << ws.hk
<< ", DK: " << ws.dk
<< ", CP: " << ws.cp
<< ", oldRp: " << ws.oldRp
<< ", earning: " << ws.earning
<< ", newRp: " << ws.newRp
<< ", capRp: " << MaximumRpAtLevel(ws.level)
<< ", standing: " << ws.standing << std::endl << std::flush;
}
}
ofs << "--------------------------------------------------" << std::endl << std::endl << std::flush;
if (!m_inactiveStandingList.empty())
{
ofs << "Inactive players decay" << std::endl << std::endl;
ofs << "Count: " << m_inactiveStandingList.size() << std::endl << std::endl;
ofs << std::flush;
for (auto& st : m_inactiveStandingList)
{
auto itrWS = m_weeklyScores.find(st.guid);
if (itrWS == m_weeklyScores.end())
continue;
auto ws = itrWS->second;
ofs << "Guid: " << st.guid
<< ", HK: " << ws.hk
<< ", DK: " << ws.dk
<< ", CP: " << ws.cp
<< ", oldRp: " << ws.oldRp
<< ", newRp: " << ws.newRp
<< ", capRp: " << MaximumRpAtLevel(ws.level)
<< ", standing: " << ws.standing << std::endl << std::flush;
}
}
ofs.close();
}
HonorScores HonorMaintenancer::GenerateScores(HonorStandingList& standingList)
{
HonorScores sc;
// initialize the breakpoint values
// [-PROGRESSIVE]
// 1.11- values (source: http://www.wowwiki.com/Honor_system_%28pre-2.0_formulas%29)
if (sWorld.GetWowPatch() < WOW_PATCH_112)
{
sc.BRK[13] = 0.002f;
sc.BRK[12] = 0.007f;
sc.BRK[11] = 0.017f;
sc.BRK[10] = 0.032f;
sc.BRK[9] = 0.057f;
sc.BRK[8] = 0.097f;
sc.BRK[7] = 0.156f;
sc.BRK[6] = 0.225f;
sc.BRK[5] = 0.324f;
sc.BRK[4] = 0.433f;
sc.BRK[3] = 0.553f;
sc.BRK[2] = 0.687f;
sc.BRK[1] = 0.835f;
sc.BRK[0] = 1.000f;
}
else
{
// 1.12+
sc.BRK[13] = 0.003f;
sc.BRK[12] = 0.008f;
sc.BRK[11] = 0.020f;
sc.BRK[10] = 0.035f;
sc.BRK[9] = 0.060f;
sc.BRK[8] = 0.100f;
sc.BRK[7] = 0.159f;
sc.BRK[6] = 0.228f;
sc.BRK[5] = 0.327f;
sc.BRK[4] = 0.436f;
sc.BRK[3] = 0.566f;
sc.BRK[2] = 0.697f;
sc.BRK[1] = 0.845f;
sc.BRK[0] = 1.000f;
}
// get the WS scores at the top of each break point
for (float & group : sc.BRK)
group = floor((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
float honor;
float tempHonor;
// initialize CP array
sc.FX[0] = 0;
bool top = false;
for (uint8 i = 1; i <= 13; i++)
{
honor = 0.0f;
tempHonor = GetStandingCPByPosition(standingList, sc.BRK[i]);
if (tempHonor)
{
honor += tempHonor;
tempHonor = GetStandingCPByPosition(standingList, sc.BRK[i] + 1);
if (tempHonor)
honor += tempHonor;
}
sc.FX[i] = honor ? honor / 2 : 0;
if (!top && !honor)
{
// top scorer
sc.FX[i] = sc.FX[i - 1] ? standingList.begin()->cp : 0;
top = true;
}
}
// set the high point if FX full filled before
// top scorer
sc.FX[14] = !top ? standingList.begin()->cp : 0;
return sc;
}
float HonorMaintenancer::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
if (sc.FX[i] > cp && cp >= sc.FX[i - 1])
return (sc.FY[i] - sc.FY[i - 1]) * (cp - sc.FX[i - 1]) / (sc.FX[i] - sc.FX[i - 1]) + sc.FY[i - 1];
return sc.FY[i];
}
float HonorMaintenancer::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;
}
float HonorMaintenancer::MaximumRpAtLevel(uint8 level)
{
if (level <= 29)
return 6500;
if (level >= 30 && level <= 35)
return 7150 + 975 * (level - 30);
if (level >= 36 && level <= 39)
return 12025 + 1300 * (level - 35);
if (level >= 40 && level <= 43)
return 17225 + 1625 * (level - 39);
if (level >= 44 && level <= 52)
return 23725 + 2275 * (level - 43);
if (level >= 53 && level <= 60)
return 44200 + 2600 * (level - 52);
return 65000;
}
void HonorMaintenancer::CheckMaintenanceDay()
{
if (sWorld.GetGameDay() >= m_nextMaintenanceDay && !m_markerToStart)
{
// Restart 15 minutes after honor weekend by server time
if (sWorld.getConfig(CONFIG_BOOL_AUTO_HONOR_RESTART))
sWorld.ShutdownServ(900, SHUTDOWN_MASK_RESTART, SHUTDOWN_EXIT_CODE);
else
sLog.outString("HonorMaintenancer: Server needs to be restarted to perform honor rank calculations.");
ToggleMaintenanceMarker();
}
}
void HonorMaintenancer::ToggleMaintenanceMarker()
{
m_markerToStart = !m_markerToStart;
CharacterDatabase.PExecute("INSERT INTO `saved_variables` (`key`, `honorMaintenanceMarker`) VALUES (0, %u) "
"ON DUPLICATE KEY UPDATE `honorMaintenanceMarker` = %u", m_markerToStart, m_markerToStart);
}
void HonorMaintenancer::SetMaintenanceDays(uint32 last, uint32 next)
{
m_lastMaintenanceDay = last;
if (!next)
m_nextMaintenanceDay = m_lastMaintenanceDay + 7;
CharacterDatabase.PExecute("INSERT INTO `saved_variables` (`key`, `lastHonorMaintenanceDay`, `nextHonorMaintenanceDay`) VALUES (0, %u, %u) "
"ON DUPLICATE KEY UPDATE `lastHonorMaintenanceDay` = %u, `nextHonorMaintenanceDay` = %u",
m_lastMaintenanceDay, m_nextMaintenanceDay, m_lastMaintenanceDay, m_nextMaintenanceDay);
}
void HonorMaintenancer::Initialize()
{
sLog.outString("Initialize Honor Maintenance system...");
QueryResult* result = CharacterDatabase.Query("SELECT `lastHonorMaintenanceDay`, `nextHonorMaintenanceDay`, `honorMaintenanceMarker` FROM `saved_variables`");
if (result)
{
Field* fields = result->Fetch();
m_lastMaintenanceDay = fields[0].GetUInt32();
m_nextMaintenanceDay = fields[1].GetUInt32();
m_markerToStart = fields[2].GetBool();
delete result;
}
if (!m_lastMaintenanceDay)
SetMaintenanceDays(sWorld.GetLastMaintenanceDay());
}
void HonorMgr::ClearHonorData()
{
m_honorCP.clear();
m_totalHK = 0;
m_totalDK = 0;
m_storedHK = 0;
m_storedDK = 0;
m_standing = 0;
m_lastWeekHK = 0;
m_rankPoints = 0.0f;
m_lastWeekCP = 0.0f;
InitRankInfo(m_rank);
InitRankInfo(m_highestRank);
}
void HonorMgr::Reset()
{
if (!m_owner)
return;
ClearHonorData();
// It will delete all honor cp from database imediatly
CharacterDatabase.PExecute("DELETE FROM `character_honor_cp` WHERE `guid` = %u", m_owner->GetGUIDLow());
SaveStoredData();
Update();
}
void HonorMgr::ClearHonorCP()
{
m_honorCP.clear();
}
void HonorMgr::Save()
{
if (!m_owner)
return;
HonorCPMap tempCP;
for (auto& honorCP : m_honorCP)
{
switch (honorCP.state)
{
case STATE_NEW:
CharacterDatabase.PExecute("INSERT INTO `character_honor_cp` (`guid`, `victimType`, `victim`, `cp`, `date`, `type`) "
" VALUES (%u, %u, %u, %.1f, %u, %u)", m_owner->GetGUIDLow(), honorCP.victimType, honorCP.victimId,
finiteAlways(honorCP.cp), honorCP.date, honorCP.type);
honorCP.state = STATE_UNCHANGED;
tempCP.push_back(honorCP);
break;
case STATE_UNCHANGED:
tempCP.push_back(honorCP);
break;
default:
break;
}
}
m_honorCP.clear();
m_honorCP = tempCP;
tempCP.clear();
// Static data, used for armory
/*CharacterDatabase.PExecute("DELETE FROM `character_honor_static` WHERE `guid` = %u", m_owner->GetGUIDLow());
std::ostringstream ss;
ss << "INSERT INTO `character_honor_static` (`guid`, `hk`, `dk`, `today_hk`, `today_dk`, "
"`yesterday_kills`, `yesterday_cp`, `thisWeek_kills`, `thisWeek_cp`, `lastWeek_kills`, `lastWeek_cp`) VALUES ("
<< m_owner->GetGUIDLow() << ", "
<< m_owner->GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS) << ", "
<< m_owner->GetUInt32Value(PLAYER_FIELD_LIFETIME_DISHONORABLE_KILLS) << ", "
<< m_owner->GetUInt16Value(PLAYER_FIELD_SESSION_KILLS, 0) << ", "
<< m_owner->GetUInt16Value(PLAYER_FIELD_SESSION_KILLS, 1) << ", "
<< m_owner->GetUInt32Value(PLAYER_FIELD_YESTERDAY_KILLS) << ", "
<< m_owner->GetUInt32Value(PLAYER_FIELD_YESTERDAY_CONTRIBUTION) << ", "
<< m_owner->GetUInt32Value(PLAYER_FIELD_THIS_WEEK_KILLS) << ", "
<< m_owner->GetUInt32Value(PLAYER_FIELD_THIS_WEEK_CONTRIBUTION) << ", "
<< m_owner->GetUInt32Value(PLAYER_FIELD_LAST_WEEK_KILLS) << ", "
<< m_owner->GetUInt32Value(PLAYER_FIELD_LAST_WEEK_CONTRIBUTION) << ")";
CharacterDatabase.Execute(ss.str().c_str());*/
}
void HonorMgr::SaveStoredData()
{
if (!m_owner)
return;
CharacterDatabase.PExecute("UPDATE `characters` SET `honorRankPoints` = %.1f, `honorStanding` = %u, `honorHighestRank` = %u, "
"`honorLastWeekHK` = %u, `honorLastWeekCP` = %.1f, `honorStoredHK` = %u, `honorStoredDK` = %u WHERE `guid` = %u",
finiteAlways(m_rankPoints), m_standing, m_highestRank.rank, m_lastWeekHK,
finiteAlways(m_lastWeekCP), m_storedHK, m_storedDK, m_owner->GetGUIDLow());
}
void HonorMgr::Load(QueryResult* result)
{
if (result)
{
m_honorCP.clear();
do
{
Field* fields = result->Fetch();
HonorCP honorCP;
honorCP.victimType = fields[0].GetUInt8();
honorCP.victimId = fields[1].GetUInt32();
honorCP.cp = fields[2].GetFloat();
honorCP.date = fields[3].GetUInt32();
honorCP.type = fields[4].GetUInt8();
honorCP.state = STATE_UNCHANGED;
m_honorCP.push_back(honorCP);
}
while (result->NextRow());
// result will be delete in character query holder later
}
}
bool HonorMgr::Add(float cp, uint8 type, Unit* source)
{
// Prevent give fake records to db with 0 honor
if (!cp || !m_owner)
return false;
// If not source, then give yourself
Unit* realSource = source;
if (!source)
source = m_owner;
HonorCP honorCP;
honorCP.date = sWorld.GetGameDay();
honorCP.cp = cp;
honorCP.victimId = (source->GetTypeId() == TYPEID_PLAYER ? source->GetGUIDLow() : source->GetEntry());
honorCP.victimType = (source == m_owner ? 0 : source->GetTypeId());
honorCP.type = type;
honorCP.state = STATE_NEW;
float honor = (type == DISHONORABLE) ? -cp : cp;
// get IP if source is player
std::string ip;
if (Player* victim = source->ToPlayer())
ip = victim->GetSession()->GetRemoteAddress();
bool plr = source->GetTypeId() == TYPEID_PLAYER;
if (m_owner->GetMap()->IsBattleGround())
sLog.outHonor("[BATTLEGROUND]: Player %s (account: %u) got %f honor for type %u, source %s %s (IP: %s)",
m_owner->GetSession()->GetPlayerName(), m_owner->GetSession()->GetAccountId(), honor, type, plr ? "player" : "unit", source->GetName(), ip.c_str());
else
sLog.outHonor("[OPEN WORLD]: Player %s (account: %u) got %f honor for type %u, source %s %s (IP: %s)",
m_owner->GetSession()->GetPlayerName(), m_owner->GetSession()->GetAccountId(), honor, type, plr ? "player" : "unit", source->GetName(), ip.c_str());
if (type == DISHONORABLE)
{
// DK penalties are subtracted from your RP score immediately
// and are not included in weekly adjustment
// remove this check to have negative ranks
m_rankPoints = m_rankPoints > honorCP.cp ? m_rankPoints - honorCP.cp : 0;
}
m_honorCP.push_back(honorCP);
SendPVPCredit(realSource, honor);
Update();
return true;
}
void HonorMgr::Update()
{
if (!m_owner)
return;
uint32 todayHK = 0;
uint32 todayDK = 0;
uint32 yesterdayKills = 0;
uint32 thisWeekKills = 0;
float yesterdayCP = 0.0f;
float thisWeekCP = 0.0f;
uint32 today = sWorld.GetGameDay();
uint32 yesterday = today - 1;
uint32 thisWeekBegin = sHonorMaintenancer.GetWeekBeginDay();
m_totalDK = m_storedDK;
m_totalHK = m_storedHK;
for (auto& itr : m_honorCP)
{
if (itr.type == HONORABLE)
{
if (itr.date == today)
++todayHK;
if (itr.date == yesterday)
++yesterdayKills;
if (itr.date >= thisWeekBegin)
{
++thisWeekKills;
++m_totalHK;
}
}
if (itr.type != DISHONORABLE)
{
if (itr.date == yesterday)
yesterdayCP += itr.cp;
if (itr.date >= thisWeekBegin)
thisWeekCP += itr.cp;
}
if (itr.type == DISHONORABLE)
{
if (itr.date == today)
++todayDK;
++m_totalDK;
}
}
m_rank = CalculateRank(m_rankPoints, m_totalHK);
if (m_rank.visualRank > 0 && (m_rank.visualRank > m_highestRank.visualRank))
SetHighestRank(m_rank);
// HIGHEST RANK
m_owner->SetByteValue(PLAYER_FIELD_BYTES, 3, m_highestRank.rank);
// RANK (Patent)
m_owner->SetByteValue(PLAYER_BYTES_3, 3, m_rank.rank);
uint32 honorBar = uint32(m_rankPoints >= 0.0f ? m_rankPoints : -1 * m_rankPoints);
honorBar = uint8(((honorBar - m_rank.minRP) / (m_rank.maxRP - m_rank.minRP)) * (m_rank.positive ? 255 : -255));
// PLAYER_FIELD_HONOR_BAR
m_owner->SetUInt32Value(PLAYER_FIELD_BYTES2, honorBar);
// TODAY
m_owner->SetUInt16Value(PLAYER_FIELD_SESSION_KILLS, 0, todayHK);
m_owner->SetUInt16Value(PLAYER_FIELD_SESSION_KILLS, 1, todayDK);
// YESTERDAY
m_owner->SetUInt32Value(PLAYER_FIELD_YESTERDAY_KILLS, yesterdayKills);
m_owner->SetUInt32Value(PLAYER_FIELD_YESTERDAY_CONTRIBUTION, uint32(yesterdayCP > 0.0f ? yesterdayCP : 0.0f));
// THIS WEEK
m_owner->SetUInt32Value(PLAYER_FIELD_THIS_WEEK_KILLS, thisWeekKills);
m_owner->SetUInt32Value(PLAYER_FIELD_THIS_WEEK_CONTRIBUTION, uint32(thisWeekCP > 0.0f ? thisWeekCP : 0.0f));
// LAST WEEK
m_owner->SetUInt32Value(PLAYER_FIELD_LAST_WEEK_KILLS, m_lastWeekHK);
m_owner->SetUInt32Value(PLAYER_FIELD_LAST_WEEK_CONTRIBUTION, uint32(m_lastWeekCP > 0.0f ? m_lastWeekCP : 0.0f));
m_owner->SetUInt32Value(PLAYER_FIELD_LAST_WEEK_RANK, m_standing);
// LIFE TIME
m_owner->SetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS, m_totalHK);
m_owner->SetUInt32Value(PLAYER_FIELD_LIFETIME_DISHONORABLE_KILLS, m_totalDK);
}
void HonorMgr::InitRankInfo(HonorRankInfo &prk)
{
prk.positive = true;
prk.rank = 0;
prk.visualRank = 0;
prk.maxRP = 2000.00f;
prk.minRP = 0.00f;
}
uint32 HonorMgr::CalculateTotalKills(Unit* victim) const
{
if (!victim)
return 0;
uint32 totalKills = 0;
uint32 id = 0;
uint32 today = sWorld.GetGameDay();
uint8 victimType = victim->GetTypeId();
switch (victimType)
{
case TYPEID_PLAYER:
id = ((Player*)victim)->GetGUIDLow();
break;
case TYPEID_UNIT:
id = victim->GetEntry();
break;
default:
return 0;
}
for (auto& honorCP : m_honorCP)
if (honorCP.victimType == victimType && honorCP.victimId == id && honorCP.date == today)
totalKills++;
return totalKills;
}
void HonorMgr::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;
if (prk.rank == 5)
{
prk.maxRP = 2000.0f;
prk.minRP = 0.0f;
}
prk.visualRank = prk.rank > NEGATIVE_HONOR_RANK_COUNT ? prk.rank - NEGATIVE_HONOR_RANK_COUNT : prk.rank * -1;
}
else
InitRankInfo(prk);
}
HonorRankInfo HonorMgr::CalculateRank(float rankPoints, uint32 totalHK)
{
HonorRankInfo prk;
InitRankInfo(prk);
// rank none
if (rankPoints == 0)
return prk;
prk.positive = rankPoints > 0;
if (!prk.positive)
rankPoints *= -1;
uint8 rankCount = prk.positive ? POSITIVE_HONOR_RANK_COUNT - 2 : NEGATIVE_HONOR_RANK_COUNT;
uint8 firstRank = prk.positive ? NEGATIVE_HONOR_RANK_COUNT + 1 : 1;
if (rankPoints < 2000.00f)
prk.rank = prk.positive ? firstRank : NEGATIVE_HONOR_RANK_COUNT;
else
{
if (rankPoints > (rankCount - 1) * 5000.00f)
prk.rank = prk.positive ? HONOR_RANK_COUNT - 1 : firstRank;
else
{
prk.rank = uint32(rankPoints / 5000.00f) + firstRank;
prk.rank = (prk.positive ? prk.rank + 1 : NEGATIVE_HONOR_RANK_COUNT - prk.rank);
}
}
CalculateRankInfo(prk);
return prk;
}
float HonorMgr::DishonorableKillPoints(uint8 level)
{
float result = 10.0f;
if (level >= 30 && level <= 35)
result = result + 1.5f * (level - 29);
if (level >= 36 && level <= 41)
result = result + 9 + 2 * (level - 35);
if (level >= 42 && level <= 50)
result = result + 21 + 3.2f * (level - 41);
if (level >= 51)
result = result + 50 + 4 * (level - 50);
if (result > 100)
return 100.0f;
else
return result;
}
float HonorMgr::HonorableKillPoints(Player* killer, Player* victim, uint32 groupSize)
{
if (!killer || !victim || !groupSize)
return 0.0;
uint32 totalKills = killer->GetHonorMgr().CalculateTotalKills(victim);
uint32 victimRank = victim->GetHonorMgr().GetRank().visualRank;
uint8 killerLevel = killer->GetLevel();
uint8 victimLevel = victim->GetLevel();
return MaNGOS::Honor::GetHonorGain(killerLevel, victimLevel, victimRank, totalKills, groupSize);
}
void HonorMgr::SendPVPCredit(Unit* victim, float honor)
{
if (!m_owner)
return;
WorldPacket data(SMSG_PVP_CREDIT, 4 + 8 + 4);
data << int32(honor);
if (!victim)
{
data << int64(0);
data << int32(0);
}
else
{
data << victim->GetObjectGuid();
if (victim->GetTypeId() == TYPEID_UNIT)
{
if (((Creature*)victim)->IsRacialLeader())
data << int32(19);
else
data << int32(0);
}
else if (victim->GetTypeId() == TYPEID_PLAYER)
data << uint32(((Player*)victim)->GetHonorMgr().GetRank().rank);
}
m_owner->SendDirectMessage(&data);
}