diff options
-rw-r--r-- | sql/updates/world/2014_12_28_00_world.sql | 2 | ||||
-rw-r--r-- | src/server/game/Achievements/AchievementMgr.cpp | 1329 | ||||
-rw-r--r-- | src/server/game/Achievements/AchievementMgr.h | 133 | ||||
-rw-r--r-- | src/server/game/DataStores/DBCEnums.h | 18 | ||||
-rw-r--r-- | src/server/game/DataStores/DBCStores.cpp | 25 | ||||
-rw-r--r-- | src/server/game/DataStores/DBCStores.h | 4 | ||||
-rw-r--r-- | src/server/game/DataStores/DBCStructure.h | 608 | ||||
-rw-r--r-- | src/server/game/DataStores/DBCfmt.h | 8 | ||||
-rw-r--r-- | src/server/game/Guilds/Guild.cpp | 6 | ||||
-rw-r--r-- | src/server/game/Guilds/Guild.h | 2 | ||||
-rw-r--r-- | src/server/game/Server/Packets/AchievementPackets.cpp | 74 | ||||
-rw-r--r-- | src/server/game/Server/Packets/AchievementPackets.h | 93 | ||||
-rw-r--r-- | src/server/game/Server/Protocol/Opcodes.cpp | 6 | ||||
-rw-r--r-- | src/server/game/Server/Protocol/Opcodes.h | 12 | ||||
-rw-r--r-- | src/server/game/World/World.cpp | 2 |
15 files changed, 1125 insertions, 1197 deletions
diff --git a/sql/updates/world/2014_12_28_00_world.sql b/sql/updates/world/2014_12_28_00_world.sql new file mode 100644 index 00000000000..e71ec1cf8bb --- /dev/null +++ b/sql/updates/world/2014_12_28_00_world.sql @@ -0,0 +1,2 @@ +DELETE FROM `achievement_criteria_data`; +DELETE FROM `disables` WHERE `sourceType`=4; diff --git a/src/server/game/Achievements/AchievementMgr.cpp b/src/server/game/Achievements/AchievementMgr.cpp index fcafd9c6d01..c392f9d445a 100644 --- a/src/server/game/Achievements/AchievementMgr.cpp +++ b/src/server/game/Achievements/AchievementMgr.cpp @@ -17,6 +17,7 @@ */ #include "AchievementMgr.h" +#include "AchievementPackets.h" #include "ArenaTeam.h" #include "ArenaTeamMgr.h" #include "Battleground.h" @@ -43,7 +44,7 @@ #include "World.h" #include "WorldPacket.h" -bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) +bool AchievementCriteriaData::IsValid(AchievementCriteria const* criteria) { if (dataType >= MAX_ACHIEVEMENT_CRITERIA_DATA_TYPE) { @@ -51,7 +52,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) return false; } - switch (criteria->type) + switch (criteria->Entry->Type) { case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE: case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE: @@ -81,7 +82,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) default: if (dataType != ACHIEVEMENT_CRITERIA_DATA_TYPE_SCRIPT) { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` has data for non-supported criteria type (Entry: %u Type: %u), ignored.", criteria->ID, criteria->type); + TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` has data for non-supported criteria type (Entry: %u Type: %u), ignored.", criteria->ID, criteria->Entry->Type); return false; } break; @@ -96,7 +97,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) 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) has non-existing creature id in value1 (%u), ignored.", - criteria->ID, criteria->type, dataType, creature.id); + criteria->ID, criteria->Entry->Type, dataType, creature.id); return false; } return true; @@ -104,19 +105,19 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) 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->type, dataType); + 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) has non-existing class in value1 (%u), ignored.", - criteria->ID, criteria->type, dataType, classRace.class_id); + 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) has non-existing race in value2 (%u), ignored.", - criteria->ID, criteria->type, dataType, classRace.race_id); + criteria->ID, criteria->Entry->Type, dataType, classRace.race_id); return false; } return true; @@ -124,7 +125,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) 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) has wrong percent value in value1 (%u), ignored.", - criteria->ID, criteria->type, dataType, health.percent); + criteria->ID, criteria->Entry->Type, dataType, health.percent); return false; } return true; @@ -135,20 +136,20 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) if (!spellEntry) { TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) has wrong spell id in value1 (%u), ignored.", - criteria->ID, criteria->type, (dataType == ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA?"ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA":"ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA"), dataType, aura.spell_id); + 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) has wrong spell effect index in value2 (%u), ignored.", - criteria->ID, criteria->type, (dataType == ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA?"ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA":"ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA"), dataType, aura.effect_idx); + 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) has non-aura spell effect (ID: %u Effect: %u), ignores.", - criteria->ID, criteria->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); + 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; @@ -157,7 +158,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) 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) has wrong ComparisionType in value2 (%u), ignored.", - criteria->ID, criteria->type, dataType, value.compType); + criteria->ID, criteria->Entry->Type, dataType, value.compType); return false; } return true; @@ -165,7 +166,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) 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) has wrong minlevel in value1 (%u), ignored.", - criteria->ID, criteria->type, dataType, level.minlevel); + criteria->ID, criteria->Entry->Type, dataType, level.minlevel); return false; } return true; @@ -173,7 +174,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) 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) has wrong gender in value1 (%u), ignored.", - criteria->ID, criteria->type, dataType, gender.gender); + criteria->ID, criteria->Entry->Type, dataType, gender.gender); return false; } return true; @@ -181,7 +182,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) 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 ScriptName set, ignored.", - criteria->ID, criteria->type, dataType); + criteria->ID, criteria->Entry->Type, dataType); return false; } return true; @@ -189,7 +190,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) 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) has wrong max players count in value1 (%u), ignored.", - criteria->ID, criteria->type, dataType, map_players.maxcount); + criteria->ID, criteria->Entry->Type, dataType, map_players.maxcount); return false; } return true; @@ -197,7 +198,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) 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) has unknown team in value1 (%u), ignored.", - criteria->ID, criteria->type, dataType, team.team); + criteria->ID, criteria->Entry->Type, dataType, team.team); return false; } return true; @@ -205,7 +206,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) 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) has unknown drunken state in value1 (%u), ignored.", - criteria->ID, criteria->type, dataType, drunk.state); + criteria->ID, criteria->Entry->Type, dataType, drunk.state); return false; } return true; @@ -213,7 +214,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) 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) has unknown holiday in value1 (%u), ignored.", - criteria->ID, criteria->type, dataType, holiday.id); + criteria->ID, criteria->Entry->Type, dataType, holiday.id); return false; } return true; @@ -223,7 +224,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) 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->type, dataType, game_event.id); + criteria->ID, criteria->Entry->Type, dataType, game_event.id); return false; } return true; @@ -234,7 +235,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) 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) has unknown quality state in value1 (%u), ignored.", - criteria->ID, criteria->type, dataType, equipped_item.item_quality); + criteria->ID, criteria->Entry->Type, dataType, equipped_item.item_quality); return false; } return true; @@ -242,19 +243,19 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) 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) must not have 0 in either value field, ignored.", - criteria->ID, criteria->type, dataType); + 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) has non-existing class in value1 (%u), ignored.", - criteria->ID, criteria->type, dataType, classRace.class_id); + 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) has non-existing race in value2 (%u), ignored.", - criteria->ID, criteria->type, dataType, classRace.race_id); + criteria->ID, criteria->Entry->Type, dataType, classRace.race_id); return false; } return true; @@ -262,12 +263,12 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) 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) have unknown title_id in value1 (%u), ignore.", - criteria->ID, criteria->type, dataType, known_title.title_id); + 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) has data for non-supported data type (%u), ignored.", criteria->ID, criteria->type, dataType); + TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) has data for non-supported data type (%u), ignored.", criteria->ID, criteria->Entry->Type, dataType); return false; } } @@ -383,8 +384,8 @@ bool AchievementCriteriaData::Meets(uint32 criteria_id, Player const* source, Un bool AchievementCriteriaDataSet::Meets(Player const* source, Unit const* target, uint32 miscValue /*= 0*/) const { - for (Storage::const_iterator itr = storage.begin(); itr != storage.end(); ++itr) - if (!itr->Meets(criteria_id, source, target, miscValue)) + for (AchievementCriteriaData const& data : storage) + if (!data.Meets(criteria_id, source, target, miscValue)) return false; return true; @@ -397,22 +398,22 @@ template<class T> AchievementMgr<T>::~AchievementMgr() { } template<class T> -void AchievementMgr<T>::SendPacket(WorldPacket* data) const { } +void AchievementMgr<T>::SendPacket(WorldPacket const* data) const { } template<> -void AchievementMgr<Guild>::SendPacket(WorldPacket* data) const +void AchievementMgr<Guild>::SendPacket(WorldPacket const* data) const { GetOwner()->BroadcastPacket(data); } template<> -void AchievementMgr<Player>::SendPacket(WorldPacket* data) const +void AchievementMgr<Player>::SendPacket(WorldPacket const* data) const { GetOwner()->GetSession()->SendPacket(data); } template<class T> -void AchievementMgr<T>::RemoveCriteriaProgress(AchievementCriteriaEntry const* entry) +void AchievementMgr<T>::RemoveCriteriaProgress(AchievementCriteria const* entry) { if (!entry) return; @@ -429,7 +430,7 @@ void AchievementMgr<T>::RemoveCriteriaProgress(AchievementCriteriaEntry const* e } template<> -void AchievementMgr<Guild>::RemoveCriteriaProgress(AchievementCriteriaEntry const* entry) +void AchievementMgr<Guild>::RemoveCriteriaProgress(AchievementCriteria const* entry) { if (!entry) return; @@ -474,27 +475,28 @@ void AchievementMgr<T>::ResetAchievementCriteria(AchievementCriteriaTypes type, if (GetOwner()->IsGameMaster()) return; - AchievementCriteriaEntryList const& achievementCriteriaList = sAchievementMgr->GetAchievementCriteriaByType(type); - for (AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i != achievementCriteriaList.end(); ++i) + AchievementCriteriaList const& achievementCriteriaList = sAchievementMgr->GetAchievementCriteriaByType(type); + for (AchievementCriteria const* achievementCriteria : achievementCriteriaList) { - AchievementCriteriaEntry const* achievementCriteria = (*i); - - AchievementEntry const* achievement = sAchievementMgr->GetAchievement(achievementCriteria->achievement); - if (!achievement) - continue; - - // don't update already completed criteria if not forced or achievement already complete - if ((IsCompletedCriteria(achievementCriteria, achievement) && !evenIfCriteriaComplete) || HasAchieved(achievement->ID)) + if (achievementCriteria->Entry->FailEvent != miscValue1 || (achievementCriteria->Entry->FailAsset && achievementCriteria->Entry->FailAsset != miscValue2)) continue; - for (uint8 j = 0; j < MAX_CRITERIA_REQUIREMENTS; ++j) - if (achievementCriteria->additionalRequirements[j].additionalRequirement_type == miscValue1 && - (!achievementCriteria->additionalRequirements[j].additionalRequirement_value || - achievementCriteria->additionalRequirements[j].additionalRequirement_value == miscValue2)) + AchievementCriteriaTreeList const* trees = sAchievementMgr->GetAchievementCriteriaTreesByCriteria(achievementCriteria->ID); + bool allComplete = true; + for (AchievementCriteriaTree const* tree : *trees) + { + // don't update already completed criteria if not forced or achievement already complete + if (!(IsCompletedCriteriaTree(tree) && !evenIfCriteriaComplete) || !HasAchieved(tree->Achievement->ID)) { - RemoveCriteriaProgress(achievementCriteria); + allComplete = false; break; } + } + + if (allComplete) + continue; + + RemoveCriteriaProgress(achievementCriteria); } } @@ -545,7 +547,6 @@ template<class T> void AchievementMgr<T>::SaveToDB(SQLTransaction& /*trans*/) { } - template<> void AchievementMgr<Player>::SaveToDB(SQLTransaction& trans) { @@ -571,6 +572,7 @@ void AchievementMgr<Player>::SaveToDB(SQLTransaction& trans) } } + /* if (!m_criteriaProgress.empty()) { for (CriteriaProgressMap::iterator iter = m_criteriaProgress.begin(); iter != m_criteriaProgress.end(); ++iter) @@ -596,6 +598,7 @@ void AchievementMgr<Player>::SaveToDB(SQLTransaction& trans) iter->second.changed = false; } } + */ } template<> @@ -626,6 +629,7 @@ void AchievementMgr<Guild>::SaveToDB(SQLTransaction& trans) guidstr.str(""); } + /* for (CriteriaProgressMap::const_iterator itr = m_criteriaProgress.begin(); itr != m_criteriaProgress.end(); ++itr) { if (!itr->second.changed) @@ -641,11 +645,11 @@ void AchievementMgr<Guild>::SaveToDB(SQLTransaction& trans) stmt->setUInt16(1, itr->first); stmt->setUInt64(2, itr->second.counter); stmt->setUInt32(3, itr->second.date); - stmt->setUInt64(4, itr->second.CompletedGUID.GetCounter()); + stmt->setUInt64(4, itr->second.PlayerGUID.GetCounter()); trans->Append(stmt); } + */ } - template<class T> void AchievementMgr<T>::LoadFromDB(PreparedQueryResult achievementResult, PreparedQueryResult criteriaResult) { @@ -682,6 +686,7 @@ void AchievementMgr<Player>::LoadFromDB(PreparedQueryResult achievementResult, P while (achievementResult->NextRow()); } + /* if (criteriaResult) { time_t now = time(NULL); @@ -692,7 +697,7 @@ void AchievementMgr<Player>::LoadFromDB(PreparedQueryResult achievementResult, P uint64 counter = fields[1].GetUInt64(); time_t date = time_t(fields[2].GetUInt32()); - AchievementCriteriaEntry const* criteria = sAchievementMgr->GetAchievementCriteria(id); + AchievementCriteria const* criteria = sAchievementMgr->GetAchievementCriteria(id); if (!criteria) { // we will remove not existed criteria for all characters @@ -705,7 +710,7 @@ void AchievementMgr<Player>::LoadFromDB(PreparedQueryResult achievementResult, P continue; } - if (criteria->timeLimit && time_t(date + criteria->timeLimit) < now) + if (criteria->Entry->StartTimer && time_t(date + criteria->Entry->StartTimer) < now) continue; CriteriaProgress& progress = m_criteriaProgress[id]; @@ -715,6 +720,7 @@ void AchievementMgr<Player>::LoadFromDB(PreparedQueryResult achievementResult, P } while (criteriaResult->NextRow()); } + */ } template<> @@ -745,6 +751,7 @@ void AchievementMgr<Guild>::LoadFromDB(PreparedQueryResult achievementResult, Pr while (achievementResult->NextRow()); } + /* if (criteriaResult) { time_t now = time(NULL); @@ -756,7 +763,7 @@ void AchievementMgr<Guild>::LoadFromDB(PreparedQueryResult achievementResult, Pr time_t date = time_t(fields[2].GetUInt32()); ObjectGuid::LowType guid = fields[3].GetUInt64(); - AchievementCriteriaEntry const* criteria = sAchievementMgr->GetAchievementCriteria(id); + AchievementCriteria const* criteria = sAchievementMgr->GetAchievementCriteria(id); if (!criteria) { // we will remove not existed criteria for all guilds @@ -768,16 +775,17 @@ void AchievementMgr<Guild>::LoadFromDB(PreparedQueryResult achievementResult, Pr continue; } - if (criteria->timeLimit && time_t(date + criteria->timeLimit) < now) + if (criteria->Entry->StartTimer && time_t(date + criteria->Entry->StartTimer) < now) continue; CriteriaProgress& progress = m_criteriaProgress[id]; progress.counter = counter; progress.date = date; - progress.CompletedGUID = ObjectGuid::Create<HighGuid::Player>(guid); + progress.PlayerGUID = ObjectGuid::Create<HighGuid::Player>(guid); progress.changed = false; } while (criteriaResult->NextRow()); } + */ } template<class T> @@ -840,7 +848,7 @@ void AchievementMgr<Guild>::Reset() } while (!m_criteriaProgress.empty()) - if (AchievementCriteriaEntry const* criteria = sAchievementMgr->GetAchievementCriteria(m_criteriaProgress.begin()->first)) + if (AchievementCriteria const* criteria = sAchievementMgr->GetAchievementCriteria(m_criteriaProgress.begin()->first)) RemoveCriteriaProgress(criteria); _achievementPoints = 0; @@ -883,12 +891,13 @@ void AchievementMgr<T>::SendAchievementEarned(AchievementEntry const* achievemen GetOwner()->VisitNearbyWorldObject(sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_SAY), _worker); } - WorldPacket data(SMSG_ACHIEVEMENT_EARNED, 8+4+8); - data << GetOwner()->GetPackGUID(); - data << uint32(achievement->ID); - data.AppendPackedTime(time(NULL)); - data << uint32(0); // does not notify player ingame - GetOwner()->SendMessageToSetInRange(&data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_SAY), true); + 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<> @@ -921,39 +930,38 @@ void AchievementMgr<Guild>::SendAchievementEarned(AchievementEntry const* achiev } template<class T> -void AchievementMgr<T>::SendCriteriaUpdate(AchievementCriteriaEntry const* /*entry*/, CriteriaProgress const* /*progress*/, uint32 /*timeElapsed*/, bool /*timedCompleted*/) const +void AchievementMgr<T>::SendCriteriaUpdate(AchievementCriteria const* /*criteria*/, CriteriaProgress const* /*progress*/, uint32 /*timeElapsed*/, bool /*timedCompleted*/) const { } template<> -void AchievementMgr<Player>::SendCriteriaUpdate(AchievementCriteriaEntry const* entry, CriteriaProgress const* progress, uint32 timeElapsed, bool timedCompleted) const +void AchievementMgr<Player>::SendCriteriaUpdate(AchievementCriteria const* criteria, CriteriaProgress const* progress, uint32 timeElapsed, bool timedCompleted) const { - WorldPacket data(SMSG_CRITERIA_UPDATE, 8 + 4 + 8); - data << uint32(entry->ID); + WorldPackets::Achievement::CriteriaUpdate criteriaUpdate; - // the counter is packed like a packed Guid - data.AppendPackedUInt64(progress->counter); + 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 - data << GetOwner()->GetPackGUID(); - if (!entry->timeLimit) - data << uint32(0); - else - data << uint32(timedCompleted ? 1 : 0); // this are some flags, 1 is for keeping the counter at 0 in client - data.AppendPackedTime(progress->date); - data << uint32(timeElapsed); // time elapsed in seconds - data << uint32(0); // unk - SendPacket(&data); + criteriaUpdate.Flags = 0; + criteriaUpdate.CurrentTime = progress->date; + criteriaUpdate.ElapsedTime = timeElapsed; + criteriaUpdate.CreationTime = 0; + + SendPacket(criteriaUpdate.Write()); } template<> -void AchievementMgr<Guild>::SendCriteriaUpdate(AchievementCriteriaEntry const* entry, CriteriaProgress const* progress, uint32 /*timeElapsed*/, bool /*timedCompleted*/) const +void AchievementMgr<Guild>::SendCriteriaUpdate(AchievementCriteria const* entry, CriteriaProgress const* progress, uint32 /*timeElapsed*/, bool /*timedCompleted*/) const { /* //will send response to criteria progress request WorldPacket data(SMSG_GUILD_CRITERIA_DATA, 3 + 1 + 1 + 8 + 8 + 4 + 4 + 4 + 4 + 4); ObjectGuid counter(0, progress->counter); // for accessing every byte individually - ObjectGuid guid = progress->CompletedGUID; + ObjectGuid guid = progress->PlayerGUID; data.WriteBits(1, 21); data.WriteBit(counter[4]); @@ -1016,7 +1024,7 @@ void AchievementMgr<Guild>::SendAllTrackedCriterias(Player* receiver, std::set<u for (std::set<uint32>::iterator itr = trackedCriterias.begin(); itr != trackedCriterias.end(); ++itr) { - AchievementCriteriaEntry const* entry = sAchievementMgr->GetAchievementCriteria(*itr); + AchievementCriteriaEntry const* entry = sAchievementMgr->GetAchievementCriteriaTree(*itr); CriteriaProgressMap::const_iterator progress = m_criteriaProgress.find(entry->ID); if (progress == m_criteriaProgress.end()) @@ -1032,14 +1040,14 @@ void AchievementMgr<Guild>::SendAllTrackedCriterias(Player* receiver, std::set<u for (std::set<uint32>::iterator itr = trackedCriterias.begin(); itr != trackedCriterias.end(); ++itr) { - AchievementCriteriaEntry const* entry = sAchievementMgr->GetAchievementCriteria(*itr); + AchievementCriteriaEntry const* entry = sAchievementMgr->GetAchievementCriteriaTree(*itr); CriteriaProgressMap::const_iterator progress = m_criteriaProgress.find(entry->ID); if (progress == m_criteriaProgress.end()) continue; counter.SetRawValue(progress->second.counter); - guid = progress->second.CompletedGUID; + guid = progress->second.PlayerGUID; criteriaBits.WriteBit(counter[4]); criteriaBits.WriteBit(counter[1]); @@ -1102,18 +1110,6 @@ void AchievementMgr<T>::CheckAllAchievementCriteria(Player* referencePlayer) UpdateAchievementCriteria(AchievementCriteriaTypes(i), 0, 0, 0, NULL, referencePlayer); } -static const uint32 achievIdByArenaSlot[MAX_ARENA_SLOT] = {1057, 1107, 1108}; -static const uint32 achievIdForDungeon[][4] = -{ - // ach_cr_id, is_dungeon, is_raid, is_heroic_dungeon - { 321, true, true, true }, - { 916, false, true, false }, - { 917, false, true, false }, - { 918, true, false, false }, - { 2219, false, false, true }, - { 0, false, false, false } -}; - // 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; } @@ -1151,18 +1147,12 @@ void AchievementMgr<T>::UpdateAchievementCriteria(AchievementCriteriaTypes type, if (IsGuild<T>() && !sWorld->getBoolConfig(CONFIG_GUILD_LEVELING_ENABLED)) return; - AchievementCriteriaEntryList const& achievementCriteriaList = sAchievementMgr->GetAchievementCriteriaByType(type, IsGuild<T>()); - for (AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i != achievementCriteriaList.end(); ++i) + AchievementCriteriaList const& achievementCriteriaList = sAchievementMgr->GetAchievementCriteriaByType(type, IsGuild<T>()); + for (AchievementCriteria const* achievementCriteria : achievementCriteriaList) { - AchievementCriteriaEntry const* achievementCriteria = (*i); - AchievementEntry const* achievement = sAchievementMgr->GetAchievement(achievementCriteria->achievement); - if (!achievement) - { - TC_LOG_ERROR("achievement", "UpdateAchievementCriteria: Achievement %u not found!", achievementCriteria->achievement); - continue; - } + AchievementCriteriaTreeList const* trees = sAchievementMgr->GetAchievementCriteriaTreesByCriteria(achievementCriteria->ID); - if (!CanUpdateCriteria(achievementCriteria, achievement, miscValue1, miscValue2, miscValue3, unit, referencePlayer)) + if (!CanUpdateCriteria(achievementCriteria, trees, miscValue1, miscValue2, miscValue3, unit, referencePlayer)) continue; // requirements not found in the dbc @@ -1253,11 +1243,11 @@ void AchievementMgr<T>::UpdateAchievementCriteria(AchievementCriteriaTypes type, SetCriteriaProgress(achievementCriteria, referencePlayer->getLevel(), referencePlayer); break; case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL: - if (uint32 skillvalue = referencePlayer->GetBaseSkillValue(achievementCriteria->reach_skill_level.skillID)) + 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->learn_skill_level.skillID)) + if (uint32 maxSkillvalue = referencePlayer->GetPureMaxSkillValue(achievementCriteria->Entry->Asset.SkillID)) SetCriteriaProgress(achievementCriteria, maxSkillvalue, referencePlayer); break; case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT: @@ -1301,7 +1291,7 @@ void AchievementMgr<T>::UpdateAchievementCriteria(AchievementCriteriaTypes type, 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->complete_quests_in_zone.zoneID) + if (quest && quest->GetZoneOrSort() >= 0 && uint32(quest->GetZoneOrSort()) == achievementCriteria->Entry->Asset.ZoneID) ++counter; } SetCriteriaProgress(achievementCriteria, counter, referencePlayer); @@ -1325,7 +1315,7 @@ void AchievementMgr<T>::UpdateAchievementCriteria(AchievementCriteriaTypes type, break; case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION: { - int32 reputation = referencePlayer->GetReputationMgr().GetReputation(achievementCriteria->gain_reputation.factionID); + int32 reputation = referencePlayer->GetReputationMgr().GetReputation(achievementCriteria->Entry->Asset.FactionID); if (reputation > 0) SetCriteriaProgress(achievementCriteria, reputation, referencePlayer); break; @@ -1343,7 +1333,7 @@ void AchievementMgr<T>::UpdateAchievementCriteria(AchievementCriteriaTypes type, SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellIter->first); for (SkillLineAbilityMap::const_iterator skillIter = bounds.first; skillIter != bounds.second; ++skillIter) { - if (skillIter->second->SkillLine == achievementCriteria->learn_skillline_spell.skillLine) + if (skillIter->second->SkillLine == achievementCriteria->Entry->Asset.SkillID) spellCount++; } } @@ -1368,7 +1358,7 @@ void AchievementMgr<T>::UpdateAchievementCriteria(AchievementCriteriaTypes type, { SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellIter->first); for (SkillLineAbilityMap::const_iterator skillIter = bounds.first; skillIter != bounds.second; ++skillIter) - if (skillIter->second->SkillLine == achievementCriteria->learn_skill_line.skillLine) + if (skillIter->second->SkillLine == achievementCriteria->Entry->Asset.SkillID) spellCount++; } SetCriteriaProgress(achievementCriteria, spellCount, referencePlayer); @@ -1388,7 +1378,7 @@ void AchievementMgr<T>::UpdateAchievementCriteria(AchievementCriteriaTypes type, break; case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_TEAM_RATING: { - uint32 reqTeamType = achievementCriteria->highest_team_rating.teamtype; + uint32 reqTeamType = achievementCriteria->Entry->Asset.TeamType; if (miscValue1) { @@ -1418,7 +1408,7 @@ void AchievementMgr<T>::UpdateAchievementCriteria(AchievementCriteriaTypes type, } case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_PERSONAL_RATING: { - uint32 reqTeamType = achievementCriteria->highest_personal_rating.teamtype; + uint32 reqTeamType = achievementCriteria->Entry->Asset.TeamType; if (miscValue1) { @@ -1472,19 +1462,22 @@ void AchievementMgr<T>::UpdateAchievementCriteria(AchievementCriteriaTypes type, break; // Not implemented yet :( } - if (IsCompletedCriteria(achievementCriteria, achievement)) - CompletedCriteriaFor(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 (achievement->Flags & ACHIEVEMENT_FLAG_SUMM) - if (IsCompletedAchievement(achievement)) - CompletedAchievement(achievement, referencePlayer); - - if (AchievementEntryList const* achRefList = sAchievementMgr->GetAchievementByReferencedId(achievement->ID)) - for (AchievementEntryList::const_iterator itr = achRefList->begin(); itr != achRefList->end(); ++itr) - if (IsCompletedAchievement(*itr)) - CompletedAchievement(*itr, referencePlayer); + 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); + } } } @@ -1497,8 +1490,9 @@ template<> uint32 GetInstanceId(Player* player) { return player->GetInstanceId(); } template<class T> -bool AchievementMgr<T>::IsCompletedCriteria(AchievementCriteriaEntry const* achievementCriteria, AchievementEntry const* achievement) +bool AchievementMgr<T>::IsCompletedCriteriaTree(AchievementCriteriaTree const* tree) { + AchievementEntry const* achievement = tree->Achievement; if (!achievement) return false; @@ -1513,116 +1507,109 @@ bool AchievementMgr<T>::IsCompletedCriteria(AchievementCriteriaEntry const* achi 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->type)) + switch (AchievementCriteriaTypes(achievementCriteria->Entry->Type)) { case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG: - return progress->counter >= achievementCriteria->win_bg.winCount; case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE: - return progress->counter >= achievementCriteria->kill_creature.creatureCount; case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL: case ACHIEVEMENT_CRITERIA_TYPE_REACH_GUILD_LEVEL: - return progress->counter >= achievementCriteria->reach_level.level; case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL: - return progress->counter >= achievementCriteria->reach_skill_level.skillLevel; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT: - return progress->counter >= 1; case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT: - return progress->counter >= achievementCriteria->complete_quest_count.totalQuestCount; case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST_DAILY: - return progress->counter >= achievementCriteria->complete_daily_quest_daily.numberOfDays; case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE: - return progress->counter >= achievementCriteria->complete_quests_in_zone.questCount; case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE: case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE: - return progress->counter >= achievementCriteria->healing_done.count; case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST: - return progress->counter >= achievementCriteria->complete_daily_quest.questCount; case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING: - return progress->counter >= achievementCriteria->fall_without_dying.fallHeight; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST: - return progress->counter >= 1; case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET: case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2: - return progress->counter >= achievementCriteria->be_spell_target.spellCount; case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: - return progress->counter >= achievementCriteria->cast_spell.castCount; case ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE: - return progress->counter >= achievementCriteria->bg_objective.completeCount; case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA: - return progress->counter >= achievementCriteria->honorable_kill_at_area.killCount; - case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL: - return progress->counter >= 1; case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL: case ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL: - return progress->counter >= achievementCriteria->honorable_kill.killCount; case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM: - return progress->counter >= achievementCriteria->own_item.itemCount; case ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA: - return progress->counter >= achievementCriteria->win_rated_arena.count; case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_PERSONAL_RATING: - return progress->counter >= achievementCriteria->highest_personal_rating.PersonalRating; - case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL: - return progress->counter >= (achievementCriteria->learn_skill_level.skillLevel * 75); case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM: - return progress->counter >= achievementCriteria->use_item.itemCount; case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM: - return progress->counter >= achievementCriteria->loot_item.itemCount; - case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA: - return progress->counter >= 1; case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT: - return progress->counter >= achievementCriteria->buy_bank_slot.numberOfSlots; case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION: - return progress->counter >= achievementCriteria->gain_reputation.reputationAmount; case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION: - return progress->counter >= achievementCriteria->gain_exalted_reputation.numberOfExaltedFactions; case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP: - return progress->counter >= achievementCriteria->visit_barber.numberOfVisits; case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM: - return progress->counter >= achievementCriteria->equip_epic_item.count; case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT: case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT: - return progress->counter >= achievementCriteria->roll_greed_on_loot.count; case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS: - return progress->counter >= achievementCriteria->hk_class.count; case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE: - return progress->counter >= achievementCriteria->hk_race.count; case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: - return progress->counter >= achievementCriteria->do_emote.count; case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM: - return progress->counter >= achievementCriteria->equip_item.count; case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD: - return progress->counter >= achievementCriteria->quest_reward_money.goldInCopper; case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY: - return progress->counter >= achievementCriteria->loot_money.goldInCopper; case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT: - return progress->counter >= achievementCriteria->use_gameobject.useCount; case ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL: - return progress->counter >= achievementCriteria->special_pvp_kill.killCount; case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT: - return progress->counter >= achievementCriteria->fish_in_gameobject.lootCount; case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS: - return progress->counter >= achievementCriteria->learn_skillline_spell.spellCount; case ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL: - return progress->counter >= achievementCriteria->win_duel.duelCount; case ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE: - return progress->counter >= achievementCriteria->loot_type.lootTypeCount; case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE: - return progress->counter >= achievementCriteria->learn_skill_line.spellCount; - case ACHIEVEMENT_CRITERIA_TYPE_EARN_ACHIEVEMENT_POINTS: - return progress->counter >= 9000; case ACHIEVEMENT_CRITERIA_TYPE_USE_LFD_TO_GROUP_WITH_PLAYERS: - return progress->counter >= achievementCriteria->use_lfg.dungeonsComplete; case ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS: - return progress->counter >= achievementCriteria->get_killing_blow.killCount; case ACHIEVEMENT_CRITERIA_TYPE_CURRENCY: - return progress->counter >= achievementCriteria->currencyGain.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: + 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 achievementCriteria->win_arena.count && progress->counter >= achievementCriteria->win_arena.count; + return requiredAmount && progress->counter >= requiredAmount; case ACHIEVEMENT_CRITERIA_TYPE_ON_LOGIN: return true; // handle all statistic-only criteria here @@ -1680,70 +1667,40 @@ void AchievementMgr<T>::CompletedCriteriaFor(AchievementEntry const* achievement } template<class T> +uint64 AchievementMgr<T>::GetTotalCriteriaTreeProgress(AchievementCriteriaTree const* criteriaTree) +{ + uint64 progress = 0; + if (criteriaTree->Criteria) + if (CriteriaProgress const* criteriaProgress = GetCriteriaProgress(criteriaTree->Criteria)) + progress += criteriaProgress->counter; + + for (AchievementCriteriaTree const* node : criteriaTree->Children) + progress += GetTotalCriteriaTreeProgress(node); + + return progress; +} + +template<class T> bool AchievementMgr<T>::IsCompletedAchievement(AchievementEntry const* entry) { // counter can never complete if (entry->Flags & ACHIEVEMENT_FLAG_COUNTER) return false; - // for achievement with referenced achievement criterias get from referenced and counter from self - uint32 achievementForTestId = entry->SharesCriteria ? entry->SharesCriteria : entry->ID; - uint32 achievementForTestCount = entry->MinimumCriteria; - - AchievementCriteriaEntryList const* cList = sAchievementMgr->GetAchievementCriteriaByAchievement(achievementForTestId); - if (!cList) + AchievementCriteriaTree const* tree = sAchievementMgr->GetAchievementCriteriaTree(entry->CriteriaTree); + if (!tree) return false; - uint64 count = 0; // 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) - { - for (AchievementCriteriaEntryList::const_iterator itr = cList->begin(); itr != cList->end(); ++itr) - { - AchievementCriteriaEntry const* criteria = *itr; - - CriteriaProgress const* progress = GetCriteriaProgress(criteria); - if (!progress) - continue; - - count += progress->counter; - - // for counters, field4 contains the main count requirement - if (count >= criteria->raw.count) - return true; - } - return false; - } - - // Default case - need complete all or - bool completed_all = true; - for (AchievementCriteriaEntryList::const_iterator itr = cList->begin(); itr != cList->end(); ++itr) - { - AchievementCriteriaEntry const* criteria = *itr; - - bool completed = IsCompletedCriteria(criteria, entry); - - // found an uncompleted criteria, but DONT return false yet - there might be a completed criteria with ACHIEVEMENT_CRITERIA_COMPLETE_FLAG_ALL - if (completed) - ++count; - else - completed_all = false; + return GetTotalCriteriaTreeProgress(tree) >= tree->Entry->Amount; - // completed as have req. count of completed criterias - if (achievementForTestCount > 0 && achievementForTestCount <= count) - return true; - } - - // all criterias completed requirement - if (completed_all && achievementForTestCount == 0) - return true; - - return false; + return IsCompletedCriteriaTree(tree); } template<class T> -CriteriaProgress* AchievementMgr<T>::GetCriteriaProgress(AchievementCriteriaEntry const* entry) +CriteriaProgress* AchievementMgr<T>::GetCriteriaProgress(AchievementCriteria const* entry) { CriteriaProgressMap::iterator iter = m_criteriaProgress.find(entry->ID); @@ -1754,25 +1711,44 @@ CriteriaProgress* AchievementMgr<T>::GetCriteriaProgress(AchievementCriteriaEntr } template<class T> -void AchievementMgr<T>::SetCriteriaProgress(AchievementCriteriaEntry const* entry, uint64 changeValue, Player* referencePlayer, ProgressType ptype) +void AchievementMgr<T>::SetCriteriaProgress(AchievementCriteria const* criteria, uint64 changeValue, Player* referencePlayer, ProgressType ptype) { // Don't allow to cheat - doing timed achievements without timer active - TimedAchievementMap::iterator timedIter = m_timedAchievements.find(entry->ID); - if (entry->timeLimit && timedIter == m_timedAchievements.end()) - return; + 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)", - entry->ID, changeValue, GetOwner()->GetGUID().ToString().c_str()); + criteria->ID, changeValue, GetOwner()->GetGUID().ToString().c_str()); - CriteriaProgress* progress = GetCriteriaProgress(entry); + CriteriaProgress* progress = GetCriteriaProgress(criteria); if (!progress) { // 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 && !entry->timeLimit) + if (changeValue == 0 && !criteria->Entry->StartTimer) return; - progress = &m_criteriaProgress[entry->ID]; + progress = &m_criteriaProgress[criteria->ID]; progress->counter = changeValue; } else @@ -1796,7 +1772,7 @@ void AchievementMgr<T>::SetCriteriaProgress(AchievementCriteriaEntry const* entr } // not update (not mark as changed) if counter will have same value - if (progress->counter == newValue && !entry->timeLimit) + if (progress->counter == newValue && !criteria->Entry->StartTimer) return; progress->counter = newValue; @@ -1804,25 +1780,30 @@ void AchievementMgr<T>::SetCriteriaProgress(AchievementCriteriaEntry const* entr progress->changed = true; progress->date = time(NULL); // set the date to the latest update. + progress->PlayerGUID = referencePlayer->GetGUID(); - AchievementEntry const* achievement = sAchievementMgr->GetAchievement(entry->achievement); uint32 timeElapsed = 0; - bool criteriaComplete = IsCompletedCriteria(entry, achievement); - if (entry->timeLimit) + if (criteria->Entry->StartTimer) { - // Client expects this in packet - timeElapsed = entry->timeLimit - (timedIter->second/IN_MILLISECONDS); + ASSERT(trees); - // Remove the timer, we wont need it anymore - if (criteriaComplete) - m_timedAchievements.erase(timedIter); - } + 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); - if (criteriaComplete && achievement->Flags & ACHIEVEMENT_FLAG_SHOW_CRITERIA_MEMBERS && !progress->CompletedGUID) - progress->CompletedGUID = referencePlayer->GetGUID(); + // Remove the timer, we wont need it anymore + if (IsCompletedCriteriaTree(tree)) + m_timedAchievements.erase(timedIter); + } + } + } - SendCriteriaUpdate(entry, progress, timeElapsed, criteriaComplete); + SendCriteriaUpdate(criteria, progress, timeElapsed, true); } template<class T> @@ -1835,8 +1816,10 @@ void AchievementMgr<T>::UpdateTimedAchievements(uint32 timeDiff) // Time is up, remove timer and reset progress if (itr->second <= timeDiff) { - AchievementCriteriaEntry const* entry = sAchievementMgr->GetAchievementCriteria(itr->first); - RemoveCriteriaProgress(entry); + AchievementCriteriaTree const* criteriaTree = sAchievementMgr->GetAchievementCriteriaTree(itr->first); + if (criteriaTree->Criteria) + RemoveCriteriaProgress(criteriaTree->Criteria); + m_timedAchievements.erase(itr++); } else @@ -1856,46 +1839,51 @@ void AchievementMgr<T>::StartTimedAchievement(AchievementCriteriaTimedTypes /*ty template<> void AchievementMgr<Player>::StartTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry, uint32 timeLost /* = 0 */) { - AchievementCriteriaEntryList const& achievementCriteriaList = sAchievementMgr->GetTimedAchievementCriteriaByType(type); - for (AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i != achievementCriteriaList.end(); ++i) + AchievementCriteriaList const& achievementCriteriaList = sAchievementMgr->GetTimedAchievementCriteriaByType(type); + for (AchievementCriteria const* criteria : achievementCriteriaList) { - if ((*i)->timedCriteriaMiscId != entry) + if (criteria->Entry->StartAsset != entry) continue; - AchievementEntry const* achievement = sAchievementMgr->GetAchievement((*i)->achievement); - if (m_timedAchievements.find((*i)->ID) == m_timedAchievements.end() && !IsCompletedCriteria(*i, achievement)) + AchievementCriteriaTreeList const* trees = sAchievementMgr->GetAchievementCriteriaTreesByCriteria(criteria->ID); + bool canStart = false; + for (AchievementCriteriaTree const* tree : *trees) { - // Start the timer - if ((*i)->timeLimit * IN_MILLISECONDS > timeLost) + if (m_timedAchievements.find(tree->ID) == m_timedAchievements.end() && !IsCompletedCriteriaTree(tree)) { - m_timedAchievements[(*i)->ID] = (*i)->timeLimit * IN_MILLISECONDS - timeLost; - - // and at client too - SetCriteriaProgress(*i, 0, GetOwner(), PROGRESS_SET); + // Start the timer + if (criteria->Entry->StartTimer * IN_MILLISECONDS > timeLost) + { + m_timedAchievements[tree->ID] = criteria->Entry->StartTimer * IN_MILLISECONDS - timeLost; + canStart = true; + } } } + + if (!canStart) + continue; + + // and at client too + SetCriteriaProgress(criteria, 0, GetOwner(), PROGRESS_SET); } } template<class T> void AchievementMgr<T>::RemoveTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry) { - AchievementCriteriaEntryList const& achievementCriteriaList = sAchievementMgr->GetTimedAchievementCriteriaByType(type); - for (AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i != achievementCriteriaList.end(); ++i) + AchievementCriteriaList const& achievementCriteriaList = sAchievementMgr->GetTimedAchievementCriteriaByType(type); + for (AchievementCriteria const* criteria : achievementCriteriaList) { - if ((*i)->timedCriteriaMiscId != entry) + if (criteria->Entry->StartAsset != entry) continue; - TimedAchievementMap::iterator timedIter = m_timedAchievements.find((*i)->ID); - // We don't have timer for this achievement - if (timedIter == m_timedAchievements.end()) - 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(*i); - - // Remove the timer - m_timedAchievements.erase(timedIter); + RemoveCriteriaProgress(criteria); } } @@ -2036,75 +2024,38 @@ struct VisibleAchievementPred template<class T> void AchievementMgr<T>::SendAllAchievementData(Player* /*receiver*/) const { - /* VisibleAchievementPred isVisible; - size_t numCriteria = m_criteriaProgress.size(); - size_t numAchievements = std::count_if(m_completedAchievements.begin(), m_completedAchievements.end(), isVisible); - ByteBuffer criteriaData(numCriteria * (4 + 4 + 4 + 4 + 8 + 8)); - ObjectGuid guid = GetOwner()->GetGUID(); - ObjectGuid counter; + WorldPackets::Achievement::AllAchievements achievementData; + achievementData.Earned.reserve(m_completedAchievements.size()); + achievementData.Progress.reserve(m_criteriaProgress.size()); - WorldPacket data(SMSG_ALL_ACHIEVEMENT_DATA, 4 + numAchievements * (4 + 4) + 4 + numCriteria * (4 + 4 + 4 + 4 + 8 + 8)); - data.WriteBits(numCriteria, 21); - for (CriteriaProgressMap::const_iterator itr = m_criteriaProgress.begin(); itr != m_criteriaProgress.end(); ++itr) + for (auto itr = m_completedAchievements.begin(); itr != m_completedAchievements.end(); ++itr) { - counter.SetRawValue(itr->second.counter); - - data.WriteBit(guid[4]); - data.WriteBit(counter[3]); - data.WriteBit(guid[5]); - data.WriteBit(counter[0]); - data.WriteBit(counter[6]); - data.WriteBit(guid[3]); - data.WriteBit(guid[0]); - data.WriteBit(counter[4]); - data.WriteBit(guid[2]); - data.WriteBit(counter[7]); - data.WriteBit(guid[7]); - data.WriteBits(0u, 2); - data.WriteBit(guid[6]); - data.WriteBit(counter[2]); - data.WriteBit(counter[1]); - data.WriteBit(counter[5]); - data.WriteBit(guid[1]); + if (!isVisible(*itr)) + continue; - criteriaData.WriteByteSeq(guid[3]); - criteriaData.WriteByteSeq(counter[5]); - criteriaData.WriteByteSeq(counter[6]); - criteriaData.WriteByteSeq(guid[4]); - criteriaData.WriteByteSeq(guid[6]); - criteriaData.WriteByteSeq(counter[2]); - criteriaData << uint32(0); // timer 2 - criteriaData.WriteByteSeq(guid[2]); - criteriaData << uint32(itr->first); // criteria id - criteriaData.WriteByteSeq(guid[5]); - criteriaData.WriteByteSeq(counter[0]); - criteriaData.WriteByteSeq(counter[3]); - criteriaData.WriteByteSeq(counter[1]); - criteriaData.WriteByteSeq(counter[4]); - criteriaData.WriteByteSeq(guid[0]); - criteriaData.WriteByteSeq(guid[7]); - criteriaData.WriteByteSeq(counter[7]); - criteriaData << uint32(0); // timer 1 - criteriaData.AppendPackedTime(itr->second.date); // criteria date - criteriaData.WriteByteSeq(guid[1]); + WorldPackets::Achievement::EarnedAchievement earned; + earned.Id = itr->first; + earned.Date = itr->second.date; + earned.Owner = GetOwner()->GetGUID(); + earned.VirtualRealmAddress = earned.NativeRealmAddress = GetVirtualRealmAddress(); + achievementData.Earned.push_back(earned); } - data.WriteBits(numAchievements, 23); - data.FlushBits(); - data.append(criteriaData); - - for (CompletedAchievementMap::const_iterator itr = m_completedAchievements.begin(); itr != m_completedAchievements.end(); ++itr) + for (auto itr = m_criteriaProgress.begin(); itr != m_criteriaProgress.end(); ++itr) { - if (!isVisible(*itr)) - continue; - - data << uint32(itr->first); - data.AppendPackedTime(itr->second.date); + 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.Progress.push_back(progress); } - SendPacket(&data); - */ + SendPacket(achievementData.Write()); } template<> @@ -2227,7 +2178,7 @@ void AchievementMgr<Guild>::SendAchievementInfo(Player* receiver, uint32 achieve { /* //will send response to criteria progress request - AchievementCriteriaEntryList const* criteria = sAchievementMgr->GetAchievementCriteriaByAchievement(achievementId); + AchievementCriteriaTreeList const* criteria = sAchievementMgr->GetAchievementCriteriaByAchievement(achievementId); if (!criteria) { // send empty packet @@ -2243,7 +2194,7 @@ void AchievementMgr<Guild>::SendAchievementInfo(Player* receiver, uint32 achieve uint32 numCriteria = 0; ByteBuffer criteriaData(criteria->size() * (8 + 8 + 4 + 4 + 4)); ByteBuffer criteriaBits(criteria->size() * (8 + 8) / 8); - for (AchievementCriteriaEntryList::const_iterator itr = criteria->begin(); itr != criteria->end(); ++itr) + for (AchievementCriteriaTreeList::const_iterator itr = criteria->begin(); itr != criteria->end(); ++itr) { uint32 criteriaId = (*itr)->ID; CriteriaProgressMap::const_iterator progress = m_criteriaProgress.find(criteriaId); @@ -2255,7 +2206,7 @@ void AchievementMgr<Guild>::SendAchievementInfo(Player* receiver, uint32 achieve criteriaBits.WriteBits(numCriteria, 21); - for (AchievementCriteriaEntryList::const_iterator itr = criteria->begin(); itr != criteria->end(); ++itr) + for (AchievementCriteriaTreeList::const_iterator itr = criteria->begin(); itr != criteria->end(); ++itr) { uint32 criteriaId = (*itr)->ID; CriteriaProgressMap::const_iterator progress = m_criteriaProgress.find(criteriaId); @@ -2263,7 +2214,7 @@ void AchievementMgr<Guild>::SendAchievementInfo(Player* receiver, uint32 achieve continue; counter.SetRawValue(progress->second.counter); - guid = progress->second.CompletedGUID; + guid = progress->second.PlayerGUID; criteriaBits.WriteBit(counter[4]); criteriaBits.WriteBit(counter[1]); @@ -2321,55 +2272,65 @@ bool AchievementMgr<T>::HasAchieved(uint32 achievementId) const } template<class T> -bool AchievementMgr<T>::CanUpdateCriteria(AchievementCriteriaEntry const* criteria, AchievementEntry const* achievement, uint64 miscValue1, uint64 miscValue2, uint64 miscValue3, Unit const* unit, Player* referencePlayer) +bool AchievementMgr<T>::CanUpdateCriteria(AchievementCriteria const* criteria, AchievementCriteriaTreeList const* trees, uint64 miscValue1, uint64 miscValue2, uint64 miscValue3, Unit const* unit, Player* referencePlayer) { if (DisableMgr::IsDisabledFor(DISABLE_TYPE_ACHIEVEMENT_CRITERIA, criteria->ID, NULL)) { - TC_LOG_TRACE("achievement", "CanUpdateCriteria: %s (Id: %u Type %s) Disabled", - criteria->name, criteria->ID, AchievementGlobalMgr::GetCriteriaTypeString(criteria->type)); + TC_LOG_TRACE("achievement", "CanUpdateCriteria: (Id: %u Type %s) Disabled", + criteria->ID, AchievementGlobalMgr::GetCriteriaTypeString(criteria->Entry->Type)); return false; } - if (achievement->MapID != -1 && referencePlayer->GetMapId() != uint32(achievement->MapID)) + bool treeRequirementPassed = false; + for (AchievementCriteriaTree const* tree : *trees) { - TC_LOG_TRACE("achievement", "CanUpdateCriteria: %s (Id: %u Type %s) Wrong map", - criteria->name, criteria->ID, AchievementGlobalMgr::GetCriteriaTypeString(criteria->type)); - return false; - } + 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); + continue; + } - if ((achievement->Faction == ACHIEVEMENT_FACTION_HORDE && referencePlayer->GetTeam() != HORDE) || - (achievement->Faction == ACHIEVEMENT_FACTION_ALLIANCE && referencePlayer->GetTeam() != ALLIANCE)) - { - TC_LOG_TRACE("achievement", "CanUpdateCriteria: %s (Id: %u Type %s) Wrong faction", - criteria->name, criteria->ID, AchievementGlobalMgr::GetCriteriaTypeString(criteria->type)); - return false; + 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 (IsCompletedCriteria(criteria, achievement)) - { - TC_LOG_TRACE("achievement", "CanUpdateCriteria: %s (Id: %u Type %s) Is Completed", - criteria->name, criteria->ID, AchievementGlobalMgr::GetCriteriaTypeString(criteria->type)); + if (!treeRequirementPassed) return false; - } if (!RequirementsSatisfied(criteria, miscValue1, miscValue2, miscValue3, unit, referencePlayer)) { - TC_LOG_TRACE("achievement", "CanUpdateCriteria: %s (Id: %u Type %s) Requirements not satisfied", - criteria->name, criteria->ID, AchievementGlobalMgr::GetCriteriaTypeString(criteria->type)); + TC_LOG_TRACE("achievement", "CanUpdateCriteria: (Id: %u Type %s) Requirements not satisfied", + criteria->ID, AchievementGlobalMgr::GetCriteriaTypeString(criteria->Entry->Type)); return false; } - if (!AdditionalRequirementsSatisfied(criteria, miscValue1, miscValue2, unit, referencePlayer)) + if (criteria->Modifier && !AdditionalRequirementsSatisfied(criteria->Modifier, miscValue1, miscValue2, unit, referencePlayer)) { - TC_LOG_TRACE("achievement", "CanUpdateCriteria: %s (Id: %u Type %s) Additional requirements not satisfied", - criteria->name, criteria->ID, AchievementGlobalMgr::GetCriteriaTypeString(criteria->type)); + TC_LOG_TRACE("achievement", "CanUpdateCriteria: (Id: %u Type %s) Additional requirements not satisfied", + criteria->ID, AchievementGlobalMgr::GetCriteriaTypeString(criteria->Entry->Type)); return false; } if (!ConditionsSatisfied(criteria, referencePlayer)) { - TC_LOG_TRACE("achievement", "CanUpdateCriteria: %s (Id: %u Type %s) Conditions not satisfied", - criteria->name, criteria->ID, AchievementGlobalMgr::GetCriteriaTypeString(criteria->type)); + TC_LOG_TRACE("achievement", "CanUpdateCriteria: (Id: %u Type %s) Conditions not satisfied", + criteria->ID, AchievementGlobalMgr::GetCriteriaTypeString(criteria->Entry->Type)); return false; } @@ -2377,35 +2338,32 @@ bool AchievementMgr<T>::CanUpdateCriteria(AchievementCriteriaEntry const* criter } template<class T> -bool AchievementMgr<T>::ConditionsSatisfied(AchievementCriteriaEntry const* criteria, Player* referencePlayer) const +bool AchievementMgr<T>::ConditionsSatisfied(AchievementCriteria const* criteria, Player* referencePlayer) const { - for (uint32 i = 0; i < MAX_CRITERIA_REQUIREMENTS; ++i) - { - if (!criteria->additionalRequirements[i].additionalRequirement_type) - continue; + if (!criteria->Entry->FailEvent) + return true; - switch (criteria->additionalRequirements[i].additionalRequirement_type) - { - case ACHIEVEMENT_CRITERIA_CONDITION_BG_MAP: - if (referencePlayer->GetMapId() != criteria->additionalRequirements[i].additionalRequirement_value) - return false; - break; - case ACHIEVEMENT_CRITERIA_CONDITION_NOT_IN_GROUP: - if (referencePlayer->GetGroup()) - return false; - break; - default: - break; - } + 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; } template<class T> -bool AchievementMgr<T>::RequirementsSatisfied(AchievementCriteriaEntry const* achievementCriteria, uint64 miscValue1, uint64 miscValue2, uint64 miscValue3, Unit const* unit, Player* referencePlayer) const +bool AchievementMgr<T>::RequirementsSatisfied(AchievementCriteria const* achievementCriteria, uint64 miscValue1, uint64 miscValue2, uint64 miscValue3, Unit const* unit, Player* referencePlayer) const { - switch (AchievementCriteriaTypes(achievementCriteria->type)) + switch (AchievementCriteriaTypes(achievementCriteria->Entry->Type)) { case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS: case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST: @@ -2460,57 +2418,36 @@ bool AchievementMgr<T>::RequirementsSatisfied(AchievementCriteriaEntry const* ac case ACHIEVEMENT_CRITERIA_TYPE_ON_LOGIN: break; case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT: - if (m_completedAchievements.find(achievementCriteria->complete_achievement.linkedAchievement) == m_completedAchievements.end()) + if (m_completedAchievements.find(achievementCriteria->Entry->Asset.AchievementID) == m_completedAchievements.end()) return false; break; case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG: - if (!miscValue1 || achievementCriteria->win_bg.bgMapID != referencePlayer->GetMapId()) + if (!miscValue1 || achievementCriteria->Entry->Asset.MapID != referencePlayer->GetMapId()) return false; break; case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE: - if (!miscValue1 || achievementCriteria->kill_creature.creatureID != miscValue1) + if (!miscValue1 || achievementCriteria->Entry->Asset.CreatureID != miscValue1) return false; break; case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL: - // update at loading or specific skill update - if (miscValue1 && miscValue1 != achievementCriteria->reach_skill_level.skillID) - return false; - break; case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL: // update at loading or specific skill update - if (miscValue1 && miscValue1 != achievementCriteria->learn_skill_level.skillID) + if (miscValue1 && miscValue1 != achievementCriteria->Entry->Asset.SkillID) return false; break; case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE: - if (miscValue1 && miscValue1 != achievementCriteria->complete_quests_in_zone.zoneID) + if (miscValue1 && miscValue1 != achievementCriteria->Entry->Asset.ZoneID) return false; break; case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND: - if (!miscValue1 || referencePlayer->GetMapId() != achievementCriteria->complete_battleground.mapID) - return false; - break; case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP: - if (!miscValue1 || referencePlayer->GetMapId() != achievementCriteria->death_at_map.mapID) + if (!miscValue1 || referencePlayer->GetMapId() != achievementCriteria->Entry->Asset.MapID) return false; break; case ACHIEVEMENT_CRITERIA_TYPE_DEATH: { if (!miscValue1) return false; - // skip wrong arena achievements, if not achievIdByArenaSlot then normal total death counter - bool notfit = false; - for (int j = 0; j < MAX_ARENA_SLOT; ++j) - { - if (achievIdByArenaSlot[j] == achievementCriteria->achievement) - { - Battleground* bg = referencePlayer->GetBattleground(); - if (!bg || !bg->isArena() || ArenaTeam::GetSlotByType(bg->GetArenaType()) != j) - notfit = true; - break; - } - } - if (notfit) - return false; break; } case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON: @@ -2522,45 +2459,13 @@ bool AchievementMgr<T>::RequirementsSatisfied(AchievementCriteriaEntry const* ac if (!map || !map->IsDungeon()) return false; - // search case - bool found = false; - for (int j = 0; achievIdForDungeon[j][0]; ++j) - { - if (achievIdForDungeon[j][0] == achievementCriteria->achievement) - { - if (map->IsRaid()) - { - // if raid accepted (ignore difficulty) - if (!achievIdForDungeon[j][2]) - break; // for - } - else if (referencePlayer->GetDungeonDifficulty() == DIFFICULTY_NORMAL) - { - // dungeon in normal mode accepted - if (!achievIdForDungeon[j][1]) - break; // for - } - else - { - // dungeon in heroic mode accepted - if (!achievIdForDungeon[j][3]) - break; // for - } - - found = true; - break; // for - } - } - if (!found) - return false; - //FIXME: work only for instances where max == min for players - if (((InstanceMap*)map)->GetMaxPlayers() != achievementCriteria->death_in_dungeon.manLimit) + if (((InstanceMap*)map)->GetMaxPlayers() != achievementCriteria->Entry->Asset.GroupSize) return false; break; } case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE: - if (!miscValue1 || miscValue1 != achievementCriteria->killed_by_creature.creatureEntry) + if (!miscValue1 || miscValue1 != achievementCriteria->Entry->Asset.CreatureID) return false; break; case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER: @@ -2568,7 +2473,7 @@ bool AchievementMgr<T>::RequirementsSatisfied(AchievementCriteriaEntry const* ac return false; break; case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM: - if (!miscValue1 || miscValue2 != achievementCriteria->death_from.type) + if (!miscValue1 || miscValue2 != achievementCriteria->Entry->Asset.DamageType) return false; break; case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST: @@ -2576,13 +2481,13 @@ bool AchievementMgr<T>::RequirementsSatisfied(AchievementCriteriaEntry const* ac // if miscValues != 0, it contains the questID. if (miscValue1) { - if (miscValue1 != achievementCriteria->complete_quest.questID) + if (miscValue1 != achievementCriteria->Entry->Asset.QuestID) return false; } else { // login case. - if (!referencePlayer->GetQuestRewardStatus(achievementCriteria->complete_quest.questID)) + if (!referencePlayer->GetQuestRewardStatus(achievementCriteria->Entry->Asset.QuestID)) return false; } @@ -2593,42 +2498,37 @@ bool AchievementMgr<T>::RequirementsSatisfied(AchievementCriteriaEntry const* ac } case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET: case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2: - if (!miscValue1 || miscValue1 != achievementCriteria->be_spell_target.spellID) - return false; - break; case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: - if (!miscValue1 || miscValue1 != achievementCriteria->cast_spell.spellID) + if (!miscValue1 || miscValue1 != achievementCriteria->Entry->Asset.SpellID) return false; break; case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL: - if (miscValue1 && miscValue1 != achievementCriteria->learn_spell.spellID) + if (miscValue1 && miscValue1 != achievementCriteria->Entry->Asset.SpellID) return false; - if (!referencePlayer->HasSpell(achievementCriteria->learn_spell.spellID)) + 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->loot_type.lootType) + if (!miscValue1 || !miscValue2 || !miscValue3 || miscValue3 != achievementCriteria->Entry->Asset.LootType) return false; break; case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM: - if (miscValue1 && achievementCriteria->own_item.itemID != miscValue1) + if (miscValue1 && achievementCriteria->Entry->Asset.ItemID != miscValue1) return false; break; case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM: - if (!miscValue1 || achievementCriteria->use_item.itemID != miscValue1) - return false; - break; case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM: - if (!miscValue1 || miscValue1 != achievementCriteria->own_item.itemID) + 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->explore_area.areaReference); + WorldMapOverlayEntry const* worldOverlayEntry = sWorldMapOverlayStore.LookupEntry(achievementCriteria->Entry->Asset.WorldMapOverlayID); if (!worldOverlayEntry) break; @@ -2658,19 +2558,19 @@ bool AchievementMgr<T>::RequirementsSatisfied(AchievementCriteriaEntry const* ac break; } case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION: - if (miscValue1 && miscValue1 != achievementCriteria->gain_reputation.factionID) + 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->equip_epic_item.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->roll_greed_on_loot.rollValue) + if (!miscValue1 || miscValue2 != achievementCriteria->Entry->Asset.RollValue) return false; ItemTemplate const* proto = sObjectMgr->GetItemTemplate(uint32(miscValue1)); @@ -2679,7 +2579,7 @@ bool AchievementMgr<T>::RequirementsSatisfied(AchievementCriteriaEntry const* ac break; } case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: - if (!miscValue1 || miscValue1 != achievementCriteria->do_emote.emoteID) + if (!miscValue1 || miscValue1 != achievementCriteria->Entry->Asset.EmoteID) return false; break; case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE: @@ -2687,9 +2587,9 @@ bool AchievementMgr<T>::RequirementsSatisfied(AchievementCriteriaEntry const* ac if (!miscValue1) return false; - if (achievementCriteria->additionalRequirements[0].additionalRequirement_type == ACHIEVEMENT_CRITERIA_CONDITION_BG_MAP) + if (achievementCriteria->Entry->FailEvent == ACHIEVEMENT_CRITERIA_CONDITION_BG_MAP) { - if (referencePlayer->GetMapId() != achievementCriteria->additionalRequirements[0].additionalRequirement_value) + if (!referencePlayer->InBattleground()) return false; // map specific case (BG in fact) expected player targeted damage/heal @@ -2697,21 +2597,13 @@ bool AchievementMgr<T>::RequirementsSatisfied(AchievementCriteriaEntry const* ac return false; } break; - case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM: - // miscValue1 = item_id - if (!miscValue1 || miscValue1 != achievementCriteria->equip_item.itemID) - return false; - break; case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT: - if (!miscValue1 || miscValue1 != achievementCriteria->use_gameobject.goEntry) - return false; - break; case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT: - if (!miscValue1 || miscValue1 != achievementCriteria->fish_in_gameobject.goEntry) + if (!miscValue1 || miscValue1 != achievementCriteria->Entry->Asset.GameObjectID) return false; break; case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS: - if (miscValue1 && miscValue1 != achievementCriteria->learn_skillline_spell.skillLine) + if (miscValue1 && miscValue1 != achievementCriteria->Entry->Asset.SkillID) return false; break; case ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM: @@ -2725,32 +2617,32 @@ bool AchievementMgr<T>::RequirementsSatisfied(AchievementCriteriaEntry const* ac break; } case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE: - if (miscValue1 && miscValue1 != achievementCriteria->learn_skill_line.skillLine) + if (miscValue1 && miscValue1 != achievementCriteria->Entry->Asset.SkillID) return false; break; case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS: - if (!miscValue1 || miscValue1 != achievementCriteria->hk_class.classID) + if (!miscValue1 || miscValue1 != achievementCriteria->Entry->Asset.ClassID) return false; break; case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE: - if (!miscValue1 || miscValue1 != achievementCriteria->hk_race.raceID) + if (!miscValue1 || miscValue1 != achievementCriteria->Entry->Asset.RaceID) return false; break; case ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE: - if (!miscValue1 || miscValue1 != achievementCriteria->bg_objective.objectiveId) + if (!miscValue1 || miscValue1 != achievementCriteria->Entry->Asset.ObjectiveId) return false; break; case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA: - if (!miscValue1 || miscValue1 != achievementCriteria->honorable_kill_at_area.areaID) + if (!miscValue1 || miscValue1 != achievementCriteria->Entry->Asset.AreaID) return false; break; case ACHIEVEMENT_CRITERIA_TYPE_CURRENCY: if (!miscValue1 || !miscValue2 || int64(miscValue2) < 0 - || miscValue1 != achievementCriteria->currencyGain.currency) + || miscValue1 != achievementCriteria->Entry->Asset.CurrencyID) return false; break; case ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA: - if (miscValue1 != achievementCriteria->win_arena.mapID) + if (miscValue1 != achievementCriteria->Entry->Asset.MapID) return false; break; default: @@ -2760,137 +2652,148 @@ bool AchievementMgr<T>::RequirementsSatisfied(AchievementCriteriaEntry const* ac } template<class T> -bool AchievementMgr<T>::AdditionalRequirementsSatisfied(AchievementCriteriaEntry const* criteria, uint64 miscValue1, uint64 /*miscValue2*/, Unit const* unit, Player* referencePlayer) const +bool AchievementMgr<T>::AdditionalRequirementsSatisfied(ModifierTreeNode const* tree, uint64 miscValue1, uint64 miscValue2, Unit const* unit, Player* referencePlayer) const { - for (uint8 i = 0; i < MAX_ADDITIONAL_CRITERIA_CONDITIONS; ++i) - { - uint32 reqType = criteria->additionalConditionType[i]; - uint32 reqValue = criteria->additionalConditionValue[i]; + 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]; - switch (AchievementCriteriaAdditionalCondition(reqType)) + switch (AchievementCriteriaAdditionalCondition(reqType)) + { + 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 { - 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()->GetDifficulty()) != 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; - default: - break; + // 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()->GetDifficulty()) != 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; + default: + break; } return true; } @@ -3134,35 +3037,166 @@ template class AchievementMgr<Guild>; template class AchievementMgr<Player>; //========================================================== +AchievementGlobalMgr::~AchievementGlobalMgr() +{ + 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; +} + +void AchievementGlobalMgr::LoadAchievementCriteriaModifiersTree() +{ + uint32 oldMSTime = getMSTime(); + + if (sModifierTreeStore.GetNumRows() == 0) + { + TC_LOG_ERROR("server.loading", ">> Loaded 0 achievement criteria modifiers."); + return; + } + + // Load modifier tree nodes + for (uint32 i = 0; i < sModifierTreeStore.GetNumRows(); ++i) + { + ModifierTreeEntry const* tree = sModifierTreeStore.LookupEntry(i); + if (!tree) + continue; + + ModifierTreeNode* node = new ModifierTreeNode(); + node->Entry = tree; + _criteriaModifiers[node->Entry->ID] = node; + } + + // Build tree + for (auto itr = _criteriaModifiers.begin(); itr != _criteriaModifiers.end(); ++itr) + { + if (!itr->second->Entry->Parent) + continue; + + auto parent = _criteriaModifiers.find(itr->second->Entry->Parent); + if (parent != _criteriaModifiers.end()) + parent->second->Children.push_back(itr->second); + } + + TC_LOG_INFO("server.loading", ">> Loaded %u achievement criteria modifiers in %u ms", uint32(_criteriaModifiers.size()), GetMSTimeDiffToNow(oldMSTime)); +} + void AchievementGlobalMgr::LoadAchievementCriteriaList() { uint32 oldMSTime = getMSTime(); - if (sAchievementCriteriaStore.GetNumRows() == 0) + if (sCriteriaTreeStore.GetNumRows() == 0) { TC_LOG_ERROR("server.loading", ">> Loaded 0 achievement criteria."); 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 (!tree->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; + achievementCriteriaTree->Criteria = nullptr; + + _achievementCriteriaTrees[achievementCriteriaTree->Entry->ID] = achievementCriteriaTree; + if (sCriteriaStore.LookupEntry(tree->CriteriaID)) + _achievementCriteriaTreeByCriteria[tree->CriteriaID].push_back(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); + } + + // Load criteria uint32 criterias = 0; uint32 guildCriterias = 0; - for (uint32 entryId = 0; entryId < sAchievementCriteriaStore.GetNumRows(); ++entryId) + for (uint32 i = 0; i < sCriteriaStore.GetNumRows(); ++i) { - AchievementCriteriaEntry const* criteria = sAchievementMgr->GetAchievementCriteria(entryId); + CriteriaEntry const* criteria = sCriteriaStore.LookupEntry(i); if (!criteria) continue; - AchievementEntry const* achievement = sAchievementMgr->GetAchievement(criteria->achievement); - - m_AchievementCriteriaListByAchievement[criteria->achievement].push_back(criteria); + auto treeItr = _achievementCriteriaTreeByCriteria.find(i); + if (treeItr == _achievementCriteriaTreeByCriteria.end()) + continue; - if (achievement && achievement->Flags & ACHIEVEMENT_FLAG_GUILD) - ++guildCriterias, m_GuildAchievementCriteriasByType[criteria->type].push_back(criteria); + AchievementCriteria* achievementCriteria = new AchievementCriteria(); + achievementCriteria->ID = i; + achievementCriteria->Entry = criteria; + auto mod = _criteriaModifiers.find(criteria->ModifierTreeId); + if (mod != _criteriaModifiers.end()) + achievementCriteria->Modifier = mod->second; else - ++criterias, m_AchievementCriteriasByType[criteria->type].push_back(criteria); + achievementCriteria->Modifier = nullptr; + + _achievementCriteria[achievementCriteria->ID] = achievementCriteria; + + bool isGuild = false, isPlayer = false; + for (AchievementCriteriaTree const* tree : treeItr->second) + { + const_cast<AchievementCriteriaTree*>(tree)->Criteria = achievementCriteria; - if (criteria->timeLimit) - m_AchievementCriteriasByTimedType[criteria->timedCriteriaStartType].push_back(criteria); + if (tree->Achievement->Flags & ACHIEVEMENT_FLAG_GUILD) + isGuild = true; + else + isPlayer = true; + } + + if (isGuild) + { + ++guildCriterias; + _guildAchievementCriteriasByType[criteria->Type].push_back(achievementCriteria); + } + + if (isPlayer) + { + ++criterias; + _achievementCriteriasByType[criteria->Type].push_back(achievementCriteria); + } + + if (criteria->StartTimer) + _achievementCriteriasByTimedType[criteria->StartEvent].push_back(achievementCriteria); } TC_LOG_INFO("server.loading", ">> Loaded %u achievement criteria and %u guild achievement crieteria in %u ms", criterias, guildCriterias, GetMSTimeDiffToNow(oldMSTime)); @@ -3186,13 +3220,13 @@ void AchievementGlobalMgr::LoadAchievementReferenceList() if (!achievement || !achievement->SharesCriteria) continue; - m_AchievementListByReferencedId[achievement->SharesCriteria].push_back(achievement); + _achievementListByReferencedId[achievement->SharesCriteria].push_back(achievement); ++count; } // Once Bitten, Twice Shy (10 player) - Icecrown Citadel if (AchievementEntry const* achievement = sAchievementMgr->GetAchievement(4539)) - const_cast<AchievementEntry*>(achievement)->MapID = 631; // Correct map requirement (currently has Ulduar) + 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)); } @@ -3201,7 +3235,7 @@ void AchievementGlobalMgr::LoadAchievementCriteriaData() { uint32 oldMSTime = getMSTime(); - m_criteriaDataMap.clear(); // need for reload case + _criteriaDataMap.clear(); // need for reload case QueryResult result = WorldDatabase.Query("SELECT criteria_id, type, value1, value2, ScriptName FROM achievement_criteria_data"); @@ -3218,7 +3252,7 @@ void AchievementGlobalMgr::LoadAchievementCriteriaData() Field* fields = result->Fetch(); uint32 criteria_id = fields[0].GetUInt32(); - AchievementCriteriaEntry const* criteria = sAchievementMgr->GetAchievementCriteria(criteria_id); + AchievementCriteria const* criteria = sAchievementMgr->GetAchievementCriteria(criteria_id); if (!criteria) { @@ -3243,7 +3277,7 @@ void AchievementGlobalMgr::LoadAchievementCriteriaData() continue; // this will allocate empty data set storage - AchievementCriteriaDataSet& dataSet = m_criteriaDataMap[criteria_id]; + AchievementCriteriaDataSet& dataSet = _criteriaDataMap[criteria_id]; dataSet.SetCriteriaId(criteria_id); // add real data only for not NONE data types @@ -3288,18 +3322,18 @@ void AchievementGlobalMgr::LoadCompletedAchievements() continue; } else if (achievement->Flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL)) - m_allCompletedAchievements[achievementId] = uint32(0xFFFFFFFF); + _allCompletedAchievements[achievementId] = uint32(0xFFFFFFFF); } while (result->NextRow()); - TC_LOG_INFO("server.loading", ">> Loaded %lu realm first completed achievements in %u ms", (unsigned long)m_allCompletedAchievements.size(), GetMSTimeDiffToNow(oldMSTime)); + TC_LOG_INFO("server.loading", ">> Loaded %lu realm first completed achievements in %u ms", (unsigned long)_allCompletedAchievements.size(), GetMSTimeDiffToNow(oldMSTime)); } void AchievementGlobalMgr::LoadRewards() { uint32 oldMSTime = getMSTime(); - m_achievementRewards.clear(); // need for reload case + _achievementRewards.clear(); // need for reload case // 0 1 2 3 4 5 6 7 QueryResult result = WorldDatabase.Query("SELECT entry, title_A, title_H, item, sender, subject, text, mailTemplate FROM achievement_reward"); @@ -3406,7 +3440,7 @@ void AchievementGlobalMgr::LoadRewards() } } - m_achievementRewards[entry] = reward; + _achievementRewards[entry] = reward; ++count; } while (result->NextRow()); @@ -3418,7 +3452,7 @@ void AchievementGlobalMgr::LoadRewardLocales() { uint32 oldMSTime = getMSTime(); - m_achievementRewardLocales.clear(); // need for reload case + _achievementRewardLocales.clear(); // need for reload case QueryResult result = WorldDatabase.Query("SELECT entry, subject_loc1, text_loc1, subject_loc2, text_loc2, subject_loc3, text_loc3, subject_loc4, text_loc4, " "subject_loc5, text_loc5, subject_loc6, text_loc6, subject_loc7, text_loc7, subject_loc8, text_loc8" @@ -3436,13 +3470,13 @@ void AchievementGlobalMgr::LoadRewardLocales() uint32 entry = fields[0].GetUInt32(); - if (m_achievementRewards.find(entry) == m_achievementRewards.end()) + if (_achievementRewards.find(entry) == _achievementRewards.end()) { TC_LOG_ERROR("sql.sql", "Table `locales_achievement_reward` (Entry: %u) has locale strings for non-existing achievement reward.", entry); continue; } - AchievementRewardLocale& data = m_achievementRewardLocales[entry]; + AchievementRewardLocale& data = _achievementRewardLocales[entry]; for (uint8 i = TOTAL_LOCALES - 1; i > 0; --i) { @@ -3453,7 +3487,7 @@ void AchievementGlobalMgr::LoadRewardLocales() } while (result->NextRow()); - TC_LOG_INFO("server.loading", ">> Loaded %u achievement reward locale strings in %u ms", uint32(m_achievementRewardLocales.size()), GetMSTimeDiffToNow(oldMSTime)); + 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 @@ -3461,14 +3495,27 @@ AchievementEntry const* AchievementGlobalMgr::GetAchievement(uint32 achievementI return sAchievementStore.LookupEntry(achievementId); } -AchievementCriteriaEntry const* AchievementGlobalMgr::GetAchievementCriteria(uint32 criteriaId) const +AchievementCriteriaTree const* AchievementGlobalMgr::GetAchievementCriteriaTree(uint32 criteriaTreeId) const { - return sAchievementCriteriaStore.LookupEntry(criteriaId); + 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 : m_allCompletedAchievements) + for (auto& realmCompletion : _allCompletedAchievements) if (realmCompletion.second == instanceId) realmCompletion.second = uint32(0xFFFFFFFF); } diff --git a/src/server/game/Achievements/AchievementMgr.h b/src/server/game/Achievements/AchievementMgr.h index 90d52cd9101..05b30c73e76 100644 --- a/src/server/game/Achievements/AchievementMgr.h +++ b/src/server/game/Achievements/AchievementMgr.h @@ -32,17 +32,45 @@ class Unit; class Player; class WorldPacket; -typedef std::vector<AchievementCriteriaEntry const*> AchievementCriteriaEntryList; -typedef std::vector<AchievementEntry const*> AchievementEntryList; +struct ModifierTreeNode +{ + ModifierTreeEntry const* Entry; + std::vector<ModifierTreeNode const*> Children; +}; + +typedef std::unordered_map<uint32, ModifierTreeNode*> ModifierTreeMap; + +struct AchievementCriteria +{ + uint32 ID; + CriteriaEntry const* Entry; + ModifierTreeNode const* Modifier; +}; + +typedef std::vector<AchievementCriteria const*> AchievementCriteriaList; +typedef std::unordered_map<uint32, AchievementCriteria*> AchievementCriteriaMap; -typedef std::unordered_map<uint32, AchievementCriteriaEntryList> AchievementCriteriaListByAchievement; -typedef std::unordered_map<uint32, AchievementEntryList> AchievementListByReferencedId; +struct AchievementCriteriaTree +{ + uint32 ID; + CriteriaTreeEntry const* Entry; + AchievementEntry const* Achievement; + AchievementCriteria const* Criteria; + std::vector<AchievementCriteriaTree const*> Children; +}; + +typedef std::unordered_map<uint32, AchievementCriteriaTree*> AchievementCriteriaTreeMap; +typedef std::vector<AchievementCriteriaTree const*> AchievementCriteriaTreeList; +typedef std::vector<AchievementEntry const*> AchievementEntryList; +typedef std::unordered_map<uint32, AchievementCriteriaTreeList> AchievementCriteriaTreeByCriteriaMap; + +typedef std::unordered_map<uint32, AchievementEntryList> AchievementListByReferencedId; struct CriteriaProgress { uint64 counter; time_t date; // latest update time. - ObjectGuid CompletedGUID; // GUID of the player that completed this criteria (guild achievements) + ObjectGuid PlayerGUID; // GUID of the player that completed this criteria (guild achievements) bool changed; }; @@ -193,7 +221,7 @@ struct AchievementCriteriaData ScriptId = _scriptId; } - bool IsValid(AchievementCriteriaEntry const* criteria); + bool IsValid(AchievementCriteria const* criteria); bool Meets(uint32 criteria_id, Player const* source, Unit const* target, uint32 miscValue1 = 0) const; }; @@ -276,32 +304,34 @@ class AchievementMgr uint32 GetAchievementPoints() const { return _achievementPoints; } private: void SendAchievementEarned(AchievementEntry const* achievement) const; - void SendCriteriaUpdate(AchievementCriteriaEntry const* entry, CriteriaProgress const* progress, uint32 timeElapsed, bool timedCompleted) const; - CriteriaProgress* GetCriteriaProgress(AchievementCriteriaEntry const* entry); - void SetCriteriaProgress(AchievementCriteriaEntry const* entry, uint64 changeValue, Player* referencePlayer, ProgressType ptype = PROGRESS_SET); - void RemoveCriteriaProgress(AchievementCriteriaEntry const* entry); + void SendCriteriaUpdate(AchievementCriteria const* entry, CriteriaProgress const* progress, uint32 timeElapsed, bool timedCompleted) const; + CriteriaProgress* GetCriteriaProgress(AchievementCriteria const* entry); + void SetCriteriaProgress(AchievementCriteria const* entry, uint64 changeValue, Player* referencePlayer, ProgressType ptype = PROGRESS_SET); + void RemoveCriteriaProgress(AchievementCriteria const* entry); void CompletedCriteriaFor(AchievementEntry const* achievement, Player* referencePlayer); - bool IsCompletedCriteria(AchievementCriteriaEntry const* achievementCriteria, AchievementEntry const* achievement); + bool IsCompletedCriteriaTree(AchievementCriteriaTree const* tree); + bool IsCompletedCriteria(AchievementCriteria const* achievementCriteria, uint64 requiredAmount); + uint64 GetTotalCriteriaTreeProgress(AchievementCriteriaTree const* criteriaTree); bool IsCompletedAchievement(AchievementEntry const* entry); - bool CanUpdateCriteria(AchievementCriteriaEntry const* criteria, AchievementEntry const* achievement, uint64 miscValue1, uint64 miscValue2, uint64 miscValue3, Unit const* unit, Player* referencePlayer); - void SendPacket(WorldPacket* data) const; + bool CanUpdateCriteria(AchievementCriteria const* criteria, AchievementCriteriaTreeList const* trees, uint64 miscValue1, uint64 miscValue2, uint64 miscValue3, Unit const* unit, Player* referencePlayer); + void SendPacket(WorldPacket const* data) const; - bool ConditionsSatisfied(AchievementCriteriaEntry const* criteria, Player* referencePlayer) const; - bool RequirementsSatisfied(AchievementCriteriaEntry const* criteria, uint64 miscValue1, uint64 miscValue2, uint64 miscValue3, Unit const* unit, Player* referencePlayer) const; - bool AdditionalRequirementsSatisfied(AchievementCriteriaEntry const* criteria, uint64 miscValue1, uint64 miscValue2, Unit const* unit, Player* referencePlayer) const; + bool ConditionsSatisfied(AchievementCriteria const* criteria, Player* referencePlayer) const; + bool RequirementsSatisfied(AchievementCriteria const* criteria, uint64 miscValue1, uint64 miscValue2, uint64 miscValue3, Unit const* unit, Player* referencePlayer) const; + bool AdditionalRequirementsSatisfied(ModifierTreeNode const* parent, uint64 miscValue1, uint64 miscValue2, Unit const* unit, Player* referencePlayer) const; T* _owner; CriteriaProgressMap m_criteriaProgress; CompletedAchievementMap m_completedAchievements; typedef std::map<uint32, uint32> TimedAchievementMap; - TimedAchievementMap m_timedAchievements; // Criteria id/time left in MS + TimedAchievementMap m_timedAchievements; // Criteria tree id/time left in MS uint32 _achievementPoints; }; class AchievementGlobalMgr { AchievementGlobalMgr() { } - ~AchievementGlobalMgr() { } + ~AchievementGlobalMgr(); public: static char const* GetCriteriaTypeString(AchievementCriteriaTypes type); @@ -313,50 +343,50 @@ class AchievementGlobalMgr return &instance; } - AchievementCriteriaEntryList const& GetAchievementCriteriaByType(AchievementCriteriaTypes type, bool guild = false) const + AchievementCriteriaTreeList const* GetAchievementCriteriaTreesByCriteria(uint32 criteriaId) const { - return guild ? m_GuildAchievementCriteriasByType[type] : m_AchievementCriteriasByType[type]; + auto itr = _achievementCriteriaTreeByCriteria.find(criteriaId); + return itr != _achievementCriteriaTreeByCriteria.end() ? &itr->second : nullptr; } - AchievementCriteriaEntryList const& GetTimedAchievementCriteriaByType(AchievementCriteriaTimedTypes type) const + AchievementCriteriaList const& GetAchievementCriteriaByType(AchievementCriteriaTypes type, bool guild = false) const { - return m_AchievementCriteriasByTimedType[type]; + return guild ? _guildAchievementCriteriasByType[type] : _achievementCriteriasByType[type]; } - AchievementCriteriaEntryList const* GetAchievementCriteriaByAchievement(uint32 id) const + AchievementCriteriaList const& GetTimedAchievementCriteriaByType(AchievementCriteriaTimedTypes type) const { - AchievementCriteriaListByAchievement::const_iterator itr = m_AchievementCriteriaListByAchievement.find(id); - return itr != m_AchievementCriteriaListByAchievement.end() ? &itr->second : NULL; + return _achievementCriteriasByTimedType[type]; } AchievementEntryList const* GetAchievementByReferencedId(uint32 id) const { - AchievementListByReferencedId::const_iterator itr = m_AchievementListByReferencedId.find(id); - return itr != m_AchievementListByReferencedId.end() ? &itr->second : NULL; + AchievementListByReferencedId::const_iterator itr = _achievementListByReferencedId.find(id); + return itr != _achievementListByReferencedId.end() ? &itr->second : NULL; } AchievementReward const* GetAchievementReward(AchievementEntry const* achievement) const { - AchievementRewards::const_iterator iter = m_achievementRewards.find(achievement->ID); - return iter != m_achievementRewards.end() ? &iter->second : NULL; + AchievementRewards::const_iterator iter = _achievementRewards.find(achievement->ID); + return iter != _achievementRewards.end() ? &iter->second : NULL; } AchievementRewardLocale const* GetAchievementRewardLocale(AchievementEntry const* achievement) const { - AchievementRewardLocales::const_iterator iter = m_achievementRewardLocales.find(achievement->ID); - return iter != m_achievementRewardLocales.end() ? &iter->second : NULL; + AchievementRewardLocales::const_iterator iter = _achievementRewardLocales.find(achievement->ID); + return iter != _achievementRewardLocales.end() ? &iter->second : NULL; } - AchievementCriteriaDataSet const* GetCriteriaDataSet(AchievementCriteriaEntry const* achievementCriteria) const + AchievementCriteriaDataSet const* GetCriteriaDataSet(AchievementCriteria const* achievementCriteria) const { - AchievementCriteriaDataMap::const_iterator iter = m_criteriaDataMap.find(achievementCriteria->ID); - return iter != m_criteriaDataMap.end() ? &iter->second : NULL; + AchievementCriteriaDataMap::const_iterator iter = _criteriaDataMap.find(achievementCriteria->ID); + return iter != _criteriaDataMap.end() ? &iter->second : NULL; } bool IsRealmCompleted(AchievementEntry const* achievement, uint32 instanceId) const { - AllCompletedAchievements::const_iterator itr = m_allCompletedAchievements.find(achievement->ID); - if (itr == m_allCompletedAchievements.end()) + AllCompletedAchievements::const_iterator itr = _allCompletedAchievements.find(achievement->ID); + if (itr == _allCompletedAchievements.end()) return false; if (achievement->Flags & ACHIEVEMENT_FLAG_REALM_FIRST_KILL) @@ -370,7 +400,7 @@ class AchievementGlobalMgr if (IsRealmCompleted(achievement, instanceId)) return; - m_allCompletedAchievements[achievement->ID] = instanceId; + _allCompletedAchievements[achievement->ID] = instanceId; } bool IsGroupCriteriaType(AchievementCriteriaTypes type) const @@ -394,6 +424,7 @@ class AchievementGlobalMgr // Removes instanceId as valid id to complete realm first kill achievements void OnInstanceDestroyed(uint32 instanceId); + void LoadAchievementCriteriaModifiersTree(); void LoadAchievementCriteriaList(); void LoadAchievementCriteriaData(); void LoadAchievementReferenceList(); @@ -401,27 +432,31 @@ class AchievementGlobalMgr void LoadRewards(); void LoadRewardLocales(); AchievementEntry const* GetAchievement(uint32 achievementId) const; - AchievementCriteriaEntry const* GetAchievementCriteria(uint32 achievementId) const; + AchievementCriteriaTree const* GetAchievementCriteriaTree(uint32 criteriaTreeId) const; + AchievementCriteria const* GetAchievementCriteria(uint32 criteriaId) const; private: - AchievementCriteriaDataMap m_criteriaDataMap; + AchievementCriteriaDataMap _criteriaDataMap; - // store achievement criterias by type to speed up lookup - AchievementCriteriaEntryList m_AchievementCriteriasByType[ACHIEVEMENT_CRITERIA_TYPE_TOTAL]; - AchievementCriteriaEntryList m_GuildAchievementCriteriasByType[ACHIEVEMENT_CRITERIA_TYPE_TOTAL]; + AchievementCriteriaTreeMap _achievementCriteriaTrees; + AchievementCriteriaMap _achievementCriteria; + ModifierTreeMap _criteriaModifiers; + + AchievementCriteriaTreeByCriteriaMap _achievementCriteriaTreeByCriteria; - AchievementCriteriaEntryList m_AchievementCriteriasByTimedType[ACHIEVEMENT_TIMED_TYPE_MAX]; + // store achievement criterias by type to speed up lookup + AchievementCriteriaList _achievementCriteriasByType[ACHIEVEMENT_CRITERIA_TYPE_TOTAL]; + AchievementCriteriaList _guildAchievementCriteriasByType[ACHIEVEMENT_CRITERIA_TYPE_TOTAL]; - // store achievement criterias by achievement to speed up lookup - AchievementCriteriaListByAchievement m_AchievementCriteriaListByAchievement; + AchievementCriteriaList _achievementCriteriasByTimedType[ACHIEVEMENT_TIMED_TYPE_MAX]; // store achievements by referenced achievement id to speed up lookup - AchievementListByReferencedId m_AchievementListByReferencedId; + AchievementListByReferencedId _achievementListByReferencedId; typedef std::map<uint32 /*achievementId*/, uint32 /*instanceId*/> AllCompletedAchievements; - AllCompletedAchievements m_allCompletedAchievements; + AllCompletedAchievements _allCompletedAchievements; - AchievementRewards m_achievementRewards; - AchievementRewardLocales m_achievementRewardLocales; + AchievementRewards _achievementRewards; + AchievementRewardLocales _achievementRewardLocales; }; #define sAchievementMgr AchievementGlobalMgr::instance() diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h index 3187b5f9f77..8b93ba5222b 100644 --- a/src/server/game/DataStores/DBCEnums.h +++ b/src/server/game/DataStores/DBCEnums.h @@ -93,12 +93,6 @@ enum AchievementFlags ACHIEVEMENT_FLAG_SHOW_CRITERIA_MEMBERS = 0x00010000 // }; -enum AchievementCriteriaLimits -{ - MAX_CRITERIA_REQUIREMENTS = 2, - MAX_ADDITIONAL_CRITERIA_CONDITIONS = 3 -}; - enum AchievementCriteriaCondition { ACHIEVEMENT_CRITERIA_CONDITION_NONE = 0, @@ -132,7 +126,7 @@ enum AchievementCriteriaAdditionalCondition ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_TARGET_AREA_OR_ZONE = 18, ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_MAP_DIFFICULTY = 20, ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_TARGET_CREATURE_YIELDS_XP = 21, // NYI - ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_ARENA_TYPE = 24, // NYI + ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_ARENA_TYPE = 24, ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_SOURCE_RACE = 25, ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_SOURCE_CLASS = 26, ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_TARGET_RACE = 27, @@ -179,6 +173,8 @@ enum AchievementCriteriaTimedTypes ACHIEVEMENT_TIMED_TYPE_CREATURE = 7, // Timer is started by killing creature with entry in timerStartEvent ACHIEVEMENT_TIMED_TYPE_ITEM = 9, // Timer is started by using item with entry in timerStartEvent ACHIEVEMENT_TIMED_TYPE_UNK = 10, // Unknown + ACHIEVEMENT_TIMED_TYPE_UNK_2 = 13, // Unknown + ACHIEVEMENT_TIMED_TYPE_SCENARIO_STAGE = 14, // Timer is started by changing stages in a scenario ACHIEVEMENT_TIMED_TYPE_MAX }; @@ -299,7 +295,13 @@ enum AchievementCriteriaTypes ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_GUILD_CHALLENGE = 139 //struct { uint32 count; } Guild Challenge }; -#define ACHIEVEMENT_CRITERIA_TYPE_TOTAL 140 +#define ACHIEVEMENT_CRITERIA_TYPE_TOTAL 188 + +enum AchievementCriteriaTreeOperator +{ + ACHIEVEMENT_CRITERIA_TREE_OPERATOR_ALL = 4, + ACHIEVEMENT_CRITERIA_TREE_OPERATOR_ANY = 8 +}; enum AreaFlags { diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index b801fd7b3f3..6647fa08fc2 100644 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -60,7 +60,6 @@ static AreaFlagByMapID sAreaFlagByMapID; // for instances wit static WMOAreaInfoByTripple sWMOAreaInfoByTripple; DBCStorage <AchievementEntry> sAchievementStore(Achievementfmt); -DBCStorage <AchievementCriteriaEntry> sAchievementCriteriaStore(AchievementCriteriafmt); DBCStorage <AreaTriggerEntry> sAreaTriggerStore(AreaTriggerEntryfmt); DBCStorage <ArmorLocationEntry> sArmorLocationStore(ArmorLocationfmt); DBCStorage <AuctionHouseEntry> sAuctionHouseStore(AuctionHouseEntryfmt); @@ -85,6 +84,8 @@ DBCStorage <CreatureFamilyEntry> sCreatureFamilyStore(CreatureFamilyfmt); DBCStorage <CreatureModelDataEntry> sCreatureModelDataStore(CreatureModelDatafmt); DBCStorage <CreatureSpellDataEntry> sCreatureSpellDataStore(CreatureSpellDatafmt); DBCStorage <CreatureTypeEntry> sCreatureTypeStore(CreatureTypefmt); +DBCStorage <CriteriaEntry> sCriteriaStore(Criteriafmt); +DBCStorage <CriteriaTreeEntry> sCriteriaTreeStore(CriteriaTreefmt); DBCStorage <CurrencyTypesEntry> sCurrencyTypesStore(CurrencyTypesfmt); uint32 PowersByClass[MAX_CLASSES][MAX_POWERS]; @@ -163,6 +164,7 @@ DBCStorage <MapEntry> sMapStore(MapEntryfmt); DBCStorage <MapDifficultyEntry> sMapDifficultyStore(MapDifficultyEntryfmt); // only for loading MapDifficultyMap sMapDifficultyMap; +DBCStorage <ModifierTreeEntry> sModifierTreeStore(ModifierTreefmt); DBCStorage <MovieEntry> sMovieStore(MovieEntryfmt); DBCStorage <MountCapabilityEntry> sMountCapabilityStore(MountCapabilityfmt); DBCStorage <MountTypeEntry> sMountTypeStore(MountTypefmt); @@ -240,19 +242,13 @@ typedef std::list<std::string> StoreProblemList; uint32 DBCFileCount = 0; -static bool LoadDBC_assert_print(uint32 fsize, uint32 rsize, const std::string& filename) -{ - TC_LOG_ERROR("misc", "Size of '%s' set by format string (%u) not equal size of C++ structure (%u).", filename.c_str(), fsize, rsize); - - // ASSERT must fail after function call - return false; -} - template<class T> inline void LoadDBC(uint32& availableDbcLocales, StoreProblemList& errors, DBCStorage<T>& storage, std::string const& dbcPath, std::string const& filename, std::string const* customFormat = NULL, std::string const* customIndexName = NULL) { // compatibility format and C++ structure sizes - ASSERT(DBCFileLoader::GetFormatRecordSize(storage.GetFormat()) == sizeof(T) || LoadDBC_assert_print(DBCFileLoader::GetFormatRecordSize(storage.GetFormat()), sizeof(T), filename)); + ASSERT(DBCFileLoader::GetFormatRecordSize(storage.GetFormat()) == sizeof(T), + "Size of '%s' set by format string (%u) not equal size of C++ structure (%u).", + filename.c_str(), DBCFileLoader::GetFormatRecordSize(storage.GetFormat()), sizeof(T)); ++DBCFileCount; std::string dbcFilename = dbcPath + filename; @@ -298,7 +294,9 @@ template<class T> inline void LoadGameTable(StoreProblemList& errors, std::string const& tableName, GameTable<T>& storage, std::string const& dbcPath, std::string const& filename) { // compatibility format and C++ structure sizes - ASSERT(DBCFileLoader::GetFormatRecordSize(storage.GetFormat()) == sizeof(T) || LoadDBC_assert_print(DBCFileLoader::GetFormatRecordSize(storage.GetFormat()), sizeof(T), filename)); + ASSERT(DBCFileLoader::GetFormatRecordSize(storage.GetFormat()) == sizeof(T), + "Size of '%s' set by format string (%u) not equal size of C++ structure (%u).", + filename.c_str(), DBCFileLoader::GetFormatRecordSize(storage.GetFormat()), sizeof(T)); ++DBCFileCount; std::string dbcFilename = dbcPath + filename; @@ -365,8 +363,6 @@ void LoadDBCStores(const std::string& dataPath) } LoadDBC(availableDbcLocales, bad_dbc_files, sAchievementStore, dbcPath, "Achievement.dbc"/*, &CustomAchievementfmt, &CustomAchievementIndex*/);//19116 - // TODO: 6.x remove this and update achievement system with new dbcs - //LoadDBC(availableDbcLocales, bad_dbc_files, sAchievementCriteriaStore, dbcPath, "Achievement_Criteria.dbc"); LoadDBC(availableDbcLocales, bad_dbc_files, sAreaTriggerStore, dbcPath, "AreaTrigger.dbc");//19116 LoadDBC(availableDbcLocales, bad_dbc_files, sAreaGroupStore, dbcPath, "AreaGroup.dbc");//19116 LoadDBC(availableDbcLocales, bad_dbc_files, sAuctionHouseStore, dbcPath, "AuctionHouse.dbc");//19116 @@ -415,6 +411,8 @@ void LoadDBCStores(const std::string& dataPath) LoadDBC(availableDbcLocales, bad_dbc_files, sCreatureModelDataStore, dbcPath, "CreatureModelData.dbc");//19116 LoadDBC(availableDbcLocales, bad_dbc_files, sCreatureSpellDataStore, dbcPath, "CreatureSpellData.dbc");//19116 LoadDBC(availableDbcLocales, bad_dbc_files, sCreatureTypeStore, dbcPath, "CreatureType.dbc");//19116 + LoadDBC(availableDbcLocales, bad_dbc_files, sCriteriaStore, dbcPath, "Criteria.dbc");//19342 + LoadDBC(availableDbcLocales, bad_dbc_files, sCriteriaTreeStore, dbcPath, "CriteriaTree.dbc");//19342 LoadDBC(availableDbcLocales, bad_dbc_files, sCurrencyTypesStore, dbcPath, "CurrencyTypes.dbc");//19116 LoadDBC(availableDbcLocales, bad_dbc_files, sDestructibleModelDataStore, dbcPath, "DestructibleModelData.dbc");//19116 LoadDBC(availableDbcLocales, bad_dbc_files, sDungeonEncounterStore, dbcPath, "DungeonEncounter.dbc");//19116 @@ -499,6 +497,7 @@ void LoadDBCStores(const std::string& dataPath) sMapDifficultyMap[MAKE_PAIR32(entry->MapID, entry->DifficultyID)] = MapDifficulty(entry->RaidDuration, entry->MaxPlayers, entry->Message_lang[0] > 0); sMapDifficultyStore.Clear(); + LoadDBC(availableDbcLocales, bad_dbc_files, sModifierTreeStore, dbcPath, "ModifierTree.dbc");//19342 LoadDBC(availableDbcLocales, bad_dbc_files, sMountCapabilityStore, dbcPath, "MountCapability.dbc");//19116 LoadDBC(availableDbcLocales, bad_dbc_files, sMountTypeStore, dbcPath, "MountType.dbc");//19116 diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h index 0c25e359483..92770e2e618 100644 --- a/src/server/game/DataStores/DBCStores.h +++ b/src/server/game/DataStores/DBCStores.h @@ -131,7 +131,6 @@ private: }; extern DBCStorage <AchievementEntry> sAchievementStore; -extern DBCStorage <AchievementCriteriaEntry> sAchievementCriteriaStore; extern DBCStorage <AreaTableEntry> sAreaStore;// recommend access using functions extern DBCStorage <AreaGroupEntry> sAreaGroupStore; extern DBCStorage <AreaTriggerEntry> sAreaTriggerStore; @@ -156,6 +155,8 @@ extern DBCStorage <CreatureFamilyEntry> sCreatureFamilyStore; extern DBCStorage <CreatureModelDataEntry> sCreatureModelDataStore; extern DBCStorage <CreatureSpellDataEntry> sCreatureSpellDataStore; extern DBCStorage <CreatureTypeEntry> sCreatureTypeStore; +extern DBCStorage <CriteriaEntry> sCriteriaStore; +extern DBCStorage <CriteriaTreeEntry> sCriteriaTreeStore; extern DBCStorage <CurrencyTypesEntry> sCurrencyTypesStore; extern DBCStorage <DestructibleModelDataEntry> sDestructibleModelDataStore; extern DBCStorage <DungeonEncounterEntry> sDungeonEncounterStore; @@ -217,6 +218,7 @@ extern DBCStorage <LockEntry> sLockStore; extern DBCStorage <MailTemplateEntry> sMailTemplateStore; extern DBCStorage <MapEntry> sMapStore; extern DBCStorage <MinorTalentEntry> sMinorTalentStore; +extern DBCStorage <ModifierTreeEntry> sModifierTreeStore; extern DBCStorage <MountCapabilityEntry> sMountCapabilityStore; extern DBCStorage <MountTypeEntry> sMountTypeStore; extern DBCStorage <NameGenEntry> sNameGenStore; diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h index a03f6370c62..32bd3c35fee 100644 --- a/src/server/game/DataStores/DBCStructure.h +++ b/src/server/game/DataStores/DBCStructure.h @@ -54,7 +54,7 @@ struct AchievementEntry //char* Reward_lang; // 11 uint32 MinimumCriteria; // 12 - need this count of completed criterias (own or referenced achievement criterias) uint32 SharesCriteria; // 13 - referenced achievement (counting of all completed criterias) - //uint32 CriteriaTree; // 14 + uint32 CriteriaTree; // 14 }; //19116 @@ -66,474 +66,6 @@ struct AchievementCategoryEntry //uint32 UIOrder; // 3 }; -struct AchievementCriteriaEntry -{ - uint32 ID; // 0 - uint32 achievement; // 1 - uint32 type; // 2 - union - { - // ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE = 0 - /// @todo also used for player deaths.. - struct - { - uint32 creatureID; // 3 - uint64 creatureCount; // 4 - } kill_creature; - - // ACHIEVEMENT_CRITERIA_TYPE_WIN_BG = 1 - struct - { - uint32 bgMapID; // 3 - uint64 winCount; // 4 - } win_bg; - - // ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL = 5 - // ACHIEVEMENT_CRITERIA_TYPE_REACH_GUILD_LEVEL = 125 - struct - { - uint32 unused; // 3 - uint64 level; // 4 - } reach_level; - - // ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL = 7 - struct - { - uint32 skillID; // 3 - uint64 skillLevel; // 4 - } reach_skill_level; - - // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT = 8 - struct - { - uint32 linkedAchievement; // 3 - } complete_achievement; - - // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT = 9 - struct - { - uint32 unused; // 3 - uint64 totalQuestCount; // 4 - } complete_quest_count; - - // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST_DAILY = 10 - struct - { - uint32 unused; // 3 - uint64 numberOfDays; // 4 - } complete_daily_quest_daily; - - // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE = 11 - struct - { - uint32 zoneID; // 3 - uint64 questCount; // 4 - } complete_quests_in_zone; - - // ACHIEVEMENT_CRITERIA_TYPE_CURRENCY = 12 - struct - { - uint32 currency; - uint64 count; - } currencyGain; - - // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST = 14 - struct - { - uint32 unused; // 3 - uint64 questCount; // 4 - } complete_daily_quest; - - // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND = 15 - struct - { - uint32 mapID; // 3 - } complete_battleground; - - // ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP = 16 - struct - { - uint32 mapID; // 3 - } death_at_map; - - // ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON = 18 - struct - { - uint32 manLimit; // 3 - } death_in_dungeon; - - // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_RAID = 19 - struct - { - uint32 groupSize; // 3 can be 5, 10 or 25 - } complete_raid; - - // ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE = 20 - struct - { - uint32 creatureEntry; // 3 - } killed_by_creature; - - // ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING = 24 - struct - { - uint32 unused; // 3 - uint64 fallHeight; // 4 - } fall_without_dying; - - // ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM = 26 - struct - { - uint32 type; // 3, see enum EnviromentalDamage - } death_from; - - // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST = 27 - struct - { - uint32 questID; // 3 - uint64 questCount; // 4 - } complete_quest; - - // ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET = 28 - // ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2 = 69 - struct - { - uint32 spellID; // 3 - uint64 spellCount; // 4 - } be_spell_target; - - // ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL = 29 - // ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2 = 110 - struct - { - uint32 spellID; // 3 - uint64 castCount; // 4 - } cast_spell; - - // ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE - struct - { - uint32 objectiveId; // 3 - uint64 completeCount; // 4 - } bg_objective; - - // ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA = 31 - struct - { - uint32 areaID; // 3 Reference to AreaTable.dbc - uint64 killCount; // 4 - } honorable_kill_at_area; - - // ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA = 32 - struct - { - uint32 mapID; // 3 Reference to Map.dbc - uint64 count; // 4 Number of times that the arena must be won. - } win_arena; - - // ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA = 33 - struct - { - uint32 mapID; // 3 Reference to Map.dbc - } play_arena; - - // ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL = 34 - struct - { - uint32 spellID; // 3 Reference to Map.dbc - } learn_spell; - - // ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM = 36 - struct - { - uint32 itemID; // 3 - uint64 itemCount; // 4 - } own_item; - - // ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA = 37 - struct - { - uint32 unused; // 3 - uint64 count; // 4 - } win_rated_arena; - - // ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_TEAM_RATING = 38 - struct - { - uint32 teamtype; // 3 {2, 3, 5} - } highest_team_rating; - - // ACHIEVEMENT_CRITERIA_TYPE_REACH_TEAM_RATING = 39 - struct - { - uint32 teamtype; // 3 {2, 3, 5} - uint64 teamrating; // 4 - } reach_team_rating; - - // ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_PERSONAL_RATING = 39 - struct - { - uint32 teamtype; // 3 {2, 3, 5} - uint64 PersonalRating; // 4 - } highest_personal_rating; - - // ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL = 40 - struct - { - uint32 skillID; // 3 - uint64 skillLevel; // 4 apprentice=1, journeyman=2, expert=3, artisan=4, master=5, grand master=6 - } learn_skill_level; - - // ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM = 41 - struct - { - uint32 itemID; // 3 - uint64 itemCount; // 4 - } use_item; - - // ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM = 42 - struct - { - uint32 itemID; // 3 - uint64 itemCount; // 4 - } loot_item; - - // ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA = 43 - struct - { - /// @todo This rank is _NOT_ the index from AreaTable.dbc - uint32 areaReference; // 3 - } explore_area; - - // ACHIEVEMENT_CRITERIA_TYPE_OWN_RANK = 44 - struct - { - /// @todo This rank is _NOT_ the index from CharTitles.dbc - uint32 rank; // 3 - } own_rank; - - // ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT = 45 - struct - { - uint32 unused; // 3 - uint64 numberOfSlots; // 4 - } buy_bank_slot; - - // ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION = 46 - struct - { - uint32 factionID; // 3 - uint64 reputationAmount; // 4 Total reputation amount, so 42000 = exalted - } gain_reputation; - - // ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION= 47 - struct - { - uint32 unused; // 3 - uint64 numberOfExaltedFactions; // 4 - } gain_exalted_reputation; - - // ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP = 48 - struct - { - uint32 unused; // 3 - uint64 numberOfVisits; // 4 - } visit_barber; - - // ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM = 49 - /// @todo where is the required itemlevel stored? - struct - { - uint32 itemSlot; // 3 - uint64 count; // 4 - } equip_epic_item; - - // ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT = 50 - struct - { - uint32 rollValue; // 3 - uint64 count; // 4 - } roll_need_on_loot; - // ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT = 51 - struct - { - uint32 rollValue; // 3 - uint64 count; // 4 - } roll_greed_on_loot; - - // ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS = 52 - struct - { - uint32 classID; // 3 - uint64 count; // 4 - } hk_class; - - // ACHIEVEMENT_CRITERIA_TYPE_HK_RACE = 53 - struct - { - uint32 raceID; // 3 - uint64 count; // 4 - } hk_race; - - // ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE = 54 - /// @todo where is the information about the target stored? - struct - { - uint32 emoteID; // 3 enum TextEmotes - uint64 count; // 4 count of emotes, always required special target or requirements - } do_emote; - // ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE = 13 - // ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE = 55 - // ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS = 56 - struct - { - uint32 unused; // 3 - uint64 count; // 4 - } healing_done; - - // ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS = 56 - struct - { - uint32 unused; - uint64 killCount; - } get_killing_blow; - - // ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM = 57 - struct - { - uint32 itemID; // 3 - uint64 count; // 4 - } equip_item; - - // ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD= 62 - struct - { - uint32 unused; // 3 - uint64 goldInCopper; // 4 - } quest_reward_money; - - // ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY = 67 - struct - { - uint32 unused; // 3 - uint64 goldInCopper; // 4 - } loot_money; - - // ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT = 68 - struct - { - uint32 goEntry; // 3 - uint64 useCount; // 4 - } use_gameobject; - - // ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL = 70 - /// @todo are those special criteria stored in the dbc or do we have to add another sql table? - struct - { - uint32 unused; // 3 - uint64 killCount; // 4 - } special_pvp_kill; - - // ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT = 72 - struct - { - uint32 goEntry; // 3 - uint64 lootCount; // 4 - } fish_in_gameobject; - - // ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS = 75 - struct - { - uint32 skillLine; // 3 - uint64 spellCount; // 4 - } learn_skillline_spell; - - // ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL = 76 - struct - { - uint32 unused; // 3 - uint64 duelCount; // 4 - } win_duel; - - // ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_POWER = 96 - struct - { - uint32 powerType; // 3 mana=0, 1=rage, 3=energy, 6=runic power - } highest_power; - - // ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_STAT = 97 - struct - { - uint32 statType; // 3 4=spirit, 3=int, 2=stamina, 1=agi, 0=strength - } highest_stat; - - // ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER = 98 - struct - { - uint32 spellSchool; // 3 - } highest_spellpower; - - // ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_RATING = 100 - struct - { - uint32 ratingType; // 3 - } highest_rating; - - // ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE = 109 - struct - { - uint32 lootType; // 3 3=fishing, 2=pickpocket, 4=disentchant - uint64 lootTypeCount; // 4 - } loot_type; - - // ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE = 112 - struct - { - uint32 skillLine; // 3 - uint64 spellCount; // 4 - } learn_skill_line; - - // ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL = 113 - struct - { - uint32 unused; // 3 - uint64 killCount; // 4 - } honorable_kill; - - struct - { - uint32 unused; - uint64 dungeonsComplete; - } use_lfg; - - struct - { - uint32 field3; // 3 main requirement - uint64 count; // 4 main requirement count - } raw; - }; - - struct - { - uint32 additionalRequirement_type; - uint32 additionalRequirement_value; - } additionalRequirements[MAX_CRITERIA_REQUIREMENTS]; - - char* name; // 9 m_description_lang - uint32 completionFlag; // 10 m_flags - uint32 timedCriteriaStartType; // 11 m_timer_start_event Only appears with timed achievements, seems to be the type of starting a timed Achievement, only type 1 and some of type 6 need manual starting - // 1: ByEventId(?) (serverside IDs), 2: ByQuestId, 5: ByCastSpellId(?) - // 6: BySpellIdTarget(some of these are unknown spells, some not, some maybe spells) - // 7: ByKillNpcId, 9: ByUseItemId - uint32 timedCriteriaMiscId; // 12 m_timer_asset_id Alway appears with timed events, used internally to start the achievement, store - uint32 timeLimit; // 13 m_timer_time time limit in seconds - uint32 showOrder; // 14 m_ui_order also used in achievement shift-links as index in state bitmask - //uint32 unk1; // 15 only one value, still unknown - //uint32 unk2; // 16 all zeros - uint32 additionalConditionType[MAX_ADDITIONAL_CRITERIA_CONDITIONS]; // 17-19 - uint32 additionalConditionValue[MAX_ADDITIONAL_CRITERIA_CONDITIONS]; // 20-22 -}; - // Temporary define until max depth is found somewhere (adt?) #define MAX_MAP_DEPTH -5000 @@ -897,6 +429,134 @@ struct CreatureTypeEntry //uint32 Flags; // 2 no exp? critters, non-combat pets, gas cloud. }; +struct CriteriaEntry +{ + uint32 ID; // 0 + uint32 Type; // 1 + union + { + uint32 ID; + // ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE = 0 + // ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE = 20 + uint32 CreatureID; + + // ACHIEVEMENT_CRITERIA_TYPE_WIN_BG = 1 + // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND = 15 + // ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP = 16 + // ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA = 32 + // ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA = 33 + uint32 MapID; + + // ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL = 7 + // ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL = 40 + // ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS = 75 + // ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE = 112 + uint32 SkillID; + + // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT = 8 + uint32 AchievementID; + + // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE = 11 + uint32 ZoneID; + + // ACHIEVEMENT_CRITERIA_TYPE_CURRENCY = 12 + uint32 CurrencyID; + + // ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON = 18 + // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_RAID = 19 + uint32 GroupSize; + + // ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM = 26 + uint32 DamageType; + + // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST = 27 + uint32 QuestID; + + // ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET = 28 + // ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2 = 69 + // ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL = 29 + // ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2 = 110 + // ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL = 34 + uint32 SpellID; + + // ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE + uint32 ObjectiveId; + + // ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA = 31 + uint32 AreaID; + + // ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM = 36 + // ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM = 41 + // ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM = 42 + // ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM = 57 + uint32 ItemID; + + // ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_TEAM_RATING = 38 + // ACHIEVEMENT_CRITERIA_TYPE_REACH_TEAM_RATING = 39 + // ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_PERSONAL_RATING = 39 + uint32 TeamType; + + // ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA = 43 + uint32 WorldMapOverlayID; + + // ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION = 46 + uint32 FactionID; + + // ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM = 49 + uint32 ItemSlot; + + // ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT = 50 + // ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT = 51 + uint32 RollValue; + + // ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS = 52 + uint32 ClassID; + + // ACHIEVEMENT_CRITERIA_TYPE_HK_RACE = 53 + uint32 RaceID; + + // ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE = 54 + uint32 EmoteID; + + // ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT = 68 + // ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT = 72 + uint32 GameObjectID; + + // ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_POWER = 96 + uint32 PowerType; + + // ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_STAT = 97 + uint32 StatType; + + // ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER = 98 + uint32 SpellSchool; + + // ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE = 109 + uint32 LootType; + } Asset; // 2 + uint32 StartEvent; // 3 + uint32 StartAsset; // 4 + uint32 StartTimer; // 5 + uint32 FailEvent; // 6 + uint32 FailAsset; // 7 + uint32 ModifierTreeId; // 8 + //uint32 Flags; // 9 + uint32 EligibilityWorldStateID; // 10 + uint32 EligibilityWorldStateValue; // 11 +}; + +struct CriteriaTreeEntry +{ + uint32 ID; // 0 + uint32 CriteriaID; // 1 + uint64 Amount; // 2 + uint32 Operator; // 3 + uint32 Parent; // 4 + //uint32 Flags; // 5 + //char* DescriptionLang; // 6 + //uint32 OrderIndex; // 7 +}; + /* not used struct CurrencyCategoryEntry { @@ -1567,6 +1227,16 @@ struct MinorTalentEntry uint32 OrderIndex; // 3 }; +struct ModifierTreeEntry +{ + uint32 ID; // 0 + uint32 Type; // 1 + uint32 Asset[2]; // 2-3 + uint32 Operator; // 4 + uint32 Amount; // 5 + uint32 Parent; // 6 +}; + struct MountCapabilityEntry { uint32 ID; // 0 diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h index 852af09fdb2..bbba02b5ea5 100644 --- a/src/server/game/DataStores/DBCfmt.h +++ b/src/server/game/DataStores/DBCfmt.h @@ -20,12 +20,11 @@ #define TRINITY_DBCSFRM_H // x - skip<uint32>, X - skip<uint8>, s - char*, f - float, i - uint32, b - uint8, d - index (not included) -// n - index (included), l - bool, p - field present in sql dbc, a - field absent in sql dbc +// n - index (included), l - uint64, p - field present in sql dbc, a - field absent in sql dbc -char const Achievementfmt[] = "niixsxiixixxiix"; +char const Achievementfmt[] = "niixsxiixixxiii"; const std::string CustomAchievementfmt = "pppaaaapapaapp"; const std::string CustomAchievementIndex = "ID"; -char const AchievementCriteriafmt[] = "niiiliiiisiiiiixxiiiiii"; char const AreaTableEntryfmt[] = "iiiniixxxxsxixiiiiixxxxxxxxxx"; char const AreaGroupEntryfmt[] = "niiiiiii"; char const AreaTriggerEntryfmt[] = "nifffxxxfffffxxxx"; @@ -49,6 +48,8 @@ char const CreatureFamilyfmt[] = "nfifiiiiixsx"; char const CreatureModelDatafmt[] = "nixxxxxxxxxxxxxffxxxxxxxxxxxxxxxxx"; char const CreatureSpellDatafmt[] = "niiiixxxx"; char const CreatureTypefmt[] = "nxx"; +char const Criteriafmt[] = "niiiiiiiixii"; +char const CriteriaTreefmt[] = "niliixxx"; char const CurrencyTypesfmt[] = "nixxxxxiiixx"; char const DestructibleModelDatafmt[] = "nixxxixxxxixxxxixxxxxxxx"; char const DungeonEncounterfmt[] = "niiixsxxx"; @@ -108,6 +109,7 @@ char const MapEntryfmt[] = "nxiixxsixxixiffxiiiixx"; char const MapDifficultyEntryfmt[] = "diisiixx"; char const MinorTalentEntryfmt[] = "niii"; char const MovieEntryfmt[] = "nxxxx"; +char const ModifierTreefmt[] = "niiiiii"; char const MountCapabilityfmt[] = "niiiiiii"; char const MountTypefmt[] = "niiiiiiiiiiiiiiiiiiiiiiii"; char const NameGenfmt[] = "dsii"; diff --git a/src/server/game/Guilds/Guild.cpp b/src/server/game/Guilds/Guild.cpp index 0bfa08d62c9..bd9b05e35bc 100644 --- a/src/server/game/Guilds/Guild.cpp +++ b/src/server/game/Guilds/Guild.cpp @@ -1550,17 +1550,19 @@ void Guild::HandleSetAchievementTracking(WorldSession* session, std::set<uint32> { std::set<uint32> criteriaIds; + /* for (std::set<uint32>::iterator achievementId = achievementIds.begin(); achievementId != achievementIds.end(); ++achievementId) { if (AchievementCriteriaEntryList const* cList = sAchievementMgr->GetAchievementCriteriaByAchievement(*achievementId)) { for (AchievementCriteriaEntryList::const_iterator itr = cList->begin(); itr != cList->end(); ++itr) { - AchievementCriteriaEntry const* criteria = *itr; + AchievementCriteriaTree const* criteria = *itr; criteriaIds.insert(criteria->ID); } } } + */ member->SetTrackedCriteriaIds(criteriaIds); m_achievementMgr.SendAllTrackedCriterias(player, member->GetTrackedCriteriaIds()); @@ -2629,7 +2631,7 @@ void Guild::BroadcastPacketToRank(WorldPacket* packet, uint8 rankId) const player->GetSession()->SendPacket(packet); } -void Guild::BroadcastPacket(WorldPacket* packet) const +void Guild::BroadcastPacket(WorldPacket const* packet) const { for (Members::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr) if (Player* player = itr->second->FindPlayer()) diff --git a/src/server/game/Guilds/Guild.h b/src/server/game/Guilds/Guild.h index 4abeb282b0c..132ecc72f89 100644 --- a/src/server/game/Guilds/Guild.h +++ b/src/server/game/Guilds/Guild.h @@ -846,7 +846,7 @@ public: void BroadcastToGuild(WorldSession* session, bool officerOnly, std::string const& msg, uint32 language = LANG_UNIVERSAL) const; void BroadcastAddonToGuild(WorldSession* session, bool officerOnly, std::string const& msg, std::string const& prefix) const; void BroadcastPacketToRank(WorldPacket* packet, uint8 rankId) const; - void BroadcastPacket(WorldPacket* packet) const; + void BroadcastPacket(WorldPacket const* packet) const; void BroadcastPacketIfTrackingAchievement(WorldPacket* packet, uint32 criteriaId) const; void MassInviteToEvent(WorldSession* session, uint32 minLevel, uint32 maxLevel, uint32 minRank); diff --git a/src/server/game/Server/Packets/AchievementPackets.cpp b/src/server/game/Server/Packets/AchievementPackets.cpp new file mode 100644 index 00000000000..d471d87426c --- /dev/null +++ b/src/server/game/Server/Packets/AchievementPackets.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "AchievementPackets.h" + +WorldPacket const* WorldPackets::Achievement::AllAchievements::Write() +{ + _worldPacket << uint32(Earned.size()); + _worldPacket << uint32(Progress.size()); + + for (EarnedAchievement const& earned : Earned) + { + _worldPacket << uint32(earned.Id); + _worldPacket.AppendPackedTime(earned.Date); + _worldPacket << earned.Owner; + _worldPacket << uint32(earned.VirtualRealmAddress); + _worldPacket << uint32(earned.NativeRealmAddress); + } + + for (CriteriaProgress const& progress : Progress) + { + _worldPacket << uint32(progress.Id); + _worldPacket << uint64(progress.Quantity); + _worldPacket << progress.Player; + _worldPacket.AppendPackedTime(progress.Date); + _worldPacket << uint32(progress.TimeFromStart); + _worldPacket << uint32(progress.TimeFromCreate); + _worldPacket.WriteBits(progress.Flags, 4); + _worldPacket.FlushBits(); + } + + return &_worldPacket; +} + +WorldPacket const* WorldPackets::Achievement::CriteriaUpdate::Write() +{ + _worldPacket << uint32(CriteriaID); + _worldPacket << uint64(Quantity); + _worldPacket << PlayerGUID; + _worldPacket << uint32(Flags); + _worldPacket.AppendPackedTime(CurrentTime); + _worldPacket << uint32(ElapsedTime); + _worldPacket << uint32(CreationTime); + + return &_worldPacket; +} + +WorldPacket const* WorldPackets::Achievement::AchievementEarned::Write() +{ + _worldPacket << Sender; + _worldPacket << Earner; + _worldPacket << uint32(AchievementID); + _worldPacket.AppendPackedTime(Time); + _worldPacket << uint32(EarnerNativeRealm); + _worldPacket << uint32(EarnerVirtualRealm); + _worldPacket.WriteBit(Initial); + _worldPacket.FlushBits(); + + return &_worldPacket; +} diff --git a/src/server/game/Server/Packets/AchievementPackets.h b/src/server/game/Server/Packets/AchievementPackets.h new file mode 100644 index 00000000000..9d609372831 --- /dev/null +++ b/src/server/game/Server/Packets/AchievementPackets.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef game_AchievementPackets_h__ +#define game_AchievementPackets_h__ + +#include "ObjectGuid.h" +#include "Packet.h" + +namespace WorldPackets +{ + namespace Achievement + { + struct EarnedAchievement + { + uint32 Id = 0; + time_t Date = time_t(0); + ObjectGuid Owner; + uint32 VirtualRealmAddress = 0; + uint32 NativeRealmAddress = 0; + }; + + struct CriteriaProgress + { + uint32 Id = 0; + uint64 Quantity = 0; + ObjectGuid Player; + uint32 Flags = 0; + time_t Date = time_t(0); + uint32 TimeFromStart = 0; + uint32 TimeFromCreate = 0; + }; + + class AllAchievements final : public ServerPacket + { + public: + AllAchievements() : ServerPacket(SMSG_ALL_ACHIEVEMENT_DATA) { } + + WorldPacket const* Write() override; + + std::vector<EarnedAchievement> Earned; + std::vector<CriteriaProgress> Progress; + }; + + class CriteriaUpdate final : public ServerPacket + { + public: + CriteriaUpdate() : ServerPacket(SMSG_CRITERIA_UPDATE, 4 + 8 + 16 + 4 + 4 + 4 + 4) { } + + WorldPacket const* Write() override; + + uint32 CriteriaID = 0; + uint64 Quantity = 0; + ObjectGuid PlayerGUID; + uint32 Flags = 0; + time_t CurrentTime = time_t(0); + uint32 ElapsedTime = 0; + uint32 CreationTime = 0; + }; + + class AchievementEarned final : public ServerPacket + { + public: + AchievementEarned() : ServerPacket(SMSG_ACHIEVEMENT_EARNED, 16 + 4 + 4 + 4 + 4 + 1 + 16) { } + + WorldPacket const* Write() override; + + ObjectGuid Earner; + uint32 EarnerNativeRealm = 0; + uint32 EarnerVirtualRealm = 0; + uint32 AchievementID = 0; + time_t Time = time_t(0); + bool Initial = false; + ObjectGuid Sender; + }; + } +} + +#endif // game_AchievementPackets_h__ diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index e18e5e984ac..603e55ff738 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -709,14 +709,14 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACCOUNT_RESTRICTED_WARNING, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACCOUNT_TOYS_UPDATE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACHIEVEMENT_DELETED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACHIEVEMENT_EARNED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACHIEVEMENT_EARNED, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACTION_BUTTONS, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACTIVATETAXIREPLY, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ADDON_INFO, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ADD_RUNE_POWER, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ADJUST_SPLINE_DURATION, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_AI_REACTION, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_ALL_ACHIEVEMENT_DATA, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_ALL_ACHIEVEMENT_DATA, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_AREA_SPIRIT_HEALER_TIME, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_AREA_TRIGGER_MESSAGE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_AREA_TRIGGER_MOVEMENT_UPDATE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); @@ -867,7 +867,7 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_CORPSE_RECLAIM_DELAY, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CREATURE_QUERY_RESPONSE, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CRITERIA_DELETED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_CRITERIA_UPDATE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_CRITERIA_UPDATE, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CROSSED_INEBRIATION_THRESHOLD, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CURRENCY_LOOT_REMOVED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CURRENCY_LOOT_RESTORED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); diff --git a/src/server/game/Server/Protocol/Opcodes.h b/src/server/game/Server/Protocol/Opcodes.h index 68fd9c30d48..ddad78a527b 100644 --- a/src/server/game/Server/Protocol/Opcodes.h +++ b/src/server/game/Server/Protocol/Opcodes.h @@ -686,16 +686,16 @@ enum OpcodeServer : uint32 SMSG_ACCOUNT_RESTRICTED_WARNING = 0xBADD, SMSG_ACCOUNT_TOYS_UPDATE = 0x0590, SMSG_ACHIEVEMENT_DELETED = 0xBADD, - SMSG_ACHIEVEMENT_EARNED = 0xBADD, + SMSG_ACHIEVEMENT_EARNED = 0x010E, SMSG_ACTION_BUTTONS = 0x1D1F, SMSG_ACTIVATETAXIREPLY = 0xBADD, SMSG_ADDON_INFO = 0x1D9F, SMSG_ADD_RUNE_POWER = 0xBADD, SMSG_ADJUST_SPLINE_DURATION = 0x0104, SMSG_AI_REACTION = 0x0BA1, - SMSG_ALL_ACHIEVEMENT_DATA = 0xBADD, - SMSG_ALL_ACHIEVEMENT_DATA_ACCOUNT = 0x0123, - SMSG_ALL_ACHIEVEMENT_DATA_PLAYER = 0x0030, + SMSG_ACCOUNT_CRITERIA_UPDATE = 0x0912, + SMSG_ALL_ACCOUNT_CRITERIA = 0x0123, + SMSG_ALL_ACHIEVEMENT_DATA = 0x0030, SMSG_AREA_SPIRIT_HEALER_TIME = 0xBADD, SMSG_AREA_TRIGGER_MESSAGE = 0xBADD, SMSG_AREA_TRIGGER_MOVEMENT_UPDATE = 0xBADD, @@ -852,9 +852,7 @@ enum OpcodeServer : uint32 SMSG_CORPSE_RECLAIM_DELAY = 0x0BE2, SMSG_CREATURE_QUERY_RESPONSE = 0x0A26, SMSG_CRITERIA_DELETED = 0xBADD, - SMSG_CRITERIA_UPDATE = 0xBADD, - SMSG_CRITERIA_UPDATE_ACCOUNT = 0x0912, - SMSG_CRITERIA_UPDATE_PLAYER = 0x1904, + SMSG_CRITERIA_UPDATE = 0x1904, SMSG_CROSSED_INEBRIATION_THRESHOLD = 0xBADD, SMSG_CURRENCY_LOOT_REMOVED = 0xBADD, SMSG_CURRENCY_LOOT_RESTORED = 0xBADD, diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 5c8706da09c..7195c58ae00 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1693,6 +1693,8 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading Achievements..."); sAchievementMgr->LoadAchievementReferenceList(); + TC_LOG_INFO("server.loading", "Loading Achievement Criteria Modifier trees..."); + sAchievementMgr->LoadAchievementCriteriaModifiersTree(); TC_LOG_INFO("server.loading", "Loading Achievement Criteria Lists..."); sAchievementMgr->LoadAchievementCriteriaList(); TC_LOG_INFO("server.loading", "Loading Achievement Criteria Data..."); |