mirror of
https://github.com/mangosfour/server.git
synced 2025-12-19 13:37:01 +00:00
530 lines
18 KiB
C++
530 lines
18 KiB
C++
/*
|
|
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
|
|
*
|
|
* 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
|
|
*/
|
|
|
|
#include "ReputationMgr.h"
|
|
#include "DBCStores.h"
|
|
#include "Player.h"
|
|
#include "WorldPacket.h"
|
|
#include "ObjectMgr.h"
|
|
|
|
const int32 ReputationMgr::PointsInRank[MAX_REPUTATION_RANK] = {36000, 3000, 3000, 3000, 6000, 12000, 21000, 1000};
|
|
|
|
ReputationRank ReputationMgr::ReputationToRank(int32 standing)
|
|
{
|
|
int32 limit = Reputation_Cap + 1;
|
|
for (int i = MAX_REPUTATION_RANK-1; i >= MIN_REPUTATION_RANK; --i)
|
|
{
|
|
limit -= PointsInRank[i];
|
|
if (standing >= limit )
|
|
return ReputationRank(i);
|
|
}
|
|
return MIN_REPUTATION_RANK;
|
|
}
|
|
|
|
int32 ReputationMgr::GetReputation(uint32 faction_id) const
|
|
{
|
|
FactionEntry const *factionEntry = sFactionStore.LookupEntry(faction_id);
|
|
|
|
if (!factionEntry)
|
|
{
|
|
sLog.outError("ReputationMgr::GetReputation: Can't get reputation of %s for unknown faction (faction id) #%u.",m_player->GetName(), faction_id);
|
|
return 0;
|
|
}
|
|
|
|
return GetReputation(factionEntry);
|
|
}
|
|
|
|
int32 ReputationMgr::GetBaseReputation(FactionEntry const* factionEntry) const
|
|
{
|
|
if (!factionEntry)
|
|
return 0;
|
|
|
|
uint32 raceMask = m_player->getRaceMask();
|
|
uint32 classMask = m_player->getClassMask();
|
|
|
|
int idx = factionEntry->GetIndexFitTo(raceMask, classMask);
|
|
|
|
return idx >= 0 ? factionEntry->BaseRepValue[idx] : 0;
|
|
}
|
|
|
|
int32 ReputationMgr::GetReputation(FactionEntry const* factionEntry) const
|
|
{
|
|
// Faction without recorded reputation. Just ignore.
|
|
if(!factionEntry)
|
|
return 0;
|
|
|
|
if(FactionState const* state = GetState(factionEntry))
|
|
return GetBaseReputation(factionEntry) + state->Standing;
|
|
|
|
return 0;
|
|
}
|
|
|
|
ReputationRank ReputationMgr::GetRank(FactionEntry const* factionEntry) const
|
|
{
|
|
int32 reputation = GetReputation(factionEntry);
|
|
return ReputationToRank(reputation);
|
|
}
|
|
|
|
ReputationRank ReputationMgr::GetBaseRank(FactionEntry const* factionEntry) const
|
|
{
|
|
int32 reputation = GetBaseReputation(factionEntry);
|
|
return ReputationToRank(reputation);
|
|
}
|
|
|
|
void ReputationMgr::ApplyForceReaction( uint32 faction_id,ReputationRank rank,bool apply )
|
|
{
|
|
if(apply)
|
|
m_forcedReactions[faction_id] = rank;
|
|
else
|
|
m_forcedReactions.erase(faction_id);
|
|
}
|
|
|
|
uint32 ReputationMgr::GetDefaultStateFlags(FactionEntry const* factionEntry) const
|
|
{
|
|
if (!factionEntry)
|
|
return 0;
|
|
|
|
uint32 raceMask = m_player->getRaceMask();
|
|
uint32 classMask = m_player->getClassMask();
|
|
|
|
int idx = factionEntry->GetIndexFitTo(raceMask, classMask);
|
|
|
|
return idx >= 0 ? factionEntry->ReputationFlags[idx] : 0;
|
|
}
|
|
|
|
void ReputationMgr::SendForceReactions()
|
|
{
|
|
WorldPacket data;
|
|
data.Initialize(SMSG_SET_FORCED_REACTIONS, 4+m_forcedReactions.size()*(4+4));
|
|
data << uint32(m_forcedReactions.size());
|
|
for(ForcedReactions::const_iterator itr = m_forcedReactions.begin(); itr != m_forcedReactions.end(); ++itr)
|
|
{
|
|
data << uint32(itr->first); // faction_id (Faction.dbc)
|
|
data << uint32(itr->second); // reputation rank
|
|
}
|
|
m_player->SendDirectMessage(&data);
|
|
}
|
|
|
|
void ReputationMgr::SendState(FactionState const* faction)
|
|
{
|
|
uint32 count = 1;
|
|
|
|
WorldPacket data(SMSG_SET_FACTION_STANDING, (16)); // last check 2.4.0
|
|
data << (float) 0; // unk 2.4.0
|
|
data << (uint8) 0; // wotlk 8634
|
|
|
|
size_t p_count = data.wpos();
|
|
data << (uint32) count; // placeholder
|
|
|
|
data << (uint32) faction->ReputationListID;
|
|
data << (uint32) faction->Standing;
|
|
|
|
for(FactionStateList::iterator itr = m_factions.begin(); itr != m_factions.end(); ++itr)
|
|
{
|
|
if (itr->second.needSend)
|
|
{
|
|
itr->second.needSend = false;
|
|
if (itr->second.ReputationListID != faction->ReputationListID)
|
|
{
|
|
data << (uint32) itr->second.ReputationListID;
|
|
data << (uint32) itr->second.Standing;
|
|
|
|
++count;
|
|
}
|
|
}
|
|
}
|
|
|
|
data.put<uint32>(p_count, count);
|
|
m_player->SendDirectMessage(&data);
|
|
}
|
|
|
|
void ReputationMgr::SendInitialReputations()
|
|
{
|
|
WorldPacket data(SMSG_INITIALIZE_FACTIONS, (4+128*5));
|
|
data << uint32 (0x00000080);
|
|
|
|
RepListID a = 0;
|
|
|
|
for (FactionStateList::const_iterator itr = m_factions.begin(); itr != m_factions.end(); ++itr)
|
|
{
|
|
// fill in absent fields
|
|
for (; a != itr->first; a++)
|
|
{
|
|
data << uint8 (0x00);
|
|
data << uint32 (0x00000000);
|
|
}
|
|
|
|
// fill in encountered data
|
|
data << uint8 (itr->second.Flags);
|
|
data << uint32 (itr->second.Standing);
|
|
|
|
++a;
|
|
}
|
|
|
|
// fill in absent fields
|
|
for (; a != 128; a++)
|
|
{
|
|
data << uint8 (0x00);
|
|
data << uint32 (0x00000000);
|
|
}
|
|
|
|
m_player->SendDirectMessage(&data);
|
|
}
|
|
|
|
void ReputationMgr::SendVisible(FactionState const* faction) const
|
|
{
|
|
if(m_player->GetSession()->PlayerLoading())
|
|
return;
|
|
|
|
// make faction visible in reputation list at client
|
|
WorldPacket data(SMSG_SET_FACTION_VISIBLE, 4);
|
|
data << faction->ReputationListID;
|
|
m_player->SendDirectMessage(&data);
|
|
}
|
|
|
|
void ReputationMgr::Initialize()
|
|
{
|
|
m_factions.clear();
|
|
m_visibleFactionCount = 0;
|
|
m_honoredFactionCount = 0;
|
|
m_reveredFactionCount = 0;
|
|
m_exaltedFactionCount = 0;
|
|
|
|
for(unsigned int i = 1; i < sFactionStore.GetNumRows(); i++)
|
|
{
|
|
FactionEntry const *factionEntry = sFactionStore.LookupEntry(i);
|
|
|
|
if( factionEntry && (factionEntry->reputationListID >= 0))
|
|
{
|
|
FactionState newFaction;
|
|
newFaction.ID = factionEntry->ID;
|
|
newFaction.ReputationListID = factionEntry->reputationListID;
|
|
newFaction.Standing = 0;
|
|
newFaction.Flags = GetDefaultStateFlags(factionEntry);
|
|
newFaction.needSend = true;
|
|
newFaction.needSave = true;
|
|
|
|
if( newFaction.Flags & FACTION_FLAG_VISIBLE )
|
|
++m_visibleFactionCount;
|
|
|
|
UpdateRankCounters(REP_HOSTILE,GetBaseRank(factionEntry));
|
|
|
|
m_factions[newFaction.ReputationListID] = newFaction;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ReputationMgr::SetReputation(FactionEntry const* factionEntry, int32 standing, bool incremental)
|
|
{
|
|
bool res = false;
|
|
// if spillover definition exists in DB, override DBC
|
|
if (const RepSpilloverTemplate *repTemplate = sObjectMgr.GetRepSpilloverTemplate(factionEntry->ID))
|
|
{
|
|
for (uint32 i = 0; i < MAX_SPILLOVER_FACTIONS; ++i)
|
|
{
|
|
if (repTemplate->faction[i])
|
|
{
|
|
if (m_player->GetReputationRank(repTemplate->faction[i]) <= ReputationRank(repTemplate->faction_rank[i]))
|
|
{
|
|
// bonuses are already given, so just modify standing by rate
|
|
int32 spilloverRep = standing * repTemplate->faction_rate[i];
|
|
SetOneFactionReputation(sFactionStore.LookupEntry(repTemplate->faction[i]), spilloverRep, incremental);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
float spillOverRepOut = standing;
|
|
// check for sub-factions that receive spillover
|
|
SimpleFactionsList const* flist = GetFactionTeamList(factionEntry->ID);
|
|
// if has no sub-factions, check for factions with same parent
|
|
if (!flist && factionEntry->team && factionEntry->spilloverRateOut != 0.0f)
|
|
{
|
|
spillOverRepOut *= factionEntry->spilloverRateOut;
|
|
if (FactionEntry const *parent = sFactionStore.LookupEntry(factionEntry->team))
|
|
{
|
|
FactionStateList::iterator parentState = m_factions.find(parent->reputationListID);
|
|
// some team factions have own reputation standing, in this case do not spill to other sub-factions
|
|
if (parentState != m_factions.end() && (parentState->second.Flags & FACTION_FLAG_TEAM_REPUTATION))
|
|
{
|
|
SetOneFactionReputation(parent, int32(spillOverRepOut), incremental);
|
|
}
|
|
else // spill to "sister" factions
|
|
{
|
|
flist = GetFactionTeamList(factionEntry->team);
|
|
}
|
|
}
|
|
}
|
|
if (flist)
|
|
{
|
|
// Spillover to affiliated factions
|
|
for (SimpleFactionsList::const_iterator itr = flist->begin(); itr != flist->end(); ++itr)
|
|
{
|
|
if (FactionEntry const *factionEntryCalc = sFactionStore.LookupEntry(*itr))
|
|
{
|
|
if (factionEntryCalc == factionEntry || GetRank(factionEntryCalc) > ReputationRank(factionEntryCalc->spilloverMaxRankIn))
|
|
continue;
|
|
int32 spilloverRep = int32(spillOverRepOut * factionEntryCalc->spilloverRateIn);
|
|
if (spilloverRep != 0 || !incremental)
|
|
res = SetOneFactionReputation(factionEntryCalc, spilloverRep, incremental);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// spillover done, update faction itself
|
|
FactionStateList::iterator faction = m_factions.find(factionEntry->reputationListID);
|
|
if (faction != m_factions.end())
|
|
{
|
|
res = SetOneFactionReputation(factionEntry, standing, incremental);
|
|
// only this faction gets reported to client, even if it has no own visible standing
|
|
SendState(&faction->second);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
bool ReputationMgr::SetOneFactionReputation(FactionEntry const* factionEntry, int32 standing, bool incremental)
|
|
{
|
|
FactionStateList::iterator itr = m_factions.find(factionEntry->reputationListID);
|
|
if (itr != m_factions.end())
|
|
{
|
|
int32 BaseRep = GetBaseReputation(factionEntry);
|
|
|
|
if(incremental)
|
|
standing += itr->second.Standing + BaseRep;
|
|
|
|
if (standing > Reputation_Cap)
|
|
standing = Reputation_Cap;
|
|
else if (standing < Reputation_Bottom)
|
|
standing = Reputation_Bottom;
|
|
|
|
ReputationRank old_rank = ReputationToRank(itr->second.Standing + BaseRep);
|
|
ReputationRank new_rank = ReputationToRank(standing);
|
|
|
|
itr->second.Standing = standing - BaseRep;
|
|
itr->second.needSend = true;
|
|
itr->second.needSave = true;
|
|
|
|
SetVisible(&itr->second);
|
|
|
|
if(new_rank <= REP_HOSTILE)
|
|
SetAtWar(&itr->second,true);
|
|
|
|
UpdateRankCounters(old_rank, new_rank);
|
|
|
|
m_player->ReputationChanged(factionEntry);
|
|
m_player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS, factionEntry->ID);
|
|
m_player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION, factionEntry->ID);
|
|
m_player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION,factionEntry->ID);
|
|
m_player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION,factionEntry->ID);
|
|
m_player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION,factionEntry->ID);
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void ReputationMgr::SetVisible(FactionTemplateEntry const*factionTemplateEntry)
|
|
{
|
|
if(!factionTemplateEntry->faction)
|
|
return;
|
|
|
|
if(FactionEntry const *factionEntry = sFactionStore.LookupEntry(factionTemplateEntry->faction))
|
|
SetVisible(factionEntry);
|
|
}
|
|
|
|
void ReputationMgr::SetVisible(FactionEntry const *factionEntry)
|
|
{
|
|
if(factionEntry->reputationListID < 0)
|
|
return;
|
|
|
|
FactionStateList::iterator itr = m_factions.find(factionEntry->reputationListID);
|
|
if (itr == m_factions.end())
|
|
return;
|
|
|
|
SetVisible(&itr->second);
|
|
}
|
|
|
|
void ReputationMgr::SetVisible(FactionState* faction)
|
|
{
|
|
// always invisible or hidden faction can't be make visible
|
|
if(faction->Flags & (FACTION_FLAG_INVISIBLE_FORCED|FACTION_FLAG_HIDDEN))
|
|
return;
|
|
|
|
// already set
|
|
if(faction->Flags & FACTION_FLAG_VISIBLE)
|
|
return;
|
|
|
|
faction->Flags |= FACTION_FLAG_VISIBLE;
|
|
faction->needSend = true;
|
|
faction->needSave = true;
|
|
|
|
++m_visibleFactionCount;
|
|
|
|
SendVisible(faction);
|
|
}
|
|
|
|
void ReputationMgr::SetAtWar( RepListID repListID, bool on )
|
|
{
|
|
FactionStateList::iterator itr = m_factions.find(repListID);
|
|
if (itr == m_factions.end())
|
|
return;
|
|
|
|
// always invisible or hidden faction can't change war state
|
|
if(itr->second.Flags & (FACTION_FLAG_INVISIBLE_FORCED|FACTION_FLAG_HIDDEN) )
|
|
return;
|
|
|
|
SetAtWar(&itr->second,on);
|
|
}
|
|
|
|
void ReputationMgr::SetAtWar(FactionState* faction, bool atWar)
|
|
{
|
|
// not allow declare war to faction unless already hated or less
|
|
if (atWar && (faction->Flags & FACTION_FLAG_PEACE_FORCED) && ReputationToRank(faction->Standing) > REP_HATED)
|
|
return;
|
|
|
|
// already set
|
|
if(((faction->Flags & FACTION_FLAG_AT_WAR) != 0) == atWar)
|
|
return;
|
|
|
|
if( atWar )
|
|
faction->Flags |= FACTION_FLAG_AT_WAR;
|
|
else
|
|
faction->Flags &= ~FACTION_FLAG_AT_WAR;
|
|
|
|
faction->needSend = true;
|
|
faction->needSave = true;
|
|
}
|
|
|
|
void ReputationMgr::SetInactive( RepListID repListID, bool on )
|
|
{
|
|
FactionStateList::iterator itr = m_factions.find(repListID);
|
|
if (itr == m_factions.end())
|
|
return;
|
|
|
|
SetInactive(&itr->second,on);
|
|
}
|
|
|
|
void ReputationMgr::SetInactive(FactionState* faction, bool inactive)
|
|
{
|
|
// always invisible or hidden faction can't be inactive
|
|
if(inactive && ((faction->Flags & (FACTION_FLAG_INVISIBLE_FORCED|FACTION_FLAG_HIDDEN)) || !(faction->Flags & FACTION_FLAG_VISIBLE) ) )
|
|
return;
|
|
|
|
// already set
|
|
if(((faction->Flags & FACTION_FLAG_INACTIVE) != 0) == inactive)
|
|
return;
|
|
|
|
if(inactive)
|
|
faction->Flags |= FACTION_FLAG_INACTIVE;
|
|
else
|
|
faction->Flags &= ~FACTION_FLAG_INACTIVE;
|
|
|
|
faction->needSend = true;
|
|
faction->needSave = true;
|
|
}
|
|
|
|
void ReputationMgr::LoadFromDB(QueryResult *result)
|
|
{
|
|
// Set initial reputations (so everything is nifty before DB data load)
|
|
Initialize();
|
|
|
|
//QueryResult *result = CharacterDatabase.PQuery("SELECT faction,standing,flags FROM character_reputation WHERE guid = '%u'",GetGUIDLow());
|
|
|
|
if(result)
|
|
{
|
|
do
|
|
{
|
|
Field *fields = result->Fetch();
|
|
|
|
FactionEntry const *factionEntry = sFactionStore.LookupEntry(fields[0].GetUInt32());
|
|
if( factionEntry && (factionEntry->reputationListID >= 0))
|
|
{
|
|
FactionState* faction = &m_factions[factionEntry->reputationListID];
|
|
|
|
// update standing to current
|
|
faction->Standing = int32(fields[1].GetUInt32());
|
|
|
|
// update counters
|
|
int32 BaseRep = GetBaseReputation(factionEntry);
|
|
ReputationRank old_rank = ReputationToRank(BaseRep);
|
|
ReputationRank new_rank = ReputationToRank(BaseRep + faction->Standing);
|
|
UpdateRankCounters(old_rank,new_rank);
|
|
|
|
uint32 dbFactionFlags = fields[2].GetUInt32();
|
|
|
|
if( dbFactionFlags & FACTION_FLAG_VISIBLE )
|
|
SetVisible(faction); // have internal checks for forced invisibility
|
|
|
|
if( dbFactionFlags & FACTION_FLAG_INACTIVE)
|
|
SetInactive(faction,true); // have internal checks for visibility requirement
|
|
|
|
if( dbFactionFlags & FACTION_FLAG_AT_WAR ) // DB at war
|
|
SetAtWar(faction,true); // have internal checks for FACTION_FLAG_PEACE_FORCED
|
|
else // DB not at war
|
|
{
|
|
// allow remove if visible (and then not FACTION_FLAG_INVISIBLE_FORCED or FACTION_FLAG_HIDDEN)
|
|
if( faction->Flags & FACTION_FLAG_VISIBLE )
|
|
SetAtWar(faction,false); // have internal checks for FACTION_FLAG_PEACE_FORCED
|
|
}
|
|
|
|
// set atWar for hostile
|
|
if(GetRank(factionEntry) <= REP_HOSTILE)
|
|
SetAtWar(faction,true);
|
|
|
|
// reset changed flag if values similar to saved in DB
|
|
if(faction->Flags==dbFactionFlags)
|
|
faction->needSend = false;
|
|
faction->needSave = false;
|
|
}
|
|
}
|
|
while( result->NextRow() );
|
|
|
|
delete result;
|
|
}
|
|
}
|
|
|
|
void ReputationMgr::SaveToDB()
|
|
{
|
|
for(FactionStateList::iterator itr = m_factions.begin(); itr != m_factions.end(); ++itr)
|
|
{
|
|
if (itr->second.needSave)
|
|
{
|
|
CharacterDatabase.PExecute("DELETE FROM character_reputation WHERE guid = '%u' AND faction='%u'", m_player->GetGUIDLow(), itr->second.ID);
|
|
CharacterDatabase.PExecute("INSERT INTO character_reputation (guid,faction,standing,flags) VALUES ('%u', '%u', '%i', '%u')", m_player->GetGUIDLow(), itr->second.ID, itr->second.Standing, itr->second.Flags);
|
|
itr->second.needSave = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ReputationMgr::UpdateRankCounters( ReputationRank old_rank, ReputationRank new_rank )
|
|
{
|
|
if(old_rank >= REP_EXALTED)
|
|
--m_exaltedFactionCount;
|
|
if(old_rank >= REP_REVERED)
|
|
--m_reveredFactionCount;
|
|
if(old_rank >= REP_HONORED)
|
|
--m_honoredFactionCount;
|
|
|
|
if(new_rank >= REP_EXALTED)
|
|
++m_exaltedFactionCount;
|
|
if(new_rank >= REP_REVERED)
|
|
++m_reveredFactionCount;
|
|
if(new_rank >= REP_HONORED)
|
|
++m_honoredFactionCount;
|
|
}
|