diff options
Diffstat (limited to 'src/server/game/Achievements/AchievementMgr.cpp')
-rw-r--r-- | src/server/game/Achievements/AchievementMgr.cpp | 3464 |
1 files changed, 649 insertions, 2815 deletions
diff --git a/src/server/game/Achievements/AchievementMgr.cpp b/src/server/game/Achievements/AchievementMgr.cpp index f15476d4071..642c21e3ff1 100644 --- a/src/server/game/Achievements/AchievementMgr.cpp +++ b/src/server/game/Achievements/AchievementMgr.cpp @@ -18,644 +18,221 @@ #include "AchievementMgr.h" #include "AchievementPackets.h" -#include "ArenaTeam.h" -#include "ArenaTeamMgr.h" -#include "Battleground.h" #include "CellImpl.h" #include "ChatTextBuilder.h" -#include "Common.h" -#include "DatabaseEnv.h" -#include "DBCEnums.h" -#include "DisableMgr.h" -#include "GameEventMgr.h" -#include "Garrison.h" #include "GridNotifiersImpl.h" #include "Group.h" -#include "Guild.h" #include "GuildMgr.h" -#include "InstanceScript.h" #include "Language.h" -#include "Map.h" -#include "MapManager.h" #include "ObjectMgr.h" -#include "Player.h" -#include "ReputationMgr.h" -#include "ScriptMgr.h" -#include "SpellMgr.h" -#include "World.h" -#include "WorldPacket.h" - -bool AchievementCriteriaData::IsValid(AchievementCriteria const* criteria) + +struct VisibleAchievementCheck { - if (dataType >= MAX_ACHIEVEMENT_CRITERIA_DATA_TYPE) + AchievementEntry const* operator()(std::pair<uint32, CompletedAchievementData> const& val) { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` for criteria (Entry: %u) contains a wrong data type (%u), ignored.", criteria->ID, dataType); - return false; + AchievementEntry const* achievement = sAchievementStore.LookupEntry(val.first); + if (achievement && !(achievement->Flags & ACHIEVEMENT_FLAG_HIDDEN)) + return achievement; + return nullptr; } +}; - switch (criteria->Entry->Type) - { - case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE: - case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE: - case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG: - case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING: - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST: // only hardcoded list - case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: - case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: - case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: - case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL: - case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL: - case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE: - case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: - case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET: - case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2: - case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM: - case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT: - case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT: - case ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE: - case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL: - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST: // only Children's Week achievements - case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM: // only Children's Week achievements - case ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS: - case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL: - case ACHIEVEMENT_CRITERIA_TYPE_ON_LOGIN: - break; - default: - if (dataType != ACHIEVEMENT_CRITERIA_DATA_TYPE_SCRIPT) - { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` contains data for a non-supported criteria type (Entry: %u Type: %u), ignored.", criteria->ID, criteria->Entry->Type); - return false; - } - break; - } +AchievementMgr::AchievementMgr() : _achievementPoints(0) { } - switch (dataType) - { - case ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE: - case ACHIEVEMENT_CRITERIA_DATA_TYPE_INSTANCE_SCRIPT: - return true; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_CREATURE: - if (!creature.id || !sObjectMgr->GetCreatureTemplate(creature.id)) - { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_CREATURE (%u) contains a non-existing creature id in value1 (%u), ignored.", - criteria->ID, criteria->Entry->Type, dataType, creature.id); - return false; - } - return true; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE: - if (!classRace.class_id && !classRace.race_id) - { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE (%u) must not have 0 in either value field, ignored.", - criteria->ID, criteria->Entry->Type, dataType); - return false; - } - if (classRace.class_id && ((1 << (classRace.class_id-1)) & CLASSMASK_ALL_PLAYABLE) == 0) - { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE (%u) contains a non-existing class in value1 (%u), ignored.", - criteria->ID, criteria->Entry->Type, dataType, classRace.class_id); - return false; - } - if (classRace.race_id && ((1 << (classRace.race_id-1)) & RACEMASK_ALL_PLAYABLE) == 0) - { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE (%u) contains a non-existing race in value2 (%u), ignored.", - criteria->ID, criteria->Entry->Type, dataType, classRace.race_id); - return false; - } - return true; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_LESS_HEALTH: - if (health.percent < 1 || health.percent > 100) - { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_PLAYER_LESS_HEALTH (%u) contains a wrong percent value in value1 (%u), ignored.", - criteria->ID, criteria->Entry->Type, dataType, health.percent); - return false; - } - return true; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA: - case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA: - { - SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(aura.spell_id); - if (!spellEntry) - { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) contains a wrong spell id in value1 (%u), ignored.", - criteria->ID, criteria->Entry->Type, (dataType == ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA ? "ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA" : "ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA"), dataType, aura.spell_id); - return false; - } - SpellEffectInfo const* effect = spellEntry->GetEffect(DIFFICULTY_NONE, aura.effect_idx); - if (!effect) - { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) contains a wrong spell effect index in value2 (%u), ignored.", - criteria->ID, criteria->Entry->Type, (dataType == ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA ? "ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA" : "ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA"), dataType, aura.effect_idx); - return false; - } - if (!effect->ApplyAuraName) - { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) contains a non-aura spell effect (ID: %u Effect: %u), ignored.", - criteria->ID, criteria->Entry->Type, (dataType == ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA ? "ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA" : "ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA"), dataType, aura.spell_id, aura.effect_idx); - return false; - } - return true; - } - case ACHIEVEMENT_CRITERIA_DATA_TYPE_VALUE: - if (value.compType >= COMP_TYPE_MAX) - { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_VALUE (%u) contains a wrong ComparisionType in value2 (%u), ignored.", - criteria->ID, criteria->Entry->Type, dataType, value.compType); - return false; - } - return true; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_LEVEL: - if (level.minlevel > STRONG_MAX_LEVEL) - { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_LEVEL (%u) contains a wrong minlevel in value1 (%u), ignored.", - criteria->ID, criteria->Entry->Type, dataType, level.minlevel); - return false; - } - return true; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_GENDER: - if (gender.gender > GENDER_NONE) - { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_GENDER (%u) contains a wrong gender value in value1 (%u), ignored.", - criteria->ID, criteria->Entry->Type, dataType, gender.gender); - return false; - } - return true; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_SCRIPT: - if (!ScriptId) - { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_SCRIPT (%u) does not have a ScriptName set, ignored.", - criteria->ID, criteria->Entry->Type, dataType); - return false; - } - return true; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_PLAYER_COUNT: - if (map_players.maxcount <= 0) - { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_PLAYER_COUNT (%u) contains a wrong max players count in value1 (%u), ignored.", - criteria->ID, criteria->Entry->Type, dataType, map_players.maxcount); - return false; - } - return true; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_TEAM: - if (team.team != ALLIANCE && team.team != HORDE) - { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_TEAM (%u) contains an unknown team value in value1 (%u), ignored.", - criteria->ID, criteria->Entry->Type, dataType, team.team); - return false; - } - return true; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_DRUNK: - if (drunk.state >= MAX_DRUNKEN) - { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_DRUNK (%u) contains an unknown drunken state value in value1 (%u), ignored.", - criteria->ID, criteria->Entry->Type, dataType, drunk.state); - return false; - } - return true; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_HOLIDAY: - if (!sHolidaysStore.LookupEntry(holiday.id)) - { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_HOLIDAY (%u) contains an unknown holiday entry in value1 (%u), ignored.", - criteria->ID, criteria->Entry->Type, dataType, holiday.id); - return false; - } - return true; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_GAME_EVENT: - { - GameEventMgr::GameEventDataMap const& events = sGameEventMgr->GetEventMap(); - if (game_event.id < 1 || game_event.id >= events.size()) - { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_GAME_EVENT (%u) has unknown game_event in value1 (%u), ignored.", - criteria->ID, criteria->Entry->Type, dataType, game_event.id); - return false; - } - return true; - } - case ACHIEVEMENT_CRITERIA_DATA_TYPE_BG_LOSS_TEAM_SCORE: - return true; // not check correctness node indexes - case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_EQUIPED_ITEM: - if (equipped_item.item_quality >= MAX_ITEM_QUALITY) - { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_EQUIPED_ITEM (%u) contains an unknown quality state value in value1 (%u), ignored.", - criteria->ID, criteria->Entry->Type, dataType, equipped_item.item_quality); - return false; - } - return true; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE: - if (!classRace.class_id && !classRace.race_id) - { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE (%u) should not have 0 in either value field. Ignored.", - criteria->ID, criteria->Entry->Type, dataType); - return false; - } - if (classRace.class_id && ((1 << (classRace.class_id-1)) & CLASSMASK_ALL_PLAYABLE) == 0) - { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE (%u) contains a non-existing class entry in value1 (%u), ignored.", - criteria->ID, criteria->Entry->Type, dataType, classRace.class_id); - return false; - } - if (classRace.race_id && ((1 << (classRace.race_id-1)) & RACEMASK_ALL_PLAYABLE) == 0) - { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE (%u) contains a non-existing race entry in value2 (%u), ignored.", - criteria->ID, criteria->Entry->Type, dataType, classRace.race_id); - return false; - } - return true; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_KNOWN_TITLE: - if (!sCharTitlesStore.LookupEntry(known_title.title_id)) - { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_KNOWN_TITLE (%u) contains an unknown title_id in value1 (%u), ignore.", - criteria->ID, criteria->Entry->Type, dataType, known_title.title_id); - return false; - } - return true; - default: - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) contains data of a non-supported data type (%u), ignored.", criteria->ID, criteria->Entry->Type, dataType); - return false; - } -} +AchievementMgr::~AchievementMgr() { } -bool AchievementCriteriaData::Meets(uint32 criteria_id, Player const* source, Unit const* target, uint32 miscValue1 /*= 0*/) const +/** +* called at player login. The player might have fulfilled some achievements when the achievement system wasn't working yet +*/ +void AchievementMgr::CheckAllAchievementCriteria(Player* referencePlayer) { - switch (dataType) - { - case ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE: - return true; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_CREATURE: - if (!target || target->GetTypeId() != TYPEID_UNIT) - return false; - return target->GetEntry() == creature.id; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE: - if (!target || target->GetTypeId() != TYPEID_PLAYER) - return false; - if (classRace.class_id && classRace.class_id != target->ToPlayer()->getClass()) - return false; - if (classRace.race_id && classRace.race_id != target->ToPlayer()->getRace()) - return false; - return true; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE: - if (source->GetTypeId() != TYPEID_PLAYER) - return false; - if (classRace.class_id && classRace.class_id != source->ToPlayer()->getClass()) - return false; - if (classRace.race_id && classRace.race_id != source->ToPlayer()->getRace()) - return false; - return true; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_LESS_HEALTH: - if (!target || target->GetTypeId() != TYPEID_PLAYER) - return false; - return !target->HealthAbovePct(health.percent); - case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA: - return source->HasAuraEffect(aura.spell_id, aura.effect_idx); - case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA: - return target && target->HasAuraEffect(aura.spell_id, aura.effect_idx); - case ACHIEVEMENT_CRITERIA_DATA_TYPE_VALUE: - return CompareValues(ComparisionType(value.compType), miscValue1, value.value); - case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_LEVEL: - if (!target) - return false; - return target->getLevel() >= level.minlevel; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_GENDER: - if (!target) - return false; - return target->getGender() == gender.gender; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_SCRIPT: - return sScriptMgr->OnCriteriaCheck(ScriptId, const_cast<Player*>(source), const_cast<Unit*>(target)); - case ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_PLAYER_COUNT: - return source->GetMap()->GetPlayersCountExceptGMs() <= map_players.maxcount; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_TEAM: - if (!target || target->GetTypeId() != TYPEID_PLAYER) - return false; - return target->ToPlayer()->GetTeam() == team.team; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_DRUNK: - return Player::GetDrunkenstateByValue(source->GetDrunkValue()) >= DrunkenState(drunk.state); - case ACHIEVEMENT_CRITERIA_DATA_TYPE_HOLIDAY: - return IsHolidayActive(HolidayIds(holiday.id)); - case ACHIEVEMENT_CRITERIA_DATA_TYPE_GAME_EVENT: - return IsEventActive(game_event.id); - case ACHIEVEMENT_CRITERIA_DATA_TYPE_BG_LOSS_TEAM_SCORE: - { - Battleground* bg = source->GetBattleground(); - if (!bg) - return false; - - uint32 score = bg->GetTeamScore(source->GetTeamId() == TEAM_ALLIANCE ? TEAM_HORDE : TEAM_ALLIANCE); - return score >= bg_loss_team_score.min_score && score <= bg_loss_team_score.max_score; - } - case ACHIEVEMENT_CRITERIA_DATA_TYPE_INSTANCE_SCRIPT: - { - if (!source->IsInWorld()) - return false; - Map* map = source->GetMap(); - if (!map->IsDungeon()) - { - TC_LOG_ERROR("achievement", "Achievement system call ACHIEVEMENT_CRITERIA_DATA_TYPE_INSTANCE_SCRIPT (%u) for achievement criteria %u in a non-dungeon/non-raid map %u", - dataType, criteria_id, map->GetId()); - return false; - } - InstanceScript* instance = map->ToInstanceMap()->GetInstanceScript(); - if (!instance) - { - TC_LOG_ERROR("achievement", "Achievement system call ACHIEVEMENT_CRITERIA_DATA_TYPE_INSTANCE_SCRIPT (%u) for achievement criteria %u in map %u, but the map does not have an instance script.", - dataType, criteria_id, map->GetId()); - return false; - } - return instance->CheckAchievementCriteriaMeet(criteria_id, source, target, miscValue1); - } - case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_EQUIPED_ITEM: - { - ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(miscValue1); - if (!pProto) - return false; - return pProto->GetBaseItemLevel() >= equipped_item.item_level && pProto->GetQuality() >= equipped_item.item_quality; - } - case ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_ID: - return source->GetMapId() == map_id.mapId; - case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_KNOWN_TITLE: - { - if (CharTitlesEntry const* titleInfo = sCharTitlesStore.LookupEntry(known_title.title_id)) - return source && source->HasTitle(titleInfo->MaskID); - - return false; - } - default: - break; - } - return false; + // suppress sending packets + for (uint32 i = 0; i < CRITERIA_TYPE_TOTAL; ++i) + UpdateCriteria(CriteriaTypes(i), 0, 0, 0, NULL, referencePlayer); } -bool AchievementCriteriaDataSet::Meets(Player const* source, Unit const* target, uint32 miscValue /*= 0*/) const +bool AchievementMgr::HasAchieved(uint32 achievementId) const { - for (AchievementCriteriaData const& data : storage) - if (!data.Meets(criteria_id, source, target, miscValue)) - return false; - - return true; + return _completedAchievements.find(achievementId) != _completedAchievements.end(); } -template<class T> -AchievementMgr<T>::AchievementMgr(T* owner): _owner(owner), _achievementPoints(0) { } - -template<class T> -AchievementMgr<T>::~AchievementMgr() { } - -template<class T> -void AchievementMgr<T>::SendPacket(WorldPacket const* /*data*/) const { } - -template<> -void AchievementMgr<Guild>::SendPacket(WorldPacket const* data) const +uint32 AchievementMgr::GetAchievementPoints() const { - GetOwner()->BroadcastPacket(data); + return _achievementPoints; } -template<> -void AchievementMgr<Player>::SendPacket(WorldPacket const* data) const +bool AchievementMgr::CanUpdateCriteriaTree(Criteria const* criteria, CriteriaTree const* tree, Player* referencePlayer) const { - GetOwner()->GetSession()->SendPacket(data); + if (HasAchieved(tree->Achievement->ID)) + { + TC_LOG_TRACE("criteria.achievement", "AchievementMgr::CanUpdateCriteriaTree: (Id: %u Type %s Achievement %u) Achievement already earned", + criteria->ID, CriteriaMgr::GetCriteriaTypeString(criteria->Entry->Type), tree->Achievement->ID); + return false; + } + + if (tree->Achievement->MapID != -1 && referencePlayer->GetMapId() != uint32(tree->Achievement->MapID)) + { + TC_LOG_TRACE("criteria.achievement", "AchievementMgr::CanUpdateCriteriaTree: (Id: %u Type %s Achievement %u) Wrong map", + criteria->ID, CriteriaMgr::GetCriteriaTypeString(criteria->Entry->Type), tree->Achievement->ID); + return false; + } + + if ((tree->Achievement->Faction == ACHIEVEMENT_FACTION_HORDE && referencePlayer->GetTeam() != HORDE) || + (tree->Achievement->Faction == ACHIEVEMENT_FACTION_ALLIANCE && referencePlayer->GetTeam() != ALLIANCE)) + { + TC_LOG_TRACE("criteria.achievement", "AchievementMgr::CanUpdateCriteriaTree: (Id: %u Type %s Achievement %u) Wrong faction", + criteria->ID, CriteriaMgr::GetCriteriaTypeString(criteria->Entry->Type), tree->Achievement->ID); + return false; + } + + return true; } -template<class T> -void AchievementMgr<T>::RemoveCriteriaProgress(AchievementCriteria const* entry) +bool AchievementMgr::CanCompleteCriteriaTree(CriteriaTree const* tree) { - if (!entry) - return; + AchievementEntry const* achievement = tree->Achievement; + if (!achievement) + return false; - CriteriaProgressMap::iterator criteriaProgress = m_criteriaProgress.find(entry->ID); - if (criteriaProgress == m_criteriaProgress.end()) - return; + // counter can never complete + if (achievement->Flags & ACHIEVEMENT_FLAG_COUNTER) + return false; - WorldPackets::Achievement::CriteriaDeleted criteriaDeleted; - criteriaDeleted.CriteriaID = entry->ID; - SendPacket(criteriaDeleted.Write()); + if (achievement->Flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL)) + { + // someone on this realm has already completed that achievement + if (sAchievementMgr->IsRealmCompleted(achievement)) + return false; + } - m_criteriaProgress.erase(criteriaProgress); + return true; } -template<> -void AchievementMgr<Guild>::RemoveCriteriaProgress(AchievementCriteria const* entry) +void AchievementMgr::CompletedCriteriaTree(CriteriaTree const* tree, Player* referencePlayer) { - if (!entry) + // counter can never complete + if (tree->Achievement->Flags & ACHIEVEMENT_FLAG_COUNTER) return; - CriteriaProgressMap::iterator criteriaProgress = m_criteriaProgress.find(entry->ID); - if (criteriaProgress == m_criteriaProgress.end()) + // already completed and stored + if (HasAchieved(tree->Achievement->ID)) return; - WorldPackets::Achievement::GuildCriteriaDeleted guildCriteriaDeleted; - guildCriteriaDeleted.GuildGUID = GetOwner()->GetGUID(); - guildCriteriaDeleted.CriteriaID = entry->ID; - SendPacket(guildCriteriaDeleted.Write()); + if (IsCompletedAchievement(tree->Achievement)) + CompletedAchievement(tree->Achievement, referencePlayer); +} + +void AchievementMgr::AfterCriteriaTreeUpdate(CriteriaTree const* tree, Player* referencePlayer) +{ + // check again the completeness for SUMM and REQ COUNT achievements, + // as they don't depend on the completed criteria but on the sum of the progress of each individual criteria + if (tree->Achievement->Flags & ACHIEVEMENT_FLAG_SUMM) + if (IsCompletedAchievement(tree->Achievement)) + CompletedAchievement(tree->Achievement, referencePlayer); - m_criteriaProgress.erase(criteriaProgress); + if (std::vector<AchievementEntry const*> const* achRefList = sAchievementMgr->GetAchievementByReferencedId(tree->Achievement->ID)) + for (AchievementEntry const* refAchievement : *achRefList) + if (IsCompletedAchievement(refAchievement)) + CompletedAchievement(refAchievement, referencePlayer); } -template<class T> -void AchievementMgr<T>::ResetAchievementCriteria(AchievementCriteriaTypes type, uint64 miscValue1, uint64 miscValue2, bool evenIfCriteriaComplete) +bool AchievementMgr::IsCompletedAchievement(AchievementEntry const* entry) { - TC_LOG_DEBUG("achievement", "ResetAchievementCriteria(%u, " UI64FMTD ", " UI64FMTD ")", type, miscValue1, miscValue2); + // counter can never complete + if (entry->Flags & ACHIEVEMENT_FLAG_COUNTER) + return false; - // disable for gamemasters with GM-mode enabled - if (GetOwner()->IsGameMaster()) - return; + CriteriaTree const* tree = sCriteriaMgr->GetCriteriaTree(entry->CriteriaTree); + if (!tree) + return false; - AchievementCriteriaList const& achievementCriteriaList = sAchievementMgr->GetAchievementCriteriaByType(type); - for (AchievementCriteria const* achievementCriteria : achievementCriteriaList) + // For SUMM achievements, we have to count the progress of each criteria of the achievement. + // Oddly, the target count is NOT contained in the achievement, but in each individual criteria + if (entry->Flags & ACHIEVEMENT_FLAG_SUMM) { - if (achievementCriteria->Entry->FailEvent != miscValue1 || (achievementCriteria->Entry->FailAsset && achievementCriteria->Entry->FailAsset != miscValue2)) - continue; - - AchievementCriteriaTreeList const* trees = sAchievementMgr->GetAchievementCriteriaTreesByCriteria(achievementCriteria->ID); - bool allComplete = true; - for (AchievementCriteriaTree const* tree : *trees) + uint64 progress = 0; + CriteriaMgr::WalkCriteriaTree(tree, [this, &progress](CriteriaTree const* criteriaTree) { - // don't update already completed criteria if not forced or achievement already complete - if (!(IsCompletedCriteriaTree(tree) && !evenIfCriteriaComplete) || !HasAchieved(tree->Achievement->ID)) - { - allComplete = false; - break; - } - } + if (criteriaTree->Criteria) + if (CriteriaProgress const* criteriaProgress = this->GetCriteriaProgress(criteriaTree->Criteria)) + progress += criteriaProgress->Counter; + }); + return progress >= tree->Entry->Amount; + } - if (allComplete) - continue; + return IsCompletedCriteriaTree(tree); +} - RemoveCriteriaProgress(achievementCriteria); - } +bool AchievementMgr::RequiredAchievementSatisfied(uint32 achievementId) const +{ + return HasAchieved(achievementId); } -template<> -void AchievementMgr<Guild>::ResetAchievementCriteria(AchievementCriteriaTypes /*type*/, uint64 /*miscValue1*/, uint64 /*miscValue2*/, bool /*evenIfCriteriaComplete*/) +PlayerAchievementMgr::PlayerAchievementMgr(Player* owner) : _owner(owner) { - // Not needed } -void DeletePlayerAchievementsFromDB(ObjectGuid guid) +void PlayerAchievementMgr::Reset() { - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + AchievementMgr::Reset(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_ACHIEVEMENT); - stmt->setUInt64(0, guid.GetCounter()); - trans->Append(stmt); + for (auto iter = _completedAchievements.begin(); iter != _completedAchievements.end(); ++iter) + { + WorldPackets::Achievement::AchievementDeleted achievementDeleted; + achievementDeleted.AchievementID = iter->first; + SendPacket(achievementDeleted.Write()); + } - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_ACHIEVEMENT_PROGRESS); - stmt->setUInt64(0, guid.GetCounter()); - trans->Append(stmt); + _completedAchievements.clear(); + _achievementPoints = 0; + DeleteFromDB(_owner->GetGUID()); - CharacterDatabase.CommitTransaction(trans); + // re-fill data + CheckAllAchievementCriteria(_owner); } -void DeleteGuildAchievementsFromDB(ObjectGuid guid) +void PlayerAchievementMgr::DeleteFromDB(ObjectGuid const& guid) { SQLTransaction trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ALL_GUILD_ACHIEVEMENTS); + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_ACHIEVEMENT); stmt->setUInt64(0, guid.GetCounter()); trans->Append(stmt); - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ALL_GUILD_ACHIEVEMENT_CRITERIA); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_ACHIEVEMENT_PROGRESS); stmt->setUInt64(0, guid.GetCounter()); trans->Append(stmt); CharacterDatabase.CommitTransaction(trans); } -template<class T> -void AchievementMgr<T>::SaveToDB(SQLTransaction& /*trans*/) -{ -} - -template<> -void AchievementMgr<Player>::SaveToDB(SQLTransaction& trans) -{ - if (!m_completedAchievements.empty()) - { - for (CompletedAchievementMap::iterator iter = m_completedAchievements.begin(); iter != m_completedAchievements.end(); ++iter) - { - if (!iter->second.changed) - continue; - - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_ACHIEVEMENT_BY_ACHIEVEMENT); - stmt->setUInt16(0, iter->first); - stmt->setUInt64(1, GetOwner()->GetGUID().GetCounter()); - trans->Append(stmt); - - stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_ACHIEVEMENT); - stmt->setUInt64(0, GetOwner()->GetGUID().GetCounter()); - stmt->setUInt16(1, iter->first); - stmt->setUInt32(2, uint32(iter->second.date)); - trans->Append(stmt); - - iter->second.changed = false; - } - } - - if (!m_criteriaProgress.empty()) - { - for (CriteriaProgressMap::iterator iter = m_criteriaProgress.begin(); iter != m_criteriaProgress.end(); ++iter) - { - if (!iter->second.changed) - continue; - - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_ACHIEVEMENT_PROGRESS_BY_CRITERIA); - stmt->setUInt64(0, GetOwner()->GetGUID().GetCounter()); - stmt->setUInt32(1, iter->first); - trans->Append(stmt); - - if (iter->second.counter) - { - stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_ACHIEVEMENT_PROGRESS); - stmt->setUInt64(0, GetOwner()->GetGUID().GetCounter()); - stmt->setUInt32(1, iter->first); - stmt->setUInt32(2, iter->second.counter); - stmt->setUInt32(3, uint32(iter->second.date)); - trans->Append(stmt); - } - - iter->second.changed = false; - } - } -} - -template<> -void AchievementMgr<Guild>::SaveToDB(SQLTransaction& trans) -{ - PreparedStatement* stmt; - std::ostringstream guidstr; - for (CompletedAchievementMap::const_iterator itr = m_completedAchievements.begin(); itr != m_completedAchievements.end(); ++itr) - { - if (!itr->second.changed) - continue; - - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_ACHIEVEMENT); - stmt->setUInt64(0, GetOwner()->GetId()); - stmt->setUInt16(1, itr->first); - trans->Append(stmt); - - stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_ACHIEVEMENT); - stmt->setUInt64(0, GetOwner()->GetId()); - stmt->setUInt16(1, itr->first); - stmt->setUInt32(2, itr->second.date); - for (GuidSet::const_iterator gItr = itr->second.guids.begin(); gItr != itr->second.guids.end(); ++gItr) - guidstr << gItr->GetCounter() << ','; - - stmt->setString(3, guidstr.str()); - trans->Append(stmt); - - guidstr.str(""); - } - - for (CriteriaProgressMap::const_iterator itr = m_criteriaProgress.begin(); itr != m_criteriaProgress.end(); ++itr) - { - if (!itr->second.changed) - continue; - - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_ACHIEVEMENT_CRITERIA); - stmt->setUInt64(0, GetOwner()->GetId()); - stmt->setUInt32(1, itr->first); - trans->Append(stmt); - - stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_ACHIEVEMENT_CRITERIA); - stmt->setUInt64(0, GetOwner()->GetId()); - stmt->setUInt32(1, itr->first); - stmt->setUInt64(2, itr->second.counter); - stmt->setUInt32(3, itr->second.date); - stmt->setUInt64(4, itr->second.PlayerGUID.GetCounter()); - trans->Append(stmt); - } -} -template<class T> -void AchievementMgr<T>::LoadFromDB(PreparedQueryResult /*achievementResult*/, PreparedQueryResult /*criteriaResult*/) -{ -} - -template<> -void AchievementMgr<Player>::LoadFromDB(PreparedQueryResult achievementResult, PreparedQueryResult criteriaResult) +void PlayerAchievementMgr::LoadFromDB(PreparedQueryResult achievementResult, PreparedQueryResult criteriaResult) { if (achievementResult) { do { Field* fields = achievementResult->Fetch(); - uint32 achievementid = fields[0].GetUInt16(); + uint32 achievementid = fields[0].GetUInt32(); // must not happen: cleanup at server startup in sAchievementMgr->LoadCompletedAchievements() - AchievementEntry const* achievement = sAchievementMgr->GetAchievement(achievementid); + AchievementEntry const* achievement = sAchievementStore.LookupEntry(achievementid); if (!achievement) continue; - CompletedAchievementData& ca = m_completedAchievements[achievementid]; - ca.date = time_t(fields[1].GetUInt32()); - ca.changed = false; + CompletedAchievementData& ca = _completedAchievements[achievementid]; + ca.Date = time_t(fields[1].GetUInt32()); + ca.Changed = false; _achievementPoints += achievement->Points; // title achievement rewards are retroactive if (AchievementReward const* reward = sAchievementMgr->GetAchievementReward(achievement)) - if (uint32 titleId = reward->titleId[Player::TeamForRace(GetOwner()->getRace()) == ALLIANCE ? 0 : 1]) + if (uint32 titleId = reward->TitleId[Player::TeamForRace(_owner->getRace()) == ALLIANCE ? 0 : 1]) if (CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(titleId)) - GetOwner()->SetTitle(titleEntry); + _owner->SetTitle(titleEntry); - } - while (achievementResult->NextRow()); + } while (achievementResult->NextRow()); } if (criteriaResult) @@ -664,15 +241,15 @@ void AchievementMgr<Player>::LoadFromDB(PreparedQueryResult achievementResult, P do { Field* fields = criteriaResult->Fetch(); - uint32 id = fields[0].GetUInt32(); + uint32 id = fields[0].GetUInt32(); uint64 counter = fields[1].GetUInt64(); - time_t date = time_t(fields[2].GetUInt32()); + time_t date = time_t(fields[2].GetUInt32()); - AchievementCriteria const* criteria = sAchievementMgr->GetAchievementCriteria(id); + Criteria const* criteria = sCriteriaMgr->GetCriteria(id); if (!criteria) { // Removing non-existing criteria data for all characters - TC_LOG_ERROR("achievement", "Non-existing achievement criteria %u data has been removed from the table `character_achievement_progress`.", id); + TC_LOG_ERROR("criteria.achievement", "Non-existing achievement criteria %u data has been removed from the table `character_achievement_progress`.", id); PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_ACHIEV_PROGRESS_CRITERIA); stmt->setUInt32(0, uint16(id)); @@ -684,1052 +261,183 @@ void AchievementMgr<Player>::LoadFromDB(PreparedQueryResult achievementResult, P if (criteria->Entry->StartTimer && time_t(date + criteria->Entry->StartTimer) < now) continue; - CriteriaProgress& progress = m_criteriaProgress[id]; - progress.counter = counter; - progress.date = date; - progress.changed = false; - } - while (criteriaResult->NextRow()); + CriteriaProgress& progress = _criteriaProgress[id]; + progress.Counter = counter; + progress.Date = date; + progress.Changed = false; + } while (criteriaResult->NextRow()); } } -template<> -void AchievementMgr<Guild>::LoadFromDB(PreparedQueryResult achievementResult, PreparedQueryResult criteriaResult) +void PlayerAchievementMgr::SaveToDB(SQLTransaction& trans) { - if (achievementResult) + if (!_completedAchievements.empty()) { - do + for (auto iter = _completedAchievements.begin(); iter != _completedAchievements.end(); ++iter) { - Field* fields = achievementResult->Fetch(); - uint32 achievementid = fields[0].GetUInt16(); - - // must not happen: cleanup at server startup in sAchievementMgr->LoadCompletedAchievements() - AchievementEntry const* achievement = sAchievementMgr->GetAchievement(achievementid); - if (!achievement) + if (!iter->second.Changed) continue; - CompletedAchievementData& ca = m_completedAchievements[achievementid]; - ca.date = time_t(fields[1].GetUInt32()); - Tokenizer guids(fields[2].GetString(), ' '); - for (uint32 i = 0; i < guids.size(); ++i) - ca.guids.insert(ObjectGuid::Create<HighGuid::Player>(uint64(strtoull(guids[i], nullptr, 10)))); + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_ACHIEVEMENT_BY_ACHIEVEMENT); + stmt->setUInt32(0, iter->first); + stmt->setUInt64(1, _owner->GetGUID().GetCounter()); + trans->Append(stmt); - ca.changed = false; + stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_ACHIEVEMENT); + stmt->setUInt64(0, _owner->GetGUID().GetCounter()); + stmt->setUInt32(1, iter->first); + stmt->setUInt32(2, uint32(iter->second.Date)); + trans->Append(stmt); - _achievementPoints += achievement->Points; + iter->second.Changed = false; } - while (achievementResult->NextRow()); } - if (criteriaResult) + if (!_criteriaProgress.empty()) { - time_t now = time(NULL); - do + for (auto iter = _criteriaProgress.begin(); iter != _criteriaProgress.end(); ++iter) { - Field* fields = criteriaResult->Fetch(); - uint32 id = fields[0].GetUInt32(); - uint64 counter = fields[1].GetUInt64(); - time_t date = time_t(fields[2].GetUInt32()); - ObjectGuid::LowType guid = fields[3].GetUInt64(); - - AchievementCriteria const* criteria = sAchievementMgr->GetAchievementCriteria(id); - if (!criteria) - { - // we will remove not existed criteria for all guilds - TC_LOG_ERROR("achievement", "Non-existing achievement criteria %u data removed from table `guild_achievement_progress`.", id); - - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_ACHIEV_PROGRESS_CRITERIA_GUILD); - stmt->setUInt32(0, uint16(id)); - CharacterDatabase.Execute(stmt); - continue; - } - - if (criteria->Entry->StartTimer && time_t(date + criteria->Entry->StartTimer) < now) + if (!iter->second.Changed) continue; - CriteriaProgress& progress = m_criteriaProgress[id]; - progress.counter = counter; - progress.date = date; - progress.PlayerGUID = ObjectGuid::Create<HighGuid::Player>(guid); - progress.changed = false; - } while (criteriaResult->NextRow()); - } -} - -template<class T> -void AchievementMgr<T>::Reset() -{ -} - -template<> -void AchievementMgr<Player>::Reset() -{ - for (CompletedAchievementMap::const_iterator iter = m_completedAchievements.begin(); iter != m_completedAchievements.end(); ++iter) - { - WorldPackets::Achievement::AchievementDeleted achievementDeleted; - achievementDeleted.AchievementID = iter->first; - SendPacket(achievementDeleted.Write()); - } - - for (CriteriaProgressMap::const_iterator iter = m_criteriaProgress.begin(); iter != m_criteriaProgress.end(); ++iter) - { - WorldPackets::Achievement::CriteriaDeleted criteriaDeleted; - criteriaDeleted.CriteriaID = iter->first; - SendPacket(criteriaDeleted.Write()); - } - - m_completedAchievements.clear(); - _achievementPoints = 0; - m_criteriaProgress.clear(); - DeleteFromDB(GetOwner()->GetGUID()); - - // re-fill data - CheckAllAchievementCriteria(GetOwner()); -} - -template<> -void AchievementMgr<Guild>::Reset() -{ - ObjectGuid guid = GetOwner()->GetGUID(); - for (CompletedAchievementMap::const_iterator iter = m_completedAchievements.begin(); iter != m_completedAchievements.end(); ++iter) - { - WorldPackets::Achievement::GuildAchievementDeleted guildAchievementDeleted; - guildAchievementDeleted.AchievementID = iter->first; - guildAchievementDeleted.GuildGUID = guid; - guildAchievementDeleted.TimeDeleted = time(NULL); - SendPacket(guildAchievementDeleted.Write()); - } - - while (!m_criteriaProgress.empty()) - if (AchievementCriteria const* criteria = sAchievementMgr->GetAchievementCriteria(m_criteriaProgress.begin()->first)) - RemoveCriteriaProgress(criteria); - - _achievementPoints = 0; - m_completedAchievements.clear(); - DeleteFromDB(GetOwner()->GetGUID()); -} - -template<class T> -void AchievementMgr<T>::SendAchievementEarned(AchievementEntry const* achievement) const -{ - // Don't send for achievements with ACHIEVEMENT_FLAG_HIDDEN - if (achievement->Flags & ACHIEVEMENT_FLAG_HIDDEN) - return; - - TC_LOG_DEBUG("achievement", "AchievementMgr::SendAchievementEarned(%u)", achievement->ID); - - if (Guild* guild = sGuildMgr->GetGuildById(GetOwner()->GetGuildId())) - { - Trinity::BroadcastTextBuilder _builder(GetOwner(), CHAT_MSG_GUILD_ACHIEVEMENT, BROADCAST_TEXT_ACHIEVEMENT_EARNED, GetOwner(), achievement->ID); - Trinity::LocalizedPacketDo<Trinity::BroadcastTextBuilder> _localizer(_builder); - guild->BroadcastWorker(_localizer, GetOwner()); - } - - if (achievement->Flags & (ACHIEVEMENT_FLAG_REALM_FIRST_KILL | ACHIEVEMENT_FLAG_REALM_FIRST_REACH)) - { - // broadcast realm first reached - WorldPackets::Achievement::ServerFirstAchievement serverFirstAchievement; - serverFirstAchievement.Name = GetOwner()->GetName(); - serverFirstAchievement.PlayerGUID = GetOwner()->GetGUID(); - serverFirstAchievement.AchievementID = achievement->ID; - sWorld->SendGlobalMessage(serverFirstAchievement.Write()); - } - // if player is in world he can tell his friends about new achievement - else if (GetOwner()->IsInWorld()) - { - Trinity::BroadcastTextBuilder _builder(GetOwner(), CHAT_MSG_ACHIEVEMENT, BROADCAST_TEXT_ACHIEVEMENT_EARNED, GetOwner(), achievement->ID); - Trinity::LocalizedPacketDo<Trinity::BroadcastTextBuilder> _localizer(_builder); - Trinity::PlayerDistWorker<Trinity::LocalizedPacketDo<Trinity::BroadcastTextBuilder> > _worker(GetOwner(), sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_SAY), _localizer); - GetOwner()->VisitNearbyWorldObject(sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_SAY), _worker); - } - - WorldPackets::Achievement::AchievementEarned achievementEarned; - achievementEarned.Sender = GetOwner()->GetGUID(); - achievementEarned.Earner = GetOwner()->GetGUID(); - achievementEarned.EarnerNativeRealm = achievementEarned.EarnerVirtualRealm = GetVirtualRealmAddress(); - achievementEarned.AchievementID = achievement->ID; - achievementEarned.Time = time(NULL); - GetOwner()->SendMessageToSetInRange(achievementEarned.Write(), sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_SAY), true); -} - -template<> -void AchievementMgr<Guild>::SendAchievementEarned(AchievementEntry const* achievement) const -{ - if (achievement->Flags & (ACHIEVEMENT_FLAG_REALM_FIRST_KILL | ACHIEVEMENT_FLAG_REALM_FIRST_REACH)) - { - // broadcast realm first reached - WorldPackets::Achievement::ServerFirstAchievement serverFirstAchievement; - serverFirstAchievement.Name = GetOwner()->GetName(); - serverFirstAchievement.PlayerGUID = GetOwner()->GetGUID(); - serverFirstAchievement.AchievementID = achievement->ID; - serverFirstAchievement.GuildAchievement = true; - sWorld->SendGlobalMessage(serverFirstAchievement.Write()); - } - - WorldPackets::Achievement::GuildAchievementEarned guildAchievementEarned; - guildAchievementEarned.AchievementID = achievement->ID; - guildAchievementEarned.GuildGUID = GetOwner()->GetGUID(); - guildAchievementEarned.TimeEarned = time(NULL); - SendPacket(guildAchievementEarned.Write()); -} - -template<class T> -void AchievementMgr<T>::SendCriteriaUpdate(AchievementCriteria const* /*criteria*/, CriteriaProgress const* /*progress*/, uint32 /*timeElapsed*/, bool /*timedCompleted*/) const -{ -} - -template<> -void AchievementMgr<Player>::SendCriteriaUpdate(AchievementCriteria const* criteria, CriteriaProgress const* progress, uint32 timeElapsed, bool timedCompleted) const -{ - WorldPackets::Achievement::CriteriaUpdate criteriaUpdate; - - criteriaUpdate.CriteriaID = criteria->ID; - criteriaUpdate.Quantity = progress->counter; - criteriaUpdate.PlayerGUID = GetOwner()->GetGUID(); - if (criteria->Entry->StartTimer) - criteriaUpdate.Flags = timedCompleted ? 1 : 0; // 1 is for keeping the counter at 0 in client - - criteriaUpdate.Flags = 0; - criteriaUpdate.CurrentTime = progress->date; - criteriaUpdate.ElapsedTime = timeElapsed; - criteriaUpdate.CreationTime = 0; - - SendPacket(criteriaUpdate.Write()); -} - -template<> -void AchievementMgr<Guild>::SendCriteriaUpdate(AchievementCriteria const* entry, CriteriaProgress const* progress, uint32 /*timeElapsed*/, bool /*timedCompleted*/) const -{ - WorldPackets::Achievement::GuildCriteriaUpdate guildCriteriaUpdate; - guildCriteriaUpdate.Progress.resize(1); - - WorldPackets::Achievement::GuildCriteriaProgress& guildCriteriaProgress = guildCriteriaUpdate.Progress[0]; - guildCriteriaProgress.CriteriaID = entry->ID; - guildCriteriaProgress.DateCreated = 0; - guildCriteriaProgress.DateStarted = 0; - guildCriteriaProgress.DateUpdated = progress->date; - guildCriteriaProgress.Quantity = progress->counter; - guildCriteriaProgress.PlayerGUID = progress->PlayerGUID; - guildCriteriaProgress.Flags = 0; - - GetOwner()->BroadcastPacketIfTrackingAchievement(guildCriteriaUpdate.Write(), entry->ID); -} - -template<class T> -void AchievementMgr<T>::SendAllTrackedCriterias(Player* /*receiver*/, std::set<uint32> const& /*trackedCriterias*/) const -{ -} - -template<> -void AchievementMgr<Guild>::SendAllTrackedCriterias(Player* receiver, std::set<uint32> const& trackedCriterias) const -{ - WorldPackets::Achievement::GuildCriteriaUpdate guildCriteriaUpdate; - guildCriteriaUpdate.Progress.reserve(trackedCriterias.size()); - - for (uint32 criteriaId : trackedCriterias) - { - auto progress = m_criteriaProgress.find(criteriaId); - if (progress == m_criteriaProgress.end()) - continue; + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_ACHIEVEMENT_PROGRESS_BY_CRITERIA); + stmt->setUInt64(0, _owner->GetGUID().GetCounter()); + stmt->setUInt32(1, iter->first); + trans->Append(stmt); - WorldPackets::Achievement::GuildCriteriaProgress guildCriteriaProgress; - guildCriteriaProgress.CriteriaID = criteriaId; - guildCriteriaProgress.DateCreated = 0; - guildCriteriaProgress.DateStarted = 0; - guildCriteriaProgress.DateUpdated = progress->second.date; - guildCriteriaProgress.Quantity = progress->second.counter; - guildCriteriaProgress.PlayerGUID = progress->second.PlayerGUID; - guildCriteriaProgress.Flags = 0; + if (iter->second.Counter) + { + stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_ACHIEVEMENT_PROGRESS); + stmt->setUInt64(0, _owner->GetGUID().GetCounter()); + stmt->setUInt32(1, iter->first); + stmt->setUInt64(2, iter->second.Counter); + stmt->setUInt32(3, uint32(iter->second.Date)); + trans->Append(stmt); + } - guildCriteriaUpdate.Progress.push_back(guildCriteriaProgress); + iter->second.Changed = false; + } } - - receiver->GetSession()->SendPacket(guildCriteriaUpdate.Write()); } -/** - * called at player login. The player might have fulfilled some achievements when the achievement system wasn't working yet - */ -template<class T> -void AchievementMgr<T>::CheckAllAchievementCriteria(Player* referencePlayer) +void PlayerAchievementMgr::ResetCriteria(CriteriaTypes type, uint64 miscValue1, uint64 miscValue2, bool evenIfCriteriaComplete) { - // suppress sending packets - for (uint32 i = 0; i < ACHIEVEMENT_CRITERIA_TYPE_TOTAL; ++i) - UpdateAchievementCriteria(AchievementCriteriaTypes(i), 0, 0, 0, NULL, referencePlayer); -} - -// Helper function to avoid having to specialize template for a 800 line long function -template <typename T> static bool IsGuild() { return false; } -template<> bool IsGuild<Guild>() { return true; } - -/** - * this function will be called whenever the user might have done a criteria relevant action - */ -template<class T> -void AchievementMgr<T>::UpdateAchievementCriteria(AchievementCriteriaTypes type, uint64 miscValue1 /*= 0*/, uint64 miscValue2 /*= 0*/, uint64 miscValue3 /*= 0*/, Unit const* unit /*= NULL*/, Player* referencePlayer /*= NULL*/) -{ - if (type >= ACHIEVEMENT_CRITERIA_TYPE_TOTAL) - { - TC_LOG_DEBUG("achievement", "UpdateAchievementCriteria: Wrong criteria type %u", type); - return; - } - - if (!referencePlayer) - { - TC_LOG_DEBUG("achievement", "UpdateAchievementCriteria: Player is NULL! Cant update criteria"); - return; - } + TC_LOG_DEBUG("criteria.achievement", "PlayerAchievementMgr::ResetCriteria(%u, " UI64FMTD ", " UI64FMTD ")", type, miscValue1, miscValue2); // disable for gamemasters with GM-mode enabled - if (referencePlayer->IsGameMaster()) - { - TC_LOG_DEBUG("achievement", "UpdateAchievementCriteria: [Player %s GM mode on] %s, %s (%u), " UI64FMTD ", " UI64FMTD ", " UI64FMTD - , referencePlayer->GetName().c_str(), GetOwner()->GetGUID().ToString().c_str(), AchievementGlobalMgr::GetCriteriaTypeString(type), type, miscValue1, miscValue2, miscValue3); + if (_owner->IsGameMaster()) return; - } - - TC_LOG_DEBUG("achievement", "UpdateAchievementCriteria: %s, %s (%u), " UI64FMTD ", " UI64FMTD ", " UI64FMTD - , GetOwner()->GetGUID().ToString().c_str(), AchievementGlobalMgr::GetCriteriaTypeString(type), type, miscValue1, miscValue2, miscValue3); - - // Lua_GetGuildLevelEnabled() is checked in achievement UI to display guild tab - //if (IsGuild<T>() && !sWorld->getBoolConfig(CONFIG_GUILD_LEVELING_ENABLED)) - // return; - AchievementCriteriaList const& achievementCriteriaList = sAchievementMgr->GetAchievementCriteriaByType(type, IsGuild<T>()); - for (AchievementCriteria const* achievementCriteria : achievementCriteriaList) + CriteriaList const& achievementCriteriaList = GetCriteriaByType(type); + for (Criteria const* achievementCriteria : achievementCriteriaList) { - AchievementCriteriaTreeList const* trees = sAchievementMgr->GetAchievementCriteriaTreesByCriteria(achievementCriteria->ID); - - if (!CanUpdateCriteria(achievementCriteria, trees, miscValue1, miscValue2, miscValue3, unit, referencePlayer)) + if (achievementCriteria->Entry->FailEvent != miscValue1 || (achievementCriteria->Entry->FailAsset && achievementCriteria->Entry->FailAsset != miscValue2)) continue; - // requirements not found in the dbc - if (AchievementCriteriaDataSet const* data = sAchievementMgr->GetCriteriaDataSet(achievementCriteria)) - if (!data->Meets(referencePlayer, unit, miscValue1)) - continue; - - switch (type) + std::vector<CriteriaTree const*> const* trees = sCriteriaMgr->GetCriteriaTreesByCriteria(achievementCriteria->ID); + bool allComplete = true; + for (CriteriaTree const* tree : *trees) { - // std. case: increment at 1 - case ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS: - case ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL: - case ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION: - case ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS: /* FIXME: for online player only currently */ - case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED: - case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED: - case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED: - case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN: - case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS: - case ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM: - case ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM: - case ACHIEVEMENT_CRITERIA_TYPE_DEATH: - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST: - case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP: - case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON: - case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE: - case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER: - case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM: - case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET: - case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2: - case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: - case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: - case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: - case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM: - case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT: - case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT: - case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: - case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT: - case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT: - case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL: - case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS: - case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE: - case ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE: - case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL: - case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL: - case ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS: - case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA: - case ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA: // This also behaves like ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA - case ACHIEVEMENT_CRITERIA_TYPE_ON_LOGIN: - case ACHIEVEMENT_CRITERIA_TYPE_PLACE_GARRISON_BUILDING: - case ACHIEVEMENT_CRITERIA_TYPE_OWN_BATTLE_PET_COUNT: - SetCriteriaProgress(achievementCriteria, 1, referencePlayer, PROGRESS_ACCUMULATE); - break; - // std case: increment at miscValue1 - case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS: - case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS: - case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD: - case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TRAVELLING: - case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER: - case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL: - case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY: - case ACHIEVEMENT_CRITERIA_TYPE_GOLD_EARNED_BY_AUCTIONS:/* FIXME: for online player only currently */ - case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_DAMAGE_RECEIVED: - case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_HEALING_RECEIVED: - case ACHIEVEMENT_CRITERIA_TYPE_USE_LFD_TO_GROUP_WITH_PLAYERS: - case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG: - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND: - case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE: - case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE: - SetCriteriaProgress(achievementCriteria, miscValue1, referencePlayer, PROGRESS_ACCUMULATE); - break; - case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE: - case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE: - case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE: - case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM: - case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM: - case ACHIEVEMENT_CRITERIA_TYPE_CURRENCY: - SetCriteriaProgress(achievementCriteria, miscValue2, referencePlayer, PROGRESS_ACCUMULATE); - break; - // std case: high value at miscValue1 - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID: - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD: /* FIXME: for online player only currently */ - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_DEALT: - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_RECEIVED: - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEAL_CAST: - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALING_RECEIVED: - SetCriteriaProgress(achievementCriteria, miscValue1, referencePlayer, PROGRESS_HIGHEST); - break; - case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL: - SetCriteriaProgress(achievementCriteria, referencePlayer->getLevel(), referencePlayer); - break; - case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL: - if (uint32 skillvalue = referencePlayer->GetBaseSkillValue(achievementCriteria->Entry->Asset.SkillID)) - SetCriteriaProgress(achievementCriteria, skillvalue, referencePlayer); - break; - case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL: - if (uint32 maxSkillvalue = referencePlayer->GetPureMaxSkillValue(achievementCriteria->Entry->Asset.SkillID)) - SetCriteriaProgress(achievementCriteria, maxSkillvalue, referencePlayer); - break; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT: - SetCriteriaProgress(achievementCriteria, referencePlayer->GetRewardedQuestCount(), referencePlayer); - break; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST_DAILY: - { - time_t nextDailyResetTime = sWorld->GetNextDailyQuestsResetTime(); - CriteriaProgress *progress = GetCriteriaProgress(achievementCriteria); - - if (!miscValue1) // Login case. - { - // reset if player missed one day. - if (progress && progress->date < (nextDailyResetTime - 2 * DAY)) - SetCriteriaProgress(achievementCriteria, 0, referencePlayer, PROGRESS_SET); - continue; - } - - ProgressType progressType; - if (!progress) - // 1st time. Start count. - progressType = PROGRESS_SET; - else if (progress->date < (nextDailyResetTime - 2 * DAY)) - // last progress is older than 2 days. Player missed 1 day => Restart count. - progressType = PROGRESS_SET; - else if (progress->date < (nextDailyResetTime - DAY)) - // last progress is between 1 and 2 days. => 1st time of the day. - progressType = PROGRESS_ACCUMULATE; - else - // last progress is within the day before the reset => Already counted today. - continue; - - SetCriteriaProgress(achievementCriteria, 1, referencePlayer, progressType); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE: - { - uint32 counter = 0; - - const RewardedQuestSet &rewQuests = referencePlayer->getRewardedQuests(); - for (RewardedQuestSet::const_iterator itr = rewQuests.begin(); itr != rewQuests.end(); ++itr) - { - Quest const* quest = sObjectMgr->GetQuestTemplate(*itr); - if (quest && quest->GetZoneOrSort() >= 0 && uint32(quest->GetZoneOrSort()) == achievementCriteria->Entry->Asset.ZoneID) - ++counter; - } - SetCriteriaProgress(achievementCriteria, counter, referencePlayer); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING: - // miscValue1 is the ingame fallheight*100 as stored in dbc - SetCriteriaProgress(achievementCriteria, miscValue1, referencePlayer); - break; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST: - case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL: - case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA: - case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP: - case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM: - case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM: - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT: - case ACHIEVEMENT_CRITERIA_TYPE_RECRUIT_GARRISON_FOLLOWER: - case ACHIEVEMENT_CRITERIA_TYPE_OWN_BATTLE_PET: - SetCriteriaProgress(achievementCriteria, 1, referencePlayer); - break; - case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT: - SetCriteriaProgress(achievementCriteria, referencePlayer->GetBankBagSlotCount(), referencePlayer); - break; - case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION: - { - int32 reputation = referencePlayer->GetReputationMgr().GetReputation(achievementCriteria->Entry->Asset.FactionID); - if (reputation > 0) - SetCriteriaProgress(achievementCriteria, reputation, referencePlayer); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION: - SetCriteriaProgress(achievementCriteria, referencePlayer->GetReputationMgr().GetExaltedFactionCount(), referencePlayer); - break; - case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS: - case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE: - { - uint32 spellCount = 0; - for (PlayerSpellMap::const_iterator spellIter = referencePlayer->GetSpellMap().begin(); - spellIter != referencePlayer->GetSpellMap().end(); - ++spellIter) - { - SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellIter->first); - for (SkillLineAbilityMap::const_iterator skillIter = bounds.first; skillIter != bounds.second; ++skillIter) - { - if (skillIter->second->SkillLine == achievementCriteria->Entry->Asset.SkillID) - spellCount++; - } - } - SetCriteriaProgress(achievementCriteria, spellCount, referencePlayer); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION: - SetCriteriaProgress(achievementCriteria, referencePlayer->GetReputationMgr().GetReveredFactionCount(), referencePlayer); - break; - case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION: - SetCriteriaProgress(achievementCriteria, referencePlayer->GetReputationMgr().GetHonoredFactionCount(), referencePlayer); - break; - case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS: - SetCriteriaProgress(achievementCriteria, referencePlayer->GetReputationMgr().GetVisibleFactionCount(), referencePlayer); - break; - case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL: - SetCriteriaProgress(achievementCriteria, referencePlayer->GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS), referencePlayer); - break; - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED: - SetCriteriaProgress(achievementCriteria, referencePlayer->GetMoney(), referencePlayer, PROGRESS_HIGHEST); - break; - case ACHIEVEMENT_CRITERIA_TYPE_EARN_ACHIEVEMENT_POINTS: - if (!miscValue1) - SetCriteriaProgress(achievementCriteria, _achievementPoints, referencePlayer, PROGRESS_SET); - else - SetCriteriaProgress(achievementCriteria, miscValue1, referencePlayer, PROGRESS_ACCUMULATE); - break; - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_PERSONAL_RATING: + // don't update already completed criteria if not forced or achievement already complete + if (!(IsCompletedCriteriaTree(tree) && !evenIfCriteriaComplete) || !HasAchieved(tree->Achievement->ID)) { - uint32 reqTeamType = achievementCriteria->Entry->Asset.TeamType; - - if (miscValue1) - { - if (miscValue2 != reqTeamType) - continue; - - SetCriteriaProgress(achievementCriteria, miscValue1, referencePlayer, PROGRESS_HIGHEST); - } - else // login case - { - for (uint32 arena_slot = 0; arena_slot < MAX_ARENA_SLOT; ++arena_slot) - { - uint32 teamId = referencePlayer->GetArenaTeamId(arena_slot); - if (!teamId) - continue; - - ArenaTeam* team = sArenaTeamMgr->GetArenaTeamById(teamId); - if (!team || team->GetType() != reqTeamType) - continue; - - if (ArenaTeamMember const* member = team->GetMember(referencePlayer->GetGUID())) - { - SetCriteriaProgress(achievementCriteria, member->PersonalRating, referencePlayer, PROGRESS_HIGHEST); - break; - } - } - } + allComplete = false; break; } - case ACHIEVEMENT_CRITERIA_TYPE_REACH_GUILD_LEVEL: - SetCriteriaProgress(achievementCriteria, miscValue1, referencePlayer); - break; - // FIXME: not triggered in code as result, need to implement - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_RAID: - case ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA: - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_TEAM_RATING: - case ACHIEVEMENT_CRITERIA_TYPE_OWN_RANK: - case ACHIEVEMENT_CRITERIA_TYPE_SPENT_GOLD_GUILD_REPAIRS: - case ACHIEVEMENT_CRITERIA_TYPE_CRAFT_ITEMS_GUILD: - case ACHIEVEMENT_CRITERIA_TYPE_CATCH_FROM_POOL: - case ACHIEVEMENT_CRITERIA_TYPE_BUY_GUILD_BANK_SLOTS: - case ACHIEVEMENT_CRITERIA_TYPE_EARN_GUILD_ACHIEVEMENT_POINTS: - case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_BATTLEGROUND: - case ACHIEVEMENT_CRITERIA_TYPE_REACH_BG_RATING: - case ACHIEVEMENT_CRITERIA_TYPE_BUY_GUILD_TABARD: - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_GUILD: - case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILLS_GUILD: - case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE_GUILD: - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ARCHAEOLOGY_PROJECTS: - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_GUILD_CHALLENGE_TYPE: - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_GUILD_CHALLENGE: - case ACHIEVEMENT_CRITERIA_TYPE_LFR_DUNGEONS_COMPLETED: - case ACHIEVEMENT_CRITERIA_TYPE_LFR_LEAVES: - case ACHIEVEMENT_CRITERIA_TYPE_LFR_VOTE_KICKS_INITIATED_BY_PLAYER: - case ACHIEVEMENT_CRITERIA_TYPE_LFR_VOTE_KICKS_NOT_INIT_BY_PLAYER: - case ACHIEVEMENT_CRITERIA_TYPE_BE_KICKED_FROM_LFR: - case ACHIEVEMENT_CRITERIA_TYPE_COUNT_OF_LFR_QUEUE_BOOSTS_BY_TANK: - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_SCENARIO_COUNT: - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_SCENARIO: - case ACHIEVEMENT_CRITERIA_TYPE_CAPTURE_BATTLE_PET: - case ACHIEVEMENT_CRITERIA_TYPE_WIN_PET_BATTLE: - case ACHIEVEMENT_CRITERIA_TYPE_LEVEL_BATTLE_PET: - case ACHIEVEMENT_CRITERIA_TYPE_CAPTURE_BATTLE_PET_CREDIT: - case ACHIEVEMENT_CRITERIA_TYPE_LEVEL_BATTLE_PET_CREDIT: - case ACHIEVEMENT_CRITERIA_TYPE_ENTER_AREA: - case ACHIEVEMENT_CRITERIA_TYPE_LEAVE_AREA: - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DUNGEON_ENCOUNTER: - case ACHIEVEMENT_CRITERIA_TYPE_UPGRADE_GARRISON_BUILDING: - case ACHIEVEMENT_CRITERIA_TYPE_CONSTRUCT_GARRISON_BUILDING: - case ACHIEVEMENT_CRITERIA_TYPE_UPGRADE_GARRISON: - case ACHIEVEMENT_CRITERIA_TYPE_START_GARRISON_MISSION: - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_GARRISON_MISSION_COUNT: - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_GARRISON_MISSION: - case ACHIEVEMENT_CRITERIA_TYPE_RECRUIT_GARRISON_FOLLOWER_COUNT: - case ACHIEVEMENT_CRITERIA_TYPE_LEARN_GARRISON_BLUEPRINT_COUNT: - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_GARRISON_SHIPMENT: - case ACHIEVEMENT_CRITERIA_TYPE_RAISE_GARRISON_FOLLOWER_ITEM_LEVEL: - case ACHIEVEMENT_CRITERIA_TYPE_RAISE_GARRISON_FOLLOWER_LEVEL: - case ACHIEVEMENT_CRITERIA_TYPE_OWN_TOY: - case ACHIEVEMENT_CRITERIA_TYPE_OWN_TOY_COUNT: - case ACHIEVEMENT_CRITERIA_TYPE_OWN_HEIRLOOMS: - break; // Not implemented yet :( - } - - for (AchievementCriteriaTree const* tree : *trees) - { - if (IsCompletedCriteriaTree(tree)) - CompletedCriteriaFor(tree->Achievement, referencePlayer); - - // check again the completeness for SUMM and REQ COUNT achievements, - // as they don't depend on the completed criteria but on the sum of the progress of each individual criteria - if (tree->Achievement->Flags & ACHIEVEMENT_FLAG_SUMM) - if (IsCompletedAchievement(tree->Achievement)) - CompletedAchievement(tree->Achievement, referencePlayer); - - if (AchievementEntryList const* achRefList = sAchievementMgr->GetAchievementByReferencedId(tree->Achievement->ID)) - for (AchievementEntry const* refAchievement : *achRefList) - if (IsCompletedAchievement(refAchievement)) - CompletedAchievement(refAchievement, referencePlayer); } - } -} - -// Only player personal achievements require instance id to check realm firsts -// Guild restrictions are handled with additionalConditionType/additionalConditionValue -template<class T> -static uint32 GetInstanceId(T* /*object*/) { return 0xFFFFFFFF; } - -template<> -uint32 GetInstanceId(Player* player) { return player->GetInstanceId(); } - -template<class T> -bool AchievementMgr<T>::IsCompletedCriteriaTree(AchievementCriteriaTree const* tree) -{ - AchievementEntry const* achievement = tree->Achievement; - if (!achievement) - return false; - - // counter can never complete - if (achievement->Flags & ACHIEVEMENT_FLAG_COUNTER) - return false; - - if (achievement->Flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL)) - { - // someone on this realm has already completed that achievement - if (sAchievementMgr->IsRealmCompleted(achievement, GetInstanceId(GetOwner()))) - return false; - } - - uint64 requiredCount = tree->Entry->Amount; - uint64 completedCount = 0; - uint32 op = tree->Entry->Operator; - bool hasAll = true; - - // Check criteria we depend on first - for (AchievementCriteriaTree const* node : tree->Children) - { - if (IsCompletedCriteriaTree(node)) - ++completedCount; - else - hasAll = false; - - if (op & ACHIEVEMENT_CRITERIA_TREE_OPERATOR_ANY && completedCount >= requiredCount) - { - if (!tree->Criteria) - return true; - - break; - } - } - - if (op & ACHIEVEMENT_CRITERIA_TREE_OPERATOR_ANY && completedCount < requiredCount) - return false; - if (op & ACHIEVEMENT_CRITERIA_TREE_OPERATOR_ALL && !hasAll) - return false; - - if (!tree->Criteria) - return true; - - return IsCompletedCriteria(tree->Criteria, requiredCount); -} - -template<class T> -bool AchievementMgr<T>::IsCompletedCriteria(AchievementCriteria const* achievementCriteria, uint64 requiredAmount) -{ - CriteriaProgress const* progress = GetCriteriaProgress(achievementCriteria); - if (!progress) - return false; - - switch (AchievementCriteriaTypes(achievementCriteria->Entry->Type)) - { - case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG: - case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE: - case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL: - case ACHIEVEMENT_CRITERIA_TYPE_REACH_GUILD_LEVEL: - case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL: - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT: - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST_DAILY: - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE: - case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE: - case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE: - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST: - case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING: - case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET: - case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2: - case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: - case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: - case ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE: - case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA: - case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL: - case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL: - case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM: - case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_PERSONAL_RATING: - case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM: - case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM: - case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT: - case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION: - case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION: - case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP: - case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM: - case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT: - case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT: - case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS: - case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE: - case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: - case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM: - case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD: - case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY: - case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT: - case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL: - case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT: - case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS: - case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL: - case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE: - case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE: - case ACHIEVEMENT_CRITERIA_TYPE_USE_LFD_TO_GROUP_WITH_PLAYERS: - case ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS: - case ACHIEVEMENT_CRITERIA_TYPE_CURRENCY: - case ACHIEVEMENT_CRITERIA_TYPE_PLACE_GARRISON_BUILDING: - case ACHIEVEMENT_CRITERIA_TYPE_OWN_BATTLE_PET_COUNT: - return progress->counter >= requiredAmount; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT: - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST: - case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL: - case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA: - case ACHIEVEMENT_CRITERIA_TYPE_RECRUIT_GARRISON_FOLLOWER: - case ACHIEVEMENT_CRITERIA_TYPE_OWN_BATTLE_PET: - return progress->counter >= 1; - case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL: - return progress->counter >= (requiredAmount * 75); - case ACHIEVEMENT_CRITERIA_TYPE_EARN_ACHIEVEMENT_POINTS: - return progress->counter >= 9000; - case ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA: - return requiredAmount && progress->counter >= requiredAmount; - case ACHIEVEMENT_CRITERIA_TYPE_ON_LOGIN: - return true; - // handle all statistic-only criteria here - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND: - case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP: - case ACHIEVEMENT_CRITERIA_TYPE_DEATH: - case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON: - case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE: - case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER: - case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM: - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_TEAM_RATING: - case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS: - case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS: - case ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS: - case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER: - case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL: - case ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL: - case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE: - case ACHIEVEMENT_CRITERIA_TYPE_GOLD_EARNED_BY_AUCTIONS: - case ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION: - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID: - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD: - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED: - case ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS: - case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION: - case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION: - case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS: - case ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM: - case ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM: - case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED: - case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED: - case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED: - case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN: - case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS: - default: - break; - } - - return false; -} - -template<class T> -void AchievementMgr<T>::CompletedCriteriaFor(AchievementEntry const* achievement, Player* referencePlayer) -{ - // counter can never complete - if (achievement->Flags & ACHIEVEMENT_FLAG_COUNTER) - return; - - // already completed and stored - if (HasAchieved(achievement->ID)) - return; - - if (IsCompletedAchievement(achievement)) - CompletedAchievement(achievement, referencePlayer); -} - -template<class T> -bool AchievementMgr<T>::IsCompletedAchievement(AchievementEntry const* entry) -{ - // counter can never complete - if (entry->Flags & ACHIEVEMENT_FLAG_COUNTER) - return false; - - AchievementCriteriaTree const* tree = sAchievementMgr->GetAchievementCriteriaTree(entry->CriteriaTree); - if (!tree) - return false; + if (allComplete) + continue; - // For SUMM achievements, we have to count the progress of each criteria of the achievement. - // Oddly, the target count is NOT contained in the achievement, but in each individual criteria - if (entry->Flags & ACHIEVEMENT_FLAG_SUMM) - { - uint64 progress = 0; - sAchievementMgr->WalkCriteriaTree(tree, [this, &progress](AchievementCriteriaTree const* criteriaTree) - { - if (criteriaTree->Criteria) - if (CriteriaProgress const* criteriaProgress = this->GetCriteriaProgress(criteriaTree->Criteria)) - progress += criteriaProgress->counter; - }); - return progress >= tree->Entry->Amount; + RemoveCriteriaProgress(achievementCriteria); } - - return IsCompletedCriteriaTree(tree); } -template<class T> -CriteriaProgress* AchievementMgr<T>::GetCriteriaProgress(AchievementCriteria const* entry) +void PlayerAchievementMgr::SendAllData(Player const* /*receiver*/) const { - CriteriaProgressMap::iterator iter = m_criteriaProgress.find(entry->ID); - - if (iter == m_criteriaProgress.end()) - return NULL; - - return &(iter->second); -} - -template<class T> -void AchievementMgr<T>::SetCriteriaProgress(AchievementCriteria const* criteria, uint64 changeValue, Player* referencePlayer, ProgressType ptype) -{ - // Don't allow to cheat - doing timed achievements without timer active - AchievementCriteriaTreeList const* trees = nullptr; - if (criteria->Entry->StartTimer) - { - trees = sAchievementMgr->GetAchievementCriteriaTreesByCriteria(criteria->ID); - if (!trees) - return; - - bool hasTreeForTimed = false; - for (AchievementCriteriaTree const* tree : *trees) - { - auto timedIter = m_timedAchievements.find(tree->ID); - if (timedIter != m_timedAchievements.end()) - { - hasTreeForTimed = true; - break; - } - - } - - if (!hasTreeForTimed) - return; - } - - TC_LOG_DEBUG("achievement", "SetCriteriaProgress(%u, " UI64FMTD ") for (%s)", - criteria->ID, changeValue, GetOwner()->GetGUID().ToString().c_str()); + VisibleAchievementCheck filterInvisible; + WorldPackets::Achievement::AllAchievementData achievementData; + achievementData.Data.Earned.reserve(_completedAchievements.size()); + achievementData.Data.Progress.reserve(_criteriaProgress.size()); - CriteriaProgress* progress = GetCriteriaProgress(criteria); - if (!progress) + for (auto itr = _completedAchievements.begin(); itr != _completedAchievements.end(); ++itr) { - // not create record for 0 counter but allow it for timed achievements - // we will need to send 0 progress to client to start the timer - if (changeValue == 0 && !criteria->Entry->StartTimer) - return; + AchievementEntry const* achievement = filterInvisible(*itr); + if (!achievement) + continue; - progress = &m_criteriaProgress[criteria->ID]; - progress->counter = changeValue; - } - else - { - uint64 newValue = 0; - switch (ptype) + WorldPackets::Achievement::EarnedAchievement earned; + earned.Id = itr->first; + earned.Date = itr->second.Date; + if (!(achievement->Flags & ACHIEVEMENT_FLAG_ACCOUNT)) { - case PROGRESS_SET: - newValue = changeValue; - break; - case PROGRESS_ACCUMULATE: - { - // avoid overflow - uint64 max_value = std::numeric_limits<uint64>::max(); - newValue = max_value - progress->counter > changeValue ? progress->counter + changeValue : max_value; - break; - } - case PROGRESS_HIGHEST: - newValue = progress->counter < changeValue ? changeValue : progress->counter; - break; + earned.Owner = _owner->GetGUID(); + earned.VirtualRealmAddress = earned.NativeRealmAddress = GetVirtualRealmAddress(); } - - // not update (not mark as changed) if counter will have same value - if (progress->counter == newValue && !criteria->Entry->StartTimer) - return; - - progress->counter = newValue; + achievementData.Data.Earned.push_back(earned); } - progress->changed = true; - progress->date = time(NULL); // set the date to the latest update. - progress->PlayerGUID = referencePlayer->GetGUID(); - - uint32 timeElapsed = 0; - - if (criteria->Entry->StartTimer) + for (auto itr = _criteriaProgress.begin(); itr != _criteriaProgress.end(); ++itr) { - ASSERT(trees); - - for (AchievementCriteriaTree const* tree : *trees) - { - auto timedIter = m_timedAchievements.find(tree->ID); - if (timedIter != m_timedAchievements.end()) - { - // Client expects this in packet - timeElapsed = criteria->Entry->StartTimer - (timedIter->second / IN_MILLISECONDS); - - // Remove the timer, we wont need it anymore - if (IsCompletedCriteriaTree(tree)) - m_timedAchievements.erase(timedIter); - } - } + WorldPackets::Achievement::CriteriaProgress progress; + progress.Id = itr->first; + progress.Quantity = itr->second.Counter; + progress.Player = itr->second.PlayerGUID; + progress.Flags = 0; + progress.Date = itr->second.Date; + progress.TimeFromStart = 0; + progress.TimeFromCreate = 0; + achievementData.Data.Progress.push_back(progress); } - SendCriteriaUpdate(criteria, progress, timeElapsed, true); -} - -template<class T> -void AchievementMgr<T>::UpdateTimedAchievements(uint32 timeDiff) -{ - if (!m_timedAchievements.empty()) - { - for (TimedAchievementMap::iterator itr = m_timedAchievements.begin(); itr != m_timedAchievements.end();) - { - // Time is up, remove timer and reset progress - if (itr->second <= timeDiff) - { - AchievementCriteriaTree const* criteriaTree = sAchievementMgr->GetAchievementCriteriaTree(itr->first); - if (criteriaTree->Criteria) - RemoveCriteriaProgress(criteriaTree->Criteria); - - m_timedAchievements.erase(itr++); - } - else - { - itr->second -= timeDiff; - ++itr; - } - } - } + SendPacket(achievementData.Write()); } -template<class T> -void AchievementMgr<T>::StartTimedAchievement(AchievementCriteriaTimedTypes /*type*/, uint32 /*entry*/, uint32 /*timeLost = 0*/) +void PlayerAchievementMgr::SendAchievementInfo(Player* receiver, uint32 /*achievementId = 0 */) const { -} + VisibleAchievementCheck filterInvisible; + WorldPackets::Achievement::RespondInspectAchievements inspectedAchievements; + inspectedAchievements.Player = _owner->GetGUID(); + inspectedAchievements.Data.Earned.reserve(_completedAchievements.size()); + inspectedAchievements.Data.Progress.reserve(_criteriaProgress.size()); -template<> -void AchievementMgr<Player>::StartTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry, uint32 timeLost /* = 0 */) -{ - AchievementCriteriaList const& achievementCriteriaList = sAchievementMgr->GetTimedAchievementCriteriaByType(type); - for (AchievementCriteria const* criteria : achievementCriteriaList) + for (auto itr = _completedAchievements.begin(); itr != _completedAchievements.end(); ++itr) { - if (criteria->Entry->StartAsset != entry) + AchievementEntry const* achievement = filterInvisible(*itr); + if (!achievement) continue; - AchievementCriteriaTreeList const* trees = sAchievementMgr->GetAchievementCriteriaTreesByCriteria(criteria->ID); - bool canStart = false; - for (AchievementCriteriaTree const* tree : *trees) + WorldPackets::Achievement::EarnedAchievement earned; + earned.Id = itr->first; + earned.Date = itr->second.Date; + if (!(achievement->Flags & ACHIEVEMENT_FLAG_ACCOUNT)) { - if (m_timedAchievements.find(tree->ID) == m_timedAchievements.end() && !IsCompletedCriteriaTree(tree)) - { - // Start the timer - if (criteria->Entry->StartTimer * IN_MILLISECONDS > timeLost) - { - m_timedAchievements[tree->ID] = criteria->Entry->StartTimer * IN_MILLISECONDS - timeLost; - canStart = true; - } - } + earned.Owner = _owner->GetGUID(); + earned.VirtualRealmAddress = earned.NativeRealmAddress = GetVirtualRealmAddress(); } - - if (!canStart) - continue; - - // and at client too - SetCriteriaProgress(criteria, 0, GetOwner(), PROGRESS_SET); + inspectedAchievements.Data.Earned.push_back(earned); } -} -template<class T> -void AchievementMgr<T>::RemoveTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry) -{ - AchievementCriteriaList const& achievementCriteriaList = sAchievementMgr->GetTimedAchievementCriteriaByType(type); - for (AchievementCriteria const* criteria : achievementCriteriaList) + for (auto itr = _criteriaProgress.begin(); itr != _criteriaProgress.end(); ++itr) { - if (criteria->Entry->StartAsset != entry) - continue; - - AchievementCriteriaTreeList const* trees = sAchievementMgr->GetAchievementCriteriaTreesByCriteria(criteria->ID); - // Remove the timer from all trees - for (AchievementCriteriaTree const* tree : *trees) - m_timedAchievements.erase(tree->ID); - - // remove progress - RemoveCriteriaProgress(criteria); + WorldPackets::Achievement::CriteriaProgress progress; + progress.Id = itr->first; + progress.Quantity = itr->second.Counter; + progress.Player = itr->second.PlayerGUID; + progress.Flags = 0; + progress.Date = itr->second.Date; + progress.TimeFromStart = 0; + progress.TimeFromCreate = 0; + inspectedAchievements.Data.Progress.push_back(progress); } + + receiver->GetSession()->SendPacket(inspectedAchievements.Write()); } -template<> -void AchievementMgr<Player>::CompletedAchievement(AchievementEntry const* achievement, Player* referencePlayer) +void PlayerAchievementMgr::CompletedAchievement(AchievementEntry const* achievement, Player* referencePlayer) { // disable for gamemasters with GM-mode enabled - if (GetOwner()->IsGameMaster()) + if (_owner->IsGameMaster()) return; if (achievement->Flags & ACHIEVEMENT_FLAG_COUNTER || HasAchieved(achievement->ID)) @@ -1739,22 +447,23 @@ void AchievementMgr<Player>::CompletedAchievement(AchievementEntry const* achiev if (Guild* guild = referencePlayer->GetGuild()) guild->AddGuildNews(GUILD_NEWS_PLAYER_ACHIEVEMENT, referencePlayer->GetGUID(), achievement->Flags & ACHIEVEMENT_FLAG_SHOW_IN_GUILD_HEADER, achievement->ID); - if (!GetOwner()->GetSession()->PlayerLoading()) + if (!_owner->GetSession()->PlayerLoading()) SendAchievementEarned(achievement); - TC_LOG_DEBUG("achievement", "AchievementMgr::CompletedAchievement(%u). %s %s", - achievement->ID, GetOwner()->GetGUID().ToString().c_str(), GetOwner()->GetName().c_str()); + TC_LOG_DEBUG("criteria.achievement", "PlayerAchievementMgr::CompletedAchievement(%u). %s", achievement->ID, GetOwnerInfo().c_str()); - CompletedAchievementData& ca = m_completedAchievements[achievement->ID]; - ca.date = time(NULL); - ca.changed = true; + CompletedAchievementData& ca = _completedAchievements[achievement->ID]; + ca.Date = time(NULL); + ca.Changed = true; - sAchievementMgr->SetRealmCompleted(achievement, GetOwner()->GetInstanceId()); + if (achievement->Flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL)) + sAchievementMgr->SetRealmCompleted(achievement); - _achievementPoints += achievement->Points; + if (!(achievement->Flags & ACHIEVEMENT_FLAG_TRACKING_FLAG)) + _achievementPoints += achievement->Points; - UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT, 0, 0, 0, NULL, referencePlayer); - UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EARN_ACHIEVEMENT_POINTS, achievement->Points, 0, 0, NULL, referencePlayer); + UpdateCriteria(CRITERIA_TYPE_COMPLETE_ACHIEVEMENT, 0, 0, 0, NULL, referencePlayer); + UpdateCriteria(CRITERIA_TYPE_EARN_ACHIEVEMENT_POINTS, achievement->Points, 0, 0, NULL, referencePlayer); // reward items and titles if any AchievementReward const* reward = sAchievementMgr->GetAchievementReward(achievement); @@ -1768,28 +477,28 @@ void AchievementMgr<Player>::CompletedAchievement(AchievementEntry const* achiev //! Since no common attributes were found, (not even in titleRewardFlags field) //! we explicitly check by ID. Maybe in the future we could move the achievement_reward //! condition fields to the condition system. - if (uint32 titleId = reward->titleId[achievement->ID == 1793 ? GetOwner()->GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER) : (GetOwner()->GetTeam() == ALLIANCE ? 0 : 1)]) + if (uint32 titleId = reward->TitleId[achievement->ID == 1793 ? _owner->GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER) : (_owner->GetTeam() == ALLIANCE ? 0 : 1)]) if (CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(titleId)) - GetOwner()->SetTitle(titleEntry); + _owner->SetTitle(titleEntry); // mail - if (reward->sender) + if (reward->SenderCreatureId) { - MailDraft draft(reward->mailTemplate); + MailDraft draft(uint16(reward->MailTemplateId)); - if (!reward->mailTemplate) + if (!reward->MailTemplateId) { // subject and text - std::string subject = reward->subject; - std::string text = reward->text; + std::string subject = reward->Subject; + std::string text = reward->Body; - LocaleConstant localeConstant = GetOwner()->GetSession()->GetSessionDbLocaleIndex(); + LocaleConstant localeConstant = _owner->GetSession()->GetSessionDbLocaleIndex(); if (localeConstant >= LOCALE_enUS) { if (AchievementRewardLocale const* loc = sAchievementMgr->GetAchievementRewardLocale(achievement)) { - ObjectMgr::GetLocaleString(loc->subject, localeConstant, subject); - ObjectMgr::GetLocaleString(loc->text, localeConstant, text); + ObjectMgr::GetLocaleString(loc->Subject, localeConstant, subject); + ObjectMgr::GetLocaleString(loc->Body, localeConstant, text); } } @@ -1798,7 +507,7 @@ void AchievementMgr<Player>::CompletedAchievement(AchievementEntry const* achiev SQLTransaction trans = CharacterDatabase.BeginTransaction(); - Item* item = reward->itemId ? Item::CreateItem(reward->itemId, 1, GetOwner()) : NULL; + Item* item = reward->ItemId ? Item::CreateItem(reward->ItemId, 1, _owner) : NULL; if (item) { // save new item before send @@ -1808,132 +517,247 @@ void AchievementMgr<Player>::CompletedAchievement(AchievementEntry const* achiev draft.AddItem(item); } - draft.SendMailTo(trans, GetOwner(), MailSender(MAIL_CREATURE, uint64(reward->sender))); + draft.SendMailTo(trans, _owner, MailSender(MAIL_CREATURE, uint64(reward->SenderCreatureId))); CharacterDatabase.CommitTransaction(trans); } } -template<> -void AchievementMgr<Guild>::CompletedAchievement(AchievementEntry const* achievement, Player* referencePlayer) +void PlayerAchievementMgr::SendCriteriaUpdate(Criteria const* criteria, CriteriaProgress const* progress, uint32 timeElapsed, bool timedCompleted) const { - TC_LOG_DEBUG("achievement", "AchievementMgr<Guild>::CompletedAchievement(%u)", achievement->ID); + WorldPackets::Achievement::CriteriaUpdate criteriaUpdate; - if (achievement->Flags & ACHIEVEMENT_FLAG_COUNTER || HasAchieved(achievement->ID)) - return; + criteriaUpdate.CriteriaID = criteria->ID; + criteriaUpdate.Quantity = progress->Counter; + criteriaUpdate.PlayerGUID = _owner->GetGUID(); + criteriaUpdate.Flags = 0; + if (criteria->Entry->StartTimer) + criteriaUpdate.Flags = timedCompleted ? 1 : 0; // 1 is for keeping the counter at 0 in client - if (achievement->Flags & ACHIEVEMENT_FLAG_SHOW_IN_GUILD_NEWS) - if (Guild* guild = referencePlayer->GetGuild()) - guild->AddGuildNews(GUILD_NEWS_GUILD_ACHIEVEMENT, ObjectGuid::Empty, achievement->Flags & ACHIEVEMENT_FLAG_SHOW_IN_GUILD_HEADER, achievement->ID); + criteriaUpdate.CurrentTime = progress->Date; + criteriaUpdate.ElapsedTime = timeElapsed; + criteriaUpdate.CreationTime = 0; - SendAchievementEarned(achievement); - CompletedAchievementData& ca = m_completedAchievements[achievement->ID]; - ca.date = time(NULL); - ca.changed = true; + SendPacket(criteriaUpdate.Write()); +} - if (achievement->Flags & ACHIEVEMENT_FLAG_SHOW_GUILD_MEMBERS) +void PlayerAchievementMgr::SendCriteriaProgressRemoved(uint32 criteriaId) +{ + WorldPackets::Achievement::CriteriaDeleted criteriaDeleted; + criteriaDeleted.CriteriaID = criteriaId; + SendPacket(criteriaDeleted.Write()); +} + +void PlayerAchievementMgr::SendAchievementEarned(AchievementEntry const* achievement) const +{ + // Don't send for achievements with ACHIEVEMENT_FLAG_HIDDEN + if (achievement->Flags & ACHIEVEMENT_FLAG_HIDDEN) + return; + + TC_LOG_DEBUG("criteria.achievement", "PlayerAchievementMgr::SendAchievementEarned(%u)", achievement->ID); + + if (!(achievement->Flags & ACHIEVEMENT_FLAG_TRACKING_FLAG)) { - if (referencePlayer->GetGuildId() == GetOwner()->GetId()) - ca.guids.insert(referencePlayer->GetGUID()); + if (Guild* guild = sGuildMgr->GetGuildById(_owner->GetGuildId())) + { + Trinity::BroadcastTextBuilder _builder(_owner, CHAT_MSG_GUILD_ACHIEVEMENT, BROADCAST_TEXT_ACHIEVEMENT_EARNED, _owner, achievement->ID); + Trinity::LocalizedPacketDo<Trinity::BroadcastTextBuilder> _localizer(_builder); + guild->BroadcastWorker(_localizer, _owner); + } - if (Group const* group = referencePlayer->GetGroup()) - for (GroupReference const* ref = group->GetFirstMember(); ref != NULL; ref = ref->next()) - if (Player const* groupMember = ref->GetSource()) - if (groupMember->GetGuildId() == GetOwner()->GetId()) - ca.guids.insert(groupMember->GetGUID()); + if (achievement->Flags & (ACHIEVEMENT_FLAG_REALM_FIRST_KILL | ACHIEVEMENT_FLAG_REALM_FIRST_REACH)) + { + // broadcast realm first reached + WorldPackets::Achievement::ServerFirstAchievement serverFirstAchievement; + serverFirstAchievement.Name = _owner->GetName(); + serverFirstAchievement.PlayerGUID = _owner->GetGUID(); + serverFirstAchievement.AchievementID = achievement->ID; + sWorld->SendGlobalMessage(serverFirstAchievement.Write()); + } + // if player is in world he can tell his friends about new achievement + else if (_owner->IsInWorld()) + { + Trinity::BroadcastTextBuilder _builder(_owner, CHAT_MSG_ACHIEVEMENT, BROADCAST_TEXT_ACHIEVEMENT_EARNED, _owner, achievement->ID); + Trinity::LocalizedPacketDo<Trinity::BroadcastTextBuilder> _localizer(_builder); + Trinity::PlayerDistWorker<Trinity::LocalizedPacketDo<Trinity::BroadcastTextBuilder> > _worker(_owner, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_SAY), _localizer); + _owner->VisitNearbyWorldObject(sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_SAY), _worker); + } } - sAchievementMgr->SetRealmCompleted(achievement, referencePlayer->GetInstanceId()); + WorldPackets::Achievement::AchievementEarned achievementEarned; + achievementEarned.Sender = _owner->GetGUID(); + achievementEarned.Earner = _owner->GetGUID(); + achievementEarned.EarnerNativeRealm = achievementEarned.EarnerVirtualRealm = GetVirtualRealmAddress(); + achievementEarned.AchievementID = achievement->ID; + achievementEarned.Time = time(NULL); + if (!(achievement->Flags & ACHIEVEMENT_FLAG_TRACKING_FLAG)) + _owner->SendMessageToSetInRange(achievementEarned.Write(), sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_SAY), true); + else + _owner->SendDirectMessage(achievementEarned.Write()); +} + +void PlayerAchievementMgr::SendPacket(WorldPacket const* data) const +{ + _owner->SendDirectMessage(data); +} - _achievementPoints += achievement->Points; +CriteriaList const& PlayerAchievementMgr::GetCriteriaByType(CriteriaTypes type) const +{ + return sCriteriaMgr->GetPlayerCriteriaByType(type); +} - UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT, 0, 0, 0, NULL, referencePlayer); - UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EARN_ACHIEVEMENT_POINTS, achievement->Points, 0, 0, NULL, referencePlayer); +GuildAchievementMgr::GuildAchievementMgr(Guild* owner) : _owner(owner) +{ } -struct VisibleAchievementCheck +void GuildAchievementMgr::Reset() { - AchievementEntry const* operator()(CompletedAchievementMap::value_type const& val) + AchievementMgr::Reset(); + + ObjectGuid guid = _owner->GetGUID(); + for (auto iter = _completedAchievements.begin(); iter != _completedAchievements.end(); ++iter) { - AchievementEntry const* achievement = sAchievementMgr->GetAchievement(val.first); - if (achievement && !(achievement->Flags & ACHIEVEMENT_FLAG_HIDDEN)) - return achievement; - return nullptr; + WorldPackets::Achievement::GuildAchievementDeleted guildAchievementDeleted; + guildAchievementDeleted.AchievementID = iter->first; + guildAchievementDeleted.GuildGUID = guid; + guildAchievementDeleted.TimeDeleted = time(NULL); + SendPacket(guildAchievementDeleted.Write()); } -}; -template<class T> -void AchievementMgr<T>::SendAllAchievementData(Player* /*receiver*/) const + _achievementPoints = 0; + _completedAchievements.clear(); + DeleteFromDB(guid); +} + +void GuildAchievementMgr::DeleteFromDB(ObjectGuid const& guid) { - VisibleAchievementCheck filterInvisible; - WorldPackets::Achievement::AllAchievementData achievementData; - achievementData.Data.Earned.reserve(m_completedAchievements.size()); - achievementData.Data.Progress.reserve(m_criteriaProgress.size()); + SQLTransaction trans = CharacterDatabase.BeginTransaction(); - for (auto itr = m_completedAchievements.begin(); itr != m_completedAchievements.end(); ++itr) - { - AchievementEntry const* achievement = filterInvisible(*itr); - if (!achievement) - continue; + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ALL_GUILD_ACHIEVEMENTS); + stmt->setUInt64(0, guid.GetCounter()); + trans->Append(stmt); - WorldPackets::Achievement::EarnedAchievement earned; - earned.Id = itr->first; - earned.Date = itr->second.date; - if (!(achievement->Flags & ACHIEVEMENT_FLAG_ACCOUNT)) + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ALL_GUILD_ACHIEVEMENT_CRITERIA); + stmt->setUInt64(0, guid.GetCounter()); + trans->Append(stmt); + + CharacterDatabase.CommitTransaction(trans); +} + +void GuildAchievementMgr::LoadFromDB(PreparedQueryResult achievementResult, PreparedQueryResult criteriaResult) +{ + if (achievementResult) + { + do { - earned.Owner = GetOwner()->GetGUID(); - earned.VirtualRealmAddress = earned.NativeRealmAddress = GetVirtualRealmAddress(); - } - achievementData.Data.Earned.push_back(earned); + Field* fields = achievementResult->Fetch(); + uint32 achievementid = fields[0].GetUInt32(); + + // must not happen: cleanup at server startup in sAchievementMgr->LoadCompletedAchievements() + AchievementEntry const* achievement = sAchievementStore.LookupEntry(achievementid); + if (!achievement) + continue; + + CompletedAchievementData& ca = _completedAchievements[achievementid]; + ca.Date = time_t(fields[1].GetUInt32()); + Tokenizer guids(fields[2].GetString(), ' '); + for (uint32 i = 0; i < guids.size(); ++i) + ca.CompletingPlayers.insert(ObjectGuid::Create<HighGuid::Player>(uint64(strtoull(guids[i], nullptr, 10)))); + + ca.Changed = false; + + _achievementPoints += achievement->Points; + } while (achievementResult->NextRow()); } - for (auto itr = m_criteriaProgress.begin(); itr != m_criteriaProgress.end(); ++itr) + if (criteriaResult) { - WorldPackets::Achievement::CriteriaProgress progress; - progress.Id = itr->first; - progress.Quantity = itr->second.counter; - progress.Player = itr->second.PlayerGUID; - progress.Flags = 0; - progress.Date = itr->second.date; - progress.TimeFromStart = 0; - progress.TimeFromCreate = 0; - achievementData.Data.Progress.push_back(progress); - } + time_t now = time(NULL); + do + { + Field* fields = criteriaResult->Fetch(); + uint32 id = fields[0].GetUInt32(); + uint64 counter = fields[1].GetUInt64(); + time_t date = time_t(fields[2].GetUInt32()); + ObjectGuid::LowType guid = fields[3].GetUInt64(); - SendPacket(achievementData.Write()); + Criteria const* criteria = sCriteriaMgr->GetCriteria(id); + if (!criteria) + { + // we will remove not existed criteria for all guilds + TC_LOG_ERROR("criteria.achievement", "Non-existing achievement criteria %u data removed from table `guild_achievement_progress`.", id); + + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_ACHIEV_PROGRESS_CRITERIA_GUILD); + stmt->setUInt32(0, uint16(id)); + CharacterDatabase.Execute(stmt); + continue; + } + + if (criteria->Entry->StartTimer && time_t(date + criteria->Entry->StartTimer) < now) + continue; + + CriteriaProgress& progress = _criteriaProgress[id]; + progress.Counter = counter; + progress.Date = date; + progress.PlayerGUID = ObjectGuid::Create<HighGuid::Player>(guid); + progress.Changed = false; + } while (criteriaResult->NextRow()); + } } -template<> -void AchievementMgr<Guild>::SendAllAchievementData(Player* receiver) const +void GuildAchievementMgr::SaveToDB(SQLTransaction& trans) { - VisibleAchievementCheck filterInvisible; - WorldPackets::Achievement::AllGuildAchievements allGuildAchievements; - allGuildAchievements.Earned.reserve(m_completedAchievements.size()); - - for (auto itr = m_completedAchievements.begin(); itr != m_completedAchievements.end(); ++itr) + PreparedStatement* stmt; + std::ostringstream guidstr; + for (auto itr = _completedAchievements.begin(); itr != _completedAchievements.end(); ++itr) { - AchievementEntry const* achievement = filterInvisible(*itr); - if (!achievement) + if (!itr->second.Changed) continue; - WorldPackets::Achievement::EarnedAchievement earned; - earned.Id = itr->first; - earned.Date = itr->second.date; - allGuildAchievements.Earned.push_back(earned); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_ACHIEVEMENT); + stmt->setUInt64(0, _owner->GetId()); + stmt->setUInt32(1, itr->first); + trans->Append(stmt); + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_ACHIEVEMENT); + stmt->setUInt64(0, _owner->GetId()); + stmt->setUInt32(1, itr->first); + stmt->setUInt32(2, uint32(itr->second.Date)); + for (GuidSet::const_iterator gItr = itr->second.CompletingPlayers.begin(); gItr != itr->second.CompletingPlayers.end(); ++gItr) + guidstr << gItr->GetCounter() << ','; + + stmt->setString(3, guidstr.str()); + trans->Append(stmt); + + guidstr.str(""); } - receiver->GetSession()->SendPacket(allGuildAchievements.Write()); + for (auto itr = _criteriaProgress.begin(); itr != _criteriaProgress.end(); ++itr) + { + if (!itr->second.Changed) + continue; + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_ACHIEVEMENT_CRITERIA); + stmt->setUInt64(0, _owner->GetId()); + stmt->setUInt32(1, itr->first); + trans->Append(stmt); + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_ACHIEVEMENT_CRITERIA); + stmt->setUInt64(0, _owner->GetId()); + stmt->setUInt32(1, itr->first); + stmt->setUInt64(2, itr->second.Counter); + stmt->setUInt32(3, uint32(itr->second.Date)); + stmt->setUInt64(4, itr->second.PlayerGUID.GetCounter()); + trans->Append(stmt); + } } -template<> -void AchievementMgr<Player>::SendAchievementInfo(Player* receiver, uint32 /*achievementId = 0 */) const +void GuildAchievementMgr::SendAllData(Player const* receiver) const { VisibleAchievementCheck filterInvisible; - WorldPackets::Achievement::RespondInspectAchievements inspectedAchievements; - inspectedAchievements.Player = GetOwner()->GetGUID(); - inspectedAchievements.Data.Earned.reserve(m_completedAchievements.size()); - inspectedAchievements.Data.Progress.reserve(m_criteriaProgress.size()); + WorldPackets::Achievement::AllGuildAchievements allGuildAchievements; + allGuildAchievements.Earned.reserve(_completedAchievements.size()); - for (auto itr = m_completedAchievements.begin(); itr != m_completedAchievements.end(); ++itr) + for (auto itr = _completedAchievements.begin(); itr != _completedAchievements.end(); ++itr) { AchievementEntry const* achievement = filterInvisible(*itr); if (!achievement) @@ -1941,52 +765,33 @@ void AchievementMgr<Player>::SendAchievementInfo(Player* receiver, uint32 /*achi WorldPackets::Achievement::EarnedAchievement earned; earned.Id = itr->first; - earned.Date = itr->second.date; - if (!(achievement->Flags & ACHIEVEMENT_FLAG_ACCOUNT)) - { - earned.Owner = GetOwner()->GetGUID(); - earned.VirtualRealmAddress = earned.NativeRealmAddress = GetVirtualRealmAddress(); - } - inspectedAchievements.Data.Earned.push_back(earned); - } - - for (auto itr = m_criteriaProgress.begin(); itr != m_criteriaProgress.end(); ++itr) - { - WorldPackets::Achievement::CriteriaProgress progress; - progress.Id = itr->first; - progress.Quantity = itr->second.counter; - progress.Player = itr->second.PlayerGUID; - progress.Flags = 0; - progress.Date = itr->second.date; - progress.TimeFromStart = 0; - progress.TimeFromCreate = 0; - inspectedAchievements.Data.Progress.push_back(progress); + earned.Date = itr->second.Date; + allGuildAchievements.Earned.push_back(earned); } - receiver->GetSession()->SendPacket(inspectedAchievements.Write()); + receiver->GetSession()->SendPacket(allGuildAchievements.Write()); } -template<> -void AchievementMgr<Guild>::SendAchievementInfo(Player* receiver, uint32 achievementId /*= 0*/) const +void GuildAchievementMgr::SendAchievementInfo(Player* receiver, uint32 achievementId /*= 0*/) const { WorldPackets::Achievement::GuildCriteriaUpdate guildCriteriaUpdate; - if (AchievementEntry const* achievement = sAchievementMgr->GetAchievement(achievementId)) + if (AchievementEntry const* achievement = sAchievementStore.LookupEntry(achievementId)) { - if (AchievementCriteriaTree const* tree = sAchievementMgr->GetAchievementCriteriaTree(achievement->CriteriaTree)) + if (CriteriaTree const* tree = sCriteriaMgr->GetCriteriaTree(achievement->CriteriaTree)) { - sAchievementMgr->WalkCriteriaTree(tree, [this, &guildCriteriaUpdate](AchievementCriteriaTree const* node) + CriteriaMgr::WalkCriteriaTree(tree, [this, &guildCriteriaUpdate](CriteriaTree const* node) { if (node->Criteria) { - auto progress = this->m_criteriaProgress.find(node->Criteria->ID); - if (progress != this->m_criteriaProgress.end()) + auto progress = this->_criteriaProgress.find(node->Criteria->ID); + if (progress != this->_criteriaProgress.end()) { WorldPackets::Achievement::GuildCriteriaProgress guildCriteriaProgress; guildCriteriaProgress.CriteriaID = node->Criteria->ID; guildCriteriaProgress.DateCreated = 0; guildCriteriaProgress.DateStarted = 0; - guildCriteriaProgress.DateUpdated = progress->second.date; - guildCriteriaProgress.Quantity = progress->second.counter; + guildCriteriaProgress.DateUpdated = progress->second.Date; + guildCriteriaProgress.Quantity = progress->second.Counter; guildCriteriaProgress.PlayerGUID = progress->second.PlayerGUID; guildCriteriaProgress.Flags = 0; @@ -2000,1073 +805,186 @@ void AchievementMgr<Guild>::SendAchievementInfo(Player* receiver, uint32 achieve receiver->GetSession()->SendPacket(guildCriteriaUpdate.Write()); } -template<class T> -bool AchievementMgr<T>::HasAchieved(uint32 achievementId) const -{ - return m_completedAchievements.find(achievementId) != m_completedAchievements.end(); -} - -template<class T> -bool AchievementMgr<T>::CanUpdateCriteria(AchievementCriteria const* criteria, AchievementCriteriaTreeList const* trees, uint64 miscValue1, uint64 miscValue2, uint64 miscValue3, Unit const* unit, Player* referencePlayer) +void GuildAchievementMgr::SendAllTrackedCriterias(Player* receiver, std::set<uint32> const& trackedCriterias) const { - if (DisableMgr::IsDisabledFor(DISABLE_TYPE_ACHIEVEMENT_CRITERIA, criteria->ID, NULL)) - { - TC_LOG_TRACE("achievement", "CanUpdateCriteria: (Id: %u Type %s) Disabled", - criteria->ID, AchievementGlobalMgr::GetCriteriaTypeString(criteria->Entry->Type)); - return false; - } + WorldPackets::Achievement::GuildCriteriaUpdate guildCriteriaUpdate; + guildCriteriaUpdate.Progress.reserve(trackedCriterias.size()); - bool treeRequirementPassed = false; - for (AchievementCriteriaTree const* tree : *trees) + for (uint32 criteriaId : trackedCriterias) { - if (HasAchieved(tree->Achievement->ID)) - { - TC_LOG_TRACE("achievement", "CanUpdateCriteria: (Id: %u Type %s Achievement %u) Achievement already earned", - criteria->ID, AchievementGlobalMgr::GetCriteriaTypeString(criteria->Entry->Type), tree->Achievement->ID); + auto progress = _criteriaProgress.find(criteriaId); + if (progress == _criteriaProgress.end()) continue; - } - - if (tree->Achievement->MapID != -1 && referencePlayer->GetMapId() != uint32(tree->Achievement->MapID)) - { - TC_LOG_TRACE("achievement", "CanUpdateCriteria: (Id: %u Type %s Achievement %u) Wrong map", - criteria->ID, AchievementGlobalMgr::GetCriteriaTypeString(criteria->Entry->Type), tree->Achievement->ID); - continue; - } - - if ((tree->Achievement->Faction == ACHIEVEMENT_FACTION_HORDE && referencePlayer->GetTeam() != HORDE) || - (tree->Achievement->Faction == ACHIEVEMENT_FACTION_ALLIANCE && referencePlayer->GetTeam() != ALLIANCE)) - { - TC_LOG_TRACE("achievement", "CanUpdateCriteria: (Id: %u Type %s Achievement %u) Wrong faction", - criteria->ID, AchievementGlobalMgr::GetCriteriaTypeString(criteria->Entry->Type), tree->Achievement->ID); - continue; - } - - treeRequirementPassed = true; - break; - } - - if (!treeRequirementPassed) - return false; - if (!RequirementsSatisfied(criteria, miscValue1, miscValue2, miscValue3, unit, referencePlayer)) - { - TC_LOG_TRACE("achievement", "CanUpdateCriteria: (Id: %u Type %s) Requirements not satisfied", - criteria->ID, AchievementGlobalMgr::GetCriteriaTypeString(criteria->Entry->Type)); - return false; - } - - if (criteria->Modifier && !AdditionalRequirementsSatisfied(criteria->Modifier, miscValue1, miscValue2, unit, referencePlayer)) - { - TC_LOG_TRACE("achievement", "CanUpdateCriteria: (Id: %u Type %s) Requirements have not been satisfied", - criteria->ID, AchievementGlobalMgr::GetCriteriaTypeString(criteria->Entry->Type)); - return false; - } + WorldPackets::Achievement::GuildCriteriaProgress guildCriteriaProgress; + guildCriteriaProgress.CriteriaID = criteriaId; + guildCriteriaProgress.DateCreated = 0; + guildCriteriaProgress.DateStarted = 0; + guildCriteriaProgress.DateUpdated = progress->second.Date; + guildCriteriaProgress.Quantity = progress->second.Counter; + guildCriteriaProgress.PlayerGUID = progress->second.PlayerGUID; + guildCriteriaProgress.Flags = 0; - if (!ConditionsSatisfied(criteria, referencePlayer)) - { - TC_LOG_TRACE("achievement", "CanUpdateCriteria: (Id: %u Type %s) Conditions have not been satisfied", - criteria->ID, AchievementGlobalMgr::GetCriteriaTypeString(criteria->Entry->Type)); - return false; + guildCriteriaUpdate.Progress.push_back(guildCriteriaProgress); } - return true; + receiver->GetSession()->SendPacket(guildCriteriaUpdate.Write()); } -template<class T> -bool AchievementMgr<T>::ConditionsSatisfied(AchievementCriteria const* criteria, Player* referencePlayer) const +void GuildAchievementMgr::CompletedAchievement(AchievementEntry const* achievement, Player* referencePlayer) { - if (!criteria->Entry->FailEvent) - return true; + TC_LOG_DEBUG("criteria.achievement", "GuildAchievementMgr::CompletedAchievement(%u)", achievement->ID); - switch (criteria->Entry->FailEvent) - { - case ACHIEVEMENT_CRITERIA_CONDITION_BG_MAP: - if (!referencePlayer->InBattleground()) - return false; - break; - case ACHIEVEMENT_CRITERIA_CONDITION_NOT_IN_GROUP: - if (referencePlayer->GetGroup()) - return false; - break; - default: - break; - } - - return true; -} + if (achievement->Flags & ACHIEVEMENT_FLAG_COUNTER || HasAchieved(achievement->ID)) + return; -template<class T> -bool AchievementMgr<T>::RequirementsSatisfied(AchievementCriteria const* achievementCriteria, uint64 miscValue1, uint64 miscValue2, uint64 miscValue3, Unit const* unit, Player* referencePlayer) const -{ - switch (AchievementCriteriaTypes(achievementCriteria->Entry->Type)) - { - case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS: - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST: - case ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION: - case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING: - case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN: - case ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS: - case ACHIEVEMENT_CRITERIA_TYPE_GOLD_EARNED_BY_AUCTIONS: - case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER: - case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL: - case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS: - case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TRAVELLING: - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID: - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD: - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALING_RECEIVED: - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEAL_CAST: - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_DEALT: - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_RECEIVED: - case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL: - case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY: - case ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL: - case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD: - case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS: - case ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS: - case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED: - case ACHIEVEMENT_CRITERIA_TYPE_REACH_GUILD_LEVEL: - case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED: - case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED: - case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL: - case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_DAMAGE_RECEIVED: - case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_HEALING_RECEIVED: - case ACHIEVEMENT_CRITERIA_TYPE_USE_LFD_TO_GROUP_WITH_PLAYERS: - case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP: - case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL: - case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: - case ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS: - if (!miscValue1) - return false; - break; - case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT: - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST_DAILY: - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT: - case ACHIEVEMENT_CRITERIA_TYPE_EARN_ACHIEVEMENT_POINTS: - case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION: - case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION: - case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION: - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED: - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_PERSONAL_RATING: - case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS: - case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL: - case ACHIEVEMENT_CRITERIA_TYPE_ON_LOGIN: - break; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT: - if (m_completedAchievements.find(achievementCriteria->Entry->Asset.AchievementID) == m_completedAchievements.end()) - return false; - break; - case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG: - if (!miscValue1 || achievementCriteria->Entry->Asset.MapID != referencePlayer->GetMapId()) - return false; - break; - case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE: - if (!miscValue1 || achievementCriteria->Entry->Asset.CreatureID != miscValue1) - return false; - break; - case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL: - case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL: - // update at loading or specific skill update - if (miscValue1 && miscValue1 != achievementCriteria->Entry->Asset.SkillID) - return false; - break; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE: - if (miscValue1 && miscValue1 != achievementCriteria->Entry->Asset.ZoneID) - return false; - break; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND: - case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP: - if (!miscValue1 || referencePlayer->GetMapId() != achievementCriteria->Entry->Asset.MapID) - return false; - break; - case ACHIEVEMENT_CRITERIA_TYPE_DEATH: - { - if (!miscValue1) - return false; - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON: - { - if (!miscValue1) - return false; + if (achievement->Flags & ACHIEVEMENT_FLAG_SHOW_IN_GUILD_NEWS) + if (Guild* guild = referencePlayer->GetGuild()) + guild->AddGuildNews(GUILD_NEWS_GUILD_ACHIEVEMENT, ObjectGuid::Empty, achievement->Flags & ACHIEVEMENT_FLAG_SHOW_IN_GUILD_HEADER, achievement->ID); - Map const* map = referencePlayer->IsInWorld() ? referencePlayer->GetMap() : sMapMgr->FindMap(referencePlayer->GetMapId(), referencePlayer->GetInstanceId()); - if (!map || !map->IsDungeon()) - return false; + SendAchievementEarned(achievement); + CompletedAchievementData& ca = _completedAchievements[achievement->ID]; + ca.Date = time(NULL); + ca.Changed = true; - //FIXME: work only for instances where max == min for players - if (map->ToInstanceMap()->GetMaxPlayers() != achievementCriteria->Entry->Asset.GroupSize) - return false; - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE: - if (!miscValue1 || miscValue1 != achievementCriteria->Entry->Asset.CreatureID) - return false; - break; - case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER: - if (!miscValue1 || !unit || unit->GetTypeId() != TYPEID_PLAYER) - return false; - break; - case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM: - if (!miscValue1 || miscValue2 != achievementCriteria->Entry->Asset.DamageType) - return false; - break; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST: - { - // if miscValues != 0, it contains the questID. - if (miscValue1) - { - if (miscValue1 != achievementCriteria->Entry->Asset.QuestID) - return false; - } - else - { - // login case. - if (!referencePlayer->GetQuestRewardStatus(achievementCriteria->Entry->Asset.QuestID)) - return false; - } + if (achievement->Flags & ACHIEVEMENT_FLAG_SHOW_GUILD_MEMBERS) + { + if (referencePlayer->GetGuildId() == _owner->GetId()) + ca.CompletingPlayers.insert(referencePlayer->GetGUID()); - if (AchievementCriteriaDataSet const* data = sAchievementMgr->GetCriteriaDataSet(achievementCriteria)) - if (!data->Meets(referencePlayer, unit)) - return false; - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET: - case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2: - case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: - case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: - if (!miscValue1 || miscValue1 != achievementCriteria->Entry->Asset.SpellID) - return false; - break; - case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL: - if (miscValue1 && miscValue1 != achievementCriteria->Entry->Asset.SpellID) - return false; - - if (!referencePlayer->HasSpell(achievementCriteria->Entry->Asset.SpellID)) - return false; - break; - case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE: - // miscValue1 = itemId - miscValue2 = count of item loot - // miscValue3 = loot_type (note: 0 = LOOT_CORPSE and then it ignored) - if (!miscValue1 || !miscValue2 || !miscValue3 || miscValue3 != achievementCriteria->Entry->Asset.LootType) - return false; - break; - case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM: - if (miscValue1 && achievementCriteria->Entry->Asset.ItemID != miscValue1) - return false; - break; - case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM: - case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM: - case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM: - if (!miscValue1 || achievementCriteria->Entry->Asset.ItemID != miscValue1) - return false; - break; - case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA: - { - WorldMapOverlayEntry const* worldOverlayEntry = sWorldMapOverlayStore.LookupEntry(achievementCriteria->Entry->Asset.WorldMapOverlayID); - if (!worldOverlayEntry) - break; + if (Group const* group = referencePlayer->GetGroup()) + for (GroupReference const* ref = group->GetFirstMember(); ref != NULL; ref = ref->next()) + if (Player const* groupMember = ref->GetSource()) + if (groupMember->GetGuildId() == _owner->GetId()) + ca.CompletingPlayers.insert(groupMember->GetGUID()); + } - bool matchFound = false; - for (int j = 0; j < MAX_WORLD_MAP_OVERLAY_AREA_IDX; ++j) - { - AreaTableEntry const* area = sAreaTableStore.LookupEntry(worldOverlayEntry->AreaID[j]); - if (!area) - break; + if (achievement->Flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL)) + sAchievementMgr->SetRealmCompleted(achievement); - if (area->AreaBit < 0) - continue; + if (!(achievement->Flags & ACHIEVEMENT_FLAG_TRACKING_FLAG)) + _achievementPoints += achievement->Points; - uint32 playerIndexOffset = uint32(area->AreaBit) / 32; - if (playerIndexOffset >= PLAYER_EXPLORED_ZONES_SIZE) - continue; + UpdateCriteria(CRITERIA_TYPE_COMPLETE_ACHIEVEMENT, 0, 0, 0, NULL, referencePlayer); + UpdateCriteria(CRITERIA_TYPE_EARN_ACHIEVEMENT_POINTS, achievement->Points, 0, 0, NULL, referencePlayer); +} - uint32 mask = 1 << (uint32(area->AreaBit) % 32); - if (referencePlayer->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + playerIndexOffset) & mask) - { - matchFound = true; - break; - } - } +void GuildAchievementMgr::SendCriteriaUpdate(Criteria const* entry, CriteriaProgress const* progress, uint32 /*timeElapsed*/, bool /*timedCompleted*/) const +{ + WorldPackets::Achievement::GuildCriteriaUpdate guildCriteriaUpdate; + guildCriteriaUpdate.Progress.resize(1); - if (!matchFound) - return false; - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION: - if (miscValue1 && miscValue1 != achievementCriteria->Entry->Asset.FactionID) - return false; - break; - case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM: - // miscValue1 = itemid miscValue2 = itemSlot - if (!miscValue1 || miscValue2 != achievementCriteria->Entry->Asset.ItemSlot) - return false; - break; - case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT: - case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT: - { - // miscValue1 = itemid miscValue2 = diced value - if (!miscValue1 || miscValue2 != achievementCriteria->Entry->Asset.RollValue) - return false; - - ItemTemplate const* proto = sObjectMgr->GetItemTemplate(uint32(miscValue1)); - if (!proto) - return false; - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: - if (!miscValue1 || miscValue1 != achievementCriteria->Entry->Asset.EmoteID) - return false; - break; - case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE: - case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE: - if (!miscValue1) - return false; - - if (achievementCriteria->Entry->FailEvent == ACHIEVEMENT_CRITERIA_CONDITION_BG_MAP) - { - if (!referencePlayer->InBattleground()) - return false; + WorldPackets::Achievement::GuildCriteriaProgress& guildCriteriaProgress = guildCriteriaUpdate.Progress[0]; + guildCriteriaProgress.CriteriaID = entry->ID; + guildCriteriaProgress.DateCreated = 0; + guildCriteriaProgress.DateStarted = 0; + guildCriteriaProgress.DateUpdated = progress->Date; + guildCriteriaProgress.Quantity = progress->Counter; + guildCriteriaProgress.PlayerGUID = progress->PlayerGUID; + guildCriteriaProgress.Flags = 0; - // map specific case (BG in fact) expected player targeted damage/heal - if (!unit || unit->GetTypeId() != TYPEID_PLAYER) - return false; - } - break; - case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT: - case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT: - if (!miscValue1 || miscValue1 != achievementCriteria->Entry->Asset.GameObjectID) - return false; - break; - case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS: - case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE: - if (miscValue1 && miscValue1 != achievementCriteria->Entry->Asset.SkillID) - return false; - break; - case ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM: - case ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM: - { - if (!miscValue1) - return false; - ItemTemplate const* proto = sObjectMgr->GetItemTemplate(uint32(miscValue1)); - if (!proto || proto->GetQuality() < ITEM_QUALITY_EPIC) - return false; - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS: - if (!miscValue1 || miscValue1 != achievementCriteria->Entry->Asset.ClassID) - return false; - break; - case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE: - if (!miscValue1 || miscValue1 != achievementCriteria->Entry->Asset.RaceID) - return false; - break; - case ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE: - if (!miscValue1 || miscValue1 != achievementCriteria->Entry->Asset.ObjectiveId) - return false; - break; - case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA: - if (!miscValue1 || miscValue1 != achievementCriteria->Entry->Asset.AreaID) - return false; - break; - case ACHIEVEMENT_CRITERIA_TYPE_CURRENCY: - if (!miscValue1 || !miscValue2 || int64(miscValue2) < 0 - || miscValue1 != achievementCriteria->Entry->Asset.CurrencyID) - return false; - break; - case ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA: - if (miscValue1 != achievementCriteria->Entry->Asset.MapID) - return false; - break; - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_TEAM_RATING: - return false; - case ACHIEVEMENT_CRITERIA_TYPE_PLACE_GARRISON_BUILDING: - if (miscValue1 != achievementCriteria->Entry->Asset.GarrBuildingID) - return false; - break; - default: - break; - } - return true; + _owner->BroadcastPacketIfTrackingAchievement(guildCriteriaUpdate.Write(), entry->ID); } -template<class T> -bool AchievementMgr<T>::AdditionalRequirementsSatisfied(ModifierTreeNode const* tree, uint64 miscValue1, uint64 miscValue2, Unit const* unit, Player* referencePlayer) const +void GuildAchievementMgr::SendCriteriaProgressRemoved(uint32 criteriaId) { - for (ModifierTreeNode const* node : tree->Children) - if (!AdditionalRequirementsSatisfied(node, miscValue1, miscValue2, unit, referencePlayer)) - return false; - - uint32 reqType = tree->Entry->Type; - if (!reqType) - return true; - - uint32 reqValue = tree->Entry->Asset[0]; + WorldPackets::Achievement::GuildCriteriaDeleted guildCriteriaDeleted; + guildCriteriaDeleted.GuildGUID = _owner->GetGUID(); + guildCriteriaDeleted.CriteriaID = criteriaId; + SendPacket(guildCriteriaDeleted.Write()); +} - switch (AchievementCriteriaAdditionalCondition(reqType)) +void GuildAchievementMgr::SendAchievementEarned(AchievementEntry const* achievement) const +{ + if (achievement->Flags & (ACHIEVEMENT_FLAG_REALM_FIRST_KILL | ACHIEVEMENT_FLAG_REALM_FIRST_REACH)) { - case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_TARGET_CREATURE_ENTRY: // 4 - if (!unit || unit->GetEntry() != reqValue) - return false; - break; - case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_TARGET_MUST_BE_PLAYER: // 5 - if (!unit || unit->GetTypeId() != TYPEID_PLAYER) - return false; - break; - case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_TARGET_MUST_BE_DEAD: // 6 - if (!unit || unit->IsAlive()) - return false; - break; - case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_TARGET_MUST_BE_ENEMY: // 7 - if (!unit || !referencePlayer->IsHostileTo(unit)) - return false; - break; - case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_SOURCE_HAS_AURA: // 8 - if (!referencePlayer->HasAura(reqValue)) - return false; - break; - case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_TARGET_HAS_AURA: // 10 - if (!unit || !unit->HasAura(reqValue)) - return false; - case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_TARGET_HAS_AURA_TYPE: // 11 - if (!unit || !unit->HasAuraType(AuraType(reqValue))) - return false; - break; - case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_ITEM_QUALITY_MIN: // 14 - { - // miscValue1 is itemid - ItemTemplate const* const item = sObjectMgr->GetItemTemplate(uint32(miscValue1)); - if (!item || item->GetQuality() < reqValue) - return false; - break; - } - case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_ITEM_QUALITY_EQUALS: // 15 - { - // miscValue1 is itemid - ItemTemplate const* const item = sObjectMgr->GetItemTemplate(uint32(miscValue1)); - if (!item || item->GetQuality() != reqValue) - return false; - break; - } - case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_SOURCE_AREA_OR_ZONE: // 17 - { - uint32 zoneId, areaId; - referencePlayer->GetZoneAndAreaId(zoneId, areaId); - if (zoneId != reqValue && areaId != reqValue) - return false; - break; - } - case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_TARGET_AREA_OR_ZONE: // 18 - { - if (!unit) - return false; - uint32 zoneId, areaId; - unit->GetZoneAndAreaId(zoneId, areaId); - if (zoneId != reqValue && areaId != reqValue) - return false; - break; - } - case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_MAP_DIFFICULTY: // 20 - if (uint32(referencePlayer->GetMap()->GetDifficultyID()) != reqValue) - return false; - break; - case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_ARENA_TYPE: - { - Battleground* bg = referencePlayer->GetBattleground(); - if (!bg || !bg->isArena() || bg->GetArenaType() != reqValue) - return false; - break; - } - case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_SOURCE_RACE: // 25 - if (referencePlayer->getRace() != reqValue) - return false; - break; - case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_SOURCE_CLASS: // 26 - if (referencePlayer->getClass() != reqValue) - return false; - break; - case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_TARGET_RACE: // 27 - if (!unit || unit->GetTypeId() != TYPEID_PLAYER || unit->getRace() != reqValue) - return false; - break; - case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_TARGET_CLASS: // 28 - if (!unit || unit->GetTypeId() != TYPEID_PLAYER || unit->getClass() != reqValue) - return false; - break; - case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_MAX_GROUP_MEMBERS: // 29 - if (referencePlayer->GetGroup() && referencePlayer->GetGroup()->GetMembersCount() >= reqValue) - return false; - break; - case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_TARGET_CREATURE_TYPE: // 30 - { - if (!unit) - return false; - Creature const* const creature = unit->ToCreature(); - if (!creature || creature->GetCreatureType() != reqValue) - return false; - break; - } - case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_SOURCE_MAP: // 32 - if (referencePlayer->GetMapId() != reqValue) - return false; - break; - case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_TITLE_BIT_INDEX: // 38 - // miscValue1 is title's bit index - if (miscValue1 != reqValue) - return false; - break; - case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_SOURCE_LEVEL: // 39 - if (referencePlayer->getLevel() != reqValue) - return false; - break; - case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_TARGET_LEVEL: // 40 - if (!unit || unit->getLevel() != reqValue) - return false; - break; - case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_TARGET_ZONE: // 41 - if (!unit || unit->GetZoneId() != reqValue) - return false; - break; - case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_TARGET_HEALTH_PERCENT_BELOW: // 46 - if (!unit || unit->GetHealthPct() >= reqValue) - return false; - break; - case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_BATTLE_PET_SPECIES: // 91 - if (miscValue1 != reqValue) - return false; - break; - case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_GARRISON_FOLLOWER_QUALITY: // 145 - { - if (!referencePlayer) - return false; - Garrison* garrison = referencePlayer->GetGarrison(); - if (!garrison) - return false; - Garrison::Follower const* follower = garrison->GetFollower(miscValue1); - if (!follower || follower->PacketInfo.Quality != reqValue) - return false; - - break; - } - case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_GARRISON_FOLLOWER_LEVEL: // 146 - { - if (!referencePlayer) - return false; - Garrison* garrison = referencePlayer->GetGarrison(); - if (!garrison) - return false; - Garrison::Follower const* follower = garrison->GetFollower(miscValue1); - if (!follower || follower->PacketInfo.FollowerLevel < reqValue) - return false; - - break; - } - case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_GARRISON_FOLLOWER_ILVL: // 184 - { - if (!referencePlayer) - return false; - Garrison* garrison = referencePlayer->GetGarrison(); - if (!garrison) - return false; - Garrison::Follower const* follower = garrison->GetFollower(miscValue1); - if (!follower || follower->GetItemLevel() < reqValue) - return false; - break; - } - default: - break; + // broadcast realm first reached + WorldPackets::Achievement::ServerFirstAchievement serverFirstAchievement; + serverFirstAchievement.Name = _owner->GetName(); + serverFirstAchievement.PlayerGUID = _owner->GetGUID(); + serverFirstAchievement.AchievementID = achievement->ID; + serverFirstAchievement.GuildAchievement = true; + sWorld->SendGlobalMessage(serverFirstAchievement.Write()); } - return true; + + WorldPackets::Achievement::GuildAchievementEarned guildAchievementEarned; + guildAchievementEarned.AchievementID = achievement->ID; + guildAchievementEarned.GuildGUID = _owner->GetGUID(); + guildAchievementEarned.TimeEarned = time(NULL); + SendPacket(guildAchievementEarned.Write()); } -template class TC_GAME_API AchievementMgr<Player>; -template class TC_GAME_API AchievementMgr<Guild>; +void GuildAchievementMgr::SendPacket(WorldPacket const* data) const +{ + _owner->BroadcastPacket(data); +} -char const* AchievementGlobalMgr::GetCriteriaTypeString(uint32 type) +CriteriaList const& GuildAchievementMgr::GetCriteriaByType(CriteriaTypes type) const { - return GetCriteriaTypeString(AchievementCriteriaTypes(type)); + return sCriteriaMgr->GetGuildCriteriaByType(type); } -char const* AchievementGlobalMgr::GetCriteriaTypeString(AchievementCriteriaTypes type) +std::string PlayerAchievementMgr::GetOwnerInfo() const { - switch (type) - { - case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE: - return "KILL_CREATURE"; - case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG: - return "TYPE_WIN_BG"; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ARCHAEOLOGY_PROJECTS: - return "COMPLETE_RESEARCH"; - case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL: - return "REACH_LEVEL"; - case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL: - return "REACH_SKILL_LEVEL"; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT: - return "COMPLETE_ACHIEVEMENT"; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT: - return "COMPLETE_QUEST_COUNT"; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST_DAILY: - return "COMPLETE_DAILY_QUEST_DAILY"; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE: - return "COMPLETE_QUESTS_IN_ZONE"; - case ACHIEVEMENT_CRITERIA_TYPE_CURRENCY: - return "CURRENCY"; - case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE: - return "DAMAGE_DONE"; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST: - return "COMPLETE_DAILY_QUEST"; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND: - return "COMPLETE_BATTLEGROUND"; - case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP: - return "DEATH_AT_MAP"; - case ACHIEVEMENT_CRITERIA_TYPE_DEATH: - return "DEATH"; - case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON: - return "DEATH_IN_DUNGEON"; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_RAID: - return "COMPLETE_RAID"; - case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE: - return "KILLED_BY_CREATURE"; - case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER: - return "KILLED_BY_PLAYER"; - case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING: - return "FALL_WITHOUT_DYING"; - case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM: - return "DEATHS_FROM"; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST: - return "COMPLETE_QUEST"; - case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET: - return "BE_SPELL_TARGET"; - case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: - return "CAST_SPELL"; - case ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE: - return "BG_OBJECTIVE_CAPTURE"; - case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA: - return "HONORABLE_KILL_AT_AREA"; - case ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA: - return "WIN_ARENA"; - case ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA: - return "PLAY_ARENA"; - case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL: - return "LEARN_SPELL"; - case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL: - return "HONORABLE_KILL"; - case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM: - return "OWN_ITEM"; - case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: - return "WIN_RATED_ARENA"; - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_TEAM_RATING: - return "HIGHEST_TEAM_RATING"; - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_PERSONAL_RATING: - return "HIGHEST_PERSONAL_RATING"; - case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL: - return "LEARN_SKILL_LEVEL"; - case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM: - return "USE_ITEM"; - case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM: - return "LOOT_ITEM"; - case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA: - return "EXPLORE_AREA"; - case ACHIEVEMENT_CRITERIA_TYPE_OWN_RANK: - return "OWN_RANK"; - case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT: - return "BUY_BANK_SLOT"; - case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION: - return "GAIN_REPUTATION"; - case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION: - return "GAIN_EXALTED_REPUTATION"; - case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP: - return "VISIT_BARBER_SHOP"; - case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM: - return "EQUIP_EPIC_ITEM"; - case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT: - return "ROLL_NEED_ON_LOOT"; - case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT: - return "GREED_ON_LOOT"; - case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS: - return "HK_CLASS"; - case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE: - return "HK_RACE"; - case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: - return "DO_EMOTE"; - case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE: - return "HEALING_DONE"; - case ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS: - return "GET_KILLING_BLOWS"; - case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM: - return "EQUIP_ITEM"; - case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS: - return "MONEY_FROM_VENDORS"; - case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS: - return "GOLD_SPENT_FOR_TALENTS"; - case ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS: - return "NUMBER_OF_TALENT_RESETS"; - case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD: - return "MONEY_FROM_QUEST_REWARD"; - case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TRAVELLING: - return "GOLD_SPENT_FOR_TRAVELLING"; - case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER: - return "GOLD_SPENT_AT_BARBER"; - case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL: - return "GOLD_SPENT_FOR_MAIL"; - case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY: - return "LOOT_MONEY"; - case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT: - return "USE_GAMEOBJECT"; - case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2: - return "BE_SPELL_TARGET2"; - case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL: - return "SPECIAL_PVP_KILL"; - case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT: - return "FISH_IN_GAMEOBJECT"; - case ACHIEVEMENT_CRITERIA_TYPE_ON_LOGIN: - return "ON_LOGIN"; - case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS: - return "LEARN_SKILLLINE_SPELLS"; - case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL: - return "WIN_DUEL"; - case ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL: - return "LOSE_DUEL"; - case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE: - return "KILL_CREATURE_TYPE"; - case ACHIEVEMENT_CRITERIA_TYPE_GOLD_EARNED_BY_AUCTIONS: - return "GOLD_EARNED_BY_AUCTIONS"; - case ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION: - return "CREATE_AUCTION"; - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID: - return "HIGHEST_AUCTION_BID"; - case ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS: - return "WON_AUCTIONS"; - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD: - return "HIGHEST_AUCTION_SOLD"; - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED: - return "HIGHEST_GOLD_VALUE_OWNED"; - case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION: - return "GAIN_REVERED_REPUTATION"; - case ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION: - return "GAIN_HONORED_REPUTATION"; - case ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS: - return "KNOWN_FACTIONS"; - case ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM: - return "LOOT_EPIC_ITEM"; - case ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM: - return "RECEIVE_EPIC_ITEM"; - case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED: - return "ROLL_NEED"; - case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED: - return "ROLL_GREED"; - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_DEALT: - return "HIT_DEALT"; - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_RECEIVED: - return "HIT_RECEIVED"; - case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_DAMAGE_RECEIVED: - return "TOTAL_DAMAGE_RECEIVED"; - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEAL_CAST: - return "HIGHEST_HEAL_CAST"; - case ACHIEVEMENT_CRITERIA_TYPE_TOTAL_HEALING_RECEIVED: - return "TOTAL_HEALING_RECEIVED"; - case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALING_RECEIVED: - return "HIGHEST_HEALING_RECEIVED"; - case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED: - return "QUEST_ABANDONED"; - case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN: - return "FLIGHT_PATHS_TAKEN"; - case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE: - return "LOOT_TYPE"; - case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: - return "CAST_SPELL2"; - case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE: - return "LEARN_SKILL_LINE"; - case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL: - return "EARN_HONORABLE_KILL"; - case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS: - return "ACCEPTED_SUMMONINGS"; - case ACHIEVEMENT_CRITERIA_TYPE_EARN_ACHIEVEMENT_POINTS: - return "EARN_ACHIEVEMENT_POINTS"; - case ACHIEVEMENT_CRITERIA_TYPE_USE_LFD_TO_GROUP_WITH_PLAYERS: - return "USE_LFD_TO_GROUP_WITH_PLAYERS"; - case ACHIEVEMENT_CRITERIA_TYPE_SPENT_GOLD_GUILD_REPAIRS: - return "SPENT_GOLD_GUILD_REPAIRS"; - case ACHIEVEMENT_CRITERIA_TYPE_REACH_GUILD_LEVEL: - return "REACH_GUILD_LEVEL"; - case ACHIEVEMENT_CRITERIA_TYPE_CRAFT_ITEMS_GUILD: - return "CRAFT_ITEMS_GUILD"; - case ACHIEVEMENT_CRITERIA_TYPE_CATCH_FROM_POOL: - return "CATCH_FROM_POOL"; - case ACHIEVEMENT_CRITERIA_TYPE_BUY_GUILD_BANK_SLOTS: - return "BUY_GUILD_BANK_SLOTS"; - case ACHIEVEMENT_CRITERIA_TYPE_EARN_GUILD_ACHIEVEMENT_POINTS: - return "EARN_GUILD_ACHIEVEMENT_POINTS"; - case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_BATTLEGROUND: - return "WIN_RATED_BATTLEGROUND"; - case ACHIEVEMENT_CRITERIA_TYPE_REACH_BG_RATING: - return "REACH_BG_RATING"; - case ACHIEVEMENT_CRITERIA_TYPE_BUY_GUILD_TABARD: - return "BUY_GUILD_TABARD"; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_GUILD: - return "COMPLETE_QUESTS_GUILD"; - case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILLS_GUILD: - return "HONORABLE_KILLS_GUILD"; - case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE_GUILD: - return "KILL_CREATURE_TYPE_GUILD"; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_GUILD_CHALLENGE_TYPE: - return "GUILD_CHALLENGE_TYPE"; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_GUILD_CHALLENGE: - return "GUILD_CHALLENGE"; - case ACHIEVEMENT_CRITERIA_TYPE_LFR_DUNGEONS_COMPLETED: - return "LFR_DUNGEONS_COMPLETED"; - case ACHIEVEMENT_CRITERIA_TYPE_LFR_LEAVES: - return "LFR_LEAVES"; - case ACHIEVEMENT_CRITERIA_TYPE_LFR_VOTE_KICKS_INITIATED_BY_PLAYER: - return "LFR_VOTE_KICKS_INITIATED_BY_PLAYER"; - case ACHIEVEMENT_CRITERIA_TYPE_LFR_VOTE_KICKS_NOT_INIT_BY_PLAYER: - return "LFR_VOTE_KICKS_NOT_INIT_BY_PLAYER"; - case ACHIEVEMENT_CRITERIA_TYPE_BE_KICKED_FROM_LFR: - return "BE_KICKED_FROM_LFR"; - case ACHIEVEMENT_CRITERIA_TYPE_COUNT_OF_LFR_QUEUE_BOOSTS_BY_TANK: - return "COUNT_OF_LFR_QUEUE_BOOSTS_BY_TANK"; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_SCENARIO_COUNT: - return "COMPLETE_SCENARIO_COUNT"; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_SCENARIO: - return "COMPLETE_SCENARIO"; - case ACHIEVEMENT_CRITERIA_TYPE_OWN_BATTLE_PET: - return "OWN_BATTLE_PET"; - case ACHIEVEMENT_CRITERIA_TYPE_OWN_BATTLE_PET_COUNT: - return "OWN_BATTLE_PET_COUNT"; - case ACHIEVEMENT_CRITERIA_TYPE_CAPTURE_BATTLE_PET: - return "CAPTURE_BATTLE_PET"; - case ACHIEVEMENT_CRITERIA_TYPE_WIN_PET_BATTLE: - return "WIN_PET_BATTLE"; - case ACHIEVEMENT_CRITERIA_TYPE_LEVEL_BATTLE_PET: - return "LEVEL_BATTLE_PET"; - case ACHIEVEMENT_CRITERIA_TYPE_CAPTURE_BATTLE_PET_CREDIT: - return "CAPTURE_BATTLE_PET_CREDIT"; - case ACHIEVEMENT_CRITERIA_TYPE_LEVEL_BATTLE_PET_CREDIT: - return "LEVEL_BATTLE_PET_CREDIT"; - case ACHIEVEMENT_CRITERIA_TYPE_ENTER_AREA: - return "ENTER_AREA"; - case ACHIEVEMENT_CRITERIA_TYPE_LEAVE_AREA: - return "LEAVE_AREA"; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DUNGEON_ENCOUNTER: - return "COMPLETE_DUNGEON_ENCOUNTER"; - case ACHIEVEMENT_CRITERIA_TYPE_PLACE_GARRISON_BUILDING: - return "PLACE_GARRISON_BUILDING"; - case ACHIEVEMENT_CRITERIA_TYPE_UPGRADE_GARRISON_BUILDING: - return "UPGRADE_GARRISON_BUILDING"; - case ACHIEVEMENT_CRITERIA_TYPE_CONSTRUCT_GARRISON_BUILDING: - return "CONSTRUCT_GARRISON_BUILDING"; - case ACHIEVEMENT_CRITERIA_TYPE_UPGRADE_GARRISON: - return "UPGRADE_GARRISON"; - case ACHIEVEMENT_CRITERIA_TYPE_START_GARRISON_MISSION: - return "START_GARRISON_MISSION"; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_GARRISON_MISSION_COUNT: - return "COMPLETE_GARRISON_MISSION_COUNT"; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_GARRISON_MISSION: - return "COMPLETE_GARRISON_MISSION"; - case ACHIEVEMENT_CRITERIA_TYPE_RECRUIT_GARRISON_FOLLOWER_COUNT: - return "RECRUIT_GARRISON_FOLLOWER_COUNT"; - case ACHIEVEMENT_CRITERIA_TYPE_LEARN_GARRISON_BLUEPRINT_COUNT: - return "LEARN_GARRISON_BLUEPRINT_COUNT"; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_GARRISON_SHIPMENT: - return "COMPLETE_GARRISON_SHIPMENT"; - case ACHIEVEMENT_CRITERIA_TYPE_RAISE_GARRISON_FOLLOWER_ITEM_LEVEL: - return "RAISE_GARRISON_FOLLOWER_ITEM_LEVEL"; - case ACHIEVEMENT_CRITERIA_TYPE_RAISE_GARRISON_FOLLOWER_LEVEL: - return "RAISE_GARRISON_FOLLOWER_LEVEL"; - case ACHIEVEMENT_CRITERIA_TYPE_OWN_TOY: - return "OWN_TOY"; - case ACHIEVEMENT_CRITERIA_TYPE_OWN_TOY_COUNT: - return "OWN_TOY_COUNT"; - case ACHIEVEMENT_CRITERIA_TYPE_RECRUIT_GARRISON_FOLLOWER: - return "RECRUIT_GARRISON_FOLLOWER"; - case ACHIEVEMENT_CRITERIA_TYPE_OWN_HEIRLOOMS: - return "OWN_HEIRLOOMS"; - } - return "MISSING_TYPE"; + return Trinity::StringFormat("%s %s", _owner->GetGUID().ToString().c_str(), _owner->GetName().c_str()); } -AchievementGlobalMgr* AchievementGlobalMgr::instance() +std::string GuildAchievementMgr::GetOwnerInfo() const +{ + return Trinity::StringFormat("Guild ID " UI64FMTD " %s", _owner->GetId(), _owner->GetName().c_str()); +} + +AchievementGlobalMgr* AchievementGlobalMgr::Instance() { static AchievementGlobalMgr instance; return &instance; } -//========================================================== -AchievementGlobalMgr::~AchievementGlobalMgr() +std::vector<AchievementEntry const*> const* AchievementGlobalMgr::GetAchievementByReferencedId(uint32 id) const { - for (AchievementCriteriaTreeMap::iterator itr = _achievementCriteriaTrees.begin(); itr != _achievementCriteriaTrees.end(); ++itr) - delete itr->second; - - for (AchievementCriteriaMap::iterator itr = _achievementCriteria.begin(); itr != _achievementCriteria.end(); ++itr) - delete itr->second; - - for (ModifierTreeMap::iterator itr = _criteriaModifiers.begin(); itr != _criteriaModifiers.end(); ++itr) - delete itr->second; + auto itr = _achievementListByReferencedId.find(id); + return itr != _achievementListByReferencedId.end() ? &itr->second : NULL; } -void AchievementGlobalMgr::LoadAchievementCriteriaModifiersTree() +AchievementReward const* AchievementGlobalMgr::GetAchievementReward(AchievementEntry const* achievement) const { - uint32 oldMSTime = getMSTime(); - - if (sModifierTreeStore.GetNumRows() == 0) - { - TC_LOG_ERROR("server.loading", ">> Loaded 0 achievement criteria modifiers."); - return; - } + auto iter = _achievementRewards.find(achievement->ID); + return iter != _achievementRewards.end() ? &iter->second : NULL; +} - // Load modifier tree nodes - for (uint32 i = 0; i < sModifierTreeStore.GetNumRows(); ++i) - { - ModifierTreeEntry const* tree = sModifierTreeStore.LookupEntry(i); - if (!tree) - continue; +AchievementRewardLocale const* AchievementGlobalMgr::GetAchievementRewardLocale(AchievementEntry const* achievement) const +{ + auto iter = _achievementRewardLocales.find(achievement->ID); + return iter != _achievementRewardLocales.end() ? &iter->second : NULL; +} - ModifierTreeNode* node = new ModifierTreeNode(); - node->Entry = tree; - _criteriaModifiers[node->Entry->ID] = node; - } +bool AchievementGlobalMgr::IsRealmCompleted(AchievementEntry const* achievement) const +{ + auto itr = _allCompletedAchievements.find(achievement->ID); + if (itr == _allCompletedAchievements.end()) + return false; - // Build tree - for (auto itr = _criteriaModifiers.begin(); itr != _criteriaModifiers.end(); ++itr) - { - if (!itr->second->Entry->Parent) - continue; + if (itr->second == std::chrono::system_clock::time_point::min()) + return false; - auto parent = _criteriaModifiers.find(itr->second->Entry->Parent); - if (parent != _criteriaModifiers.end()) - parent->second->Children.push_back(itr->second); - } + // Allow completing the realm first kill for entire minute after first person did it + // it may allow more than one group to achieve it (highly unlikely) + // but apparently this is how blizz handles it as well + if (achievement->Flags & ACHIEVEMENT_FLAG_REALM_FIRST_KILL) + return (std::chrono::system_clock::now() - itr->second) > Minutes(1); - TC_LOG_INFO("server.loading", ">> Loaded %u achievement criteria modifiers in %u ms", uint32(_criteriaModifiers.size()), GetMSTimeDiffToNow(oldMSTime)); + return true; } -void AchievementGlobalMgr::LoadAchievementCriteriaList() +void AchievementGlobalMgr::SetRealmCompleted(AchievementEntry const* achievement) { - uint32 oldMSTime = getMSTime(); - - if (sCriteriaTreeStore.GetNumRows() == 0) - { - TC_LOG_ERROR("server.loading", ">> Loaded 0 achievement criteria."); + if (IsRealmCompleted(achievement)) return; - } - - std::unordered_map<uint32 /*criteriaTreeID*/, AchievementEntry const*> achievementCriteriaTreeIds; - for (uint32 i = 0; i < sAchievementStore.GetNumRows(); ++i) - if (AchievementEntry const* achievement = sAchievementStore.LookupEntry(i)) - if (achievement->CriteriaTree) - achievementCriteriaTreeIds[achievement->CriteriaTree] = achievement; - - // Load criteria tree nodes - for (uint32 i = 0; i < sCriteriaTreeStore.GetNumRows(); ++i) - { - CriteriaTreeEntry const* tree = sCriteriaTreeStore.LookupEntry(i); - if (!tree) - continue; - - // Find linked achievement - auto achievementItr = achievementCriteriaTreeIds.find(tree->ID); - CriteriaTreeEntry const* cur = tree; - while (achievementItr == achievementCriteriaTreeIds.end()) - { - if (!cur->Parent) - break; - - cur = sCriteriaTreeStore.LookupEntry(cur->Parent); - if (!cur) - break; - - achievementItr = achievementCriteriaTreeIds.find(cur->ID); - } - - if (achievementItr == achievementCriteriaTreeIds.end()) - continue; - - AchievementCriteriaTree* achievementCriteriaTree = new AchievementCriteriaTree(); - achievementCriteriaTree->ID = i; - achievementCriteriaTree->Achievement = achievementItr->second; - achievementCriteriaTree->Entry = tree; - - _achievementCriteriaTrees[achievementCriteriaTree->Entry->ID] = achievementCriteriaTree; - } - - // Build tree - for (auto itr = _achievementCriteriaTrees.begin(); itr != _achievementCriteriaTrees.end(); ++itr) - { - if (!itr->second->Entry->Parent) - continue; - - auto parent = _achievementCriteriaTrees.find(itr->second->Entry->Parent); - if (parent != _achievementCriteriaTrees.end()) - { - parent->second->Children.push_back(itr->second); - while (parent != _achievementCriteriaTrees.end()) - { - auto cur = parent; - parent = _achievementCriteriaTrees.find(parent->second->Entry->Parent); - if (parent == _achievementCriteriaTrees.end()) - { - if (sCriteriaStore.LookupEntry(itr->second->Entry->CriteriaID)) - _achievementCriteriaTreeByCriteria[itr->second->Entry->CriteriaID].push_back(cur->second); - } - } - } - else if (sCriteriaStore.LookupEntry(itr->second->Entry->CriteriaID)) - _achievementCriteriaTreeByCriteria[itr->second->Entry->CriteriaID].push_back(itr->second); - } - - // Load criteria - uint32 criterias = 0; - uint32 guildCriterias = 0; - for (uint32 i = 0; i < sCriteriaStore.GetNumRows(); ++i) - { - CriteriaEntry const* criteria = sCriteriaStore.LookupEntry(i); - if (!criteria) - continue; - - auto treeItr = _achievementCriteriaTreeByCriteria.find(i); - if (treeItr == _achievementCriteriaTreeByCriteria.end()) - continue; - - AchievementCriteria* achievementCriteria = new AchievementCriteria(); - achievementCriteria->ID = i; - achievementCriteria->Entry = criteria; - auto mod = _criteriaModifiers.find(criteria->ModifierTreeId); - if (mod != _criteriaModifiers.end()) - achievementCriteria->Modifier = mod->second; - - _achievementCriteria[achievementCriteria->ID] = achievementCriteria; - - for (AchievementCriteriaTree const* tree : treeItr->second) - { - if (tree->Achievement->Flags & ACHIEVEMENT_FLAG_GUILD) - achievementCriteria->FlagsCu |= ACHIEVEMENT_CRITERIA_FLAG_CU_GUILD; - else if (tree->Achievement->Flags & ACHIEVEMENT_FLAG_ACCOUNT) - achievementCriteria->FlagsCu |= ACHIEVEMENT_CRITERIA_FLAG_CU_ACCOUNT; - else - achievementCriteria->FlagsCu |= ACHIEVEMENT_CRITERIA_FLAG_CU_PLAYER; - } - - if (achievementCriteria->FlagsCu & ACHIEVEMENT_CRITERIA_FLAG_CU_GUILD) - { - ++guildCriterias; - _guildAchievementCriteriasByType[criteria->Type].push_back(achievementCriteria); - } - - if (achievementCriteria->FlagsCu & (ACHIEVEMENT_CRITERIA_FLAG_CU_PLAYER | ACHIEVEMENT_CRITERIA_FLAG_CU_ACCOUNT)) - { - ++criterias; - _achievementCriteriasByType[criteria->Type].push_back(achievementCriteria); - } - if (criteria->StartTimer) - _achievementCriteriasByTimedType[criteria->StartEvent].push_back(achievementCriteria); - } - - for (auto& p : _achievementCriteriaTrees) - const_cast<AchievementCriteriaTree*>(p.second)->Criteria = GetAchievementCriteria(p.second->Entry->CriteriaID); - - TC_LOG_INFO("server.loading", ">> Loaded %u achievement criteria and %u guild achievement crieteria in %u ms.", criterias, guildCriterias, GetMSTimeDiffToNow(oldMSTime)); + _allCompletedAchievements[achievement->ID] = std::chrono::system_clock::now(); } +//========================================================== void AchievementGlobalMgr::LoadAchievementReferenceList() { uint32 oldMSTime = getMSTime(); @@ -3081,7 +999,7 @@ void AchievementGlobalMgr::LoadAchievementReferenceList() for (uint32 entryId = 0; entryId < sAchievementStore.GetNumRows(); ++entryId) { - AchievementEntry const* achievement = sAchievementMgr->GetAchievement(entryId); + AchievementEntry const* achievement = sAchievementStore.LookupEntry(entryId); if (!achievement || !achievement->SharesCriteria) continue; @@ -3090,77 +1008,23 @@ void AchievementGlobalMgr::LoadAchievementReferenceList() } // Once Bitten, Twice Shy (10 player) - Icecrown Citadel - if (AchievementEntry const* achievement = sAchievementMgr->GetAchievement(4539)) + if (AchievementEntry const* achievement = sAchievementStore.LookupEntry(4539)) const_cast<AchievementEntry*>(achievement)->MapID = 631; // Correct map requirement (currently has Ulduar); 6.0.3 note - it STILL has ulduar requirement TC_LOG_INFO("server.loading", ">> Loaded %u achievement references in %u ms.", count, GetMSTimeDiffToNow(oldMSTime)); } -void AchievementGlobalMgr::LoadAchievementCriteriaData() -{ - uint32 oldMSTime = getMSTime(); - - _criteriaDataMap.clear(); // need for reload case - - QueryResult result = WorldDatabase.Query("SELECT criteria_id, type, value1, value2, ScriptName FROM achievement_criteria_data"); - - if (!result) - { - TC_LOG_INFO("server.loading", ">> Loaded 0 additional achievement criteria data. DB table `achievement_criteria_data` is empty."); - return; - } - - uint32 count = 0; - - do - { - Field* fields = result->Fetch(); - uint32 criteria_id = fields[0].GetUInt32(); - - AchievementCriteria const* criteria = sAchievementMgr->GetAchievementCriteria(criteria_id); - - if (!criteria) - { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` contains data for non-existing criteria (Entry: %u). Ignored.", criteria_id); - continue; - } - - uint32 dataType = fields[1].GetUInt8(); - std::string scriptName = fields[4].GetString(); - uint32 scriptId = 0; - if (!scriptName.empty()) - { - if (dataType != ACHIEVEMENT_CRITERIA_DATA_TYPE_SCRIPT) - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` contains a ScriptName for non-scripted data type (Entry: %u, type %u), useless data.", criteria_id, dataType); - else - scriptId = sObjectMgr->GetScriptId(scriptName); - } - - AchievementCriteriaData data(dataType, fields[2].GetUInt32(), fields[3].GetUInt32(), scriptId); - - if (!data.IsValid(criteria)) - continue; - - // this will allocate empty data set storage - AchievementCriteriaDataSet& dataSet = _criteriaDataMap[criteria_id]; - dataSet.SetCriteriaId(criteria_id); - - // add real data only for not NONE data types - if (data.dataType != ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE) - dataSet.Add(data); - - // counting data by and data types - ++count; - } - while (result->NextRow()); - - TC_LOG_INFO("server.loading", ">> Loaded %u additional achievement criteria data in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); -} - void AchievementGlobalMgr::LoadCompletedAchievements() { uint32 oldMSTime = getMSTime(); + // Populate _allCompletedAchievements with all realm first achievement ids to make multithreaded access safer + // while it will not prevent races, it will prevent crashes that happen because std::unordered_map key was added + // instead the only potential race will happen on value associated with the key + for (AchievementEntry const* achievement : sAchievementStore) + if (achievement->Flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL)) + _allCompletedAchievements[achievement->ID] = std::chrono::system_clock::time_point::min(); + QueryResult result = CharacterDatabase.Query("SELECT achievement FROM character_achievement GROUP BY achievement"); if (!result) @@ -3173,21 +1037,21 @@ void AchievementGlobalMgr::LoadCompletedAchievements() { Field* fields = result->Fetch(); - uint16 achievementId = fields[0].GetUInt16(); - const AchievementEntry* achievement = sAchievementMgr->GetAchievement(achievementId); + uint32 achievementId = fields[0].GetUInt32(); + AchievementEntry const* achievement = sAchievementStore.LookupEntry(achievementId); if (!achievement) { // Remove non-existing achievements from all characters - TC_LOG_ERROR("achievement", "Non-existing achievement %u data has been removed from the table `character_achievement`.", achievementId); + TC_LOG_ERROR("criteria.achievement", "Non-existing achievement %u data has been removed from the table `character_achievement`.", achievementId); PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_ACHIEVMENT); - stmt->setUInt16(0, uint16(achievementId)); + stmt->setUInt32(0, achievementId); CharacterDatabase.Execute(stmt); continue; } else if (achievement->Flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL)) - _allCompletedAchievements[achievementId] = uint32(0xFFFFFFFF); + _allCompletedAchievements[achievementId] = std::chrono::system_clock::time_point::max(); } while (result->NextRow()); @@ -3215,7 +1079,7 @@ void AchievementGlobalMgr::LoadRewards() { Field* fields = result->Fetch(); uint32 entry = fields[0].GetUInt32(); - AchievementEntry const* achievement = GetAchievement(entry); + AchievementEntry const* achievement = sAchievementStore.LookupEntry(entry); if (!achievement) { TC_LOG_ERROR("sql.sql", "Table `achievement_reward` contains a wrong achievement entry (Entry: %u), ignored.", entry); @@ -3223,85 +1087,85 @@ void AchievementGlobalMgr::LoadRewards() } AchievementReward reward; - reward.titleId[0] = fields[1].GetUInt32(); - reward.titleId[1] = fields[2].GetUInt32(); - reward.itemId = fields[3].GetUInt32(); - reward.sender = fields[4].GetUInt32(); - reward.subject = fields[5].GetString(); - reward.text = fields[6].GetString(); - reward.mailTemplate = fields[7].GetUInt32(); + reward.TitleId[0] = fields[1].GetUInt32(); + reward.TitleId[1] = fields[2].GetUInt32(); + reward.ItemId = fields[3].GetUInt32(); + reward.SenderCreatureId = fields[4].GetUInt32(); + reward.Subject = fields[5].GetString(); + reward.Body = fields[6].GetString(); + reward.MailTemplateId = fields[7].GetUInt32(); // must be title or mail at least - if (!reward.titleId[0] && !reward.titleId[1] && !reward.sender) + if (!reward.TitleId[0] && !reward.TitleId[1] && !reward.SenderCreatureId) { TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) does not contain title or item reward data. Ignored.", entry); continue; } - if (achievement->Faction == ACHIEVEMENT_FACTION_ANY && (!reward.titleId[0] ^ !reward.titleId[1])) - TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) contains the title (A: %u H: %u) for only one team.", entry, reward.titleId[0], reward.titleId[1]); + if (achievement->Faction == ACHIEVEMENT_FACTION_ANY && (!reward.TitleId[0] ^ !reward.TitleId[1])) + TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) contains the title (A: %u H: %u) for only one team.", entry, reward.TitleId[0], reward.TitleId[1]); - if (reward.titleId[0]) + if (reward.TitleId[0]) { - CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.titleId[0]); + CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.TitleId[0]); if (!titleEntry) { - TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) contains an invalid title id (%u) in `title_A`, set to 0", entry, reward.titleId[0]); - reward.titleId[0] = 0; + TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) contains an invalid title id (%u) in `title_A`, set to 0", entry, reward.TitleId[0]); + reward.TitleId[0] = 0; } } - if (reward.titleId[1]) + if (reward.TitleId[1]) { - CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.titleId[1]); + CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.TitleId[1]); if (!titleEntry) { - TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) contains an invalid title id (%u) in `title_H`, set to 0", entry, reward.titleId[1]); - reward.titleId[1] = 0; + TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) contains an invalid title id (%u) in `title_H`, set to 0", entry, reward.TitleId[1]); + reward.TitleId[1] = 0; } } //check mail data before item for report including wrong item case - if (reward.sender) + if (reward.SenderCreatureId) { - if (!sObjectMgr->GetCreatureTemplate(reward.sender)) + if (!sObjectMgr->GetCreatureTemplate(reward.SenderCreatureId)) { - TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) contains an invalid creature entry %u as sender, mail reward skipped.", entry, reward.sender); - reward.sender = 0; + TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) contains an invalid creature entry %u as sender, mail reward skipped.", entry, reward.SenderCreatureId); + reward.SenderCreatureId = 0; } } else { - if (reward.itemId) + if (reward.ItemId) TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) does not have sender data, but contains an item reward. Item will not be rewarded.", entry); - if (!reward.subject.empty()) + if (!reward.Subject.empty()) TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) does not have sender data, but contains a mail subject.", entry); - if (!reward.text.empty()) + if (!reward.Body.empty()) TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) does not have sender data, but contains mail text.", entry); - if (reward.mailTemplate) - TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) does not have sender data, but has a mailTemplate.", entry); + if (reward.MailTemplateId) + TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) does not have sender data, but has a MailTemplateId.", entry); } - if (reward.mailTemplate) + if (reward.MailTemplateId) { - if (!sMailTemplateStore.LookupEntry(reward.mailTemplate)) + if (!sMailTemplateStore.LookupEntry(reward.MailTemplateId)) { - TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) is using an invalid mailTemplate (%u).", entry, reward.mailTemplate); - reward.mailTemplate = 0; + TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) is using an invalid MailTemplateId (%u).", entry, reward.MailTemplateId); + reward.MailTemplateId = 0; } - else if (!reward.subject.empty() || !reward.text.empty()) - TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) is using mailTemplate (%u) and mail subject/text.", entry, reward.mailTemplate); + else if (!reward.Subject.empty() || !reward.Body.empty()) + TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) is using MailTemplateId (%u) and mail subject/text.", entry, reward.MailTemplateId); } - if (reward.itemId) + if (reward.ItemId) { - if (!sObjectMgr->GetItemTemplate(reward.itemId)) + if (!sObjectMgr->GetItemTemplate(reward.ItemId)) { - TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) contains an invalid item id %u, reward mail will not contain the rewarded item.", entry, reward.itemId); - reward.itemId = 0; + TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) contains an invalid item id %u, reward mail will not contain the rewarded item.", entry, reward.ItemId); + reward.ItemId = 0; } } @@ -3346,41 +1210,11 @@ void AchievementGlobalMgr::LoadRewardLocales() for (uint8 i = OLD_TOTAL_LOCALES - 1; i > 0; --i) { LocaleConstant locale = (LocaleConstant) i; - ObjectMgr::AddLocaleString(fields[1 + 2 * (i - 1)].GetString(), locale, data.subject); - ObjectMgr::AddLocaleString(fields[1 + 2 * (i - 1) + 1].GetString(), locale, data.text); + ObjectMgr::AddLocaleString(fields[1 + 2 * (i - 1)].GetString(), locale, data.Subject); + ObjectMgr::AddLocaleString(fields[1 + 2 * (i - 1) + 1].GetString(), locale, data.Body); } } while (result->NextRow()); TC_LOG_INFO("server.loading", ">> Loaded %u achievement reward locale strings in %u ms.", uint32(_achievementRewardLocales.size()), GetMSTimeDiffToNow(oldMSTime)); } - -AchievementEntry const* AchievementGlobalMgr::GetAchievement(uint32 achievementId) const -{ - return sAchievementStore.LookupEntry(achievementId); -} - -AchievementCriteriaTree const* AchievementGlobalMgr::GetAchievementCriteriaTree(uint32 criteriaTreeId) const -{ - auto itr = _achievementCriteriaTrees.find(criteriaTreeId); - if (itr == _achievementCriteriaTrees.end()) - return nullptr; - - return itr->second; -} - -AchievementCriteria const* AchievementGlobalMgr::GetAchievementCriteria(uint32 criteriaId) const -{ - auto itr = _achievementCriteria.find(criteriaId); - if (itr == _achievementCriteria.end()) - return nullptr; - - return itr->second; -} - -void AchievementGlobalMgr::OnInstanceDestroyed(uint32 instanceId) -{ - for (auto& realmCompletion : _allCompletedAchievements) - if (realmCompletion.second == instanceId) - realmCompletion.second = uint32(0xFFFFFFFF); -} |