/*
* Copyright (C) 2008-2014 TrinityCore
*
* 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, see .
*/
#include "PhaseMgr.h"
#include "Chat.h"
#include "Group.h"
#include "Language.h"
#include "ObjectMgr.h"
#include "Player.h"
//////////////////////////////////////////////////////////////////
// Updating
PhaseMgr::PhaseMgr(Player* _player) : player(_player), phaseData(_player), _UpdateFlags(0)
{
_PhaseDefinitionStore = sObjectMgr->GetPhaseDefinitionStore();
_SpellPhaseStore = sObjectMgr->GetSpellPhaseStore();
}
void PhaseMgr::Update()
{
if (IsUpdateInProgress())
return;
if (_UpdateFlags & PHASE_UPDATE_FLAG_CLIENTSIDE_CHANGED)
{
phaseData.SendPhaseshiftToPlayer();
player->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PHASE);
}
if (_UpdateFlags & PHASE_UPDATE_FLAG_SERVERSIDE_CHANGED)
phaseData.SendPhaseMaskToPlayer();
_UpdateFlags = 0;
}
void PhaseMgr::RemoveUpdateFlag(PhaseUpdateFlag updateFlag)
{
_UpdateFlags &= ~updateFlag;
if (updateFlag == PHASE_UPDATE_FLAG_ZONE_UPDATE)
{
// Update zone changes
if (phaseData.HasActiveDefinitions())
{
phaseData.ResetDefinitions();
_UpdateFlags |= (PHASE_UPDATE_FLAG_CLIENTSIDE_CHANGED | PHASE_UPDATE_FLAG_SERVERSIDE_CHANGED);
}
if (_PhaseDefinitionStore->find(player->GetZoneId()) != _PhaseDefinitionStore->end())
Recalculate();
}
Update();
}
/////////////////////////////////////////////////////////////////
// Notifier
void PhaseMgr::NotifyConditionChanged(PhaseUpdateData const& updateData)
{
if (NeedsPhaseUpdateWithData(updateData))
{
Recalculate();
Update();
}
}
//////////////////////////////////////////////////////////////////
// Phasing Definitions
void PhaseMgr::Recalculate()
{
if (phaseData.HasActiveDefinitions())
{
phaseData.ResetDefinitions();
_UpdateFlags |= (PHASE_UPDATE_FLAG_CLIENTSIDE_CHANGED | PHASE_UPDATE_FLAG_SERVERSIDE_CHANGED);
}
PhaseDefinitionStore::const_iterator itr = _PhaseDefinitionStore->find(player->GetZoneId());
if (itr != _PhaseDefinitionStore->end())
{
for (PhaseDefinitionContainer::const_iterator phase = itr->second.begin(); phase != itr->second.end(); ++phase)
{
if (CheckDefinition(&(*phase)))
{
phaseData.AddPhaseDefinition(&(*phase));
if (phase->phasemask)
_UpdateFlags |= PHASE_UPDATE_FLAG_SERVERSIDE_CHANGED;
if (phase->phaseId || phase->terrainswapmap)
_UpdateFlags |= PHASE_UPDATE_FLAG_CLIENTSIDE_CHANGED;
if (phase->IsLastDefinition())
break;
}
}
}
}
inline bool PhaseMgr::CheckDefinition(PhaseDefinition const* phaseDefinition)
{
ConditionList const* conditions = sConditionMgr->GetConditionsForPhaseDefinition(phaseDefinition->zoneId, phaseDefinition->entry);
if (!conditions)
return true;
ConditionSourceInfo srcInfo(player);
return sConditionMgr->IsObjectMeetToConditions(srcInfo, *conditions);
}
bool PhaseMgr::NeedsPhaseUpdateWithData(PhaseUpdateData const& updateData) const
{
PhaseDefinitionStore::const_iterator itr = _PhaseDefinitionStore->find(player->GetZoneId());
if (itr != _PhaseDefinitionStore->end())
{
for (PhaseDefinitionContainer::const_iterator phase = itr->second.begin(); phase != itr->second.end(); ++phase)
{
ConditionList const* conditionList = sConditionMgr->GetConditionsForPhaseDefinition(phase->zoneId, phase->entry);
if (!conditionList)
continue;
for (ConditionList::const_iterator condition = conditionList->begin(); condition != conditionList->end(); ++condition)
if (updateData.IsConditionRelated(*condition))
return true;
}
}
return false;
}
//////////////////////////////////////////////////////////////////
// Auras
void PhaseMgr::RegisterPhasingAuraEffect(AuraEffect const* auraEffect)
{
std::list phases;
if (auraEffect->GetAuraType() == SPELL_AURA_PHASE)
{
PhaseInfo phaseInfo;
if (auraEffect->GetMiscValue())
{
_UpdateFlags |= PHASE_UPDATE_FLAG_SERVERSIDE_CHANGED;
phaseInfo.phasemask = auraEffect->GetMiscValue();
}
else
{
SpellPhaseStore::const_iterator itr = _SpellPhaseStore->find(auraEffect->GetId());
if (itr != _SpellPhaseStore->end())
{
if (itr->second.phasemask)
{
_UpdateFlags |= PHASE_UPDATE_FLAG_SERVERSIDE_CHANGED;
phaseInfo.phasemask = itr->second.phasemask;
}
if (itr->second.terrainswapmap)
phaseInfo.terrainswapmap = itr->second.terrainswapmap;
}
}
phaseInfo.phaseId = auraEffect->GetMiscValueB();
if (phaseInfo.NeedsClientSideUpdate())
_UpdateFlags |= PHASE_UPDATE_FLAG_CLIENTSIDE_CHANGED;
phases.push_back(phaseInfo);
}
else if (auraEffect->GetAuraType() == SPELL_AURA_PHASE_GROUP)
{
uint32 group = auraEffect->GetMiscValueB();
std::set const& groupPhases = GetPhasesForGroup(group);
for (auto itr = groupPhases.begin(); itr != groupPhases.end(); ++itr)
{
PhaseInfo phaseInfo;
phaseInfo.phaseId = auraEffect->GetMiscValueB();
if (phaseInfo.NeedsClientSideUpdate())
_UpdateFlags |= PHASE_UPDATE_FLAG_CLIENTSIDE_CHANGED;
phases.push_back(phaseInfo);
}
}
for (auto itr = phases.begin(); itr != phases.end(); ++itr)
phaseData.AddAuraInfo(auraEffect->GetId(), *itr);
Update();
}
void PhaseMgr::UnRegisterPhasingAuraEffect(AuraEffect const* auraEffect)
{
_UpdateFlags |= phaseData.RemoveAuraInfo(auraEffect->GetId());
Update();
}
//////////////////////////////////////////////////////////////////
// Commands
void PhaseMgr::SendDebugReportToPlayer(Player* const debugger)
{
ChatHandler(debugger->GetSession()).PSendSysMessage(LANG_PHASING_REPORT_STATUS, player->GetName().c_str(), player->GetZoneId(), player->getLevel(), player->GetTeamId(), _UpdateFlags);
PhaseDefinitionStore::const_iterator itr = _PhaseDefinitionStore->find(player->GetZoneId());
if (itr == _PhaseDefinitionStore->end())
ChatHandler(debugger->GetSession()).PSendSysMessage(LANG_PHASING_NO_DEFINITIONS, player->GetZoneId());
else
{
for (PhaseDefinitionContainer::const_iterator phase = itr->second.begin(); phase != itr->second.end(); ++phase)
{
if (CheckDefinition(&(*phase)))
ChatHandler(debugger->GetSession()).PSendSysMessage(LANG_PHASING_SUCCESS, phase->entry, phase->IsNegatingPhasemask() ? "negated Phase" : "Phase", phase->phasemask);
else
ChatHandler(debugger->GetSession()).PSendSysMessage(LANG_PHASING_FAILED, phase->phasemask, phase->entry, phase->zoneId);
if (phase->IsLastDefinition())
{
ChatHandler(debugger->GetSession()).PSendSysMessage(LANG_PHASING_LAST_PHASE, phase->phasemask, phase->entry, phase->zoneId);
break;
}
}
}
ChatHandler(debugger->GetSession()).PSendSysMessage(LANG_PHASING_LIST, phaseData._PhasemaskThroughDefinitions, phaseData._PhasemaskThroughAuras, phaseData._CustomPhasemask);
ChatHandler(debugger->GetSession()).PSendSysMessage(LANG_PHASING_PHASEMASK, phaseData.GetPhaseMaskForSpawn(), player->GetPhaseMask());
}
void PhaseMgr::SetCustomPhase(uint32 phaseMask)
{
phaseData._CustomPhasemask = phaseMask;
_UpdateFlags |= PHASE_UPDATE_FLAG_SERVERSIDE_CHANGED;
Update();
}
//////////////////////////////////////////////////////////////////
// Phase Data
uint32 PhaseData::GetCurrentPhasemask() const
{
if (player->IsGameMaster())
return uint32(PHASEMASK_ANYWHERE);
if (_CustomPhasemask)
return _CustomPhasemask;
return GetPhaseMaskForSpawn();
}
inline uint32 PhaseData::GetPhaseMaskForSpawn() const
{
uint32 const phase = (_PhasemaskThroughDefinitions | _PhasemaskThroughAuras);
return (phase ? phase : PHASEMASK_NORMAL);
}
void PhaseData::SendPhaseMaskToPlayer()
{
// Server side update
uint32 const phasemask = GetCurrentPhasemask();
if (player->GetPhaseMask() == phasemask)
return;
player->SetPhaseMask(phasemask, false);
if (player->IsVisible())
player->UpdateObjectVisibility();
}
void PhaseData::SendPhaseshiftToPlayer()
{
// Client side update
std::set phaseIds;
std::set terrainswaps;
for (PhaseInfoContainer::const_iterator itr = spellPhaseInfo.begin(); itr != spellPhaseInfo.end(); ++itr)
{
for (auto ph = itr->second.begin(); ph != itr->second.end(); ++ph)
{
if (ph->terrainswapmap)
terrainswaps.insert(ph->terrainswapmap);
if (ph->phaseId)
phaseIds.insert(ph->phaseId);
}
}
// Phase Definitions
for (std::list::const_iterator itr = activePhaseDefinitions.begin(); itr != activePhaseDefinitions.end(); ++itr)
{
if ((*itr)->phaseId)
phaseIds.insert((*itr)->phaseId);
if ((*itr)->terrainswapmap)
terrainswaps.insert((*itr)->terrainswapmap);
}
player->GetSession()->SendSetPhaseShift(phaseIds, terrainswaps);
}
void PhaseData::GetActivePhases(std::set& phases) const
{
for (PhaseInfoContainer::const_iterator itr = spellPhaseInfo.begin(); itr != spellPhaseInfo.end(); ++itr)
for (auto phase = itr->second.begin(); phase != itr->second.end(); ++phase)
if (phase->phaseId)
phases.insert(phase->phaseId);
// Phase Definitions
for (std::list::const_iterator itr = activePhaseDefinitions.begin(); itr != activePhaseDefinitions.end(); ++itr)
if ((*itr)->phaseId)
phases.insert((*itr)->phaseId);
}
void PhaseData::AddPhaseDefinition(PhaseDefinition const* phaseDefinition)
{
if (phaseDefinition->IsOverwritingExistingPhases())
{
activePhaseDefinitions.clear();
_PhasemaskThroughDefinitions = phaseDefinition->phasemask;
}
else
{
if (phaseDefinition->IsNegatingPhasemask())
_PhasemaskThroughDefinitions &= ~phaseDefinition->phasemask;
else
_PhasemaskThroughDefinitions |= phaseDefinition->phasemask;
}
activePhaseDefinitions.push_back(phaseDefinition);
}
void PhaseData::AddAuraInfo(uint32 spellId, PhaseInfo const& phaseInfo)
{
if (phaseInfo.phasemask)
_PhasemaskThroughAuras |= phaseInfo.phasemask;
spellPhaseInfo[spellId].push_back(phaseInfo);
}
uint32 PhaseData::RemoveAuraInfo(uint32 spellId)
{
PhaseInfoContainer::const_iterator rAura = spellPhaseInfo.find(spellId);
if (rAura != spellPhaseInfo.end())
{
uint32 updateflag = 0;
for (auto phase = rAura->second.begin(); phase != rAura->second.end(); ++phase)
{
if (phase->NeedsClientSideUpdate())
updateflag |= PHASE_UPDATE_FLAG_CLIENTSIDE_CHANGED;
if (phase->NeedsServerSideUpdate())
{
_PhasemaskThroughAuras = 0;
updateflag |= PHASE_UPDATE_FLAG_SERVERSIDE_CHANGED;
}
}
if (updateflag & PHASE_UPDATE_FLAG_SERVERSIDE_CHANGED)
{
spellPhaseInfo.erase(rAura);
for (PhaseInfoContainer::const_iterator itr = spellPhaseInfo.begin(); itr != spellPhaseInfo.end(); ++itr)
for (auto ph = itr->second.begin(); ph != itr->second.end(); ++ph)
_PhasemaskThroughAuras |= ph->phasemask;
}
return updateflag;
}
return 0;
}
//////////////////////////////////////////////////////////////////
// Phase Update Data
void PhaseUpdateData::AddQuestUpdate(uint32 questId)
{
AddConditionType(CONDITION_QUESTREWARDED);
AddConditionType(CONDITION_QUESTTAKEN);
AddConditionType(CONDITION_QUEST_COMPLETE);
AddConditionType(CONDITION_QUEST_NONE);
_questId = questId;
}
bool PhaseUpdateData::IsConditionRelated(Condition const* condition) const
{
switch (condition->ConditionType)
{
case CONDITION_QUESTREWARDED:
case CONDITION_QUESTTAKEN:
case CONDITION_QUEST_COMPLETE:
case CONDITION_QUEST_NONE:
return condition->ConditionValue1 == _questId && ((1 << condition->ConditionType) & _conditionTypeFlags);
default:
return (1 << condition->ConditionType) & _conditionTypeFlags;
}
}
bool PhaseMgr::IsConditionTypeSupported(ConditionTypes conditionType)
{
switch (conditionType)
{
case CONDITION_QUESTREWARDED:
case CONDITION_QUESTTAKEN:
case CONDITION_QUEST_COMPLETE:
case CONDITION_QUEST_NONE:
case CONDITION_TEAM:
case CONDITION_CLASS:
case CONDITION_RACE:
case CONDITION_INSTANCE_INFO:
case CONDITION_LEVEL:
return true;
default:
return false;
}
}
void PhaseMgr::GetActivePhases(std::set& phases) const
{
phaseData.GetActivePhases(phases);
}