diff options
author | et65 <et65@ashbringer.fr> | 2014-12-28 19:50:19 +0100 |
---|---|---|
committer | et65 <et65@ashbringer.fr> | 2014-12-28 19:50:19 +0100 |
commit | e46921748d9173f0dcaa32f8a53626c21cf4a2a2 (patch) | |
tree | e9467c092c4c9189b34ac10e95bc684c7d77ce3e /src | |
parent | 4f557347ce26dcb3d7ce697ecd203ff92da5198a (diff) | |
parent | 27137ca06e99209bd1a47a0ff7082ff14fcae8c9 (diff) |
Merge remote-tracking branch 'trinitycore/6.x' into 6.x
Diffstat (limited to 'src')
51 files changed, 1740 insertions, 1574 deletions
diff --git a/src/server/bnetserver/Realms/WorldListener.cpp b/src/server/bnetserver/Realms/WorldListener.cpp index d84425496c7..8a6133c44e6 100644 --- a/src/server/bnetserver/Realms/WorldListener.cpp +++ b/src/server/bnetserver/Realms/WorldListener.cpp @@ -110,6 +110,9 @@ void WorldListener::HandleToonOnlineStatusChange(Battlenet::RealmHandle const& r } } else if (session->IsToonOnline()) + { session->AsyncWrite(new Battlenet::WoWRealm::ToonLoggedOut()); + session->SetToonOnline(false); + } } } diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index 6f14b3a04a8..2e86a78148a 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -2276,7 +2276,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u if (linked) ProcessEvent(linked, unit, var0, var1, bvar, spell, gob); else - TC_LOG_ERROR("sql.sql", "SmartScript::ProcessAction: Entry " SI64FMTD " SourceType %u, Event %u, Link Event %u not found or invalid, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.link); + TC_LOG_DEBUG("sql.sql", "SmartScript::ProcessAction: Entry " SI64FMTD " SourceType %u, Event %u, Link Event %u not found or invalid, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.link); } } diff --git a/src/server/game/Accounts/RBAC.h b/src/server/game/Accounts/RBAC.h index bb347113e60..54dcb73fb35 100644 --- a/src/server/game/Accounts/RBAC.h +++ b/src/server/game/Accounts/RBAC.h @@ -100,6 +100,7 @@ enum RBACPermissions RBAC_PERM_COMMANDS_PINFO_CHECK_PERSONAL_DATA = 48, RBAC_PERM_EMAIL_CONFIRM_FOR_PASS_CHANGE = 49, RBAC_PERM_MAY_CHECK_OWN_EMAIL = 50, + RBAC_PERM_ALLOW_TWO_SIDE_TRADE = 51, // Free space for core permissions (till 149) // Roles (Permissions with delegated permissions) use 199 and descending 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/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h index 01b28e6ec72..878498a94da 100644 --- a/src/server/game/DataStores/DB2Stores.h +++ b/src/server/game/DataStores/DB2Stores.h @@ -22,6 +22,7 @@ #include "DB2Structure.h" #include <string> #include <map> +#include "SharedDefines.h" extern DB2Storage<BroadcastTextEntry> sBroadcastTextStore; extern DB2Storage<HolidaysEntry> sHolidaysStore; diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h index 9aefb440d98..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 { @@ -611,6 +613,7 @@ enum CurrencyTypes CURRENCY_TYPE_VALOR_POINTS = 396, CURRENCY_TYPE_CONQUEST_META_ARENA = 483, CURRENCY_TYPE_CONQUEST_META_RBG = 484, + CURRENCY_TYPE_APEXIS_CRYSTALS = 823, }; #endif 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/Entities/Item/ItemTemplate.h b/src/server/game/Entities/Item/ItemTemplate.h index 8f88feec644..ddf9dc65bec 100644 --- a/src/server/game/Entities/Item/ItemTemplate.h +++ b/src/server/game/Entities/Item/ItemTemplate.h @@ -670,6 +670,8 @@ struct ItemTemplate case INVTYPE_SHIELD: case INVTYPE_HOLDABLE: return true; + default: + break; } switch (GetClass()) diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 367f1f57d3a..904600a2481 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -1039,6 +1039,7 @@ bool Player::Create(ObjectGuid::LowType guidlow, WorldPackets::Character::Charac SetUInt32Value(PLAYER_FIELD_COINAGE, sWorld->getIntConfig(CONFIG_START_PLAYER_MONEY)); SetCurrency(CURRENCY_TYPE_HONOR_POINTS, sWorld->getIntConfig(CONFIG_CURRENCY_START_HONOR_POINTS)); + SetCurrency(CURRENCY_TYPE_APEXIS_CRYSTALS, sWorld->getIntConfig(CONFIG_CURRENCY_START_APEXIS_CRYSTALS)); SetCurrency(CURRENCY_TYPE_JUSTICE_POINTS, sWorld->getIntConfig(CONFIG_CURRENCY_START_JUSTICE_POINTS)); SetCurrency(CURRENCY_TYPE_CONQUEST_POINTS, sWorld->getIntConfig(CONFIG_CURRENCY_START_CONQUEST_POINTS)); @@ -7302,6 +7303,7 @@ uint32 Player::GetCurrencyWeekCap(CurrencyTypesEntry const* currency) const uint32 Player::GetCurrencyTotalCap(CurrencyTypesEntry const* currency) const { + // @TODO: Possibly use caps from CurrencyTypes.dbc uint32 cap = currency->MaxQty; switch (currency->ID) @@ -7313,6 +7315,13 @@ uint32 Player::GetCurrencyTotalCap(CurrencyTypesEntry const* currency) const cap = honorcap; break; } + case CURRENCY_TYPE_APEXIS_CRYSTALS: + { + uint32 apexiscap = sWorld->getIntConfig(CONFIG_CURRENCY_MAX_APEXIS_CRYSTALS); + if (apexiscap > 0) + cap = apexiscap; + break; + } case CURRENCY_TYPE_JUSTICE_POINTS: { uint32 justicecap = sWorld->getIntConfig(CONFIG_CURRENCY_MAX_JUSTICE_POINTS); @@ -20844,12 +20853,15 @@ void Player::SetRestBonus(float rest_bonus_new) m_rest_bonus = rest_bonus_new; // update data for client - if (GetSession()->IsARecruiter() || (GetSession()->GetRecruiterId() != 0)) + if ((GetsRecruitAFriendBonus(true) && (GetSession()->IsARecruiter() || GetSession()->GetRecruiterId() != 0))) SetByteValue(PLAYER_BYTES_2, 3, REST_STATE_RAF_LINKED); - else if (m_rest_bonus > 10) - SetByteValue(PLAYER_BYTES_2, 3, REST_STATE_RESTED); // Set Reststate = Rested - else if (m_rest_bonus <= 1) - SetByteValue(PLAYER_BYTES_2, 3, REST_STATE_NOT_RAF_LINKED); // Set Reststate = Normal + else + { + if (m_rest_bonus > 10) + SetByteValue(PLAYER_BYTES_2, 3, REST_STATE_RESTED); + else if (m_rest_bonus <= 1) + SetByteValue(PLAYER_BYTES_2, 3, REST_STATE_NOT_RAF_LINKED); + } //RestTickUpdate SetUInt32Value(PLAYER_REST_STATE_EXPERIENCE, uint32(m_rest_bonus)); 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/Handlers/QueryHandler.cpp b/src/server/game/Handlers/QueryHandler.cpp index 4beddd757da..6cd8acf08dd 100644 --- a/src/server/game/Handlers/QueryHandler.cpp +++ b/src/server/game/Handlers/QueryHandler.cpp @@ -132,60 +132,39 @@ void WorldSession::HandleCreatureQuery(WorldPackets::Query::QueryCreature& packe } /// Only _static_ data is sent in this packet !!! -void WorldSession::HandleGameObjectQueryOpcode(WorldPacket& recvData) +void WorldSession::HandleGameObjectQueryOpcode(WorldPackets::Query::QueryGameObject& packet) { - uint32 entry; - recvData >> entry; - ObjectGuid guid; - recvData >> guid; + WorldPackets::Query::QueryGameObjectResponse response; + GameObjectTemplate const* gameObjectInfo = sObjectMgr->GetGameObjectTemplate(packet.Entry); - const GameObjectTemplate* info = sObjectMgr->GetGameObjectTemplate(entry); - if (info) - { - std::string Name; - std::string IconName; - std::string CastBarCaption; - - Name = info->name; - IconName = info->IconName; - CastBarCaption = info->castBarCaption; + response.Entry = packet.Entry; - int loc_idx = GetSessionDbLocaleIndex(); - if (loc_idx >= 0) - { - if (GameObjectLocale const* gl = sObjectMgr->GetGameObjectLocale(entry)) - { - ObjectMgr::GetLocaleString(gl->Name, loc_idx, Name); - ObjectMgr::GetLocaleString(gl->CastBarCaption, loc_idx, CastBarCaption); - } - } - TC_LOG_DEBUG("network", "WORLD: CMSG_GAMEOBJECT_QUERY '%s' - Entry: %u. ", info->name.c_str(), entry); - WorldPacket data (SMSG_GAMEOBJECT_QUERY_RESPONSE, 150); - data << uint32(entry); - data << uint32(info->type); - data << uint32(info->displayId); - data << Name; - data << uint8(0) << uint8(0) << uint8(0); // name2, name3, name4 - data << IconName; // 2.0.3, string. Icon name to use instead of default icon for go's (ex: "Attack" makes sword) - data << CastBarCaption; // 2.0.3, string. Text will appear in Cast Bar when using GO (ex: "Collecting") - data << info->unk1; // 2.0.3, string - data.append(info->raw.data, MAX_GAMEOBJECT_DATA); - data << float(info->size); // go size - for (uint32 i = 0; i < MAX_GAMEOBJECT_QUEST_ITEMS; ++i) - data << uint32(info->questItems[i]); // itemId[6], quest drop - data << int32(info->unkInt32); // 4.x, unknown - SendPacket(&data); - TC_LOG_DEBUG("network", "WORLD: Sent SMSG_GAMEOBJECT_QUERY_RESPONSE"); - } - else + if (gameObjectInfo) { - TC_LOG_DEBUG("network", "WORLD: CMSG_GAMEOBJECT_QUERY - Missing gameobject info for (%s, ENTRY: %u)", - guid.ToString().c_str(), entry); - WorldPacket data (SMSG_GAMEOBJECT_QUERY_RESPONSE, 4); - data << uint32(entry | 0x80000000); - SendPacket(&data); - TC_LOG_DEBUG("network", "WORLD: Sent SMSG_GAMEOBJECT_QUERY_RESPONSE"); + response.Allow = true; + WorldPackets::Query::GameObjectStats& stats = response.Stats; + + stats.CastBarCaption = gameObjectInfo->castBarCaption; + stats.DisplayID = gameObjectInfo->displayId; + stats.IconName = gameObjectInfo->IconName; + stats.Name[0] = gameObjectInfo->name; + + for (uint8 i = 0; i < MAX_GAMEOBJECT_QUEST_ITEMS; i++) + if (gameObjectInfo->questItems[i]) + stats.QuestItems.push_back(gameObjectInfo->questItems[i]); + for (uint32 i = 0; i < MAX_GAMEOBJECT_DATA; i++) + stats.Data[i] = gameObjectInfo->raw.data[i]; + + stats.Size = gameObjectInfo->size; + stats.Type = gameObjectInfo->type; + stats.UnkString = gameObjectInfo->unk1; + stats.UnkInt32 = gameObjectInfo->unkInt32; + stats.Expansion = 0; } + else + response.Allow = false; + + SendPacket(response.Write()); } void WorldSession::HandleCorpseQueryOpcode(WorldPacket& /*recvData*/) diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp index 0b468ca0b47..64373e4b674 100644 --- a/src/server/game/Handlers/SpellHandler.cpp +++ b/src/server/game/Handlers/SpellHandler.cpp @@ -36,6 +36,7 @@ #include "Player.h" #include "Config.h" #include "SpellPackets.h" +#include "GameObjectPackets.h" void WorldSession::HandleClientCastFlags(WorldPacket& recvPacket, uint8 castFlags, SpellCastTargets& targets) { @@ -284,14 +285,9 @@ void WorldSession::HandleOpenItemOpcode(WorldPacket& recvPacket) pUser->SendLoot(item->GetGUID(), LOOT_CORPSE); } -void WorldSession::HandleGameObjectUseOpcode(WorldPacket& recvData) +void WorldSession::HandleGameObjectUseOpcode(WorldPackets::GameObject::GameObjectUse& packet) { - ObjectGuid guid; - recvData >> guid; - - TC_LOG_DEBUG("network", "WORLD: Recvd CMSG_GAMEOBJ_USE Message [%s]", guid.ToString().c_str()); - - if (GameObject* obj = GetPlayer()->GetMap()->GetGameObject(guid)) + if (GameObject* obj = GetPlayer()->GetMap()->GetGameObject(packet.Guid)) { if (!obj->IsWithinDistInMap(GetPlayer(), obj->GetInteractionDistance())) return; @@ -305,18 +301,13 @@ void WorldSession::HandleGameObjectUseOpcode(WorldPacket& recvData) } } -void WorldSession::HandleGameobjectReportUse(WorldPacket& recvPacket) +void WorldSession::HandleGameobjectReportUse(WorldPackets::GameObject::GameObjectReportUse& packet) { - ObjectGuid guid; - recvPacket >> guid; - - TC_LOG_DEBUG("network", "WORLD: Recvd CMSG_GAMEOBJ_REPORT_USE Message [%s]", guid.ToString().c_str()); - // ignore for remote control state if (_player->m_mover != _player) return; - GameObject* go = GetPlayer()->GetMap()->GetGameObject(guid); + GameObject* go = GetPlayer()->GetMap()->GetGameObject(packet.Guid); if (!go) return; @@ -331,9 +322,6 @@ void WorldSession::HandleGameobjectReportUse(WorldPacket& recvPacket) void WorldSession::HandleCastSpellOpcode(WorldPackets::Spells::SpellCastRequest& castRequest) { - - TC_LOG_DEBUG("network", "WORLD: got cast spell packet, castCount: %u, spellId: %u, castFlags: %u", castRequest.CastID, castRequest.SpellID, castRequest.SendCastFlags); - // ignore for remote control state (for player case) Unit* mover = _player->m_mover; if (mover != _player && mover->GetTypeId() == TYPEID_PLAYER) diff --git a/src/server/game/Handlers/TradeHandler.cpp b/src/server/game/Handlers/TradeHandler.cpp index dd5563e7a52..aa4579508f2 100644 --- a/src/server/game/Handlers/TradeHandler.cpp +++ b/src/server/game/Handlers/TradeHandler.cpp @@ -773,7 +773,9 @@ void WorldSession::HandleInitiateTradeOpcode(WorldPacket& recvPacket) return; } - if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_TRADE) && pOther->GetTeam() !=_player->GetTeam()) + if (pOther->GetTeam() != _player->GetTeam() && + (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_TRADE) && + !GetPlayer()->GetSession()->HasPermission(rbac::RBAC_PERM_ALLOW_TWO_SIDE_TRADE))) { info.Status = TRADE_STATUS_WRONG_FACTION; SendTradeStatus(info); 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/Packets/GameObjectPackets.cpp b/src/server/game/Server/Packets/GameObjectPackets.cpp new file mode 100644 index 00000000000..21e8305eb5e --- /dev/null +++ b/src/server/game/Server/Packets/GameObjectPackets.cpp @@ -0,0 +1,28 @@ +/* + * 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 "GameObjectPackets.h" + +void WorldPackets::GameObject::GameObjectUse::Read() +{ + _worldPacket >> Guid; +} + +void WorldPackets::GameObject::GameObjectReportUse::Read() +{ + _worldPacket >> Guid; +} diff --git a/src/server/game/Server/Packets/GameObjectPackets.h b/src/server/game/Server/Packets/GameObjectPackets.h new file mode 100644 index 00000000000..76b3478844b --- /dev/null +++ b/src/server/game/Server/Packets/GameObjectPackets.h @@ -0,0 +1,51 @@ +/* + * 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 GOPackets_h__ +#define GOPackets_h__ + +#include "Packet.h" +#include "DB2Stores.h" +#include "GameObject.h" +#include "WorldSession.h" + +namespace WorldPackets +{ + namespace GameObject + { + class GameObjectUse final : public ClientPacket + { + public: + GameObjectUse(WorldPacket&& packet) : ClientPacket(CMSG_GAMEOBJ_USE, std::move(packet)) { } + + void Read() override; + + ObjectGuid Guid; + }; + + class GameObjectReportUse final : public ClientPacket + { + public: + GameObjectReportUse(WorldPacket&& packet) : ClientPacket(CMSG_GAMEOBJ_REPORT_USE, std::move(packet)) { } + + void Read() override; + + ObjectGuid Guid; + }; + } +} +#endif // GOPackets_h__ diff --git a/src/server/game/Server/Packets/QueryPackets.cpp b/src/server/game/Server/Packets/QueryPackets.cpp index f100013d7a4..84792b3c21e 100644 --- a/src/server/game/Server/Packets/QueryPackets.cpp +++ b/src/server/game/Server/Packets/QueryPackets.cpp @@ -221,3 +221,44 @@ WorldPacket const* WorldPackets::Query::HotfixNotifyBlob::Write() return &_worldPacket; } + +void WorldPackets::Query::QueryGameObject::Read() +{ + _worldPacket >> Entry; + _worldPacket >> Guid; +} + +WorldPacket const* WorldPackets::Query::QueryGameObjectResponse::Write() +{ + _worldPacket << Entry; + _worldPacket.WriteBit(Allow); + + if (Allow) + { + _worldPacket << Stats.UnkInt32; + if (Stats.UnkInt32 == 0) + return &_worldPacket; + + _worldPacket << Stats.Type; + _worldPacket << Stats.DisplayID; + for (int8 i = 0; i < 4; i++) + _worldPacket << Stats.Name[i]; + + _worldPacket << Stats.IconName; + _worldPacket << Stats.CastBarCaption; + _worldPacket << Stats.UnkString; + + for (uint32 i = 0; i < MAX_GAMEOBJECT_DATA; i++) + _worldPacket << Stats.Data[i]; + + _worldPacket << Stats.Size; + + _worldPacket << uint8(Stats.QuestItems.size()); + for (int32 questItem : Stats.QuestItems) + _worldPacket << questItem; + + _worldPacket << Stats.Expansion; + } + + return &_worldPacket; +} diff --git a/src/server/game/Server/Packets/QueryPackets.h b/src/server/game/Server/Packets/QueryPackets.h index 4581c20c6e5..749ff1343a2 100644 --- a/src/server/game/Server/Packets/QueryPackets.h +++ b/src/server/game/Server/Packets/QueryPackets.h @@ -210,6 +210,42 @@ namespace WorldPackets HotfixData const* Hotfixes = nullptr; }; + + class QueryGameObject final : public ClientPacket + { + public: + QueryGameObject(WorldPacket&& packet) : ClientPacket(CMSG_GAMEOBJECT_QUERY, std::move(packet)) { } + + void Read() override; + uint32 Entry = 0; + ObjectGuid Guid; + }; + + struct GameObjectStats + { + std::string Name[4]; + std::string IconName; + std::string CastBarCaption; + std::string UnkString; + int32 UnkInt32 = 0; + uint32 Type = 0; + uint32 DisplayID = 0; + uint32 Data[MAX_GAMEOBJECT_DATA]; + float Size = 0.0f; + std::vector<int32> QuestItems; + uint32 Expansion = 0; + }; + + class QueryGameObjectResponse final : public ServerPacket + { + public: + QueryGameObjectResponse() : ServerPacket(SMSG_GAMEOBJECT_QUERY_RESPONSE, 165) { } // Guess size + + WorldPacket const* Write() override; + uint32 Entry = 0; + bool Allow = false; + GameObjectStats Stats; + }; } } diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index e18e5e984ac..1cca6672086 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -33,6 +33,7 @@ #include "Packets/TalentPackets.h" #include "Packets/TradePackets.h" #include "Packets/ItemPackets.h" +#include "Packets/GameObjectPackets.h" template<class PacketClass, void(WorldSession::*HandlerFunction)(PacketClass&)> class PacketHandler : public ClientOpcodeHandler @@ -299,10 +300,10 @@ void OpcodeTable::Initialize() DEFINE_OPCODE_HANDLER_OLD(CMSG_FAR_SIGHT, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleFarSightOpcode ); DEFINE_OPCODE_HANDLER_OLD(CMSG_FORCE_MOVE_ROOT_ACK, STATUS_UNHANDLED, PROCESS_THREADSAFE, &WorldSession::HandleMoveRootAck ); DEFINE_OPCODE_HANDLER_OLD(CMSG_FORCE_MOVE_UNROOT_ACK, STATUS_UNHANDLED, PROCESS_THREADSAFE, &WorldSession::HandleMoveUnRootAck ); - DEFINE_OPCODE_HANDLER_OLD(CMSG_GAMEOBJECT_QUERY, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::HandleGameObjectQueryOpcode ); - DEFINE_OPCODE_HANDLER_OLD(CMSG_GAMEOBJ_REPORT_USE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleGameobjectReportUse ); - DEFINE_OPCODE_HANDLER_OLD(CMSG_GAMEOBJ_USE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleGameObjectUseOpcode ); - DEFINE_OPCODE_HANDLER_OLD(CMSG_GET_MAIL_LIST, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleGetMailList ); + DEFINE_HANDLER(CMSG_GAMEOBJECT_QUERY, STATUS_LOGGEDIN, PROCESS_INPLACE, WorldPackets::Query::QueryGameObject, &WorldSession::HandleGameObjectQueryOpcode); + DEFINE_HANDLER(CMSG_GAMEOBJ_REPORT_USE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::GameObject::GameObjectReportUse, &WorldSession::HandleGameobjectReportUse); + DEFINE_HANDLER(CMSG_GAMEOBJ_USE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::GameObject::GameObjectUse, &WorldSession::HandleGameObjectUseOpcode); + DEFINE_OPCODE_HANDLER_OLD(CMSG_GET_MAIL_LIST, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleGetMailList); DEFINE_OPCODE_HANDLER_OLD(CMSG_GET_MIRRORIMAGE_DATA, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleMirrorImageDataRequest ); DEFINE_OPCODE_HANDLER_OLD(CMSG_GMRESPONSE_RESOLVE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleGMResponseResolve ); DEFINE_OPCODE_HANDLER_OLD(CMSG_GMSURVEY_SUBMIT, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::HandleGMSurveySubmit ); @@ -709,14 +710,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 +868,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); @@ -927,7 +928,7 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_GAMEOBJECT_DESPAWN_ANIM, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_GAMEOBJECT_DESPAWN, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_GAMEOBJECT_PAGETEXT, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_GAMEOBJECT_QUERY_RESPONSE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_GAMEOBJECT_QUERY_RESPONSE, STATUS_LOGGEDIN, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_GAMEOBJECT_RESET_STATE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_GAMESPEED_SET, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_GAMETIME_SET, 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/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 3a9f5e70204..350291c2b4e 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -334,9 +334,6 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) while (m_Socket[CONNECTION_TYPE_REALM] && !_recvQueue.empty() && _recvQueue.peek(true) != firstDelayedPacket && _recvQueue.next(packet, updater)) { - if (!AntiDOS.EvaluateOpcode(*packet, currentTime)) - KickPlayer(); - ClientOpcodeHandler const* opHandle = opcodeTable[static_cast<OpcodeClient>(packet->GetOpcode())]; try { @@ -361,7 +358,7 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) "Player is currently not in world yet.", GetOpcodeNameForLogging(static_cast<OpcodeClient>(packet->GetOpcode())).c_str()); } } - else if (_player->IsInWorld()) + else if (_player->IsInWorld() && AntiDOS.EvaluateOpcode(*packet, currentTime)) { sScriptMgr->OnPacketReceive(this, *packet); opHandle->Call(this, *packet); @@ -373,7 +370,7 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) if (!_player && !m_playerRecentlyLogout && !m_playerLogout) // There's a short delay between _player = null and m_playerRecentlyLogout = true during logout LogUnexpectedOpcode(packet, "STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT", "the player has not logged in yet and not recently logout"); - else + else if (AntiDOS.EvaluateOpcode(*packet, currentTime)) { // not expected _player or must checked in packet hanlder sScriptMgr->OnPacketReceive(this, *packet); @@ -386,7 +383,7 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) LogUnexpectedOpcode(packet, "STATUS_TRANSFER", "the player has not logged in yet"); else if (_player->IsInWorld()) LogUnexpectedOpcode(packet, "STATUS_TRANSFER", "the player is still in world"); - else + else if(AntiDOS.EvaluateOpcode(*packet, currentTime)) { sScriptMgr->OnPacketReceive(this, *packet); opHandle->Call(this, *packet); @@ -406,9 +403,12 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) if (packet->GetOpcode() == CMSG_CHAR_ENUM) m_playerRecentlyLogout = false; - sScriptMgr->OnPacketReceive(this, *packet); - opHandle->Call(this, *packet); - LogUnprocessedTail(packet); + if (AntiDOS.EvaluateOpcode(*packet, currentTime)) + { + sScriptMgr->OnPacketReceive(this, *packet); + opHandle->Call(this, *packet); + LogUnprocessedTail(packet); + } break; case STATUS_NEVER: TC_LOG_ERROR("network.opcode", "Received not allowed opcode %s from %s", GetOpcodeNameForLogging(static_cast<OpcodeClient>(packet->GetOpcode())).c_str() @@ -1170,8 +1170,11 @@ bool WorldSession::DosProtection::EvaluateOpcode(WorldPacket& p, time_t time) co case POLICY_LOG: return true; case POLICY_KICK: + { TC_LOG_INFO("network", "AntiDOS: Player kicked!"); + Session->KickPlayer(); return false; + } case POLICY_BAN: { BanMode bm = (BanMode)sWorld->getIntConfig(CONFIG_PACKET_SPOOF_BANMODE); @@ -1185,7 +1188,7 @@ bool WorldSession::DosProtection::EvaluateOpcode(WorldPacket& p, time_t time) co } sWorld->BanAccount(bm, nameOrIp, duration, "DOS (Packet Flooding/Spoofing", "Server: AutoDOS"); TC_LOG_INFO("network", "AntiDOS: Player automatically banned for %u seconds.", duration); - + Session->KickPlayer(); return false; } default: // invalid policy diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 52f1c76555a..5a4a3465474 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -182,6 +182,7 @@ namespace WorldPackets class QueryPageText; class QueryNPCText; class DBQueryBulk; + class QueryGameObject; } namespace Quest @@ -198,6 +199,12 @@ namespace WorldPackets class AutoEquipItem; class DestroyItem; } + + namespace GameObject + { + class GameObjectReportUse; + class GameObjectUse; + } } enum AccountDataType @@ -694,16 +701,16 @@ class WorldSession void HandleRequestAccountData(WorldPackets::ClientConfig::RequestAccountData& request); void HandleSetActionButtonOpcode(WorldPackets::Spells::SetActionButton& packet); - void HandleGameObjectUseOpcode(WorldPacket& recPacket); + void HandleGameObjectUseOpcode(WorldPackets::GameObject::GameObjectUse& packet); void HandleMeetingStoneInfo(WorldPacket& recPacket); - void HandleGameobjectReportUse(WorldPacket& recvPacket); + void HandleGameobjectReportUse(WorldPackets::GameObject::GameObjectReportUse& packet); void HandleNameQueryOpcode(WorldPackets::Query::QueryPlayerName& packet); void HandleQueryTimeOpcode(WorldPacket& recvPacket); void HandleCreatureQuery(WorldPackets::Query::QueryCreature& packet); void HandleDBQueryBulk(WorldPackets::Query::DBQueryBulk& packet); - void HandleGameObjectQueryOpcode(WorldPacket& recvPacket); + void HandleGameObjectQueryOpcode(WorldPackets::Query::QueryGameObject& packet); void HandleMoveWorldportAckOpcode(WorldPackets::Movement::WorldPortAck& packet); void HandleMoveWorldportAckOpcode(); // for server-side calls diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 66e67d0f09e..9daf32188df 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -3555,7 +3555,7 @@ void Spell::SendSpellCooldown() { // Handle pet cooldowns here if needed instead of in PetAI to avoid hidden cooldown restarts Creature* _creature = m_caster->ToCreature(); - if (_creature && _creature->IsPet()) + if (_creature && (_creature->IsPet() || _creature->IsGuardian())) _creature->AddCreatureSpellCooldown(m_spellInfo->Id); return; diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 544abc9ca87..811e6d9c0f9 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -3618,6 +3618,11 @@ void SpellMgr::LoadSpellInfoCorrections() spellInfo->ManaCost = 0; spellInfo->ManaPerSecond = 0; break; + // Stonecore spells + case 95284: // Teleport (from entrance to Slabhide) + case 95285: // Teleport (from Slabhide to entrance) + const_cast<SpellEffectInfo*>(spellInfo->GetEffect(EFFECT_0))->TargetB = SpellImplicitTargetInfo(TARGET_DEST_DB); + break; // Halls Of Origination spells // Temple Guardian Anhuur case 76606: // Disable Beacon Beams L diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index eddad11e593..7195c58ae00 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -851,6 +851,20 @@ void World::LoadConfigSettings(bool reload) } m_int_configs[CONFIG_CURRENCY_MAX_HONOR_POINTS] *= 100; //precision mod + m_int_configs[CONFIG_CURRENCY_START_APEXIS_CRYSTALS] = sConfigMgr->GetIntDefault("Currency.StartApexisCrystals", 0); + if (int32(m_int_configs[CONFIG_CURRENCY_START_APEXIS_CRYSTALS]) < 0) + { + TC_LOG_ERROR("server.loading", "Currency.StartApexisCrystals (%i) must be >= 0, set to default 0.", m_int_configs[CONFIG_CURRENCY_START_APEXIS_CRYSTALS]); + m_int_configs[CONFIG_CURRENCY_START_APEXIS_CRYSTALS] = 0; + } + m_int_configs[CONFIG_CURRENCY_MAX_APEXIS_CRYSTALS] = sConfigMgr->GetIntDefault("Currency.MaxApexisCrystals", 20000); + if (int32(m_int_configs[CONFIG_CURRENCY_MAX_APEXIS_CRYSTALS]) < 0) + { + TC_LOG_ERROR("server.loading", "Currency.MaxApexisCrystals (%i) can't be negative. Set to default 20000.", m_int_configs[CONFIG_CURRENCY_MAX_APEXIS_CRYSTALS]); + m_int_configs[CONFIG_CURRENCY_MAX_APEXIS_CRYSTALS] = 20000; + } + m_int_configs[CONFIG_CURRENCY_MAX_APEXIS_CRYSTALS] *= 100; //precision mod + m_int_configs[CONFIG_CURRENCY_START_JUSTICE_POINTS] = sConfigMgr->GetIntDefault("Currency.StartJusticePoints", 0); if (int32(m_int_configs[CONFIG_CURRENCY_START_JUSTICE_POINTS]) < 0) { @@ -1679,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..."); diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index 7f583da894b..8f77ab786c7 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -229,6 +229,8 @@ enum WorldIntConfigs CONFIG_START_PLAYER_LEVEL, CONFIG_START_HEROIC_PLAYER_LEVEL, CONFIG_START_PLAYER_MONEY, + CONFIG_CURRENCY_START_APEXIS_CRYSTALS, + CONFIG_CURRENCY_MAX_APEXIS_CRYSTALS, CONFIG_CURRENCY_START_JUSTICE_POINTS, CONFIG_CURRENCY_MAX_JUSTICE_POINTS, CONFIG_CURRENCY_START_HONOR_POINTS, diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 5a99d30100b..674560f2500 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -1978,7 +1978,7 @@ public: static bool HandleMuteInfoHelper(uint32 accountId, char const* accountName, ChatHandler *handler) { PreparedStatement *stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_MUTE_INFO); - stmt->setUInt16(0, accountId); + stmt->setUInt32(0, accountId); PreparedQueryResult result = LoginDatabase.Query(stmt); if (!result) diff --git a/src/server/scripts/Maelstrom/Stonecore/boss_corborus.cpp b/src/server/scripts/Maelstrom/Stonecore/boss_corborus.cpp index 1cf0cc56242..36289fb1169 100644 --- a/src/server/scripts/Maelstrom/Stonecore/boss_corborus.cpp +++ b/src/server/scripts/Maelstrom/Stonecore/boss_corborus.cpp @@ -93,7 +93,9 @@ class boss_corborus : public CreatureScript void Reset() override { _Reset(); + countTrashingCharge = 0; + events.ScheduleEvent(EVENT_DAMPENING_WAVE, 10000); events.ScheduleEvent(EVENT_CRYSTAL_BARRAGE, 15000); events.ScheduleEvent(EVENT_SUBMERGE, 36000); @@ -110,7 +112,7 @@ class boss_corborus : public CreatureScript stateIntro = IN_PROGRESS; - if (Creature* Millhouse = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_MILLHOUSE_MANASTORM))) + if (Creature* Millhouse = instance->GetCreature(DATA_MILLHOUSE_MANASTORM)) { Millhouse->InterruptNonMeleeSpells(true); Millhouse->RemoveAllAuras(); @@ -144,18 +146,18 @@ class boss_corborus : public CreatureScript instance->SetData(DATA_MILLHOUSE_EVENT_FACE, 0); // Open rock gate and cast visual from nearby worldtrigger - instance->HandleGameObject(instance->GetGuidData(GAMEOBJECT_CORBORUS_ROCKDOOR), true); + instance->SetData(DATA_HANDLE_CORBORUS_ROCKDOOR, 0); if (Creature* worldtrigger = me->FindNearestCreature(NPC_WORLDTRIGGER, 60.0f)) worldtrigger->CastSpell(worldtrigger, SPELL_DOOR_BREAK, true); // Make Corborus charge - me->CastSpell(me, SPELL_RING_WYRM_CHARGE, true); + DoCast(me, SPELL_RING_WYRM_CHARGE, true); events.ScheduleEvent(EVENT_CORBORUS_KNOCKBACK, 1000); break; case EVENT_CORBORUS_KNOCKBACK: // Spawn Twilight Documents (quest gameobject) - if (Creature* Millhouse = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_MILLHOUSE_MANASTORM))) + if (Creature* Millhouse = instance->GetCreature(DATA_MILLHOUSE_MANASTORM)) Millhouse->CastSpell(Millhouse, SPELL_TWILIGHT_DOCUMENTS, true); // Knockback Millhouse and other mobs @@ -231,12 +233,14 @@ class boss_corborus : public CreatureScript void JustSummoned(Creature* summon) override { - if (summon->GetEntry() != NPC_TRASHING_CHARGE) - return; + if (summon->GetEntry() == NPC_TRASHING_CHARGE) + { + summon->SetReactState(REACT_PASSIVE); + summon->CastSpell(summon, SPELL_TRASHING_CHARGE_EFFECT); + summon->DespawnOrUnsummon(6000); + } - summon->SetReactState(REACT_PASSIVE); - summon->CastSpell(summon, SPELL_TRASHING_CHARGE_EFFECT); - summon->DespawnOrUnsummon(6000); + BossAI::JustSummoned(summon); } private: @@ -269,7 +273,7 @@ class npc_rock_borer : public CreatureScript void IsSummonedBy(Unit* summoner) override { me->SetInCombatState(false, summoner); - DoCast(SPELL_ROCK_BORER_EMERGE); + DoCast(me, SPELL_ROCK_BORER_EMERGE); } void UpdateAI(uint32 diff) override @@ -291,7 +295,7 @@ class npc_rock_borer : public CreatureScript me->SetReactState(REACT_AGGRESSIVE); break; case EVENT_ROCK_BORE: - DoCast(SPELL_ROCK_BORE); + DoCast(me, SPELL_ROCK_BORE); events.ScheduleEvent(EVENT_ROCK_BORE, urand(15000, 20000)); // Need sniffs for this timer break; default: diff --git a/src/server/scripts/Maelstrom/Stonecore/boss_high_priestess_azil.cpp b/src/server/scripts/Maelstrom/Stonecore/boss_high_priestess_azil.cpp index 08d56f2fa50..c7fa900ff8b 100644 --- a/src/server/scripts/Maelstrom/Stonecore/boss_high_priestess_azil.cpp +++ b/src/server/scripts/Maelstrom/Stonecore/boss_high_priestess_azil.cpp @@ -92,7 +92,7 @@ enum Events // Phase 2: Fury of Earth EVENT_EARTH_FURY_FLY_UP, EVENT_EARTH_FURY_FLY_ABOVE_PLATFORM, - EVENT_EARTH_FURY_CHECK_SEAT0, + EVENT_EARTH_FURY_PREPARE_SHARD, EVENT_EARTH_FURY_LAUNCH_SHARD, EVENT_EARTH_FURY_FLY_DOWN, EVENT_START_ATTACK, @@ -101,12 +101,6 @@ enum Events EVENT_SEISMIC_SHARD_MOUNT }; -enum EventGroups -{ - EVENT_GROUP_PHASE_ONE, - EVENT_GROUP_ADDS, -}; - enum Points { POINT_NONE, @@ -122,7 +116,6 @@ Position const GroundPos = { 1331.82f, 980.314f, 207.542f }; Position const AbovePlatformPos = { 1336.21f, 960.813f, 215.0f }; // TO-DO: -// - Find out why NPCs summoned by boss are usually two times bigger than their normal size. // - Find more sniffs and script Force Grip spell (79351) class boss_high_priestess_azil : public CreatureScript @@ -132,11 +125,7 @@ class boss_high_priestess_azil : public CreatureScript struct boss_high_priestess_azilAI : public BossAI { - boss_high_priestess_azilAI(Creature* creature) : BossAI(creature, DATA_HIGH_PRIESTESS_AZIL) - { - ASSERT(creature->GetVehicleKit()); - } - + boss_high_priestess_azilAI(Creature* creature) : BossAI(creature, DATA_HIGH_PRIESTESS_AZIL) { } void Reset() override { _Reset(); @@ -146,9 +135,9 @@ class boss_high_priestess_azil : public CreatureScript me->SetReactState(REACT_PASSIVE); events.ScheduleEvent(EVENT_INTRO_MOVE, 2000); - events.ScheduleEvent(EVENT_CURSE_OF_BLOOD, 6000, EVENT_GROUP_PHASE_ONE); - events.ScheduleEvent(EVENT_FORCE_GRIP, urand(8000,10000), EVENT_GROUP_PHASE_ONE); - events.ScheduleEvent(EVENT_SUMMON_GRAVITY_WELL, 16000, EVENT_GROUP_PHASE_ONE); + events.ScheduleEvent(EVENT_CURSE_OF_BLOOD, 6000); + events.ScheduleEvent(EVENT_FORCE_GRIP, urand(8000,10000)); + events.ScheduleEvent(EVENT_SUMMON_GRAVITY_WELL, 16000); events.ScheduleEvent(EVENT_ENERGY_SHIELD, urand(35000,36000)); events.ScheduleEvent(EVENT_SUMMON_WAVE_SOUTH, 0); events.ScheduleEvent(EVENT_SUMMON_WAVE_WEST, 40000); @@ -158,32 +147,16 @@ class boss_high_priestess_azil : public CreatureScript { _EnterCombat(); - DoCast(SPELL_ENERGY_SHIELD); + DoCast(me, SPELL_ENERGY_SHIELD); Talk(SAY_AGGRO); } void JustDied(Unit* /*killer*/) override { - Talk(SAY_DEATH); - } - - /* - void PassengerBoarded(Unit* who, int8 seatId, bool apply) override - { - if (!apply || who->GetEntry() != NPC_SEISMIC_SHARD) - return; + _JustDied(); - Movement::MoveSplineInit init(who); - init.DisableTransportPathTransformations(); - if (seatId == 0) - init.MoveTo(12.13748f, 0.0f, 2.442475f); - else if (seatId == 1) - init.MoveTo(12.13748f, 17.5f, 11.19248f); - else - init.MoveTo(12.13748f, -17.5f, 11.19248f); - init.Launch(); + Talk(SAY_DEATH); } - */ void MovementInform(uint32 type, uint32 id) override { @@ -195,6 +168,7 @@ class boss_high_priestess_azil : public CreatureScript case POINT_INTRO_MOVE: me->RemoveAurasDueToSpell(SPELL_ENERGY_SHIELD); me->SetReactState(REACT_AGGRESSIVE); + DoStartMovement(me->GetVictim()); break; case POINT_FLY_UP: me->SetCanFly(true); @@ -203,21 +177,22 @@ class boss_high_priestess_azil : public CreatureScript break; case POINT_ABOVE_PLATFORM: me->SetFacingTo(5.218534f); - DoCast(SPELL_EARTH_FURY_CASTING_VISUAL); - DoCast(SPELL_SEISMIC_SHARD_SUMMON_1); - DoCast(SPELL_SEISMIC_SHARD_SUMMON_2); - DoCast(SPELL_SEISMIC_SHARD_SUMMON_3); - events.ScheduleEvent(EVENT_EARTH_FURY_CHECK_SEAT0, 6700); + DoCast(me, SPELL_EARTH_FURY_CASTING_VISUAL); + DoCast(me, SPELL_SEISMIC_SHARD_SUMMON_1); + DoCast(me, SPELL_SEISMIC_SHARD_SUMMON_2); + DoCast(me, SPELL_SEISMIC_SHARD_SUMMON_3); + events.ScheduleEvent(EVENT_EARTH_FURY_PREPARE_SHARD, 6700); break; case POINT_GROUND: - DoCast(SPELL_EJECT_ALL_PASSENGERS); + DoCast(me, SPELL_EJECT_ALL_PASSENGERS); me->SetCanFly(false); me->SetDisableGravity(false); me->SetReactState(REACT_AGGRESSIVE); + DoStartMovement(me->GetVictim()); // Find more sniffs to correct these timers, this was copied from Reset() void. - events.ScheduleEvent(EVENT_CURSE_OF_BLOOD, 6000, EVENT_GROUP_PHASE_ONE); - events.ScheduleEvent(EVENT_FORCE_GRIP, urand(8000, 10000), EVENT_GROUP_PHASE_ONE); - events.ScheduleEvent(EVENT_SUMMON_GRAVITY_WELL, 16000, EVENT_GROUP_PHASE_ONE); + events.ScheduleEvent(EVENT_CURSE_OF_BLOOD, 6000); + events.ScheduleEvent(EVENT_FORCE_GRIP, urand(8000, 10000)); + events.ScheduleEvent(EVENT_SUMMON_GRAVITY_WELL, 16000); break; default: break; @@ -244,20 +219,20 @@ class boss_high_priestess_azil : public CreatureScript case EVENT_CURSE_OF_BLOOD: if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true)) DoCast(target, SPELL_CURSE_OF_BLOOD); - events.ScheduleEvent(EVENT_CURSE_OF_BLOOD, urand(13000, 15000), EVENT_GROUP_PHASE_ONE); + events.ScheduleEvent(EVENT_CURSE_OF_BLOOD, urand(13000, 15000)); break; case EVENT_FORCE_GRIP: DoCastVictim(SPELL_FORCE_GRIP); - events.ScheduleEvent(EVENT_CURSE_OF_BLOOD, urand(13000, 15000), EVENT_GROUP_PHASE_ONE); + events.ScheduleEvent(EVENT_CURSE_OF_BLOOD, urand(13000, 15000)); break; case EVENT_SUMMON_GRAVITY_WELL: if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true)) DoCast(target, SPELL_SUMMON_GRAVITY_WELL); - events.ScheduleEvent(EVENT_SUMMON_GRAVITY_WELL, urand(13000, 15000), EVENT_GROUP_PHASE_ONE); + events.ScheduleEvent(EVENT_SUMMON_GRAVITY_WELL, urand(13000, 15000)); break; case EVENT_ENERGY_SHIELD: - events.CancelEventGroup(EVENT_GROUP_PHASE_ONE); - DoCast(SPELL_EARTH_FURY_ENERGY_SHIELD); + events.Reset(); + DoCast(me, SPELL_EARTH_FURY_ENERGY_SHIELD); events.ScheduleEvent(EVENT_EARTH_FURY, 0); break; case EVENT_EARTH_FURY: @@ -273,9 +248,8 @@ class boss_high_priestess_azil : public CreatureScript case EVENT_EARTH_FURY_FLY_ABOVE_PLATFORM: me->GetMotionMaster()->MovePoint(POINT_ABOVE_PLATFORM, AbovePlatformPos); break; - case EVENT_EARTH_FURY_CHECK_SEAT0: - if (!me->GetVehicleKit()->GetPassenger(0)) - DoCast(SPELL_SEISMIC_SHARD_PREPARE); + case EVENT_EARTH_FURY_PREPARE_SHARD: + DoCast(me, SPELL_SEISMIC_SHARD_PREPARE); events.ScheduleEvent(EVENT_EARTH_FURY_LAUNCH_SHARD, 1800); break; case EVENT_EARTH_FURY_LAUNCH_SHARD: @@ -283,10 +257,10 @@ class boss_high_priestess_azil : public CreatureScript { me->SetFacingToObject(target); DoCast(target, SPELL_SEISMIC_SHARD_TARGETING); - DoCast(SPELL_SEISMIC_SHARD_LAUNCH); + DoCast(me, SPELL_SEISMIC_SHARD_LAUNCH); countSeismicShard -= 1; } - events.ScheduleEvent(countSeismicShard > 0 ? EVENT_EARTH_FURY_CHECK_SEAT0 : EVENT_EARTH_FURY_FLY_DOWN, 4800); + events.ScheduleEvent(countSeismicShard > 0 ? EVENT_EARTH_FURY_PREPARE_SHARD : EVENT_EARTH_FURY_FLY_DOWN, 4800); break; case EVENT_EARTH_FURY_FLY_DOWN: { @@ -369,8 +343,7 @@ public: { npc_gravity_wellAI(Creature* creature) : ScriptedAI(creature) { - me->SetReactState(REACT_PASSIVE); - DoCast(SPELL_GRAVITY_WELL_VISUAL); + DoCast(me, SPELL_GRAVITY_WELL_VISUAL); events.ScheduleEvent(EVENT_GRAVITY_WELL_AURA_DAMAGE, 3200); events.ScheduleEvent(EVENT_GRAVITY_WELL_AURA_PULL, 4500); if (!me->GetMap()->IsHeroic()) @@ -397,10 +370,10 @@ public: { case EVENT_GRAVITY_WELL_AURA_DAMAGE: me->RemoveAurasDueToSpell(SPELL_GRAVITY_WELL_VISUAL); - DoCast(SPELL_GRAVITY_WELL_AURA_DAMAGE); + DoCast(me, SPELL_GRAVITY_WELL_AURA_DAMAGE); break; case EVENT_GRAVITY_WELL_AURA_PULL: - DoCast(SPELL_GRAVITY_WELL_AURA_PULL); + DoCast(me, SPELL_GRAVITY_WELL_AURA_PULL); break; default: break; @@ -430,8 +403,7 @@ public: { instance = creature->GetInstanceScript(); me->SetDisableGravity(true); - me->SetReactState(REACT_PASSIVE); - DoCast(SPELL_SEISMIC_SHARD_VISUAL); + DoCast(me, SPELL_SEISMIC_SHARD_VISUAL); Movement::MoveSplineInit init(me); FillPath(me->GetPosition(), init.Path()); @@ -512,7 +484,7 @@ public: void HandleScript(SpellEffIndex /*effIndex*/) { Unit* caster = GetCaster(); - for (uint8 i = 0; i < 3; i++) + for (int i = 0; i < 3; i++) caster->CastSpell(caster, SPELL_SUMMON_ADD_SOUTH, true); } @@ -548,7 +520,7 @@ public: void HandleScript(SpellEffIndex /*effIndex*/) { Unit* caster = GetCaster(); - for (uint8 i = 0; i < 10; i++) + for (int i = 0; i < 10; i++) caster->CastSpell(caster, SPELL_SUMMON_ADD_WEST, true); } @@ -652,15 +624,6 @@ public: }; // 79332 - Gravity Well (pull units within 10 yards) -class PulledRecentlyCheck -{ -public: - bool operator()(WorldObject* object) const - { - return (object->ToUnit() && object->ToUnit()->HasAura(SPELL_GRAVITY_WELL_PULL)); - } -}; - class spell_gravity_well_pull : public SpellScriptLoader { public: @@ -675,15 +638,9 @@ public: GetSpell()->SetSpellValue(SPELLVALUE_RADIUS_MOD, int32(GetCaster()->GetObjectScale() * 10000 * 2 / 3)); } - void FilterTargets(std::list<WorldObject*>& unitList) - { - unitList.remove_if(PulledRecentlyCheck()); - } - void Register() override { BeforeCast += SpellCastFn(spell_gravity_well_pull_SpellScript::SetRadiusMod); - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_gravity_well_pull_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); } }; @@ -693,34 +650,7 @@ public: } }; -// 86862 - Seismic Shard (forces target to cast 86863) -class spell_seismic_shard_prepare : public SpellScriptLoader -{ -public: - spell_seismic_shard_prepare() : SpellScriptLoader("spell_seismic_shard_prepare") { } - - class spell_seismic_shard_prepare_SpellScript : public SpellScript - { - PrepareSpellScript(spell_seismic_shard_prepare_SpellScript); - - void SetTarget(WorldObject*& target) - { - target = GetCaster()->FindNearestCreature(NPC_SEISMIC_SHARD, 50.0f); - } - - void Register() override - { - OnObjectTargetSelect += SpellObjectTargetSelectFn(spell_seismic_shard_prepare_SpellScript::SetTarget, EFFECT_0, TARGET_UNIT_NEARBY_ENTRY); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_seismic_shard_prepare_SpellScript(); - } -}; - -// 86863 - Seismic Shard (moves shard to seat 0) +// 86863 - Seismic Shard (makes shard reenter Azil) class spell_seismic_shard_change_seat : public SpellScriptLoader { public: @@ -730,23 +660,14 @@ public: { PrepareSpellScript(spell_seismic_shard_change_seat_SpellScript); - void SetTarget(WorldObject*& target) - { - if (InstanceScript* instance = GetCaster()->GetInstanceScript()) - target = ObjectAccessor::GetCreature(*GetCaster(), instance->GetGuidData(DATA_HIGH_PRIESTESS_AZIL)); - } - - void ChangeSeat(SpellEffIndex /*effIndex*/) + void ExitVehicle() { GetCaster()->ExitVehicle(); - if (GetHitUnit()->IsVehicle()) - GetCaster()->EnterVehicle(GetHitUnit(), 0); } void Register() override { - OnObjectTargetSelect += SpellObjectTargetSelectFn(spell_seismic_shard_change_seat_SpellScript::SetTarget, EFFECT_0, TARGET_UNIT_NEARBY_ENTRY); - OnEffectHitTarget += SpellEffectFn(spell_seismic_shard_change_seat_SpellScript::ChangeSeat, EFFECT_0, SPELL_EFFECT_APPLY_AURA); + BeforeCast += SpellCastFn(spell_seismic_shard_change_seat_SpellScript::ExitVehicle); } }; @@ -800,7 +721,6 @@ void AddSC_boss_high_priestess_azil() new spell_gravity_well_damage_nearby(); new spell_gravity_well_damage(); new spell_gravity_well_pull(); - new spell_seismic_shard_prepare(); new spell_seismic_shard_change_seat(); new spell_seismic_shard(); } diff --git a/src/server/scripts/Maelstrom/Stonecore/boss_ozruk.cpp b/src/server/scripts/Maelstrom/Stonecore/boss_ozruk.cpp index 0b260de944a..b5448acb556 100644 --- a/src/server/scripts/Maelstrom/Stonecore/boss_ozruk.cpp +++ b/src/server/scripts/Maelstrom/Stonecore/boss_ozruk.cpp @@ -89,8 +89,6 @@ class boss_ozruk : public CreatureScript events.ScheduleEvent(EVENT_ELEMENTIUM_BULWARK, 5000); events.ScheduleEvent(EVENT_GROUND_SLAM, 10000); events.ScheduleEvent(EVENT_ELEMENTIUM_SPIKE_SHIELD, 13000); - - RemoveBouncerSpikes(); } void EnterCombat(Unit* /*victim*/) override @@ -102,12 +100,13 @@ class boss_ozruk : public CreatureScript void JustSummoned(Creature* summon) override { - if (summon->GetEntry() != NPC_RUPTURE_CONTROLLER) - return; + if (summon->GetEntry() == NPC_RUPTURE_CONTROLLER) + { + summon->CastSpell(summon, SPELL_RUPTURE, true); + summon->DespawnOrUnsummon(10000); + } - summon->SetReactState(REACT_PASSIVE); - summon->CastSpell(summon, SPELL_RUPTURE, true); - summon->DespawnOrUnsummon(10000); + BossAI::JustSummoned(summon); } void DamageTaken(Unit* /*attacker*/, uint32 &damage) override @@ -116,14 +115,14 @@ class boss_ozruk : public CreatureScript return; DoCast(me, SPELL_ENRAGE); - me->Say(SAY_ENRAGE); + Talk(SAY_ENRAGE); } - void JustDied(Unit* killer) override + void JustDied(Unit* /*killer*/) override { - me->Say(SAY_DEATH, killer); // receiver is the killer, sniff source! + _JustDied(); - RemoveBouncerSpikes(); + Talk(SAY_DEATH); } void UpdateAI(uint32 diff) override @@ -133,7 +132,7 @@ class boss_ozruk : public CreatureScript events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING) || me->HasAura(SPELL_ELEMENTIUM_SPIKE_SHIELD)) + if (me->HasUnitState(UNIT_STATE_CASTING)) return; while (uint32 eventId = events.ExecuteEvent()) @@ -156,7 +155,7 @@ class boss_ozruk : public CreatureScript events.ScheduleEvent(EVENT_SHATTER, 10000); break; case EVENT_SHATTER: - RemoveBouncerSpikes(); + summons.DespawnEntry(NPC_BOUNCER_SPIKE); me->SetReactState(REACT_PASSIVE); me->AttackStop(); DoCast(me, SPELL_SHATTER); @@ -168,6 +167,7 @@ class boss_ozruk : public CreatureScript break; case EVENT_START_ATTACK: me->SetReactState(REACT_AGGRESSIVE); + DoStartMovement(me->GetVictim()); break; default: break; @@ -176,18 +176,6 @@ class boss_ozruk : public CreatureScript DoMeleeAttackIfReady(); } - - void RemoveBouncerSpikes() - { - Vehicle* vehicle = me->GetVehicleKit(); - if (!vehicle) - return; - - for (uint8 i = 0; i < vehicle->GetAvailableSeatCount(); i++) - if (Unit* passenger = vehicle->GetPassenger(i)) - if (Creature* creature = passenger->ToCreature()) - creature->RemoveFromWorld(); - } }; CreatureAI* GetAI(Creature* creature) const override @@ -229,7 +217,6 @@ public: if (!rupture) return; - rupture->SetReactState(REACT_PASSIVE); rupture->CastSpell(rupture, SPELL_RUPTURE_DAMAGE, true); } diff --git a/src/server/scripts/Maelstrom/Stonecore/boss_slabhide.cpp b/src/server/scripts/Maelstrom/Stonecore/boss_slabhide.cpp index c5f0718e240..0b5b3e153be 100644 --- a/src/server/scripts/Maelstrom/Stonecore/boss_slabhide.cpp +++ b/src/server/scripts/Maelstrom/Stonecore/boss_slabhide.cpp @@ -199,7 +199,7 @@ class boss_slabhide : public CreatureScript events.ScheduleEvent(EVENT_STALACTITE, 400); break; case POINT_SLABHIDE_LAND: - //DoCast(SPELL_COOLDOWN_5S); // unknown purpose + //DoCast(me, SPELL_COOLDOWN_5S); // unknown purpose events.ScheduleEvent(EVENT_ATTACK, 1200); break; default: @@ -225,11 +225,12 @@ class boss_slabhide : public CreatureScript instance->SetData(DATA_SLABHIDE_ROCK_WALL, false); break; case EVENT_LAVA_FISSURE: - DoCast(SPELL_LAVA_FISSURE); + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true)) + DoCast(target, SPELL_LAVA_FISSURE); events.ScheduleEvent(EVENT_LAVA_FISSURE, urand(6000, 8000)); break; case EVENT_SAND_BLAST: - DoCast(SPELL_SAND_BLAST); + DoCast(me, SPELL_SAND_BLAST); events.ScheduleEvent(EVENT_SAND_BLAST, urand(8000, 11000)); break; case EVENT_AIR_PHASE: @@ -248,7 +249,7 @@ class boss_slabhide : public CreatureScript me->SetByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_HOVER); me->SetHover(true); - DoCast(SPELL_STALACTITE_SUMMON); + DoCast(me, SPELL_STALACTITE_SUMMON); events.ScheduleEvent(EVENT_LAND, 8000); break; @@ -267,7 +268,7 @@ class boss_slabhide : public CreatureScript events.ScheduleEvent(EVENT_LAVA_FISSURE, urand(6000, 8000)); events.ScheduleEvent(EVENT_SAND_BLAST, urand(8000, 10000)); - DoCast(SPELL_CRYSTAL_STORM); + DoCast(me, SPELL_CRYSTAL_STORM); me->SetReactState(REACT_AGGRESSIVE); break; default: @@ -315,8 +316,7 @@ public: { npc_lava_fissureAI(Creature* creature) : ScriptedAI(creature) { - me->SetReactState(REACT_PASSIVE); - me->CastSpell(me, SPELL_LAVA_FISSURE_CRACK, true); + DoCast(me, SPELL_LAVA_FISSURE_CRACK, true); events.ScheduleEvent(EVENT_LAVA_FISSURE_ERUPTION, 6000); } @@ -330,7 +330,7 @@ public: { case EVENT_LAVA_FISSURE_ERUPTION: me->RemoveAurasDueToSpell(SPELL_LAVA_FISSURE_CRACK); - me->CastSpell(me, SPELL_LAVA_FISSURE_ERUPTION, true); + DoCast(me, SPELL_LAVA_FISSURE_ERUPTION, true); me->DespawnOrUnsummon(14000); break; default: @@ -359,14 +359,16 @@ public: { npc_stalactite_triggerAI(Creature* creature) : ScriptedAI(creature) { - me->SetReactState(REACT_PASSIVE); me->SetDisableGravity(true); - me->CastSpell(me, SPELL_STALACTITE_SHADE, true); + DoCast(me, SPELL_STALACTITE_SHADE, true); events.ScheduleEvent(EVENT_STALACTITE_MISSLE, 5600); } void UpdateAI(uint32 diff) override { + if (events.Empty()) + return; + events.Update(diff); while (uint32 eventId = events.ExecuteEvent()) @@ -374,7 +376,7 @@ public: switch (eventId) { case EVENT_STALACTITE_MISSLE: - DoCast(SPELL_STALACTITE_MISSLE); + DoCast(me, SPELL_STALACTITE_MISSLE); me->DespawnOrUnsummon(11000); break; default: @@ -394,15 +396,6 @@ public: }; // 81035 - Stalactite (check if player is near to summon stalactite) -class NotPlayerCheck -{ - public: - bool operator()(WorldObject* object) const - { - return (object->GetTypeId() != TYPEID_PLAYER); - } -}; - class spell_s81035_stalactite : public SpellScriptLoader { public: @@ -412,11 +405,6 @@ public: { PrepareSpellScript(spell_s81035_stalactite_SpellScript); - void FilterTargets(std::list<WorldObject*>& targets) - { - targets.remove_if(NotPlayerCheck()); - } - void SummonStalactiteTrigger() { Unit* caster = GetCaster(); @@ -425,7 +413,6 @@ public: void Register() override { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_s81035_stalactite_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); OnHit += SpellHitFn(spell_s81035_stalactite_SpellScript::SummonStalactiteTrigger); } }; diff --git a/src/server/scripts/Maelstrom/Stonecore/instance_stonecore.cpp b/src/server/scripts/Maelstrom/Stonecore/instance_stonecore.cpp index b2ac21a1edf..1e68c612616 100644 --- a/src/server/scripts/Maelstrom/Stonecore/instance_stonecore.cpp +++ b/src/server/scripts/Maelstrom/Stonecore/instance_stonecore.cpp @@ -34,6 +34,17 @@ // TO-DO: // - Find out spell IDs for both Stonecore Teleporters (spellclick). +ObjectData const creatureData[] = +{ + { NPC_MILLHOUSE_MANASTORM, DATA_MILLHOUSE_MANASTORM }, + { NPC_CORBORUS, DATA_CORBORUS }, + { NPC_SLABHIDE, DATA_SLABHIDE }, + { NPC_HIGH_PRIESTESS_AZIL, DATA_HIGH_PRIESTESS_AZIL }, + { NPC_STONECORE_TELEPORTER, DATA_STONECORE_TELEPORTER }, + { NPC_STONECORE_TELEPORTER_2, DATA_STONECORE_TELEPORTER_2 }, + { 0, 0 } // END +}; + class instance_stonecore : public InstanceMapScript { public: @@ -45,6 +56,7 @@ class instance_stonecore : public InstanceMapScript { SetHeaders(DataHeader); SetBossNumber(MAX_ENCOUNTER); + LoadObjectData(creatureData, nullptr); } void OnGameObjectCreate(GameObject* go) override @@ -67,27 +79,10 @@ class instance_stonecore : public InstanceMapScript { switch (creature->GetEntry()) { - case NPC_MILLHOUSE_MANASTORM: - millhouseGUID = creature->GetGUID(); - break; - case NPC_CORBORUS: - corobrusGUID = creature->GetGUID(); - break; - case NPC_SLABHIDE: - slabhideGUID = creature->GetGUID(); - break; - case NPC_HIGH_PRIESTESS_AZIL: - highPriestessAzilGUID = creature->GetGUID(); - break; case NPC_STONECORE_TELEPORTER: case NPC_STONECORE_TELEPORTER_2: - if (GetBossState(DATA_SLABHIDE) != DONE) - stonecoreTeleporterGUID[creature->GetEntry() - NPC_STONECORE_TELEPORTER] = creature->GetGUID(); - else // If Slabhide is already dead, no need to store teleporter guids - { - creature->CastSpell(creature, SPELL_TELEPORTER_ACTIVE_VISUAL, true); - creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK); - } + if (GetBossState(DATA_SLABHIDE) == DONE) + ActivateTeleporter(creature); break; default: break; @@ -95,7 +90,7 @@ class instance_stonecore : public InstanceMapScript // Check if creature is part of Millhouse event creature->SearchFormation(); - if (CreatureGroup* group = creature->GetFormation()) // TO-DO: Fix formations + if (CreatureGroup* group = creature->GetFormation()) { switch (group->GetId()) { @@ -109,6 +104,8 @@ class instance_stonecore : public InstanceMapScript break; } } + + InstanceScript::OnCreatureCreate(creature); } bool SetBossState(uint32 type, EncounterState state) override @@ -123,16 +120,9 @@ class instance_stonecore : public InstanceMapScript // Activate teleporters if (state == DONE) { - for (int8 i = 0; i < MAX_STONECORE_TELEPORTERS; i++) - { - if (Creature* teleporter = instance->GetCreature(stonecoreTeleporterGUID[i])) - { - teleporter->CastSpell(teleporter, SPELL_TELEPORTER_ACTIVE_VISUAL, true); - teleporter->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK); - } - } + ActivateTeleporter(GetCreature(DATA_STONECORE_TELEPORTER)); + ActivateTeleporter(GetCreature(DATA_STONECORE_TELEPORTER_2)); } - break; default: break; @@ -158,6 +148,9 @@ class instance_stonecore : public InstanceMapScript { switch (type) { + case DATA_HANDLE_CORBORUS_ROCKDOOR: + HandleGameObject(corborusRockDoorGUID, true); + break; case DATA_MILLHOUSE_EVENT_FACE: MillhouseEvent_Face(); break; @@ -179,35 +172,11 @@ class instance_stonecore : public InstanceMapScript } } - ObjectGuid GetGuidData(uint32 type) const override - { - switch (type) - { - case DATA_MILLHOUSE_MANASTORM: - return millhouseGUID; - case GAMEOBJECT_CORBORUS_ROCKDOOR: - return corborusRockDoorGUID; - case DATA_CORBORUS: - return corobrusGUID; - case DATA_SLABHIDE: - return slabhideGUID; - case DATA_HIGH_PRIESTESS_AZIL: - return highPriestessAzilGUID; - case NPC_STONECORE_TELEPORTER: - case NPC_STONECORE_TELEPORTER_2: - return stonecoreTeleporterGUID[type - NPC_STONECORE_TELEPORTER]; - default: - break; - } - - return ObjectGuid::Empty; - } - private: // Face Millhouse and other nearby mobs to Corborus void MillhouseEvent_Face() { - if (Creature* Millhouse = instance->GetCreature(millhouseGUID)) + if (Creature* Millhouse = GetCreature(DATA_MILLHOUSE_MANASTORM)) Millhouse->SetFacingTo(1.570796f); for (GuidVector::const_iterator i = millhouseLastGroupGUIDs.begin(); i != millhouseLastGroupGUIDs.end(); ++i) if (Creature* creature = instance->GetCreature(*i)) @@ -217,7 +186,7 @@ class instance_stonecore : public InstanceMapScript // Knock back Millhouse and other mobs void MillhouseEvent_Knockback() { - if (Creature* Millhouse = instance->GetCreature(millhouseGUID)) + if (Creature* Millhouse = GetCreature(DATA_MILLHOUSE_MANASTORM)) Millhouse->CastSpell(Millhouse, SPELL_RING_WYRM_KNOCKBACK, true); for (GuidVector::const_iterator itr = millhouseLastGroupGUIDs.begin(); itr != millhouseLastGroupGUIDs.end(); ++itr) if (Creature* creature = instance->GetCreature(*itr)) @@ -227,7 +196,7 @@ class instance_stonecore : public InstanceMapScript // Despawn all mobs void MillhouseEvent_Despawn() { - if (Creature* Millhouse = instance->GetCreature(millhouseGUID)) + if (Creature* Millhouse = GetCreature(DATA_MILLHOUSE_MANASTORM)) Millhouse->DespawnOrUnsummon(3000); for (GuidVector::const_iterator itr = millhouseTrashGUIDs.begin(); itr != millhouseTrashGUIDs.end(); ++itr) if (Creature* creature = instance->GetCreature(*itr)) @@ -237,14 +206,18 @@ class instance_stonecore : public InstanceMapScript creature->DespawnOrUnsummon(3000); } - ObjectGuid millhouseGUID; + void ActivateTeleporter(Creature* teleporter) + { + if (!teleporter) + return; + + teleporter->CastSpell(teleporter, SPELL_TELEPORTER_ACTIVE_VISUAL, true); + teleporter->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK); + } + GuidVector millhouseTrashGUIDs; GuidVector millhouseLastGroupGUIDs; ObjectGuid corborusRockDoorGUID; - ObjectGuid corobrusGUID; - ObjectGuid slabhideGUID; - ObjectGuid highPriestessAzilGUID; - ObjectGuid stonecoreTeleporterGUID[2]; GuidVector slabhideRockWallGUIDs; EncounterState slabhideIntro; diff --git a/src/server/scripts/Maelstrom/Stonecore/stonecore.cpp b/src/server/scripts/Maelstrom/Stonecore/stonecore.cpp index abfb82284d3..3500290c358 100644 --- a/src/server/scripts/Maelstrom/Stonecore/stonecore.cpp +++ b/src/server/scripts/Maelstrom/Stonecore/stonecore.cpp @@ -30,11 +30,6 @@ enum Texts SAY_MILLHOUSE_EVENT_2 = 1, }; -enum NPCs -{ - NPC_GENERIC_TRIGGER_LAB = 40350, -}; - enum Spells { // Millhouse Manastorm @@ -96,6 +91,9 @@ Position const MillhousePointGroup2 = { 977.3045f, 895.2347f, 306.3298f }; Position const MillhousePointGroup3 = { 1049.823f, 871.4349f, 295.006f }; Position const MillhousePointGroup4 = { 1149.04f, 884.431f, 284.9406f }; +// TO-DO: +// - Millhouse Manastorm should face and cast SPELL_TIGULE_AND_FORORS_SPECIAL_BLEND, but he won't. :( + // 43391 - Millhouse Manastorm class npc_sc_millhouse_manastorm : public CreatureScript { @@ -175,27 +173,28 @@ class npc_sc_millhouse_manastorm : public CreatureScript me->CombatStop(true); me->DeleteThreatList(); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT); - me->SetReactState(REACT_AGGRESSIVE); - switch (pointId) { case POINT_MILLHOUSE_GROUP_2: - if (Creature* worldtrigger = me->FindNearestCreature(NPC_WORLDTRIGGER, 150.0f)) - me->SetFacingToObject(worldtrigger); // o: 5.497359f (sniff data) - me->CastSpell(me, SPELL_ANCHOR_HERE, true); - me->AddAura(SPELL_TIGULE_AND_FORORS_SPECIAL_BLEND, me); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT); + me->SetReactState(REACT_AGGRESSIVE); + if (Creature* worldtrigger = me->FindNearestCreature(NPC_WORLDTRIGGER, 200.0f)) + me->SetFacingToObject(worldtrigger); + DoCast(me, SPELL_ANCHOR_HERE); + DoCast(me, SPELL_TIGULE_AND_FORORS_SPECIAL_BLEND); events.ScheduleEvent(EVENT_READY_FOR_COMBAT, 10000); break; case POINT_MILLHOUSE_GROUP_3: + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT); + me->SetReactState(REACT_AGGRESSIVE); me->SetFacingTo(5.931499f); - me->CastSpell(me, SPELL_ANCHOR_HERE, true); - me->AddAura(SPELL_TIGULE_AND_FORORS_SPECIAL_BLEND, me); + DoCast(me, SPELL_ANCHOR_HERE); + DoCast(me, SPELL_TIGULE_AND_FORORS_SPECIAL_BLEND); events.ScheduleEvent(EVENT_READY_FOR_COMBAT, 10000); break; case POINT_MILLHOUSE_GROUP_4: me->SetFacingTo(3.455752f); - me->CastSpell(me, SPELL_ANCHOR_HERE, true); + DoCast(me, SPELL_ANCHOR_HERE); Talk(SAY_MILLHOUSE_EVENT_2); events.ScheduleEvent(EVENT_CAST_IMPENDING_DOOM, 1000); break; @@ -206,14 +205,14 @@ class npc_sc_millhouse_manastorm : public CreatureScript void UpdateAI(uint32 diff) override { - // Only update events if Millhouse is aggressive - if (me->GetReactState() != REACT_AGGRESSIVE) + // Do not update events if Millhouse is aggressive and has no combat. + if (!UpdateVictim() && me->GetReactState() == REACT_AGGRESSIVE) return; events.Update(diff); // Impending Doom is exception because it needs to be interrupted. - if (me->HasUnitState(UNIT_STATE_CASTING) && me->GetCurrentSpell(CURRENT_GENERIC_SPELL)->GetSpellInfo()->Id != SPELL_IMPENDING_DOOM_CHANNEL) + if (me->HasUnitState(UNIT_STATE_CASTING) && !me->FindCurrentSpellBySpellId(SPELL_IMPENDING_DOOM)) return; while (uint32 eventId = events.ExecuteEvent()) @@ -221,7 +220,7 @@ class npc_sc_millhouse_manastorm : public CreatureScript switch (eventId) { case EVENT_FROSTBOLT_VOLLEY: - DoCast(SPELL_FROSTBOLT_VOLLEY); + DoCastAOE(SPELL_FROSTBOLT_VOLLEY); events.ScheduleEvent(EVENT_FROSTBOLT_VOLLEY, 7000); break; case EVENT_SHADOWFURY: @@ -240,8 +239,8 @@ class npc_sc_millhouse_manastorm : public CreatureScript ScheduleEvents(); break; case EVENT_CAST_IMPENDING_DOOM: - DoCast(SPELL_IMPENDING_DOOM); - DoCast(SPELL_IMPENDING_DOOM_CHANNEL); + DoCast(me, SPELL_IMPENDING_DOOM); + DoCast(me, SPELL_IMPENDING_DOOM_CHANNEL); events.ScheduleEvent(EVENT_INTERRUPT_IMPENDING_DOOM, urand(15000,20000)); break; case EVENT_INTERRUPT_IMPENDING_DOOM: @@ -341,11 +340,6 @@ class spell_sc_twilight_documents : public SpellScriptLoader return true; } - void SetTarget(WorldObject*& target) - { - target = GetCaster()->FindNearestCreature(NPC_GENERIC_TRIGGER_LAB, 100.0f); - } - void SpawnGameObject(SpellEffIndex /*effIndex*/) { if (WorldLocation* loc = GetHitDest()) @@ -354,7 +348,6 @@ class spell_sc_twilight_documents : public SpellScriptLoader void Register() override { - OnObjectTargetSelect += SpellObjectTargetSelectFn(spell_sc_twilight_documents_SpellScript::SetTarget, EFFECT_0, TARGET_DEST_NEARBY_ENTRY); OnEffectHit += SpellEffectFn(spell_sc_twilight_documents_SpellScript::SpawnGameObject, EFFECT_0, SPELL_EFFECT_DUMMY); } }; @@ -372,7 +365,7 @@ class JumpCheck bool operator()(WorldObject* object) const { Player* player = object->ToPlayer(); - return (player && player->HasUnitState(UNIT_STATE_JUMPING)); + return (player && (player->IsFalling() || player->HasUnitState(UNIT_STATE_JUMPING))); } }; @@ -410,7 +403,7 @@ public: bool OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override { if (InstanceScript* instance = player->GetInstanceScript()) - if (Creature* corborus = ObjectAccessor::GetCreature(*player, instance->GetGuidData(DATA_CORBORUS))) + if (Creature* corborus = instance->GetCreature(DATA_CORBORUS)) corborus->AI()->DoAction(ACTION_CORBORUS_INTRO); return true; } @@ -424,7 +417,7 @@ public: bool OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override { if (InstanceScript* instance = player->GetInstanceScript()) - if (Creature* slabhide = ObjectAccessor::GetCreature(*player, instance->GetGuidData(DATA_SLABHIDE))) + if (Creature* slabhide = instance->GetCreature(DATA_SLABHIDE)) slabhide->AI()->DoAction(ACTION_SLABHIDE_INTRO); return true; } diff --git a/src/server/scripts/Maelstrom/Stonecore/stonecore.h b/src/server/scripts/Maelstrom/Stonecore/stonecore.h index 98a9878e0d3..a13a0efad58 100644 --- a/src/server/scripts/Maelstrom/Stonecore/stonecore.h +++ b/src/server/scripts/Maelstrom/Stonecore/stonecore.h @@ -32,11 +32,16 @@ enum DataTypes // Additional Data DATA_MILLHOUSE_MANASTORM, DATA_MILLHOUSE_EVENT_FACE, + DATA_HANDLE_CORBORUS_ROCKDOOR, DATA_MILLHOUSE_EVENT_KNOCKBACK, DATA_MILLHOUSE_EVENT_DESPAWN, DATA_SLABHIDE_INTRO, DATA_SLABHIDE_ROCK_WALL, + + // Teleporters + DATA_STONECORE_TELEPORTER, + DATA_STONECORE_TELEPORTER_2, }; enum Misc diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp index 645df91d8ea..1b6df68c5c4 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp @@ -362,7 +362,7 @@ class NecroticPlagueTargetCheck : public std::unary_function<Unit*, bool> bool operator()(Unit* unit) const { - if (!unit || unit == _sourceObj || !unit->isTargetableForAttack() || unit->IsTotem() || unit->HasAura(SPELL_PLAGUE_AVOIDANCE)) + if (!unit || unit == _sourceObj || !unit->isTargetableForAttack() || unit->GetTypeId() != TYPEID_PLAYER || unit->HasAura(SPELL_PLAGUE_AVOIDANCE)) return false; if ((_notAura1 && unit->HasAura(_notAura1)) || (_notAura2 && unit->HasAura(_notAura2))) return false; diff --git a/src/server/scripts/Northrend/Nexus/Nexus/boss_magus_telestra.cpp b/src/server/scripts/Northrend/Nexus/Nexus/boss_magus_telestra.cpp index 1df5f5eac8f..c12b0889f9d 100644 --- a/src/server/scripts/Northrend/Nexus/Nexus/boss_magus_telestra.cpp +++ b/src/server/scripts/Northrend/Nexus/Nexus/boss_magus_telestra.cpp @@ -19,6 +19,7 @@ #include "ScriptMgr.h" #include "ScriptedCreature.h" #include "nexus.h" +#include "GameEventMgr.h" enum Spells { diff --git a/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp b/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp index 0665bfd240c..ed902dba260 100644 --- a/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp +++ b/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp @@ -192,6 +192,7 @@ class instance_oculus : public InstanceMapScript FreeDragons(); if (Creature* varos = instance->GetCreature(VarosGUID)) varos->SetPhaseMask(1, true); + events.ScheduleEvent(EVENT_VAROS_INTRO, 15000); } break; case DATA_VAROS: @@ -209,6 +210,7 @@ class instance_oculus : public InstanceMapScript { eregos->SetPhaseMask(1, true); GreaterWhelps(); + events.ScheduleEvent(EVENT_EREGOS_INTRO, 5000); } } break; @@ -267,6 +269,28 @@ class instance_oculus : public InstanceMapScript } } + void Update(uint32 diff) override + { + events.Update(diff); + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_VAROS_INTRO: + if (Creature* varos = instance->GetCreature(VarosGUID)) + varos->AI()->Talk(SAY_VAROS_INTRO_TEXT); + break; + case EVENT_EREGOS_INTRO: + if (Creature* eregos = instance->GetCreature(EregosGUID)) + eregos->AI()->Talk(SAY_EREGOS_INTRO_TEXT); + break; + default: + break; + } + } + } + void GreaterWhelps() { for (ObjectGuid guid : GreaterWhelpList) @@ -289,6 +313,8 @@ class instance_oculus : public InstanceMapScript ObjectGuid EregosCacheGUID; GuidList GreaterWhelpList; + + EventMap events; }; InstanceScript* GetInstanceScript(InstanceMap* map) const override diff --git a/src/server/scripts/Northrend/Nexus/Oculus/oculus.h b/src/server/scripts/Northrend/Nexus/Oculus/oculus.h index 268cdb54e3e..d1144df9486 100644 --- a/src/server/scripts/Northrend/Nexus/Oculus/oculus.h +++ b/src/server/scripts/Northrend/Nexus/Oculus/oculus.h @@ -79,6 +79,18 @@ enum OculusSpells SPELL_DEATH_SPELL = 50415 }; +enum InstanceTexts +{ + SAY_EREGOS_INTRO_TEXT = 0, + SAY_VAROS_INTRO_TEXT = 4 +}; + +enum InstanceEvents +{ + EVENT_VAROS_INTRO = 1, + EVENT_EREGOS_INTRO +}; + enum Misc { POINT_MOVE_OUT = 1 diff --git a/src/server/scripts/Outland/zone_hellfire_peninsula.cpp b/src/server/scripts/Outland/zone_hellfire_peninsula.cpp index 79e6649495b..58f77f22f97 100644 --- a/src/server/scripts/Outland/zone_hellfire_peninsula.cpp +++ b/src/server/scripts/Outland/zone_hellfire_peninsula.cpp @@ -140,8 +140,8 @@ enum AncestralWolf { EMOTE_WOLF_LIFT_HEAD = 0, EMOTE_WOLF_HOWL = 1, - SAY_WOLF_WELCOME = 2, - SPELL_ANCESTRAL_WOLF_BUFF = 29981, + SAY_WOLF_WELCOME = 0, + SPELL_ANCESTRAL_WOLF_BUFF = 29938, NPC_RYGA = 17123 }; @@ -166,11 +166,16 @@ public: void Reset() override { ryga = NULL; + } + + // Override Evade Mode event, recast buff that was removed by standard handler + void EnterEvadeMode() override + { + npc_escortAI::EnterEvadeMode(); DoCast(me, SPELL_ANCESTRAL_WOLF_BUFF, true); } void MoveInLineOfSight(Unit* who) override - { if (!ryga && who->GetEntry() == NPC_RYGA && me->IsWithinDistInMap(who, 15.0f)) if (Creature* temp = who->ToCreature()) @@ -188,10 +193,48 @@ public: break; case 2: Talk(EMOTE_WOLF_HOWL); + DoCast(me, SPELL_ANCESTRAL_WOLF_BUFF, true); + break; + // Move Ryga into position + case 48: + if (Creature* ryga = me->FindNearestCreature(NPC_RYGA,70)) + { + if (ryga->IsAlive() && !ryga->IsInCombat()) + { + ryga->SetWalk(true); + ryga->SetSpeed(MOVE_WALK, 1.5f); + ryga->GetMotionMaster()->MovePoint(0, 517.340698f, 3885.03975f, 190.455978f, true); + Reset(); + } + } break; + // Ryga Kneels and welcomes spirit wolf case 50: - if (ryga && ryga->IsAlive() && !ryga->IsInCombat()) - ryga->AI()->Talk(SAY_WOLF_WELCOME); + if (Creature* ryga = me->FindNearestCreature(NPC_RYGA,70)) + { + if (ryga->IsAlive() && !ryga->IsInCombat()) + { + ryga->SetFacingTo(0.776773f); + ryga->SetStandState(UNIT_STAND_STATE_KNEEL); + ryga->AI()->Talk(SAY_WOLF_WELCOME); + Reset(); + } + } + break; + // Ryga returns to spawn point + case 51: + if (Creature* ryga = me->FindNearestCreature(NPC_RYGA,70)) + { + if (ryga->IsAlive() && !ryga->IsInCombat()) + { + float fRetX, fRetY, fRetZ, fRetO; + ryga->GetRespawnPosition(fRetX, fRetY, fRetZ, &fRetO); + ryga->SetHomePosition(fRetX, fRetY, fRetZ, fRetO); + ryga->SetStandState(UNIT_STAND_STATE_STAND); + ryga->GetMotionMaster()->MoveTargetedHome(); + Reset(); + } + } break; } } diff --git a/src/server/scripts/Spells/spell_mage.cpp b/src/server/scripts/Spells/spell_mage.cpp index baf7ff48cb5..fc8ba0ffad0 100644 --- a/src/server/scripts/Spells/spell_mage.cpp +++ b/src/server/scripts/Spells/spell_mage.cpp @@ -392,7 +392,7 @@ class spell_mage_cone_of_cold : public SpellScriptLoader }; // 42955 Conjure Refreshment -/// Updated 4.3.4 +/// Updated 6.0.3 struct ConjureRefreshmentData { uint32 minLevel; @@ -400,7 +400,7 @@ struct ConjureRefreshmentData uint32 spellId; }; -uint8 const MAX_CONJURE_REFRESHMENT_SPELLS = 7; +uint8 const MAX_CONJURE_REFRESHMENT_SPELLS = 9; ConjureRefreshmentData const _conjureData[MAX_CONJURE_REFRESHMENT_SPELLS] = { { 33, 43, 92739 }, @@ -409,7 +409,9 @@ ConjureRefreshmentData const _conjureData[MAX_CONJURE_REFRESHMENT_SPELLS] = { 64, 73, 92805 }, { 74, 79, 74625 }, { 80, 84, 92822 }, - { 85, 85, 92727 } + { 85, 89, 92727 }, + { 90, 99, 116130 }, + { 100, 100, 167143 } }; // 42955 - Conjure Refreshment @@ -462,6 +464,66 @@ class spell_mage_conjure_refreshment : public SpellScriptLoader } }; +uint8 const MAX_CONJURE_REFRESHMENT_TABLE_SPELLS = 5; +ConjureRefreshmentData const _conjureTableData[MAX_CONJURE_REFRESHMENT_TABLE_SPELLS] = +{ + { 73, 79, 120056 }, + { 80, 84, 120055 }, + { 85, 89, 120054 }, + { 90, 99, 120053 }, + { 100, 100, 167145 } +}; + +// 43987 - Conjure Refreshment Table +class spell_mage_conjure_refreshment_table : public SpellScriptLoader +{ +public: + spell_mage_conjure_refreshment_table() : SpellScriptLoader("spell_mage_conjure_refreshment_table") { } + + class spell_mage_conjure_refreshment_table_SpellScript : public SpellScript + { + PrepareSpellScript(spell_mage_conjure_refreshment_table_SpellScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + for (uint8 i = 0; i < MAX_CONJURE_REFRESHMENT_TABLE_SPELLS; ++i) + if (!sSpellMgr->GetSpellInfo(_conjureTableData[i].spellId)) + return false; + return true; + } + + bool Load() override + { + if (GetCaster()->GetTypeId() != TYPEID_PLAYER) + return false; + return true; + } + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + uint8 level = GetHitUnit()->getLevel(); + for (uint8 i = 0; i < MAX_CONJURE_REFRESHMENT_TABLE_SPELLS; ++i) + { + ConjureRefreshmentData const& spellData = _conjureTableData[i]; + if (level < spellData.minLevel || level > spellData.maxLevel) + continue; + GetHitUnit()->CastSpell(GetHitUnit(), spellData.spellId); + break; + } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_mage_conjure_refreshment_table_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_mage_conjure_refreshment_table_SpellScript(); + } +}; + // 543 - Fire War class spell_mage_fire_frost_ward : public SpellScriptLoader { @@ -1468,6 +1530,7 @@ void AddSC_mage_spell_scripts() new spell_mage_cold_snap(); new spell_mage_cone_of_cold(); new spell_mage_conjure_refreshment(); + new spell_mage_conjure_refreshment_table(); new spell_mage_fire_frost_ward(); new spell_mage_focus_magic(); new spell_mage_frostbolt(); diff --git a/src/server/scripts/Spells/spell_quest.cpp b/src/server/scripts/Spells/spell_quest.cpp index e9cf81f2e64..434a8f75693 100644 --- a/src/server/scripts/Spells/spell_quest.cpp +++ b/src/server/scripts/Spells/spell_quest.cpp @@ -1076,9 +1076,12 @@ class spell_q14112_14145_chum_the_water: public SpellScriptLoader // http://old01.wowhead.com/quest=9452 - Red Snapper - Very Tasty! enum RedSnapperVeryTasty { - SPELL_CAST_NET = 29866, - ITEM_RED_SNAPPER = 23614, - SPELL_NEW_SUMMON_TEST = 49214, + ITEM_RED_SNAPPER = 23614, + + SPELL_CAST_NET = 29866, + SPELL_NEW_SUMMON_TEST = 49214, + + GO_SCHOOL_OF_RED_SNAPPER = 181616 }; class spell_q9452_cast_net: public SpellScriptLoader @@ -1095,6 +1098,15 @@ class spell_q9452_cast_net: public SpellScriptLoader return GetCaster()->GetTypeId() == TYPEID_PLAYER; } + SpellCastResult CheckCast() + { + GameObject* go = GetCaster()->FindNearestGameObject(GO_SCHOOL_OF_RED_SNAPPER, 3.0f); + if (!go || go->GetRespawnTime()) + return SPELL_FAILED_REQUIRES_SPELL_FOCUS; + + return SPELL_CAST_OK; + } + void HandleDummy(SpellEffIndex /*effIndex*/) { Player* caster = GetCaster()->ToPlayer(); @@ -1104,9 +1116,19 @@ class spell_q9452_cast_net: public SpellScriptLoader caster->CastSpell(caster, SPELL_NEW_SUMMON_TEST, true); } + void HandleActiveObject(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); + GetHitGObj()->SetRespawnTime(roll_chance_i(50) ? 2 * MINUTE : 3 * MINUTE); + GetHitGObj()->Use(GetCaster()); + GetHitGObj()->SetLootState(GO_JUST_DEACTIVATED); + } + void Register() override { + OnCheckCast += SpellCheckCastFn(spell_q9452_cast_net_SpellScript::CheckCast); OnEffectHit += SpellEffectFn(spell_q9452_cast_net_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + OnEffectHitTarget += SpellEffectFn(spell_q9452_cast_net_SpellScript::HandleActiveObject, EFFECT_1, SPELL_EFFECT_ACTIVATE_OBJECT); } }; diff --git a/src/server/shared/DataStores/DB2StorageLoader.h b/src/server/shared/DataStores/DB2StorageLoader.h index 4254fcc1121..cc9bfa76b5c 100644 --- a/src/server/shared/DataStores/DB2StorageLoader.h +++ b/src/server/shared/DataStores/DB2StorageLoader.h @@ -21,6 +21,8 @@ #include "Define.h" #include "Utilities/ByteConverter.h" #include <cassert> +#include <list> +#include "Common.h" class DB2FileLoader { diff --git a/src/server/shared/Networking/MessageBuffer.h b/src/server/shared/Networking/MessageBuffer.h index 2dcd4fbc161..a214af9d127 100644 --- a/src/server/shared/Networking/MessageBuffer.h +++ b/src/server/shared/Networking/MessageBuffer.h @@ -81,6 +81,14 @@ public: } } + // Ensures there's "some" free space, make sure to call Normalize() before this + void EnsureFreeSpace() + { + // Double the size of the buffer if it's already full + if (GetRemainingSpace() == 0) + _storage.resize(_storage.size() * 2); + } + void Write(void const* data, std::size_t size) { if (size) diff --git a/src/server/shared/Networking/Socket.h b/src/server/shared/Networking/Socket.h index 5c891d925fa..6c367e5649d 100644 --- a/src/server/shared/Networking/Socket.h +++ b/src/server/shared/Networking/Socket.h @@ -92,7 +92,8 @@ public: return; _readBuffer.Normalize(); - _socket.async_read_some(boost::asio::buffer(_readBuffer.GetWritePointer(), READ_BLOCK_SIZE), + _readBuffer.EnsureFreeSpace(); + _socket.async_read_some(boost::asio::buffer(_readBuffer.GetWritePointer(), _readBuffer.GetRemainingSpace()), std::bind(&Socket<T>::ReadHandlerInternal, this->shared_from_this(), std::placeholders::_1, std::placeholders::_2)); } diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index 11c081bea44..3e3a04aee55 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -3250,6 +3250,22 @@ Currency.StartHonorPoints = 0 Currency.MaxHonorPoints = 4000 # +# Currency.StartApexisCrystals +# Amount of Apexis Crystals that new players will start with +# Default: 0 (with precision) +# + +Currency.StartApexisCrystals = 0 + +# +# Currency.MaxApexisCrystals +# Amount Apexis Crystals a player can have +# Default: 20000 +# + +Currency.MaxApexisCrystals = 20000 + +# # Currency.StartJusticePoints # Amount of justice points that new players will start with # Default: 0 (with precision) |