aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2021-05-10 22:45:54 +0200
committerShauren <shauren.trinity@gmail.com>2021-05-10 22:45:54 +0200
commit6352a84bf78a7afe0ea8d8caccee33dd09e09694 (patch)
treefcc56a9d49d9725450860eaa5ad75b9d0fa1b2b7
parent5bef47f1e9bff85fa297d4d00f27772c47a747cc (diff)
Core/Quests: Unify quest objective updating into one function and replace iterating entire quest log to find objective with direct {type, id} lookup
-rw-r--r--src/server/game/Entities/Player/Player.cpp653
-rw-r--r--src/server/game/Entities/Player/Player.h14
-rw-r--r--src/server/game/Handlers/MiscHandler.cpp29
-rw-r--r--src/server/game/Quests/QuestDef.h39
4 files changed, 229 insertions, 506 deletions
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 54375f7136e..b01611ff9e3 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -15024,7 +15024,7 @@ bool Player::CanAddQuest(Quest const* quest, bool msg) const
return true;
}
-bool Player::CanCompleteQuest(uint32 quest_id)
+bool Player::CanCompleteQuest(uint32 quest_id, uint32 ignoredQuestObjectiveId /*= 0*/)
{
if (quest_id)
{
@@ -15049,6 +15049,9 @@ bool Player::CanCompleteQuest(uint32 quest_id)
{
for (QuestObjective const& obj : qInfo->GetObjectives())
{
+ if (ignoredQuestObjectiveId && obj.ID == ignoredQuestObjectiveId)
+ continue;
+
if (!(obj.Flags & QUEST_OBJECTIVE_FLAG_OPTIONAL) && !(obj.Flags & QUEST_OBJECTIVE_FLAG_PART_OF_PROGRESS_BAR))
{
if (!IsQuestObjectiveComplete(q_status.Slot, qInfo, obj))
@@ -15298,7 +15301,8 @@ void Player::AddQuest(Quest const* quest, Object* questGiver)
uint32 quest_id = quest->GetQuestId();
// if not exist then created with set uState == NEW and rewarded=false
- QuestStatusData& questStatusData = m_QuestStatus[quest_id];
+ auto questStatusItr = m_QuestStatus.emplace(quest_id, QuestStatusData{}).first;
+ QuestStatusData& questStatusData = questStatusItr->second;
QuestStatus oldStatus = questStatusData.Status;
// check for repeatable quests status reset
@@ -15309,6 +15313,7 @@ void Player::AddQuest(Quest const* quest, Object* questGiver)
for (QuestObjective const& obj : quest->GetObjectives())
{
+ m_questObjectiveStatus.emplace(std::make_pair(QuestObjectiveType(obj.Type), obj.ObjectID), QuestObjectiveStatusData { questStatusItr, &obj });
switch (obj.Type)
{
case QUEST_OBJECTIVE_MIN_REPUTATION:
@@ -16344,6 +16349,13 @@ void Player::RemoveActiveQuest(uint32 questId, bool update /*= true*/)
QuestStatusMap::iterator itr = m_QuestStatus.find(questId);
if (itr != m_QuestStatus.end())
{
+ for (auto objectiveItr = m_questObjectiveStatus.begin(); objectiveItr != m_questObjectiveStatus.end(); )
+ {
+ if (objectiveItr->second.QuestStatusItr == itr)
+ objectiveItr = m_questObjectiveStatus.erase(objectiveItr);
+ else
+ ++objectiveItr;
+ }
m_QuestStatus.erase(itr);
m_QuestStatusSave[questId] = QUEST_DELETE_SAVE_TYPE;
}
@@ -16732,82 +16744,31 @@ void Player::GroupEventHappens(uint32 questId, WorldObject const* pEventObject)
void Player::ItemAddedQuestCheck(uint32 entry, uint32 count)
{
- for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
- {
- uint32 questid = GetQuestSlotQuestId(i);
- if (questid == 0)
- continue;
-
- QuestStatusData& q_status = m_QuestStatus[questid];
-
- if (q_status.Status != QUEST_STATUS_INCOMPLETE)
- continue;
-
- Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid);
- if (!qInfo || !qInfo->HasQuestObjectiveType(QUEST_OBJECTIVE_ITEM))
- continue;
-
- for (QuestObjective const& obj : qInfo->GetObjectives())
- {
- if (obj.Type != QUEST_OBJECTIVE_ITEM || !IsQuestObjectiveCompletable(i, qInfo, obj))
- continue;
-
- if (uint32(obj.ObjectID) != entry)
- continue;
-
- uint32 reqItemCount = obj.Amount;
- uint32 curItemCount = GetQuestSlotObjectiveData(i, obj);
- if (curItemCount < reqItemCount)
- {
- uint32 newItemCount = std::min<uint32>(curItemCount + count, reqItemCount);
- SetQuestObjectiveData(obj, newItemCount);
- }
-
- if (CanCompleteQuest(questid))
- CompleteQuest(questid);
-
- break;
- }
- }
-
- UpdateForQuestWorldObjects();
+ UpdateQuestObjectiveProgress(QUEST_OBJECTIVE_ITEM, entry, count);
}
void Player::ItemRemovedQuestCheck(uint32 entry, uint32 count)
{
- for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
+ for (QuestObjectiveStatusMap::value_type const& objectiveItr : Trinity::Containers::MapEqualRange(m_questObjectiveStatus, { QUEST_OBJECTIVE_ITEM, entry }))
{
- uint32 questid = GetQuestSlotQuestId(i);
- if (!questid)
- continue;
+ uint32 questId = objectiveItr.second.QuestStatusItr->first;
+ Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
+ uint16 logSlot = objectiveItr.second.QuestStatusItr->second.Slot;
+ QuestObjective const& objective = *objectiveItr.second.Objective;
- Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid);
- if (!qInfo || !qInfo->HasQuestObjectiveType(QUEST_OBJECTIVE_ITEM))
+ if (!IsQuestObjectiveCompletable(logSlot, quest, objective))
continue;
- for (QuestObjective const& obj : qInfo->GetObjectives())
- {
- if (obj.Type != QUEST_OBJECTIVE_ITEM || !IsQuestObjectiveCompletable(i, qInfo, obj))
- continue;
-
- if (uint32(obj.ObjectID) != entry)
- continue;
-
- uint32 reqItemCount = obj.Amount;
- uint16 curItemCount = GetQuestSlotObjectiveData(i, obj);
-
- if (curItemCount >= reqItemCount) // we may have more than what the status shows
- curItemCount = GetItemCount(entry, false);
+ int32 curItemCount = GetQuestSlotObjectiveData(logSlot, objective);
+ if (curItemCount >= objective.Amount) // we may have more than what the status shows
+ curItemCount = GetItemCount(entry, false);
- uint16 newItemCount = (count > curItemCount) ? 0 : curItemCount - count;
-
- if (newItemCount < reqItemCount)
- {
- SetQuestObjectiveData(obj, newItemCount);
- IncompleteQuest(questid);
- }
+ int32 newItemCount = (int32(count) > curItemCount) ? 0 : curItemCount - count;
- break;
+ if (newItemCount < objective.Amount)
+ {
+ SetQuestObjectiveData(objective, newItemCount);
+ IncompleteQuest(questId);
}
}
@@ -16841,503 +16802,205 @@ void Player::KilledMonsterCredit(uint32 entry, ObjectGuid guid /*= ObjectGuid::E
StartCriteriaTimer(CRITERIA_TIMED_TYPE_CREATURE, real_entry); // MUST BE CALLED FIRST
UpdateCriteria(CRITERIA_TYPE_KILL_CREATURE, real_entry, addKillCount, 0, killed);
- bool completedObjectiveInSequencedQuest = false;
- for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
- {
- uint32 questid = GetQuestSlotQuestId(i);
- if (!questid)
- continue;
-
- Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid);
- if (!qInfo || !qInfo->HasQuestObjectiveType(QUEST_OBJECTIVE_MONSTER))
- continue;
-
-
- if (m_QuestStatus[questid].Status != QUEST_STATUS_INCOMPLETE)
- continue;
-
- if (GetGroup() && GetGroup()->isRaidGroup() && qInfo->IsAllowedInRaid(GetMap()->GetDifficultyID()))
- continue;
-
- for (QuestObjective const& obj : qInfo->GetObjectives())
- {
- if (obj.Type != QUEST_OBJECTIVE_MONSTER || !IsQuestObjectiveCompletable(i, qInfo, obj))
- continue;
-
- if (uint32(obj.ObjectID) != entry)
- continue;
-
- uint32 reqKillCount = obj.Amount;
- uint16 curKillCount = GetQuestSlotObjectiveData(i, obj);
- if (curKillCount < reqKillCount)
- {
- SetQuestObjectiveData(obj, curKillCount + addKillCount);
- SendQuestUpdateAddCredit(qInfo, guid, obj, curKillCount + addKillCount);
- if (!completedObjectiveInSequencedQuest && qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_SEQUENCED_OBJECTIVES) && IsQuestObjectiveComplete(i, qInfo, obj))
- completedObjectiveInSequencedQuest = true;
- }
-
- if (CanCompleteQuest(questid))
- CompleteQuest(questid);
-
- // same objective target can be in many active quests, but not in 2 objectives for single quest (code optimization).
- break;
- }
- }
-
- if (completedObjectiveInSequencedQuest)
- UpdateForQuestWorldObjects();
+ UpdateQuestObjectiveProgress(QUEST_OBJECTIVE_MONSTER, entry, 1, guid);
}
void Player::KilledPlayerCredit()
{
- uint16 addKillCount = 1;
- bool completedObjectiveInSequencedQuest = false;
- for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
- {
- uint32 questid = GetQuestSlotQuestId(i);
- if (!questid)
- continue;
-
- Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid);
- if (!qInfo || !qInfo->HasQuestObjectiveType(QUEST_OBJECTIVE_PLAYERKILLS))
- continue;
-
- // just if !ingroup || !noraidgroup || raidgroup
- QuestStatusData& q_status = m_QuestStatus[questid];
- if (q_status.Status == QUEST_STATUS_INCOMPLETE && (!GetGroup() || !GetGroup()->isRaidGroup() || qInfo->IsAllowedInRaid(GetMap()->GetDifficultyID())))
- {
- for (QuestObjective const& obj : qInfo->GetObjectives())
- {
- if (obj.Type != QUEST_OBJECTIVE_PLAYERKILLS || !IsQuestObjectiveCompletable(i, qInfo, obj))
- continue;
-
- uint32 curKillCount = GetQuestSlotObjectiveData(i, obj);
- if (curKillCount < uint32(obj.Amount))
- {
- SetQuestObjectiveData(obj, curKillCount + addKillCount);
- SendQuestUpdateAddPlayer(qInfo, curKillCount + addKillCount);
- if (!completedObjectiveInSequencedQuest && qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_SEQUENCED_OBJECTIVES) && IsQuestObjectiveComplete(i, qInfo, obj))
- completedObjectiveInSequencedQuest = true;
- }
-
- if (CanCompleteQuest(questid))
- CompleteQuest(questid);
-
- // Quest can't have more than one player kill objective (code optimisation)
- break;
- }
- }
- }
-
- if (completedObjectiveInSequencedQuest)
- UpdateForQuestWorldObjects();
+ UpdateQuestObjectiveProgress(QUEST_OBJECTIVE_PLAYERKILLS, 0, 1);
}
void Player::KillCreditGO(uint32 entry, ObjectGuid guid)
{
- uint16 addCastCount = 1;
- bool completedObjectiveInSequencedQuest = false;
- for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
- {
- uint32 questid = GetQuestSlotQuestId(i);
- if (!questid)
- continue;
-
- Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid);
- if (!qInfo || !qInfo->HasQuestObjectiveType(QUEST_OBJECTIVE_GAMEOBJECT))
- continue;
-
- if (m_QuestStatus[questid].Status != QUEST_STATUS_INCOMPLETE)
- continue;
-
- if (GetGroup() && GetGroup()->isRaidGroup() && qInfo->IsAllowedInRaid(GetMap()->GetDifficultyID()))
- continue;
-
- for (QuestObjective const& obj : qInfo->GetObjectives())
- {
- if (obj.Type != QUEST_OBJECTIVE_GAMEOBJECT || !IsQuestObjectiveCompletable(i, qInfo, obj))
- continue;
-
- // other not this creature/GO related objectives
- if (uint32(obj.ObjectID) != entry)
- continue;
-
- uint32 reqCastCount = obj.Amount;
- uint32 curCastCount = GetQuestSlotObjectiveData(i, obj);
- if (curCastCount < reqCastCount)
- {
- SetQuestObjectiveData(obj, curCastCount + addCastCount);
- SendQuestUpdateAddCredit(qInfo, guid, obj, curCastCount + addCastCount);
- if (!completedObjectiveInSequencedQuest && qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_SEQUENCED_OBJECTIVES) && IsQuestObjectiveComplete(i, qInfo, obj))
- completedObjectiveInSequencedQuest = true;
- }
-
- if (CanCompleteQuest(questid))
- CompleteQuest(questid);
-
- // same objective target can be in many active quests, but not in 2 objectives for single quest (code optimization).
- break;
- }
- }
-
- if (completedObjectiveInSequencedQuest)
- UpdateForQuestWorldObjects();
+ UpdateQuestObjectiveProgress(QUEST_OBJECTIVE_GAMEOBJECT, entry, 1, guid);
}
void Player::TalkedToCreature(uint32 entry, ObjectGuid guid)
{
- uint16 addTalkCount = 1;
- bool completedObjectiveInSequencedQuest = false;
- for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
- {
- uint32 questid = GetQuestSlotQuestId(i);
- if (!questid)
- continue;
-
- Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid);
- if (!qInfo || !qInfo->HasQuestObjectiveType(QUEST_OBJECTIVE_TALKTO))
- continue;
-
- if (m_QuestStatus[questid].Status != QUEST_STATUS_INCOMPLETE)
- continue;
-
- if (GetGroup() && GetGroup()->isRaidGroup() && qInfo->IsAllowedInRaid(GetMap()->GetDifficultyID()))
- continue;
-
- for (QuestObjective const& obj : qInfo->GetObjectives())
- {
- if (obj.Type != QUEST_OBJECTIVE_TALKTO || !IsQuestObjectiveCompletable(i, qInfo, obj))
- continue;
-
- if (uint32(obj.ObjectID) != entry)
- continue;
-
- uint32 reqTalkCount = obj.Amount;
- uint32 curTalkCount = GetQuestSlotObjectiveData(i, obj);
- if (curTalkCount < reqTalkCount)
- {
- SetQuestObjectiveData(obj, curTalkCount + addTalkCount);
- SendQuestUpdateAddCredit(qInfo, guid, obj, curTalkCount + addTalkCount);
- if (!completedObjectiveInSequencedQuest && qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_SEQUENCED_OBJECTIVES) && IsQuestObjectiveComplete(i, qInfo, obj))
- completedObjectiveInSequencedQuest = true;
- }
-
- if (CanCompleteQuest(questid))
- CompleteQuest(questid);
-
- // Quest can't have more than one objective for the same creature (code optimisation)
- break;
- }
- }
-
- if (completedObjectiveInSequencedQuest)
- UpdateForQuestWorldObjects();
+ UpdateQuestObjectiveProgress(QUEST_OBJECTIVE_TALKTO, entry, 1, guid);
}
void Player::KillCreditCriteriaTreeObjective(QuestObjective const& questObjective)
{
- if (questObjective.Type != QUEST_OBJECTIVE_CRITERIA_TREE)
- return;
+ UpdateQuestObjectiveProgress(QUEST_OBJECTIVE_CRITERIA_TREE, questObjective.ObjectID, 1);
+}
- Quest const* quest = ASSERT_NOTNULL(sObjectMgr->GetQuestTemplate(questObjective.QuestID));
- if (!IsQuestObjectiveComplete(FindQuestSlot(questObjective.QuestID), quest, questObjective))
- {
- SetQuestObjectiveData(questObjective, 1);
- SendQuestUpdateAddCreditSimple(questObjective);
+void Player::MoneyChanged(uint64 value)
+{
+ UpdateQuestObjectiveProgress(QUEST_OBJECTIVE_MONEY, 0, int64(value) - int64(GetMoney()));
+}
- if (CanCompleteQuest(questObjective.QuestID))
- CompleteQuest(questObjective.QuestID);
+void Player::ReputationChanged(FactionEntry const* factionEntry, int32 change)
+{
+ UpdateQuestObjectiveProgress(QUEST_OBJECTIVE_MIN_REPUTATION, factionEntry->ID, change);
+ UpdateQuestObjectiveProgress(QUEST_OBJECTIVE_MAX_REPUTATION, factionEntry->ID, change);
+ UpdateQuestObjectiveProgress(QUEST_OBJECTIVE_INCREASE_REPUTATION, factionEntry->ID, change);
+}
- if (quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_SEQUENCED_OBJECTIVES))
- UpdateForQuestWorldObjects();
- }
+void Player::CurrencyChanged(uint32 currencyId, int32 change)
+{
+ UpdateQuestObjectiveProgress(QUEST_OBJECTIVE_CURRENCY, currencyId, change);
+ UpdateQuestObjectiveProgress(QUEST_OBJECTIVE_HAVE_CURRENCY, currencyId, change);
+ UpdateQuestObjectiveProgress(QUEST_OBJECTIVE_OBTAIN_CURRENCY, currencyId, change);
}
-void Player::MoneyChanged(uint64 value)
+void Player::UpdateQuestObjectiveProgress(QuestObjectiveType objectiveType, int32 objectId, int64 addCount, ObjectGuid victimGuid)
{
- bool completedObjectiveInSequencedQuest = false;
- for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
+ bool anyObjectiveChangedCompletionState = false;
+
+ for (QuestObjectiveStatusMap::value_type const& objectiveItr : Trinity::Containers::MapEqualRange(m_questObjectiveStatus, { objectiveType, objectId }))
{
- uint32 questid = GetQuestSlotQuestId(i);
- if (!questid)
- continue;
+ uint32 questId = objectiveItr.second.QuestStatusItr->first;
+ Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
- Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid);
- if (!qInfo)
- continue;
+ if (!QuestObjective::CanAlwaysBeProgressedInRaid(objectiveType))
+ if (GetGroup() && GetGroup()->isRaidGroup() && quest->IsAllowedInRaid(GetMap()->GetDifficultyID()))
+ continue;
- if (!qInfo->HasQuestObjectiveType(QUEST_OBJECTIVE_MONEY))
+ uint16 logSlot = objectiveItr.second.QuestStatusItr->second.Slot;
+ QuestObjective const& objective = *objectiveItr.second.Objective;
+ if (!IsQuestObjectiveCompletable(logSlot, quest, objective))
continue;
- QuestStatusData& q_status = m_QuestStatus[questid];
-
- for (QuestObjective const& obj : qInfo->GetObjectives())
+ bool objectiveWasComplete = IsQuestObjectiveComplete(logSlot, quest, objective);
+ if (!objectiveWasComplete || addCount < 0)
{
- if (obj.Type != QUEST_OBJECTIVE_MONEY || !IsQuestObjectiveCompletable(i, qInfo, obj))
- continue;
-
- if (!IsQuestObjectiveComplete(i, qInfo, obj))
+ bool objectiveIsNowComplete = false;
+ if (objective.IsStoringValue())
{
- if (int64(value) >= int64(obj.Amount))
+ int32 currentProgress = GetQuestSlotObjectiveData(logSlot, objective);
+ if (addCount > 0 ? (currentProgress < objective.Amount) : (currentProgress > 0))
{
- if (CanCompleteQuest(questid))
- CompleteQuest(questid);
+ int32 newProgress = advstd::clamp<int32>(currentProgress + addCount, 0, objective.Amount);
+ SetQuestObjectiveData(objective, newProgress);
+ if (addCount > 0 && !(objective.Flags & QUEST_OBJECTIVE_FLAG_HIDE_CREDIT_MSG))
+ {
+ if (objectiveType != QUEST_OBJECTIVE_PLAYERKILLS)
+ SendQuestUpdateAddCredit(quest, victimGuid, objective, newProgress);
+ else
+ SendQuestUpdateAddPlayer(quest, newProgress);
+ }
- if (!completedObjectiveInSequencedQuest && qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_SEQUENCED_OBJECTIVES))
- completedObjectiveInSequencedQuest = true;
+ objectiveIsNowComplete = IsQuestObjectiveComplete(logSlot, quest, objective);
}
}
- else if (q_status.Status == QUEST_STATUS_COMPLETE)
+ else if (objective.IsStoringFlag())
{
- if (int64(value) < obj.Amount)
- IncompleteQuest(questid);
- }
- }
- }
+ SetQuestObjectiveData(objective, addCount > 0);
- if (completedObjectiveInSequencedQuest)
- UpdateForQuestWorldObjects();
-}
+ if (addCount > 0 && !(objective.Flags & QUEST_OBJECTIVE_FLAG_HIDE_CREDIT_MSG))
+ SendQuestUpdateAddCreditSimple(objective);
-void Player::ReputationChanged(FactionEntry const* factionEntry, int32 change)
-{
- bool completedObjectiveInSequencedQuest = false;
- for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
- {
- if (uint32 questid = GetQuestSlotQuestId(i))
- {
- if (Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid))
+ objectiveIsNowComplete = IsQuestObjectiveComplete(logSlot, quest, objective);
+ }
+ else
{
- QuestStatusData& q_status = m_QuestStatus[questid];
-
- if (!qInfo->HasQuestObjectiveType(QUEST_OBJECTIVE_MIN_REPUTATION)
- && !qInfo->HasQuestObjectiveType(QUEST_OBJECTIVE_MAX_REPUTATION)
- && (!qInfo->HasQuestObjectiveType(QUEST_OBJECTIVE_INCREASE_REPUTATION) || q_status.Status != QUEST_STATUS_INCOMPLETE))
- continue;
-
- for (QuestObjective const& obj : qInfo->GetObjectives())
+ switch (objectiveType)
{
- if (uint32(obj.ObjectID) != factionEntry->ID || !IsQuestObjectiveCompletable(i, qInfo, obj))
- continue;
-
- switch (obj.Type)
- {
- case QUEST_OBJECTIVE_MIN_REPUTATION:
- if (!IsQuestObjectiveComplete(i, qInfo, obj))
- {
- if (GetReputationMgr().GetReputation(factionEntry) + change >= obj.Amount)
- {
- if (CanCompleteQuest(questid))
- CompleteQuest(questid);
-
- if (qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_SEQUENCED_OBJECTIVES))
- completedObjectiveInSequencedQuest = true;
- }
- }
- else if (q_status.Status == QUEST_STATUS_COMPLETE)
- {
- if (GetReputationMgr().GetReputation(factionEntry) + change < obj.Amount)
- IncompleteQuest(questid);
- }
- break;
- case QUEST_OBJECTIVE_MAX_REPUTATION:
- if (!IsQuestObjectiveComplete(i, qInfo, obj))
- {
- if (GetReputationMgr().GetReputation(factionEntry) + change <= obj.Amount)
- {
- if (CanCompleteQuest(questid))
- CompleteQuest(questid);
-
- if (qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_SEQUENCED_OBJECTIVES))
- completedObjectiveInSequencedQuest = true;
- }
- }
- else if (q_status.Status == QUEST_STATUS_COMPLETE)
- {
- if (GetReputationMgr().GetReputation(factionEntry) + change > obj.Amount)
- IncompleteQuest(questid);
- }
- break;
- case QUEST_OBJECTIVE_INCREASE_REPUTATION:
- {
- int32 currentProgress = GetQuestSlotObjectiveData(i, obj);
- if (change > 0 && currentProgress < obj.Amount)
- {
- SetQuestObjectiveData(obj, currentProgress + change);
- SendQuestUpdateAddCredit(qInfo, ObjectGuid::Empty, obj, currentProgress + change);
-
- if (CanCompleteQuest(questid))
- CompleteQuest(questid);
-
- if (!completedObjectiveInSequencedQuest && qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_SEQUENCED_OBJECTIVES) && IsQuestObjectiveComplete(i, qInfo, obj))
- completedObjectiveInSequencedQuest = true;
- }
- break;
- }
- default:
- break;
- }
+ case QUEST_OBJECTIVE_CURRENCY:
+ objectiveIsNowComplete = GetCurrency(objectId) + addCount >= objective.Amount;
+ break;
+ case QUEST_OBJECTIVE_LEARNSPELL:
+ objectiveIsNowComplete = addCount != 0;
+ break;
+ case QUEST_OBJECTIVE_MIN_REPUTATION:
+ objectiveIsNowComplete = GetReputationMgr().GetReputation(objectId) + addCount >= objective.Amount;
+ break;
+ case QUEST_OBJECTIVE_MAX_REPUTATION:
+ objectiveIsNowComplete = GetReputationMgr().GetReputation(objectId) + addCount <= objective.Amount;
+ break;
+ case QUEST_OBJECTIVE_MONEY:
+ objectiveIsNowComplete = int64(GetMoney()) + addCount >= objective.Amount;
+ break;
+ case QUEST_OBJECTIVE_PROGRESS_BAR:
+ objectiveIsNowComplete = IsQuestObjectiveProgressBarComplete(logSlot, quest);
+ break;
+ default:
+ ASSERT(false, "Unhandled quest objective type %u", uint32(objectiveType));
+ break;
}
}
- }
- }
- if (completedObjectiveInSequencedQuest)
- UpdateForQuestWorldObjects();
-}
-
-void Player::CurrencyChanged(uint32 currencyId, int32 change)
-{
- bool completedObjectiveInSequencedQuest = false;
- for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
- {
- uint32 questid = GetQuestSlotQuestId(i);
- if (!questid)
- continue;
-
- Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid);
- if (!qInfo)
- continue;
-
- QuestStatusData& q_status = m_QuestStatus[questid];
-
- if (!qInfo->HasQuestObjectiveType(QUEST_OBJECTIVE_CURRENCY)
- && !qInfo->HasQuestObjectiveType(QUEST_OBJECTIVE_HAVE_CURRENCY)
- && (!qInfo->HasQuestObjectiveType(QUEST_OBJECTIVE_OBTAIN_CURRENCY) || q_status.Status != QUEST_STATUS_INCOMPLETE))
- continue;
-
- for (QuestObjective const& obj : qInfo->GetObjectives())
- {
- if (uint32(obj.ObjectID) != currencyId || !IsQuestObjectiveCompletable(i, qInfo, obj))
- continue;
-
- QuestStatusData& q_status = m_QuestStatus[questid];
- switch (obj.Type)
+ if (objective.Flags & QUEST_OBJECTIVE_FLAG_PART_OF_PROGRESS_BAR)
{
- case QUEST_OBJECTIVE_CURRENCY:
+ if (IsQuestObjectiveProgressBarComplete(logSlot, quest))
{
- if (!IsQuestObjectiveComplete(i, qInfo, obj))
+ auto progressBarObjectiveItr = std::find_if(quest->GetObjectives().begin(), quest->GetObjectives().end(), [](QuestObjective const& otherObjective)
{
- if (int64(GetCurrency(currencyId)) + change >= obj.Amount)
- {
- if (CanCompleteQuest(questid))
- CompleteQuest(questid);
+ return otherObjective.Type == QUEST_OBJECTIVE_PROGRESS_BAR && !(otherObjective.Flags & QUEST_OBJECTIVE_FLAG_PART_OF_PROGRESS_BAR);
+ });
+ if (progressBarObjectiveItr != quest->GetObjectives().end())
+ SendQuestUpdateAddCreditSimple(*progressBarObjectiveItr);
- if (qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_SEQUENCED_OBJECTIVES))
- completedObjectiveInSequencedQuest = true;
- }
- }
- else if (q_status.Status == QUEST_STATUS_COMPLETE)
- {
- if (int64(GetCurrency(currencyId)) + change < obj.Amount)
- IncompleteQuest(questid);
- }
- break;
- }
- case QUEST_OBJECTIVE_HAVE_CURRENCY:
- {
- int32 newProgress = int32(std::min<int64>(int64(GetCurrency(currencyId)) + change, obj.Amount));
- SetQuestObjectiveData(obj, newProgress);
- if (!IsQuestObjectiveComplete(i, qInfo, obj))
- {
- if (newProgress >= obj.Amount)
- {
- if (CanCompleteQuest(questid))
- CompleteQuest(questid);
-
- if (qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_SEQUENCED_OBJECTIVES))
- completedObjectiveInSequencedQuest = true;
- }
- }
- else if (q_status.Status == QUEST_STATUS_COMPLETE)
- {
- if (newProgress < obj.Amount)
- IncompleteQuest(questid);
- }
- break;
+ objectiveIsNowComplete = true;
}
- case QUEST_OBJECTIVE_OBTAIN_CURRENCY:
- {
- int32 currentProgress = GetQuestSlotObjectiveData(i, obj);
- if (change > 0 && currentProgress < obj.Amount)
- {
- SetQuestObjectiveData(obj, currentProgress + change);
- SendQuestUpdateAddCredit(qInfo, ObjectGuid::Empty, obj, currentProgress + change);
+ }
- if (CanCompleteQuest(questid))
- CompleteQuest(questid);
+ if (objectiveWasComplete != objectiveIsNowComplete)
+ anyObjectiveChangedCompletionState = true;
- if (!completedObjectiveInSequencedQuest && qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_SEQUENCED_OBJECTIVES) && IsQuestObjectiveComplete(i, qInfo, obj))
- completedObjectiveInSequencedQuest = true;
- }
- break;
- }
- default:
- break;
- }
+ if (objectiveIsNowComplete && CanCompleteQuest(questId, objective.ID))
+ CompleteQuest(questId);
+ else if (objectiveItr.second.QuestStatusItr->second.Status == QUEST_STATUS_COMPLETE)
+ IncompleteQuest(questId);
}
}
- if (completedObjectiveInSequencedQuest)
+ if (anyObjectiveChangedCompletionState)
UpdateForQuestWorldObjects();
}
bool Player::HasQuestForItem(uint32 itemid) const
{
- for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
+ // Search incomplete objective first
+ for (QuestObjectiveStatusMap::value_type const& objectiveItr : Trinity::Containers::MapEqualRange(m_questObjectiveStatus, { QUEST_OBJECTIVE_ITEM, itemid }))
{
- uint32 questid = GetQuestSlotQuestId(i);
- if (questid == 0)
+ Quest const* qInfo = ASSERT_NOTNULL(sObjectMgr->GetQuestTemplate(objectiveItr.second.QuestStatusItr->first));
+ QuestObjective const& objective = *objectiveItr.second.Objective;
+ if (!IsQuestObjectiveCompletable(objectiveItr.second.QuestStatusItr->second.Slot, qInfo, objective))
continue;
- QuestStatusMap::const_iterator qs_itr = m_QuestStatus.find(questid);
- if (qs_itr == m_QuestStatus.end())
+ // hide quest if player is in raid-group and quest is no raid quest
+ if (GetGroup() && GetGroup()->isRaidGroup() && !qInfo->IsAllowedInRaid(GetMap()->GetDifficultyID()))
+ if (!InBattleground()) //there are two ways.. we can make every bg-quest a raidquest, or add this code here.. i don't know if this can be exploited by other quests, but i think all other quests depend on a specific area.. but keep this in mind, if something strange happens later
+ continue;
+
+ if (!IsQuestObjectiveComplete(objectiveItr.second.QuestStatusItr->second.Slot, qInfo, objective))
+ return true;
+ }
+
+ // This part - for ItemDrop
+ for (std::pair<uint32 const, QuestStatusData> const& questStatus : m_QuestStatus)
+ {
+ if (questStatus.second.Status != QUEST_STATUS_INCOMPLETE)
continue;
- QuestStatusData const& q_status = qs_itr->second;
+ Quest const* qInfo = ASSERT_NOTNULL(sObjectMgr->GetQuestTemplate(questStatus.first));
+ // hide quest if player is in raid-group and quest is no raid quest
+ if (GetGroup() && GetGroup()->isRaidGroup() && !qInfo->IsAllowedInRaid(GetMap()->GetDifficultyID()))
+ if (!InBattleground())
+ continue;
- if (q_status.Status == QUEST_STATUS_INCOMPLETE)
+ for (uint8 j = 0; j < QUEST_ITEM_DROP_COUNT; ++j)
{
- Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid);
- if (!qInfo)
+ // examined item is a source item
+ if (qInfo->ItemDrop[j] != itemid)
continue;
- // hide quest if player is in raid-group and quest is no raid quest
- if (GetGroup() && GetGroup()->isRaidGroup() && !qInfo->IsAllowedInRaid(GetMap()->GetDifficultyID()))
- if (!InBattleground()) //there are two ways.. we can make every bg-quest a raidquest, or add this code here.. i don't know if this can be exploited by other quests, but i think all other quests depend on a specific area.. but keep this in mind, if something strange happens later
- continue;
+ ItemTemplate const* pProto = ASSERT_NOTNULL(sObjectMgr->GetItemTemplate(itemid));
- // There should be no mixed ReqItem/ReqSource drop
- // This part for ReqItem drop
- for (QuestObjective const& obj : qInfo->GetObjectives())
- {
- if (obj.Type == QUEST_OBJECTIVE_ITEM && IsQuestObjectiveCompletable(i, qInfo, obj) && itemid == uint32(obj.ObjectID) && GetQuestSlotObjectiveData(i, obj) < obj.Amount)
- return true;
- }
- // This part - for ReqSource
- for (uint8 j = 0; j < QUEST_ITEM_DROP_COUNT; ++j)
- {
- // examined item is a source item
- if (qInfo->ItemDrop[j] == itemid)
- {
- ItemTemplate const* pProto = ASSERT_NOTNULL(sObjectMgr->GetItemTemplate(itemid));
+ // allows custom amount drop when not 0
+ uint32 maxAllowedCount = qInfo->ItemDropQuantity[j] ? qInfo->ItemDropQuantity[j] : pProto->GetMaxStackSize();
- // 'unique' item
- if (pProto->GetMaxCount() && GetItemCount(itemid, true) < pProto->GetMaxCount())
- return true;
+ // 'unique' item
+ if (pProto->GetMaxCount() && pProto->GetMaxCount() < maxAllowedCount)
+ maxAllowedCount = pProto->GetMaxCount();
- // allows custom amount drop when not 0
- if (qInfo->ItemDropQuantity[j])
- {
- if (GetItemCount(itemid, true) < qInfo->ItemDropQuantity[j])
- return true;
- } else if (GetItemCount(itemid, true) < pProto->GetMaxStackSize())
- return true;
- }
- }
+ if (GetItemCount(itemid, true) < maxAllowedCount)
+ return true;
}
}
+
return false;
}
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index e74a37c7bf9..ceaf1fc8b5a 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -24,6 +24,7 @@
#include "DBCEnums.h"
#include "EquipmentSet.h"
#include "GroupReference.h"
+#include "Hash.h"
#include "ItemDefines.h"
#include "ItemEnchantmentMgr.h"
#include "MapReference.h"
@@ -529,6 +530,15 @@ enum AtLoginFlags
};
typedef std::map<uint32, QuestStatusData> QuestStatusMap;
+
+struct QuestObjectiveStatusData
+{
+ QuestStatusMap::iterator QuestStatusItr;
+ QuestObjective const* Objective;
+};
+
+using QuestObjectiveStatusMap = std::unordered_multimap<std::pair<QuestObjectiveType, int32>, QuestObjectiveStatusData>;
+
typedef std::set<uint32> RewardedQuestSet;
enum QuestSaveType
@@ -1446,7 +1456,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
bool CanSeeStartQuest(Quest const* quest);
bool CanTakeQuest(Quest const* quest, bool msg);
bool CanAddQuest(Quest const* quest, bool msg) const;
- bool CanCompleteQuest(uint32 quest_id);
+ bool CanCompleteQuest(uint32 quest_id, uint32 ignoredQuestObjectiveId = 0);
bool CanCompleteRepeatableQuest(Quest const* quest);
bool CanRewardQuest(Quest const* quest, bool msg) const;
bool CanRewardQuest(Quest const* quest, LootItemType rewardType, uint32 rewardId, bool msg) const;
@@ -1529,6 +1539,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void MoneyChanged(uint64 value);
void ReputationChanged(FactionEntry const* factionEntry, int32 change);
void CurrencyChanged(uint32 currencyId, int32 change);
+ void UpdateQuestObjectiveProgress(QuestObjectiveType objectiveType, int32 objectId, int64 addCount, ObjectGuid victimGuid = ObjectGuid::Empty);
bool HasQuestForItem(uint32 itemId) const;
bool HasQuestForGO(int32 goId) const;
void UpdateForQuestWorldObjects();
@@ -2825,6 +2836,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
uint32 m_ExtraFlags;
QuestStatusMap m_QuestStatus;
+ QuestObjectiveStatusMap m_questObjectiveStatus;
QuestStatusSaveMap m_QuestStatusSave;
RewardedQuestSet m_RewardedQuests;
diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp
index 2ffacd75ff9..0f8ad12a769 100644
--- a/src/server/game/Handlers/MiscHandler.cpp
+++ b/src/server/game/Handlers/MiscHandler.cpp
@@ -489,9 +489,10 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPackets::AreaTrigger::AreaTrigge
if (player->IsAlive())
{
+ // not using Player::UpdateQuestObjectiveProgress, ObjectID in quest_objectives can be set to -1, areatrigger_involvedrelation then holds correct id
if (std::unordered_set<uint32> const* quests = sObjectMgr->GetQuestsForAreaTrigger(packet.AreaTriggerID))
{
- bool completedObjectiveInSequencedQuest = false;
+ bool anyObjectiveChangedCompletionState = false;
for (uint32 questId : *quests)
{
Quest const* qInfo = sObjectMgr->GetQuestTemplate(questId);
@@ -500,14 +501,22 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPackets::AreaTrigger::AreaTrigge
{
for (QuestObjective const& obj : qInfo->Objectives)
{
- if (obj.Type == QUEST_OBJECTIVE_AREATRIGGER && !player->IsQuestObjectiveComplete(slot, qInfo, obj))
- {
- player->SetQuestObjectiveData(obj, 1);
- player->SendQuestUpdateAddCreditSimple(obj);
- if (qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_SEQUENCED_OBJECTIVES))
- completedObjectiveInSequencedQuest = true;
- break;
- }
+ if (obj.Type != QUEST_OBJECTIVE_AREATRIGGER)
+ continue;
+
+ if (!player->IsQuestObjectiveCompletable(slot, qInfo, obj))
+ continue;
+
+ if (player->IsQuestObjectiveComplete(slot, qInfo, obj))
+ continue;
+
+ if (obj.ObjectID != -1 && obj.ObjectID != packet.AreaTriggerID)
+ continue;
+
+ player->SetQuestObjectiveData(obj, 1);
+ player->SendQuestUpdateAddCreditSimple(obj);
+ anyObjectiveChangedCompletionState = true;
+ break;
}
if (player->CanCompleteQuest(questId))
@@ -515,7 +524,7 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPackets::AreaTrigger::AreaTrigge
}
}
- if (completedObjectiveInSequencedQuest)
+ if (anyObjectiveChangedCompletionState)
player->UpdateForQuestWorldObjects();
}
}
diff --git a/src/server/game/Quests/QuestDef.h b/src/server/game/Quests/QuestDef.h
index bb2ab57fcc3..9b74cbb978e 100644
--- a/src/server/game/Quests/QuestDef.h
+++ b/src/server/game/Quests/QuestDef.h
@@ -373,6 +373,26 @@ struct QuestObjective
std::string Description;
std::vector<int32> VisualEffects;
+ bool IsStoringValue() const
+ {
+ switch (Type)
+ {
+ case QUEST_OBJECTIVE_MONSTER:
+ case QUEST_OBJECTIVE_ITEM:
+ case QUEST_OBJECTIVE_GAMEOBJECT:
+ case QUEST_OBJECTIVE_TALKTO:
+ case QUEST_OBJECTIVE_PLAYERKILLS:
+ case QUEST_OBJECTIVE_WINPVPPETBATTLES:
+ case QUEST_OBJECTIVE_HAVE_CURRENCY:
+ case QUEST_OBJECTIVE_OBTAIN_CURRENCY:
+ case QUEST_OBJECTIVE_INCREASE_REPUTATION:
+ return true;
+ default:
+ break;
+ }
+ return false;
+ }
+
bool IsStoringFlag() const
{
switch (Type)
@@ -389,6 +409,25 @@ struct QuestObjective
}
return false;
}
+
+ static constexpr bool CanAlwaysBeProgressedInRaid(QuestObjectiveType type)
+ {
+ switch (type)
+ {
+ case QUEST_OBJECTIVE_ITEM:
+ case QUEST_OBJECTIVE_CURRENCY:
+ case QUEST_OBJECTIVE_LEARNSPELL:
+ case QUEST_OBJECTIVE_MIN_REPUTATION:
+ case QUEST_OBJECTIVE_MAX_REPUTATION:
+ case QUEST_OBJECTIVE_MONEY:
+ case QUEST_OBJECTIVE_HAVE_CURRENCY:
+ case QUEST_OBJECTIVE_INCREASE_REPUTATION:
+ return true;
+ default:
+ break;
+ }
+ return false;
+ }
};
using QuestObjectives = std::vector<QuestObjective>;