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:
@@ -17,6 +17,7 @@
|
||||
|
||||
using Framework.Constants;
|
||||
using Game.Achievements;
|
||||
using Game.BattleGrounds;
|
||||
using Game.Chat;
|
||||
using Game.DataStorage;
|
||||
using Game.Garrisons;
|
||||
@@ -25,9 +26,9 @@ using Game.Mails;
|
||||
using Game.Maps;
|
||||
using Game.Misc;
|
||||
using Game.Spells;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Game.BattleGrounds;
|
||||
|
||||
namespace Game.Entities
|
||||
{
|
||||
@@ -164,6 +165,7 @@ namespace Game.Entities
|
||||
List<uint> m_monthlyquests = new();
|
||||
MultiMap<uint, uint> m_seasonalquests = new();
|
||||
Dictionary<uint, QuestStatusData> m_QuestStatus = new();
|
||||
MultiMap<Tuple<QuestObjectiveType, int>, QuestObjectiveStatusData> m_questObjectiveStatus = new();
|
||||
Dictionary<uint, QuestSaveType> m_QuestStatusSave = new();
|
||||
List<uint> m_DFQuests = new();
|
||||
List<uint> m_RewardedQuests = new();
|
||||
@@ -625,4 +627,10 @@ namespace Game.Entities
|
||||
Deleted
|
||||
}
|
||||
}
|
||||
|
||||
struct QuestObjectiveStatusData
|
||||
{
|
||||
public KeyValuePair<uint, QuestStatusData> QuestStatusPair;
|
||||
public QuestObjective Objective;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ using Game.Networking.Packets;
|
||||
using Game.Spells;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Game.Entities
|
||||
{
|
||||
@@ -462,22 +461,22 @@ namespace Game.Entities
|
||||
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)
|
||||
return false;
|
||||
|
||||
if (!qInfo.IsRepeatable() && GetQuestRewardStatus(quest_id))
|
||||
if (!qInfo.IsRepeatable() && GetQuestRewardStatus(questId))
|
||||
return false; // not allow re-complete quest
|
||||
|
||||
// auto complete quest
|
||||
if ((qInfo.IsAutoComplete() || qInfo.Flags.HasAnyFlag(QuestFlags.AutoComplete)) && CanTakeQuest(qInfo, false))
|
||||
return true;
|
||||
|
||||
var q_status = m_QuestStatus.LookupByKey(quest_id);
|
||||
var q_status = m_QuestStatus.LookupByKey(questId);
|
||||
if (q_status == null)
|
||||
return false;
|
||||
|
||||
@@ -485,6 +484,9 @@ namespace Game.Entities
|
||||
{
|
||||
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 (!IsQuestObjectiveComplete(q_status.Slot, qInfo, obj))
|
||||
@@ -731,6 +733,7 @@ namespace Game.Entities
|
||||
|
||||
uint quest_id = quest.Id;
|
||||
|
||||
// if not exist then created with set uState == NEW and rewarded=false
|
||||
if (!m_QuestStatus.ContainsKey(quest_id))
|
||||
m_QuestStatus[quest_id] = new QuestStatusData();
|
||||
|
||||
@@ -745,6 +748,7 @@ namespace Game.Entities
|
||||
|
||||
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)
|
||||
{
|
||||
case QuestObjectiveType.MinReputation:
|
||||
@@ -1775,16 +1779,22 @@ namespace Game.Entities
|
||||
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);
|
||||
m_QuestStatusSave[quest_id] = QuestSaveType.Delete;
|
||||
foreach (var objective in m_questObjectiveStatus.KeyValueList)
|
||||
{
|
||||
if (objective.Value.QuestStatusPair.Value == questStatus)
|
||||
m_questObjectiveStatus.Remove(objective);
|
||||
}
|
||||
m_QuestStatus.Remove(questId);
|
||||
m_QuestStatusSave[questId] = QuestSaveType.Delete;
|
||||
}
|
||||
|
||||
if (update)
|
||||
SendQuestUpdate(quest_id);
|
||||
SendQuestUpdate(questId);
|
||||
}
|
||||
|
||||
public void RemoveRewardedQuest(uint questId, bool update = true)
|
||||
@@ -2162,81 +2172,31 @@ namespace Game.Entities
|
||||
|
||||
public void ItemAddedQuestCheck(uint entry, uint count)
|
||||
{
|
||||
for (byte i = 0; i < SharedConst.MaxQuestLogSize; ++i)
|
||||
{
|
||||
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();
|
||||
UpdateQuestObjectiveProgress(QuestObjectiveType.Item, (int)entry, 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);
|
||||
if (questid == 0)
|
||||
uint questId = objectiveStatusData.QuestStatusPair.Key;
|
||||
Quest quest = Global.ObjectMgr.GetQuestTemplate(questId);
|
||||
ushort logSlot = objectiveStatusData.QuestStatusPair.Value.Slot;
|
||||
QuestObjective objective = objectiveStatusData.Objective;
|
||||
|
||||
if (!IsQuestObjectiveCompletable(logSlot, quest, objective))
|
||||
continue;
|
||||
|
||||
Quest qInfo = Global.ObjectMgr.GetQuestTemplate(questid);
|
||||
if (qInfo == null || !qInfo.HasQuestObjectiveType(QuestObjectiveType.Item))
|
||||
continue;
|
||||
int curItemCount = GetQuestSlotObjectiveData(logSlot, objective);
|
||||
if (curItemCount >= objective.Amount) // we may have more than what the status shows
|
||||
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))
|
||||
continue;
|
||||
|
||||
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;
|
||||
SetQuestObjectiveData(objective, newItemCount);
|
||||
IncompleteQuest(questId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2270,489 +2230,209 @@ namespace Game.Entities
|
||||
StartCriteriaTimer(CriteriaTimedTypes.Creature, real_entry); // MUST BE CALLED FIRST
|
||||
UpdateCriteria(CriteriaTypes.KillCreature, real_entry, addKillCount, 0, killed);
|
||||
|
||||
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.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();
|
||||
UpdateQuestObjectiveProgress(QuestObjectiveType.Monster, (int)entry, 1, guid);
|
||||
}
|
||||
|
||||
public void KilledPlayerCredit()
|
||||
{
|
||||
ushort addKillCount = 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();
|
||||
UpdateQuestObjectiveProgress(QuestObjectiveType.PlayerKills, 0, 1);
|
||||
}
|
||||
|
||||
public void KillCreditGO(uint entry, ObjectGuid guid = default)
|
||||
{
|
||||
ushort addCastCount = 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.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();
|
||||
UpdateQuestObjectiveProgress(QuestObjectiveType.GameObject, (int)entry, 1, guid);
|
||||
}
|
||||
|
||||
public void KillCreditCriteriaTreeObjective(QuestObjective questObjective)
|
||||
{
|
||||
if (questObjective.Type != QuestObjectiveType.CriteriaTree)
|
||||
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();
|
||||
}
|
||||
UpdateQuestObjectiveProgress(QuestObjectiveType.CriteriaTree, questObjective.ObjectID, 1);
|
||||
}
|
||||
|
||||
public void TalkedToCreature(uint entry, ObjectGuid guid)
|
||||
{
|
||||
ushort addTalkCount = 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.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();
|
||||
UpdateQuestObjectiveProgress(QuestObjectiveType.TalkTo, (int)entry, 1, guid);
|
||||
}
|
||||
|
||||
public void MoneyChanged(ulong value)
|
||||
{
|
||||
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.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();
|
||||
UpdateQuestObjectiveProgress(QuestObjectiveType.Money, 0, (long)value - (long)GetMoney());
|
||||
}
|
||||
|
||||
public void ReputationChanged(FactionRecord FactionRecord, int change)
|
||||
{
|
||||
bool completedObjectiveInSequencedQuest = false;
|
||||
for (byte i = 0; i < SharedConst.MaxQuestLogSize; ++i)
|
||||
{
|
||||
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();
|
||||
UpdateQuestObjectiveProgress(QuestObjectiveType.MinReputation, (int)FactionRecord.Id, change);
|
||||
UpdateQuestObjectiveProgress(QuestObjectiveType.MaxReputation, (int)FactionRecord.Id, change);
|
||||
UpdateQuestObjectiveProgress(QuestObjectiveType.IncreaseReputation, (int)FactionRecord.Id, change);
|
||||
}
|
||||
|
||||
void CurrencyChanged(uint currencyId, int change)
|
||||
{
|
||||
bool completedObjectiveInSequencedQuest = false;
|
||||
for (byte i = 0; i < SharedConst.MaxQuestLogSize; ++i)
|
||||
UpdateQuestObjectiveProgress(QuestObjectiveType.Currency, (int)currencyId, change);
|
||||
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);
|
||||
if (questid == 0)
|
||||
continue;
|
||||
uint questId = objectiveStatusData.QuestStatusPair.Key;
|
||||
Quest quest = Global.ObjectMgr.GetQuestTemplate(questId);
|
||||
|
||||
Quest qInfo = Global.ObjectMgr.GetQuestTemplate(questid);
|
||||
if (qInfo == null)
|
||||
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))
|
||||
if (!QuestObjective.CanAlwaysBeProgressedInRaid(objectiveType))
|
||||
if (GetGroup() && GetGroup().IsRaidGroup() && quest.IsAllowedInRaid(GetMap().GetDifficultyID()))
|
||||
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 (!IsQuestObjectiveComplete(i, qInfo, obj))
|
||||
{
|
||||
if (GetCurrency(currencyId) + change >= obj.Amount)
|
||||
{
|
||||
if (CanCompleteQuest(questid))
|
||||
CompleteQuest(questid);
|
||||
if (objectiveType == QuestObjectiveType.PlayerKills && objective.Flags.HasAnyFlag(QuestObjectiveFlags.KillPlayersSameFaction))
|
||||
{
|
||||
Player victim = Global.ObjAccessor.GetPlayer(GetMap(), victimGuid);
|
||||
if (victim?.GetTeam() != GetTeam())
|
||||
continue;
|
||||
}
|
||||
|
||||
if (qInfo.HasSpecialFlag(QuestSpecialFlags.SequencedObjectives))
|
||||
completedObjectiveInSequencedQuest = true;
|
||||
}
|
||||
}
|
||||
else if (q_status.Status == QuestStatus.Complete)
|
||||
int currentProgress = GetQuestSlotObjectiveData(logSlot, objective);
|
||||
if (addCount > 0 ? (currentProgress < objective.Amount) : (currentProgress > 0))
|
||||
{
|
||||
int newProgress = (int)Math.Clamp(currentProgress + addCount, 0, objective.Amount);
|
||||
SetQuestObjectiveData(objective, newProgress);
|
||||
if (addCount > 0 && !objective.Flags.HasAnyFlag(QuestObjectiveFlags.HideCreditMsg))
|
||||
{
|
||||
if (GetCurrency(currencyId) + change < obj.Amount)
|
||||
IncompleteQuest(questid);
|
||||
if (objectiveType != QuestObjectiveType.PlayerKills)
|
||||
SendQuestUpdateAddCredit(quest, victimGuid, objective, (uint)newProgress);
|
||||
else
|
||||
SendQuestUpdateAddPlayer(quest, (uint)newProgress);
|
||||
}
|
||||
break;
|
||||
|
||||
case QuestObjectiveType.HaveCurrency:
|
||||
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;
|
||||
objectiveIsNowComplete = IsQuestObjectiveComplete(logSlot, quest, objective);
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
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);
|
||||
if (questid == 0)
|
||||
Quest qInfo = Global.ObjectMgr.GetQuestTemplate(objectiveItr.QuestStatusPair.Key);
|
||||
QuestObjective objective = objectiveItr.Objective;
|
||||
if (!IsQuestObjectiveCompletable(objectiveItr.QuestStatusPair.Value.Slot, qInfo, objective))
|
||||
continue;
|
||||
|
||||
var q_status = m_QuestStatus.LookupByKey(questid);
|
||||
if (q_status == null)
|
||||
continue;
|
||||
|
||||
if (q_status.Status == QuestStatus.Incomplete)
|
||||
{
|
||||
Quest qInfo = Global.ObjectMgr.GetQuestTemplate(questid);
|
||||
if (qInfo == null)
|
||||
// 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;
|
||||
|
||||
// hide quest if player is in raid-group and quest is no raid quest
|
||||
if (GetGroup() != null && 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.QuestStatusPair.Value.Slot, qInfo, objective))
|
||||
return true;
|
||||
}
|
||||
|
||||
// There should be no mixed ReqItem/ReqSource drop
|
||||
// This part for ReqItem drop
|
||||
foreach (QuestObjective obj in qInfo.Objectives)
|
||||
{
|
||||
if (obj.Type == QuestObjectiveType.Item && IsQuestObjectiveCompletable(i, qInfo, obj) && itemid == obj.ObjectID && GetQuestSlotObjectiveData(i, obj) < obj.Amount)
|
||||
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);
|
||||
// This part - for ItemDrop
|
||||
foreach (var questStatus in m_QuestStatus)
|
||||
{
|
||||
if (questStatus.Value.Status != QuestStatus.Incomplete)
|
||||
continue;
|
||||
|
||||
// 'unique' item
|
||||
if (pProto.GetMaxCount() != 0 && GetItemCount(itemid, true) < pProto.GetMaxCount())
|
||||
return true;
|
||||
Quest qInfo = Global.ObjectMgr.GetQuestTemplate(questStatus.Key);
|
||||
// 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;
|
||||
|
||||
// allows custom amount drop when not 0
|
||||
if (qInfo.ItemDropQuantity[j] != 0)
|
||||
{
|
||||
if (GetItemCount(itemid, true) < qInfo.ItemDropQuantity[j])
|
||||
return true;
|
||||
}
|
||||
else if (GetItemCount(itemid, true) < pProto.GetMaxStackSize())
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (byte j = 0; j < SharedConst.QuestItemDropCount; ++j)
|
||||
{
|
||||
// examined item is a source item
|
||||
if (qInfo.ItemDrop[j] != itemid)
|
||||
continue;
|
||||
|
||||
ItemTemplate pProto = Global.ObjectMgr.GetItemTemplate(itemid);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -201,10 +201,11 @@ namespace Game
|
||||
|
||||
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);
|
||||
if (quests != null)
|
||||
{
|
||||
bool completedObjectiveInSequencedQuest = false;
|
||||
bool anyObjectiveChangedCompletionState = false;
|
||||
foreach (uint questId in quests)
|
||||
{
|
||||
Quest qInfo = Global.ObjectMgr.GetQuestTemplate(questId);
|
||||
@@ -213,14 +214,22 @@ namespace Game
|
||||
{
|
||||
foreach (QuestObjective obj in qInfo.Objectives)
|
||||
{
|
||||
if (obj.Type == QuestObjectiveType.AreaTrigger && !player.IsQuestObjectiveComplete(slot, qInfo, obj))
|
||||
{
|
||||
player.SetQuestObjectiveData(obj, 1);
|
||||
player.SendQuestUpdateAddCreditSimple(obj);
|
||||
if (qInfo.HasSpecialFlag(QuestSpecialFlags.SequencedObjectives))
|
||||
completedObjectiveInSequencedQuest = true;
|
||||
break;
|
||||
}
|
||||
if (obj.Type != QuestObjectiveType.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))
|
||||
@@ -228,7 +237,7 @@ namespace Game
|
||||
}
|
||||
}
|
||||
|
||||
if (completedObjectiveInSequencedQuest)
|
||||
if (anyObjectiveChangedCompletionState)
|
||||
player.UpdateForQuestWorldObjects();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -810,6 +810,26 @@ namespace Game
|
||||
public string Description;
|
||||
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()
|
||||
{
|
||||
switch (Type)
|
||||
@@ -826,5 +846,24 @@ namespace Game
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user