Core/Quests: Unify quest objective updating into one function and replace iterating entire quest log to find objective with direct {type, id} lookup

Port From (https://github.com/TrinityCore/TrinityCore/commit/6352a84bf78a7afe0ea8d8caccee33dd09e09694)
This commit is contained in:
hondacrx
2021-05-11 12:22:07 -04:00
parent 5cbd815f53
commit 5915e1a9a0
4 changed files with 251 additions and 515 deletions
+9 -1
View File
@@ -17,6 +17,7 @@
using Framework.Constants; using Framework.Constants;
using Game.Achievements; using Game.Achievements;
using Game.BattleGrounds;
using Game.Chat; using Game.Chat;
using Game.DataStorage; using Game.DataStorage;
using Game.Garrisons; using Game.Garrisons;
@@ -25,9 +26,9 @@ using Game.Mails;
using Game.Maps; using Game.Maps;
using Game.Misc; using Game.Misc;
using Game.Spells; using Game.Spells;
using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using Game.BattleGrounds;
namespace Game.Entities namespace Game.Entities
{ {
@@ -164,6 +165,7 @@ namespace Game.Entities
List<uint> m_monthlyquests = new(); List<uint> m_monthlyquests = new();
MultiMap<uint, uint> m_seasonalquests = new(); MultiMap<uint, uint> m_seasonalquests = new();
Dictionary<uint, QuestStatusData> m_QuestStatus = new(); Dictionary<uint, QuestStatusData> m_QuestStatus = new();
MultiMap<Tuple<QuestObjectiveType, int>, QuestObjectiveStatusData> m_questObjectiveStatus = new();
Dictionary<uint, QuestSaveType> m_QuestStatusSave = new(); Dictionary<uint, QuestSaveType> m_QuestStatusSave = new();
List<uint> m_DFQuests = new(); List<uint> m_DFQuests = new();
List<uint> m_RewardedQuests = new(); List<uint> m_RewardedQuests = new();
@@ -625,4 +627,10 @@ namespace Game.Entities
Deleted Deleted
} }
} }
struct QuestObjectiveStatusData
{
public KeyValuePair<uint, QuestStatusData> QuestStatusPair;
public QuestObjective Objective;
}
} }
+184 -504
View File
@@ -27,7 +27,6 @@ using Game.Networking.Packets;
using Game.Spells; using Game.Spells;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace Game.Entities namespace Game.Entities
{ {
@@ -462,22 +461,22 @@ namespace Game.Entities
return true; return true;
} }
public bool CanCompleteQuest(uint quest_id) public bool CanCompleteQuest(uint questId, uint ignoredQuestObjectiveId = 0)
{ {
if (quest_id != 0) if (questId != 0)
{ {
Quest qInfo = Global.ObjectMgr.GetQuestTemplate(quest_id); Quest qInfo = Global.ObjectMgr.GetQuestTemplate(questId);
if (qInfo == null) if (qInfo == null)
return false; return false;
if (!qInfo.IsRepeatable() && GetQuestRewardStatus(quest_id)) if (!qInfo.IsRepeatable() && GetQuestRewardStatus(questId))
return false; // not allow re-complete quest return false; // not allow re-complete quest
// auto complete quest // auto complete quest
if ((qInfo.IsAutoComplete() || qInfo.Flags.HasAnyFlag(QuestFlags.AutoComplete)) && CanTakeQuest(qInfo, false)) if ((qInfo.IsAutoComplete() || qInfo.Flags.HasAnyFlag(QuestFlags.AutoComplete)) && CanTakeQuest(qInfo, false))
return true; return true;
var q_status = m_QuestStatus.LookupByKey(quest_id); var q_status = m_QuestStatus.LookupByKey(questId);
if (q_status == null) if (q_status == null)
return false; return false;
@@ -485,6 +484,9 @@ namespace Game.Entities
{ {
foreach (QuestObjective obj in qInfo.Objectives) foreach (QuestObjective obj in qInfo.Objectives)
{ {
if (ignoredQuestObjectiveId != 0 && obj.Id == ignoredQuestObjectiveId)
continue;
if (!obj.Flags.HasAnyFlag(QuestObjectiveFlags.Optional) && !obj.Flags.HasAnyFlag(QuestObjectiveFlags.PartOfProgressBar)) if (!obj.Flags.HasAnyFlag(QuestObjectiveFlags.Optional) && !obj.Flags.HasAnyFlag(QuestObjectiveFlags.PartOfProgressBar))
{ {
if (!IsQuestObjectiveComplete(q_status.Slot, qInfo, obj)) if (!IsQuestObjectiveComplete(q_status.Slot, qInfo, obj))
@@ -731,6 +733,7 @@ namespace Game.Entities
uint quest_id = quest.Id; uint quest_id = quest.Id;
// if not exist then created with set uState == NEW and rewarded=false
if (!m_QuestStatus.ContainsKey(quest_id)) if (!m_QuestStatus.ContainsKey(quest_id))
m_QuestStatus[quest_id] = new QuestStatusData(); m_QuestStatus[quest_id] = new QuestStatusData();
@@ -745,6 +748,7 @@ namespace Game.Entities
foreach (QuestObjective obj in quest.Objectives) foreach (QuestObjective obj in quest.Objectives)
{ {
m_questObjectiveStatus.Add(Tuple.Create(obj.Type, obj.ObjectID), new QuestObjectiveStatusData() { QuestStatusPair = KeyValuePair.Create(quest_id, questStatusData), Objective = obj });
switch (obj.Type) switch (obj.Type)
{ {
case QuestObjectiveType.MinReputation: case QuestObjectiveType.MinReputation:
@@ -1775,16 +1779,22 @@ namespace Game.Entities
SendQuestUpdate(questId); SendQuestUpdate(questId);
} }
public void RemoveActiveQuest(uint quest_id, bool update = true) public void RemoveActiveQuest(uint questId, bool update = true)
{ {
if (m_QuestStatus.ContainsKey(quest_id)) var questStatus = m_QuestStatus.LookupByKey(questId);
if (questStatus != null)
{ {
m_QuestStatus.Remove(quest_id); foreach (var objective in m_questObjectiveStatus.KeyValueList)
m_QuestStatusSave[quest_id] = QuestSaveType.Delete; {
if (objective.Value.QuestStatusPair.Value == questStatus)
m_questObjectiveStatus.Remove(objective);
}
m_QuestStatus.Remove(questId);
m_QuestStatusSave[questId] = QuestSaveType.Delete;
} }
if (update) if (update)
SendQuestUpdate(quest_id); SendQuestUpdate(questId);
} }
public void RemoveRewardedQuest(uint questId, bool update = true) public void RemoveRewardedQuest(uint questId, bool update = true)
@@ -2162,81 +2172,31 @@ namespace Game.Entities
public void ItemAddedQuestCheck(uint entry, uint count) public void ItemAddedQuestCheck(uint entry, uint count)
{ {
for (byte i = 0; i < SharedConst.MaxQuestLogSize; ++i) UpdateQuestObjectiveProgress(QuestObjectiveType.Item, (int)entry, count);
{
uint questid = GetQuestSlotQuestId(i);
if (questid == 0)
continue;
QuestStatusData q_status = m_QuestStatus[questid];
if (q_status.Status != QuestStatus.Incomplete)
continue;
Quest qInfo = Global.ObjectMgr.GetQuestTemplate(questid);
if (qInfo == null || !qInfo.HasQuestObjectiveType(QuestObjectiveType.Item))
continue;
foreach (QuestObjective obj in qInfo.Objectives)
{
if (obj.Type != QuestObjectiveType.Item || !IsQuestObjectiveCompletable(i, qInfo, obj))
continue;
if (obj.ObjectID != entry)
continue;
int reqItemCount = obj.Amount;
int curItemCount = GetQuestSlotObjectiveData(i, obj);
if (curItemCount < reqItemCount)
{
int newItemCount = (int)Math.Min(curItemCount + count, reqItemCount);
SetQuestObjectiveData(obj, newItemCount);
}
if (CanCompleteQuest(questid))
CompleteQuest(questid);
break;
}
}
UpdateForQuestWorldObjects();
} }
public void ItemRemovedQuestCheck(uint entry, uint count) public void ItemRemovedQuestCheck(uint entry, uint count)
{ {
for (byte i = 0; i < SharedConst.MaxQuestLogSize; ++i) foreach (var objectiveStatusData in m_questObjectiveStatus.LookupByKey(Tuple.Create(QuestObjectiveType.Item, entry)))
{ {
uint questid = GetQuestSlotQuestId(i); uint questId = objectiveStatusData.QuestStatusPair.Key;
if (questid == 0) Quest quest = Global.ObjectMgr.GetQuestTemplate(questId);
ushort logSlot = objectiveStatusData.QuestStatusPair.Value.Slot;
QuestObjective objective = objectiveStatusData.Objective;
if (!IsQuestObjectiveCompletable(logSlot, quest, objective))
continue; continue;
Quest qInfo = Global.ObjectMgr.GetQuestTemplate(questid); int curItemCount = GetQuestSlotObjectiveData(logSlot, objective);
if (qInfo == null || !qInfo.HasQuestObjectiveType(QuestObjectiveType.Item)) if (curItemCount >= objective.Amount) // we may have more than what the status shows
continue; curItemCount = (int)GetItemCount(entry, false);
foreach (QuestObjective obj in qInfo.Objectives) int newItemCount = (int)((count > curItemCount) ? 0 : curItemCount - count);
if (newItemCount < objective.Amount)
{ {
if (obj.Type != QuestObjectiveType.Item || !IsQuestObjectiveCompletable(i, qInfo, obj)) SetQuestObjectiveData(objective, newItemCount);
continue; IncompleteQuest(questId);
if (obj.ObjectID != entry)
continue;
uint reqItemCount = (uint)obj.Amount;
int curItemCount = GetQuestSlotObjectiveData(i, obj);
if (curItemCount >= reqItemCount) // we may have more than what the status shows
curItemCount = (int)GetItemCount(entry, false);
int newItemCount = (int)((count > curItemCount) ? 0 : curItemCount - count);
if (newItemCount < reqItemCount)
{
SetQuestObjectiveData(obj, newItemCount);
IncompleteQuest(questid);
}
break;
} }
} }
@@ -2270,489 +2230,209 @@ namespace Game.Entities
StartCriteriaTimer(CriteriaTimedTypes.Creature, real_entry); // MUST BE CALLED FIRST StartCriteriaTimer(CriteriaTimedTypes.Creature, real_entry); // MUST BE CALLED FIRST
UpdateCriteria(CriteriaTypes.KillCreature, real_entry, addKillCount, 0, killed); UpdateCriteria(CriteriaTypes.KillCreature, real_entry, addKillCount, 0, killed);
bool completedObjectiveInSequencedQuest = false; UpdateQuestObjectiveProgress(QuestObjectiveType.Monster, (int)entry, 1, guid);
for (byte i = 0; i < SharedConst.MaxQuestLogSize; ++i)
{
uint questid = GetQuestSlotQuestId(i);
if (questid == 0)
continue;
Quest qInfo = Global.ObjectMgr.GetQuestTemplate(questid);
if (qInfo == null || !qInfo.HasQuestObjectiveType(QuestObjectiveType.Monster))
continue;
if (m_QuestStatus[questid].Status != QuestStatus.Incomplete)
continue;
if (GetGroup() && GetGroup().IsRaidGroup() && qInfo.IsAllowedInRaid(GetMap().GetDifficultyID()))
continue;
foreach (QuestObjective obj in qInfo.Objectives)
{
if (obj.Type != QuestObjectiveType.Monster || !IsQuestObjectiveCompletable(i, qInfo, obj))
continue;
if (obj.ObjectID != entry)
continue;
uint reqKillCount = (uint)obj.Amount;
int curKillCount = GetQuestSlotObjectiveData(i, obj);
if (curKillCount < reqKillCount)
{
SetQuestObjectiveData(obj, curKillCount + addKillCount);
SendQuestUpdateAddCredit(qInfo, guid, obj, (uint)(curKillCount + addKillCount));
if (!completedObjectiveInSequencedQuest && qInfo.HasSpecialFlag(QuestSpecialFlags.SequencedObjectives) && 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();
} }
public void KilledPlayerCredit() public void KilledPlayerCredit()
{ {
ushort addKillCount = 1; UpdateQuestObjectiveProgress(QuestObjectiveType.PlayerKills, 0, 1);
bool completedObjectiveInSequencedQuest = false;
for (byte i = 0; i < SharedConst.MaxQuestLogSize; ++i)
{
uint questid = GetQuestSlotQuestId(i);
if (questid == 0)
continue;
Quest qInfo = Global.ObjectMgr.GetQuestTemplate(questid);
if (qInfo == null || !qInfo.HasQuestObjectiveType(QuestObjectiveType.PlayerKills))
continue;
// just if !ingroup || !noraidgroup || raidgroup
QuestStatusData q_status = m_QuestStatus[questid];
if (q_status.Status == QuestStatus.Incomplete && (GetGroup() == null || !GetGroup().IsRaidGroup() || qInfo.IsAllowedInRaid(GetMap().GetDifficultyID())))
{
foreach (QuestObjective obj in qInfo.Objectives)
{
if (obj.Type != QuestObjectiveType.PlayerKills || !IsQuestObjectiveCompletable(i, qInfo, obj))
continue;
int curKillCount = GetQuestSlotObjectiveData(i, obj);
if (curKillCount < obj.Amount)
{
SetQuestObjectiveData(obj, curKillCount + addKillCount);
SendQuestUpdateAddPlayer(qInfo, (uint)(curKillCount + addKillCount));
if (!completedObjectiveInSequencedQuest && qInfo.HasSpecialFlag(QuestSpecialFlags.SequencedObjectives) && 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();
} }
public void KillCreditGO(uint entry, ObjectGuid guid = default) public void KillCreditGO(uint entry, ObjectGuid guid = default)
{ {
ushort addCastCount = 1; UpdateQuestObjectiveProgress(QuestObjectiveType.GameObject, (int)entry, 1, guid);
bool completedObjectiveInSequencedQuest = false;
for (byte i = 0; i < SharedConst.MaxQuestLogSize; ++i)
{
uint questid = GetQuestSlotQuestId(i);
if (questid == 0)
continue;
Quest qInfo = Global.ObjectMgr.GetQuestTemplate(questid);
if (qInfo == null || !qInfo.HasQuestObjectiveType(QuestObjectiveType.GameObject))
continue;
if (m_QuestStatus[questid].Status != QuestStatus.Incomplete)
continue;
if (GetGroup() && GetGroup().IsRaidGroup() && qInfo.IsAllowedInRaid(GetMap().GetDifficultyID()))
continue;
foreach (QuestObjective obj in qInfo.Objectives)
{
if (obj.Type != QuestObjectiveType.GameObject || !IsQuestObjectiveCompletable(i, qInfo, obj))
continue;
int reqTarget = obj.ObjectID;
// other not this creature/GO related objectives
if (obj.ObjectID != entry)
continue;
uint reqCastCount = (uint)obj.Amount;
int curCastCount = GetQuestSlotObjectiveData(i, obj);
if (curCastCount < reqCastCount)
{
SetQuestObjectiveData(obj, curCastCount + addCastCount);
SendQuestUpdateAddCredit(qInfo, guid, obj, (uint)(curCastCount + addCastCount));
if (!completedObjectiveInSequencedQuest && qInfo.HasSpecialFlag(QuestSpecialFlags.SequencedObjectives) && 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();
} }
public void KillCreditCriteriaTreeObjective(QuestObjective questObjective) public void KillCreditCriteriaTreeObjective(QuestObjective questObjective)
{ {
if (questObjective.Type != QuestObjectiveType.CriteriaTree) UpdateQuestObjectiveProgress(QuestObjectiveType.CriteriaTree, questObjective.ObjectID, 1);
return;
Quest quest = Global.ObjectMgr.GetQuestTemplate(questObjective.QuestID);
if (!IsQuestObjectiveComplete(FindQuestSlot(questObjective.QuestID), quest, questObjective))
{
SetQuestObjectiveData(questObjective, 1);
SendQuestUpdateAddCreditSimple(questObjective);
if (CanCompleteQuest(questObjective.QuestID))
CompleteQuest(questObjective.QuestID);
if (quest.HasSpecialFlag(QuestSpecialFlags.SequencedObjectives))
UpdateForQuestWorldObjects();
}
} }
public void TalkedToCreature(uint entry, ObjectGuid guid) public void TalkedToCreature(uint entry, ObjectGuid guid)
{ {
ushort addTalkCount = 1; UpdateQuestObjectiveProgress(QuestObjectiveType.TalkTo, (int)entry, 1, guid);
bool completedObjectiveInSequencedQuest = false;
for (byte i = 0; i < SharedConst.MaxQuestLogSize; ++i)
{
uint questid = GetQuestSlotQuestId(i);
if (questid == 0)
continue;
Quest qInfo = Global.ObjectMgr.GetQuestTemplate(questid);
if (qInfo == null || !qInfo.HasQuestObjectiveType(QuestObjectiveType.TalkTo))
continue;
if (m_QuestStatus[questid].Status != QuestStatus.Incomplete)
continue;
if (GetGroup() && GetGroup().IsRaidGroup() && qInfo.IsAllowedInRaid(GetMap().GetDifficultyID()))
continue;
foreach (QuestObjective obj in qInfo.Objectives)
{
if (obj.Type != QuestObjectiveType.TalkTo || !IsQuestObjectiveCompletable(i, qInfo, obj))
continue;
if (obj.ObjectID != entry)
continue;
uint reqTalkCount = (uint)obj.Amount;
int curTalkCount = GetQuestSlotObjectiveData(i, obj);
if (curTalkCount < reqTalkCount)
{
SetQuestObjectiveData(obj, curTalkCount + addTalkCount);
SendQuestUpdateAddCredit(qInfo, guid, obj, (uint)(curTalkCount + addTalkCount));
if (!completedObjectiveInSequencedQuest && qInfo.HasSpecialFlag(QuestSpecialFlags.SequencedObjectives) && 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();
} }
public void MoneyChanged(ulong value) public void MoneyChanged(ulong value)
{ {
bool completedObjectiveInSequencedQuest = false; UpdateQuestObjectiveProgress(QuestObjectiveType.Money, 0, (long)value - (long)GetMoney());
for (byte i = 0; i < SharedConst.MaxQuestLogSize; ++i)
{
uint questid = GetQuestSlotQuestId(i);
if (questid == 0)
continue;
Quest qInfo = Global.ObjectMgr.GetQuestTemplate(questid);
if (qInfo == null || !qInfo.HasQuestObjectiveType(QuestObjectiveType.Money))
continue;
QuestStatusData q_status = m_QuestStatus[questid];
foreach (QuestObjective obj in qInfo.Objectives)
{
if (obj.Type != QuestObjectiveType.Money || !IsQuestObjectiveCompletable(i, qInfo, obj))
continue;
if (!IsQuestObjectiveComplete(i, qInfo, obj))
{
if ((long)value >= obj.Amount)
{
if (CanCompleteQuest(questid))
CompleteQuest(questid);
if (!completedObjectiveInSequencedQuest && qInfo.HasSpecialFlag(QuestSpecialFlags.SequencedObjectives))
completedObjectiveInSequencedQuest = true;
}
}
else if (q_status.Status == QuestStatus.Complete)
{
if ((long)value < obj.Amount)
IncompleteQuest(questid);
}
}
}
if (completedObjectiveInSequencedQuest)
UpdateForQuestWorldObjects();
} }
public void ReputationChanged(FactionRecord FactionRecord, int change) public void ReputationChanged(FactionRecord FactionRecord, int change)
{ {
bool completedObjectiveInSequencedQuest = false; UpdateQuestObjectiveProgress(QuestObjectiveType.MinReputation, (int)FactionRecord.Id, change);
for (byte i = 0; i < SharedConst.MaxQuestLogSize; ++i) UpdateQuestObjectiveProgress(QuestObjectiveType.MaxReputation, (int)FactionRecord.Id, change);
{ UpdateQuestObjectiveProgress(QuestObjectiveType.IncreaseReputation, (int)FactionRecord.Id, change);
uint questid = GetQuestSlotQuestId(i);
if (questid != 0)
{
Quest qInfo = Global.ObjectMgr.GetQuestTemplate(questid);
if (qInfo != null)
{
QuestStatusData q_status = m_QuestStatus[questid];
if (!qInfo.HasQuestObjectiveType(QuestObjectiveType.MinReputation) && !qInfo.HasQuestObjectiveType(QuestObjectiveType.MaxReputation)
&& (!qInfo.HasQuestObjectiveType(QuestObjectiveType.IncreaseReputation) || q_status.Status != QuestStatus.Incomplete))
continue;
foreach (QuestObjective obj in qInfo.Objectives)
{
if (obj.ObjectID != FactionRecord.Id || !IsQuestObjectiveCompletable(i, qInfo, obj))
continue;
switch (obj.Type)
{
case QuestObjectiveType.MinReputation:
if (!IsQuestObjectiveComplete(i, qInfo, obj))
{
if (GetReputationMgr().GetReputation(FactionRecord) + change >= obj.Amount)
{
if (CanCompleteQuest(questid))
CompleteQuest(questid);
if (qInfo.HasSpecialFlag(QuestSpecialFlags.SequencedObjectives))
completedObjectiveInSequencedQuest = true;
}
}
else if (q_status.Status == QuestStatus.Complete)
{
if (GetReputationMgr().GetReputation(FactionRecord) + change < obj.Amount)
IncompleteQuest(questid);
}
break;
case QuestObjectiveType.MaxReputation:
if (!IsQuestObjectiveComplete(i, qInfo, obj))
{
if (GetReputationMgr().GetReputation(FactionRecord) + change <= obj.Amount)
{
if (CanCompleteQuest(questid))
CompleteQuest(questid);
if (qInfo.HasSpecialFlag(QuestSpecialFlags.SequencedObjectives))
completedObjectiveInSequencedQuest = true;
}
}
else if (q_status.Status == QuestStatus.Complete)
{
if (GetReputationMgr().GetReputation(FactionRecord) + change > obj.Amount)
IncompleteQuest(questid);
}
break;
case QuestObjectiveType.IncreaseReputation:
int currentProgress = GetQuestSlotObjectiveData(i, obj);
if (change > 0 && currentProgress < obj.Amount)
{
SetQuestObjectiveData(obj, currentProgress + change);
SendQuestUpdateAddCredit(qInfo, ObjectGuid.Empty, obj, (uint)(currentProgress + change));
if (CanCompleteQuest(questid))
CompleteQuest(questid);
if (!completedObjectiveInSequencedQuest && qInfo.HasSpecialFlag(QuestSpecialFlags.SequencedObjectives) && IsQuestObjectiveComplete(i, qInfo, obj))
completedObjectiveInSequencedQuest = true;
}
break;
}
}
}
}
}
if (completedObjectiveInSequencedQuest)
UpdateForQuestWorldObjects();
} }
void CurrencyChanged(uint currencyId, int change) void CurrencyChanged(uint currencyId, int change)
{ {
bool completedObjectiveInSequencedQuest = false; UpdateQuestObjectiveProgress(QuestObjectiveType.Currency, (int)currencyId, change);
for (byte i = 0; i < SharedConst.MaxQuestLogSize; ++i) UpdateQuestObjectiveProgress(QuestObjectiveType.HaveCurrency, (int)currencyId, change);
UpdateQuestObjectiveProgress(QuestObjectiveType.ObtainCurrency, (int)currencyId, change);
}
void UpdateQuestObjectiveProgress(QuestObjectiveType objectiveType, int objectId, long addCount, ObjectGuid victimGuid = default)
{
bool anyObjectiveChangedCompletionState = false;
foreach (var objectiveStatusData in m_questObjectiveStatus.LookupByKey(Tuple.Create(objectiveType, objectId)))
{ {
uint questid = GetQuestSlotQuestId(i); uint questId = objectiveStatusData.QuestStatusPair.Key;
if (questid == 0) Quest quest = Global.ObjectMgr.GetQuestTemplate(questId);
continue;
Quest qInfo = Global.ObjectMgr.GetQuestTemplate(questid); if (!QuestObjective.CanAlwaysBeProgressedInRaid(objectiveType))
if (qInfo == null) if (GetGroup() && GetGroup().IsRaidGroup() && quest.IsAllowedInRaid(GetMap().GetDifficultyID()))
continue;
QuestStatusData q_status = m_QuestStatus[questid];
if (!qInfo.HasQuestObjectiveType(QuestObjectiveType.Currency) && !qInfo.HasQuestObjectiveType(QuestObjectiveType.HaveCurrency)
&& (!qInfo.HasQuestObjectiveType(QuestObjectiveType.ObtainCurrency) || q_status.Status != QuestStatus.Incomplete))
continue;
foreach (QuestObjective obj in qInfo.Objectives)
{
if (obj.ObjectID != currencyId || !IsQuestObjectiveCompletable(i, qInfo, obj))
continue; continue;
switch (obj.Type) ushort logSlot = objectiveStatusData.QuestStatusPair.Value.Slot;
QuestObjective objective = objectiveStatusData.Objective;
if (!IsQuestObjectiveCompletable(logSlot, quest, objective))
continue;
bool objectiveWasComplete = IsQuestObjectiveComplete(logSlot, quest, objective);
if (!objectiveWasComplete || addCount < 0)
{
bool objectiveIsNowComplete = false;
if (objective.IsStoringValue())
{ {
case QuestObjectiveType.Currency: if (objectiveType == QuestObjectiveType.PlayerKills && objective.Flags.HasAnyFlag(QuestObjectiveFlags.KillPlayersSameFaction))
if (!IsQuestObjectiveComplete(i, qInfo, obj)) {
{ Player victim = Global.ObjAccessor.GetPlayer(GetMap(), victimGuid);
if (GetCurrency(currencyId) + change >= obj.Amount) if (victim?.GetTeam() != GetTeam())
{ continue;
if (CanCompleteQuest(questid)) }
CompleteQuest(questid);
if (qInfo.HasSpecialFlag(QuestSpecialFlags.SequencedObjectives)) int currentProgress = GetQuestSlotObjectiveData(logSlot, objective);
completedObjectiveInSequencedQuest = true; if (addCount > 0 ? (currentProgress < objective.Amount) : (currentProgress > 0))
} {
} int newProgress = (int)Math.Clamp(currentProgress + addCount, 0, objective.Amount);
else if (q_status.Status == QuestStatus.Complete) SetQuestObjectiveData(objective, newProgress);
if (addCount > 0 && !objective.Flags.HasAnyFlag(QuestObjectiveFlags.HideCreditMsg))
{ {
if (GetCurrency(currencyId) + change < obj.Amount) if (objectiveType != QuestObjectiveType.PlayerKills)
IncompleteQuest(questid); SendQuestUpdateAddCredit(quest, victimGuid, objective, (uint)newProgress);
else
SendQuestUpdateAddPlayer(quest, (uint)newProgress);
} }
break;
case QuestObjectiveType.HaveCurrency: objectiveIsNowComplete = IsQuestObjectiveComplete(logSlot, quest, objective);
int newProgress = (int)Math.Min(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(QuestSpecialFlags.SequencedObjectives))
completedObjectiveInSequencedQuest = true;
}
}
else if (q_status.Status == QuestStatus.Complete)
{
if (newProgress < obj.Amount)
IncompleteQuest(questid);
}
break;
case QuestObjectiveType.ObtainCurrency:
int currentProgress = GetQuestSlotObjectiveData(i, obj);
if (change > 0 && currentProgress < obj.Amount)
{
SetQuestObjectiveData(obj, currentProgress + change);
SendQuestUpdateAddCredit(qInfo, ObjectGuid.Empty, obj, (uint)(currentProgress + change));
if (CanCompleteQuest(questid))
CompleteQuest(questid);
if (!completedObjectiveInSequencedQuest && qInfo.HasSpecialFlag(QuestSpecialFlags.SequencedObjectives) && IsQuestObjectiveComplete(i, qInfo, obj))
completedObjectiveInSequencedQuest = true;
}
break;
} }
else if (objective.IsStoringFlag())
{
SetQuestObjectiveData(objective, addCount > 0 ? 1 : 0);
if (addCount > 0 && !objective.Flags.HasAnyFlag(QuestObjectiveFlags.HideCreditMsg))
SendQuestUpdateAddCreditSimple(objective);
objectiveIsNowComplete = IsQuestObjectiveComplete(logSlot, quest, objective);
}
else
{
switch (objectiveType)
{
case QuestObjectiveType.Currency:
objectiveIsNowComplete = GetCurrency((uint)objectId) + addCount >= objective.Amount;
break;
case QuestObjectiveType.LearnSpell:
objectiveIsNowComplete = addCount != 0;
break;
case QuestObjectiveType.MinReputation:
objectiveIsNowComplete = GetReputationMgr().GetReputation((uint)objectId) + addCount >= objective.Amount;
break;
case QuestObjectiveType.MaxReputation:
objectiveIsNowComplete = GetReputationMgr().GetReputation((uint)objectId) + addCount <= objective.Amount;
break;
case QuestObjectiveType.Money:
objectiveIsNowComplete = (long)GetMoney() + addCount >= objective.Amount;
break;
case QuestObjectiveType.ProgressBar:
objectiveIsNowComplete = IsQuestObjectiveProgressBarComplete(logSlot, quest);
break;
default:
Cypher.Assert(false, "Unhandled quest objective type {objectiveType}");
break;
}
}
if (objective.Flags.HasAnyFlag(QuestObjectiveFlags.PartOfProgressBar))
{
if (IsQuestObjectiveProgressBarComplete(logSlot, quest))
{
var progressBarObjective = quest.Objectives.Find(otherObjective => otherObjective.Type == QuestObjectiveType.ProgressBar && !otherObjective.Flags.HasAnyFlag(QuestObjectiveFlags.PartOfProgressBar));
if (progressBarObjective != null)
SendQuestUpdateAddCreditSimple(progressBarObjective);
objectiveIsNowComplete = true;
}
}
if (objectiveWasComplete != objectiveIsNowComplete)
anyObjectiveChangedCompletionState = true;
if (objectiveIsNowComplete && CanCompleteQuest(questId, objective.Id))
CompleteQuest(questId);
else if (objectiveStatusData.QuestStatusPair.Value.Status == QuestStatus.Complete)
IncompleteQuest(questId);
} }
} }
if (completedObjectiveInSequencedQuest) if (anyObjectiveChangedCompletionState)
UpdateForQuestWorldObjects(); UpdateForQuestWorldObjects();
} }
public bool HasQuestForItem(uint itemid) public bool HasQuestForItem(uint itemid)
{ {
for (byte i = 0; i < SharedConst.MaxQuestLogSize; ++i) // Search incomplete objective first
foreach (var objectiveItr in m_questObjectiveStatus.LookupByKey(Tuple.Create(QuestObjectiveType.Item, itemid)))
{ {
uint questid = GetQuestSlotQuestId(i); Quest qInfo = Global.ObjectMgr.GetQuestTemplate(objectiveItr.QuestStatusPair.Key);
if (questid == 0) QuestObjective objective = objectiveItr.Objective;
if (!IsQuestObjectiveCompletable(objectiveItr.QuestStatusPair.Value.Slot, qInfo, objective))
continue; continue;
var q_status = m_QuestStatus.LookupByKey(questid); // hide quest if player is in raid-group and quest is no raid quest
if (q_status == null) if (GetGroup() && GetGroup().IsRaidGroup() && !qInfo.IsAllowedInRaid(GetMap().GetDifficultyID()))
continue; 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
if (q_status.Status == QuestStatus.Incomplete)
{
Quest qInfo = Global.ObjectMgr.GetQuestTemplate(questid);
if (qInfo == null)
continue; continue;
// hide quest if player is in raid-group and quest is no raid quest if (!IsQuestObjectiveComplete(objectiveItr.QuestStatusPair.Value.Slot, qInfo, objective))
if (GetGroup() != null && GetGroup().IsRaidGroup() && !qInfo.IsAllowedInRaid(GetMap().GetDifficultyID())) return true;
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;
// There should be no mixed ReqItem/ReqSource drop // This part - for ItemDrop
// This part for ReqItem drop foreach (var questStatus in m_QuestStatus)
foreach (QuestObjective obj in qInfo.Objectives) {
{ if (questStatus.Value.Status != QuestStatus.Incomplete)
if (obj.Type == QuestObjectiveType.Item && IsQuestObjectiveCompletable(i, qInfo, obj) && itemid == obj.ObjectID && GetQuestSlotObjectiveData(i, obj) < obj.Amount) continue;
return true;
}
// This part - for ReqSource
for (byte j = 0; j < SharedConst.QuestItemDropCount; ++j)
{
// examined item is a source item
if (qInfo.ItemDrop[j] == itemid)
{
ItemTemplate pProto = Global.ObjectMgr.GetItemTemplate(itemid);
// 'unique' item Quest qInfo = Global.ObjectMgr.GetQuestTemplate(questStatus.Key);
if (pProto.GetMaxCount() != 0 && GetItemCount(itemid, true) < pProto.GetMaxCount()) // hide quest if player is in raid-group and quest is no raid quest
return true; if (GetGroup() && GetGroup().IsRaidGroup() && !qInfo.IsAllowedInRaid(GetMap().GetDifficultyID()))
if (!InBattleground())
continue;
// allows custom amount drop when not 0 for (byte j = 0; j < SharedConst.QuestItemDropCount; ++j)
if (qInfo.ItemDropQuantity[j] != 0) {
{ // examined item is a source item
if (GetItemCount(itemid, true) < qInfo.ItemDropQuantity[j]) if (qInfo.ItemDrop[j] != itemid)
return true; continue;
}
else if (GetItemCount(itemid, true) < pProto.GetMaxStackSize()) ItemTemplate pProto = Global.ObjectMgr.GetItemTemplate(itemid);
return true;
} // allows custom amount drop when not 0
} uint maxAllowedCount = qInfo.ItemDropQuantity[j] != 0 ? qInfo.ItemDropQuantity[j] : pProto.GetMaxStackSize();
// 'unique' item
if (pProto.GetMaxCount() != 0 && pProto.GetMaxCount() < maxAllowedCount)
maxAllowedCount = pProto.GetMaxCount();
if (GetItemCount(itemid, true) < maxAllowedCount)
return true;
} }
} }
return false; return false;
} }
+19 -10
View File
@@ -201,10 +201,11 @@ namespace Game
if (player.IsAlive()) if (player.IsAlive())
{ {
// not using Player.UpdateQuestObjectiveProgress, ObjectID in quest_objectives can be set to -1, areatrigger_involvedrelation then holds correct id
List<uint> quests = Global.ObjectMgr.GetQuestsForAreaTrigger(packet.AreaTriggerID); List<uint> quests = Global.ObjectMgr.GetQuestsForAreaTrigger(packet.AreaTriggerID);
if (quests != null) if (quests != null)
{ {
bool completedObjectiveInSequencedQuest = false; bool anyObjectiveChangedCompletionState = false;
foreach (uint questId in quests) foreach (uint questId in quests)
{ {
Quest qInfo = Global.ObjectMgr.GetQuestTemplate(questId); Quest qInfo = Global.ObjectMgr.GetQuestTemplate(questId);
@@ -213,14 +214,22 @@ namespace Game
{ {
foreach (QuestObjective obj in qInfo.Objectives) foreach (QuestObjective obj in qInfo.Objectives)
{ {
if (obj.Type == QuestObjectiveType.AreaTrigger && !player.IsQuestObjectiveComplete(slot, qInfo, obj)) if (obj.Type != QuestObjectiveType.AreaTrigger)
{ continue;
player.SetQuestObjectiveData(obj, 1);
player.SendQuestUpdateAddCreditSimple(obj); if (!player.IsQuestObjectiveCompletable(slot, qInfo, obj))
if (qInfo.HasSpecialFlag(QuestSpecialFlags.SequencedObjectives)) continue;
completedObjectiveInSequencedQuest = true;
break; 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)) if (player.CanCompleteQuest(questId))
@@ -228,7 +237,7 @@ namespace Game
} }
} }
if (completedObjectiveInSequencedQuest) if (anyObjectiveChangedCompletionState)
player.UpdateForQuestWorldObjects(); player.UpdateForQuestWorldObjects();
} }
} }
+39
View File
@@ -810,6 +810,26 @@ namespace Game
public string Description; public string Description;
public int[] VisualEffects = new int[0]; public int[] VisualEffects = new int[0];
public bool IsStoringValue()
{
switch (Type)
{
case QuestObjectiveType.Monster:
case QuestObjectiveType.Item:
case QuestObjectiveType.GameObject:
case QuestObjectiveType.TalkTo:
case QuestObjectiveType.PlayerKills:
case QuestObjectiveType.WinPvpPetBattles:
case QuestObjectiveType.HaveCurrency:
case QuestObjectiveType.ObtainCurrency:
case QuestObjectiveType.IncreaseReputation:
return true;
default:
break;
}
return false;
}
public bool IsStoringFlag() public bool IsStoringFlag()
{ {
switch (Type) switch (Type)
@@ -826,5 +846,24 @@ namespace Game
} }
return false; return false;
} }
public static bool CanAlwaysBeProgressedInRaid(QuestObjectiveType type)
{
switch (type)
{
case QuestObjectiveType.Item:
case QuestObjectiveType.Currency:
case QuestObjectiveType.LearnSpell:
case QuestObjectiveType.MinReputation:
case QuestObjectiveType.MaxReputation:
case QuestObjectiveType.Money:
case QuestObjectiveType.HaveCurrency:
case QuestObjectiveType.IncreaseReputation:
return true;
default:
break;
}
return false;
}
} }
} }