Core/Quests: Quest improvements

Port From (https://github.com/TrinityCore/TrinityCore/commit/91b91b502f30f781c8dea77dec4ce2f469b89744)
This commit is contained in:
hondacrx
2021-05-11 11:39:14 -04:00
parent 852cd21cae
commit 8847de54d5
13 changed files with 539 additions and 305 deletions
+2
View File
@@ -498,6 +498,8 @@ namespace Framework.Constants
// Room For More Custom Flags // Room For More Custom Flags
DbAllowed = Repeatable | ExplorationOrEvent | AutoAccept | DfQuest | Monthly, DbAllowed = Repeatable | ExplorationOrEvent | AutoAccept | DfQuest | Monthly,
SequencedObjectives = 0x20 // Internal flag computed only
} }
public enum QuestSaveType public enum QuestSaveType
+6 -1
View File
@@ -1689,7 +1689,12 @@ namespace Game.Achievements
if (objective == null) if (objective == null)
return false; return false;
if (referencePlayer.GetQuestRewardStatus(objective.QuestID) || !referencePlayer.IsQuestObjectiveComplete(objective)) Quest quest = Global.ObjectMgr.GetQuestTemplate(objective.QuestID);
if (quest == null)
return false;
ushort slot = referencePlayer.FindQuestSlot(objective.QuestID);
if (slot >= SharedConst.MaxQuestLogSize || referencePlayer.GetQuestRewardStatus(objective.QuestID) || !referencePlayer.IsQuestObjectiveComplete(slot, quest, objective))
return false; return false;
break; break;
} }
+9 -1
View File
@@ -365,7 +365,15 @@ namespace Game.Conditions
if (questObj == null) if (questObj == null)
break; break;
condMeets = (!player.GetQuestRewardStatus(questObj.QuestID) && player.IsQuestObjectiveComplete(questObj)); Quest quest = Global.ObjectMgr.GetQuestTemplate(questObj.QuestID);
if (quest == null)
break;
ushort slot = player.FindQuestSlot(questObj.QuestID);
if (slot >= SharedConst.MaxQuestLogSize)
break;
condMeets = (!player.GetQuestRewardStatus(questObj.QuestID) && player.IsQuestObjectiveComplete(slot, quest, questObj));
} }
break; break;
} }
+4 -2
View File
@@ -2123,7 +2123,9 @@ namespace Game
if (condition.QuestKillID != 0) if (condition.QuestKillID != 0)
{ {
Quest quest = Global.ObjectMgr.GetQuestTemplate(condition.QuestKillID); Quest quest = Global.ObjectMgr.GetQuestTemplate(condition.QuestKillID);
if (quest != null && player.GetQuestStatus(condition.QuestKillID) != QuestStatus.Complete) ushort questSlot = player.FindQuestSlot(condition.QuestKillID);
if (quest != null && player.GetQuestStatus(condition.QuestKillID) != QuestStatus.Complete && questSlot < SharedConst.MaxQuestLogSize)
{ {
results = new bool[condition.QuestKillMonster.Length]; results = new bool[condition.QuestKillMonster.Length];
for (var i = 0; i < results.Length; ++i) for (var i = 0; i < results.Length; ++i)
@@ -2136,7 +2138,7 @@ namespace Game
var questObjective = quest.Objectives.Find(objective => objective.Type == QuestObjectiveType.Monster && objective.ObjectID == condition.QuestKillMonster[i]); var questObjective = quest.Objectives.Find(objective => objective.Type == QuestObjectiveType.Monster && objective.ObjectID == condition.QuestKillMonster[i]);
if (questObjective != null) if (questObjective != null)
results[i] = player.GetQuestObjectiveData(quest, questObjective.StorageIndex) >= questObjective.Amount; results[i] = player.GetQuestSlotObjectiveData(questSlot, questObjective) >= questObjective.Amount;
} }
} }
+25 -23
View File
@@ -709,6 +709,7 @@ namespace Game.Entities
// add to quest log // add to quest log
if (slot < SharedConst.MaxQuestLogSize && questStatusData.Status != QuestStatus.None) if (slot < SharedConst.MaxQuestLogSize && questStatusData.Status != QuestStatus.None)
{ {
questStatusData.Slot = slot;
SetQuestSlot(slot, quest_id, (uint)quest_time); // cast can't be helped SetQuestSlot(slot, quest_id, (uint)quest_time); // cast can't be helped
if (questStatusData.Status == QuestStatus.Complete) if (questStatusData.Status == QuestStatus.Complete)
@@ -719,14 +720,6 @@ namespace Game.Entities
++slot; ++slot;
} }
// Resize quest objective data to proper size
int maxStorageIndex = 0;
foreach (QuestObjective obj in quest.Objectives)
if (obj.StorageIndex > maxStorageIndex)
maxStorageIndex = obj.StorageIndex;
questStatusData.ObjectiveData = new int[maxStorageIndex + 1];
m_QuestStatus[quest_id] = questStatusData; m_QuestStatus[quest_id] = questStatusData;
Log.outDebug(LogFilter.ServerLoading, "Quest status is {0} for quest {1} for player (GUID: {2})", questStatusData.Status, quest_id, GetGUID().ToString()); Log.outDebug(LogFilter.ServerLoading, "Quest status is {0} for quest {1} for player (GUID: {2})", questStatusData.Status, quest_id, GetGUID().ToString());
} }
@@ -747,28 +740,26 @@ namespace Game.Entities
uint questID = result.Read<uint>(0); uint questID = result.Read<uint>(0);
Quest quest = Global.ObjectMgr.GetQuestTemplate(questID); Quest quest = Global.ObjectMgr.GetQuestTemplate(questID);
ushort slot = FindQuestSlot(questID);
var questStatusData = m_QuestStatus.LookupByKey(questID); var questStatusData = m_QuestStatus.LookupByKey(questID);
if (questStatusData != null && slot < SharedConst.MaxQuestLogSize && quest != null) if (questStatusData != null && questStatusData.Slot < SharedConst.MaxQuestLogSize && quest != null)
{ {
byte objectiveIndex = result.Read<byte>(1); byte storageIndex = result.Read<byte>(1);
var objectiveItr = quest.Objectives.FirstOrDefault(objective => objective.StorageIndex == objectiveIndex); var objective = quest.Objectives.FirstOrDefault(objective => objective.StorageIndex == storageIndex);
if (objectiveIndex < questStatusData.ObjectiveData.Length && objectiveItr != null) if (objective != null)
{ {
int data = result.Read<int>(2); int data = result.Read<int>(2);
questStatusData.ObjectiveData[objectiveIndex] = data; if (!objective.IsStoringFlag())
if (!objectiveItr.IsStoringFlag()) SetQuestSlotCounter(questStatusData.Slot, storageIndex, (ushort)data);
SetQuestSlotCounter(slot, objectiveIndex, (ushort)data);
else if (data != 0) else if (data != 0)
SetQuestSlotObjectiveFlag(slot, (sbyte)objectiveIndex); SetQuestSlotObjectiveFlag(questStatusData.Slot, (sbyte)storageIndex);
} }
else else
Log.outError(LogFilter.Player, "Player {0} ({1}) has quest {2} out of range objective index {3}.", GetName(), GetGUID().ToString(), questID, objectiveIndex); Log.outError(LogFilter.Player, $"Player {GetName()} ({GetGUID()}) has quest {questID} out of range objective index {storageIndex}.");
} }
else else
Log.outError(LogFilter.Player, "Player {0} ({1}) does not have quest {2} but has objective data for it.", GetName(), GetGUID().ToString(), questID); Log.outError(LogFilter.Player, $"Player {GetName()} ({GetGUID()}) does not have quest {questID} but has objective data for it.");
} }
while (result.NextRow()); while (result.NextRow());
} }
@@ -1943,7 +1934,7 @@ namespace Game.Entities
if (!isTransaction) if (!isTransaction)
trans = new SQLTransaction(); trans = new SQLTransaction();
PreparedStatement stmt = null; PreparedStatement stmt;
bool keepAbandoned = !Global.WorldMgr.GetCleaningFlags().HasAnyFlag(CleaningFlags.Queststatus); bool keepAbandoned = !Global.WorldMgr.GetCleaningFlags().HasAnyFlag(CleaningFlags.Queststatus);
foreach (var save in m_QuestStatusSave) foreach (var save in m_QuestStatusSave)
@@ -1961,13 +1952,24 @@ namespace Game.Entities
trans.Append(stmt); trans.Append(stmt);
// Save objectives // Save objectives
for (int i = 0; i < data.ObjectiveData.Length; ++i) stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_CHAR_QUESTSTATUS_OBJECTIVES_BY_QUEST);
stmt.AddValue(0, GetGUID().GetCounter());
stmt.AddValue(1, save.Key);
trans.Append(stmt);
Quest quest = Global.ObjectMgr.GetQuestTemplate(save.Key);
foreach (QuestObjective obj in quest.Objectives)
{ {
int count = GetQuestSlotObjectiveData(data.Slot, obj);
if (count == 0)
continue;
stmt = DB.Characters.GetPreparedStatement(CharStatements.REP_CHAR_QUESTSTATUS_OBJECTIVES); stmt = DB.Characters.GetPreparedStatement(CharStatements.REP_CHAR_QUESTSTATUS_OBJECTIVES);
stmt.AddValue(0, GetGUID().GetCounter()); stmt.AddValue(0, GetGUID().GetCounter());
stmt.AddValue(1, save.Key); stmt.AddValue(1, save.Key);
stmt.AddValue(2, i); stmt.AddValue(2, obj.StorageIndex);
stmt.AddValue(3, data.ObjectiveData[i]); stmt.AddValue(3, count);
trans.Append(stmt); trans.Append(stmt);
} }
} }
+451 -266
View File
@@ -487,7 +487,7 @@ namespace Game.Entities
{ {
if (!obj.Flags.HasAnyFlag(QuestObjectiveFlags.Optional) && !obj.Flags.HasAnyFlag(QuestObjectiveFlags.PartOfProgressBar)) if (!obj.Flags.HasAnyFlag(QuestObjectiveFlags.Optional) && !obj.Flags.HasAnyFlag(QuestObjectiveFlags.PartOfProgressBar))
{ {
if (!IsQuestObjectiveComplete(obj)) if (!IsQuestObjectiveComplete(q_status.Slot, qInfo, obj))
return false; return false;
} }
} }
@@ -740,14 +740,8 @@ namespace Game.Entities
// check for repeatable quests status reset // check for repeatable quests status reset
questStatusData.Status = QuestStatus.Incomplete; questStatusData.Status = QuestStatus.Incomplete;
int maxStorageIndex = 0;
foreach (QuestObjective obj in quest.Objectives)
if (obj.StorageIndex > maxStorageIndex)
maxStorageIndex = obj.StorageIndex;
questStatusData.ObjectiveData = new int[maxStorageIndex + 1];
GiveQuestSourceItem(quest); GiveQuestSourceItem(quest);
AdjustQuestObjectiveProgress(quest);
foreach (QuestObjective obj in quest.Objectives) foreach (QuestObjective obj in quest.Objectives)
{ {
@@ -802,10 +796,9 @@ namespace Game.Entities
caster.CastSpell(this, spellInfo.Id, new CastSpellExtraArgs(TriggerCastFlags.FullMask).SetCastDifficulty(spellInfo.Difficulty)); caster.CastSpell(this, spellInfo.Id, new CastSpellExtraArgs(TriggerCastFlags.FullMask).SetCastDifficulty(spellInfo.Difficulty));
} }
questStatusData.Slot = log_slot;
SetQuestSlot(log_slot, quest_id, qtime); SetQuestSlot(log_slot, quest_id, qtime);
AdjustQuestReqItemCount(quest);
m_QuestStatusSave[quest_id] = QuestSaveType.Default; m_QuestStatusSave[quest_id] = QuestSaveType.Default;
StartCriteriaTimer(CriteriaTimedTypes.Quest, quest_id); StartCriteriaTimer(CriteriaTimedTypes.Quest, quest_id);
@@ -1960,14 +1953,18 @@ namespace Game.Entities
if (qInfo == null) if (qInfo == null)
return 0; return 0;
ushort slot = FindQuestSlot(quest_id);
if (slot >= SharedConst.MaxQuestLogSize)
return 0;
foreach (QuestObjective obj in qInfo.Objectives) foreach (QuestObjective obj in qInfo.Objectives)
if (obj.ObjectID == entry) if (obj.ObjectID == entry)
return (ushort)GetQuestObjectiveData(qInfo, obj.StorageIndex); return (ushort)GetQuestSlotObjectiveData(slot, obj);
return 0; return 0;
} }
public void AdjustQuestReqItemCount(Quest quest) public void AdjustQuestObjectiveProgress(Quest quest)
{ {
// adjust progress of quest objectives that rely on external counters, like items // adjust progress of quest objectives that rely on external counters, like items
if (quest.HasQuestObjectiveType(QuestObjectiveType.Item)) if (quest.HasQuestObjectiveType(QuestObjectiveType.Item))
@@ -1980,6 +1977,12 @@ namespace Game.Entities
uint curItemCount = GetItemCount((uint)obj.ObjectID, true); uint curItemCount = GetItemCount((uint)obj.ObjectID, true);
SetQuestObjectiveData(obj, (int)Math.Min(curItemCount, reqItemCount)); SetQuestObjectiveData(obj, (int)Math.Min(curItemCount, reqItemCount));
} }
else if (obj.Type == QuestObjectiveType.HaveCurrency)
{
uint reqCurrencyCount = (uint)obj.Amount;
uint curCurrencyCount = GetCurrency((uint)obj.ObjectID);
SetQuestObjectiveData(obj, (int)Math.Min(reqCurrencyCount, curCurrencyCount));
}
} }
} }
} }
@@ -2016,6 +2019,33 @@ namespace Game.Entities
return m_playerData.QuestLog[slot].EndTime; return m_playerData.QuestLog[slot].EndTime;
} }
bool GetQuestSlotObjectiveFlag(ushort slot, sbyte objectiveIndex)
{
if (objectiveIndex < SharedConst.MaxQuestCounts)
return ((m_playerData.QuestLog[slot].ObjectiveFlags) & (1 << objectiveIndex)) != 0;
return false;
}
public int GetQuestSlotObjectiveData(ushort slot, QuestObjective objective)
{
if (objective.StorageIndex < 0)
{
Log.outError(LogFilter.Player, $"Player.GetQuestObjectiveData: Called for quest {objective.QuestID} with invalid StorageIndex {objective.StorageIndex} (objective data is not tracked)");
return 0;
}
if (objective.StorageIndex >= SharedConst.MaxQuestCounts)
{
Log.outError(LogFilter.Player, $"Player.GetQuestObjectiveData: Player '{GetName()}' ({GetGUID()}) quest {objective.QuestID} out of range StorageIndex {objective.StorageIndex}");
return 0;
}
if (!objective.IsStoringFlag())
return GetQuestSlotCounter(slot, (byte)objective.StorageIndex);
return GetQuestSlotObjectiveFlag(slot, objective.StorageIndex) ? 1 : 0;
}
public void SetQuestSlot(ushort slot, uint quest_id, uint timer = 0) public void SetQuestSlot(ushort slot, uint quest_id, uint timer = 0)
{ {
var questLogField = m_values.ModifyValue(m_playerData).ModifyValue(m_playerData.QuestLog, slot); var questLogField = m_values.ModifyValue(m_playerData).ModifyValue(m_playerData.QuestLog, slot);
@@ -2149,30 +2179,27 @@ namespace Game.Entities
foreach (QuestObjective obj in qInfo.Objectives) foreach (QuestObjective obj in qInfo.Objectives)
{ {
if (obj.Type != QuestObjectiveType.Item) if (obj.Type != QuestObjectiveType.Item || !IsQuestObjectiveCompletable(i, qInfo, obj))
continue; continue;
int reqItem = obj.ObjectID; if (obj.ObjectID != entry)
if (reqItem == entry) continue;
int reqItemCount = obj.Amount;
int curItemCount = GetQuestSlotObjectiveData(i, obj);
if (curItemCount < reqItemCount)
{ {
int reqItemCount = obj.Amount; int newItemCount = (int)Math.Min(curItemCount + count, reqItemCount);
int curItemCount = GetQuestObjectiveData(qInfo, obj.StorageIndex); SetQuestObjectiveData(obj, newItemCount);
if (curItemCount < reqItemCount)
{
int newItemCount = (int)Math.Min(curItemCount + count, reqItemCount);
SetQuestObjectiveData(obj, newItemCount);
//SendQuestUpdateAddItem(qInfo, j, additemcount);
// FIXME: verify if there's any packet sent updating item
}
if (CanCompleteQuest(questid))
CompleteQuest(questid);
return;
} }
if (CanCompleteQuest(questid))
CompleteQuest(questid);
break;
} }
} }
UpdateForQuestWorldObjects(); UpdateForQuestWorldObjects();
} }
@@ -2190,29 +2217,29 @@ namespace Game.Entities
foreach (QuestObjective obj in qInfo.Objectives) foreach (QuestObjective obj in qInfo.Objectives)
{ {
if (obj.Type != QuestObjectiveType.Item) if (obj.Type != QuestObjectiveType.Item || !IsQuestObjectiveCompletable(i, qInfo, obj))
continue; continue;
int reqItem = obj.ObjectID; if (obj.ObjectID != entry)
if (reqItem == 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)
{ {
uint reqItemCount = (uint)obj.Amount; SetQuestObjectiveData(obj, newItemCount);
int curItemCount = GetQuestObjectiveData(qInfo, obj.StorageIndex); IncompleteQuest(questid);
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);
}
return;
} }
break;
} }
} }
UpdateForQuestWorldObjects(); UpdateForQuestWorldObjects();
} }
@@ -2243,6 +2270,7 @@ 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;
for (byte i = 0; i < SharedConst.MaxQuestLogSize; ++i) for (byte i = 0; i < SharedConst.MaxQuestLogSize; ++i)
{ {
uint questid = GetQuestSlotQuestId(i); uint questid = GetQuestSlotQuestId(i);
@@ -2253,40 +2281,47 @@ namespace Game.Entities
if (qInfo == null || !qInfo.HasQuestObjectiveType(QuestObjectiveType.Monster)) if (qInfo == null || !qInfo.HasQuestObjectiveType(QuestObjectiveType.Monster))
continue; continue;
// just if !ingroup || !noraidgroup || raidgroup if (m_QuestStatus[questid].Status != QuestStatus.Incomplete)
QuestStatusData q_status = m_QuestStatus[questid]; continue;
if (q_status.Status == QuestStatus.Incomplete && (GetGroup() == null || !GetGroup().IsRaidGroup() || qInfo.IsAllowedInRaid(GetMap().GetDifficultyID())))
if (GetGroup() && GetGroup().IsRaidGroup() && qInfo.IsAllowedInRaid(GetMap().GetDifficultyID()))
continue;
foreach (QuestObjective obj in qInfo.Objectives)
{ {
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)
{ {
if (obj.Type != QuestObjectiveType.Monster) SetQuestObjectiveData(obj, curKillCount + addKillCount);
continue; SendQuestUpdateAddCredit(qInfo, guid, obj, (uint)(curKillCount + addKillCount));
if (!completedObjectiveInSequencedQuest && qInfo.HasSpecialFlag(QuestSpecialFlags.SequencedObjectives) && IsQuestObjectiveComplete(i, qInfo, obj))
int reqkill = obj.ObjectID; completedObjectiveInSequencedQuest = true;
if (reqkill == real_entry)
{
int curKillCount = GetQuestObjectiveData(qInfo, obj.StorageIndex);
if (curKillCount < obj.Amount)
{
SetQuestObjectiveData(obj, curKillCount + addKillCount);
SendQuestUpdateAddCredit(qInfo, guid, obj, (uint)(curKillCount + addKillCount));
}
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 (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; ushort addKillCount = 1;
bool completedObjectiveInSequencedQuest = false;
for (byte i = 0; i < SharedConst.MaxQuestLogSize; ++i) for (byte i = 0; i < SharedConst.MaxQuestLogSize; ++i)
{ {
uint questid = GetQuestSlotQuestId(i); uint questid = GetQuestSlotQuestId(i);
@@ -2303,14 +2338,16 @@ namespace Game.Entities
{ {
foreach (QuestObjective obj in qInfo.Objectives) foreach (QuestObjective obj in qInfo.Objectives)
{ {
if (obj.Type != QuestObjectiveType.PlayerKills) if (obj.Type != QuestObjectiveType.PlayerKills || !IsQuestObjectiveCompletable(i, qInfo, obj))
continue; continue;
int curKillCount = GetQuestObjectiveData(qInfo, obj.StorageIndex); int curKillCount = GetQuestSlotObjectiveData(i, obj);
if (curKillCount < obj.Amount) if (curKillCount < obj.Amount)
{ {
SetQuestObjectiveData(obj, curKillCount + addKillCount); SetQuestObjectiveData(obj, curKillCount + addKillCount);
SendQuestUpdateAddPlayer(qInfo, (uint)(curKillCount + addKillCount)); SendQuestUpdateAddPlayer(qInfo, (uint)(curKillCount + addKillCount));
if (!completedObjectiveInSequencedQuest && qInfo.HasSpecialFlag(QuestSpecialFlags.SequencedObjectives) && IsQuestObjectiveComplete(i, qInfo, obj))
completedObjectiveInSequencedQuest = true;
} }
if (CanCompleteQuest(questid)) if (CanCompleteQuest(questid))
@@ -2321,11 +2358,15 @@ namespace Game.Entities
} }
} }
} }
if (completedObjectiveInSequencedQuest)
UpdateForQuestWorldObjects();
} }
public void KillCreditGO(uint entry, ObjectGuid guid = default) public void KillCreditGO(uint entry, ObjectGuid guid = default)
{ {
ushort addCastCount = 1; ushort addCastCount = 1;
bool completedObjectiveInSequencedQuest = false;
for (byte i = 0; i < SharedConst.MaxQuestLogSize; ++i) for (byte i = 0; i < SharedConst.MaxQuestLogSize; ++i)
{ {
uint questid = GetQuestSlotQuestId(i); uint questid = GetQuestSlotQuestId(i);
@@ -2333,39 +2374,46 @@ namespace Game.Entities
continue; continue;
Quest qInfo = Global.ObjectMgr.GetQuestTemplate(questid); Quest qInfo = Global.ObjectMgr.GetQuestTemplate(questid);
if (qInfo == null) if (qInfo == null || !qInfo.HasQuestObjectiveType(QuestObjectiveType.GameObject))
continue; continue;
QuestStatusData q_status = m_QuestStatus[questid]; if (m_QuestStatus[questid].Status != QuestStatus.Incomplete)
continue;
if (q_status.Status == QuestStatus.Incomplete) if (GetGroup() && GetGroup().IsRaidGroup() && qInfo.IsAllowedInRaid(GetMap().GetDifficultyID()))
continue;
foreach (QuestObjective obj in qInfo.Objectives)
{ {
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)
{ {
if (obj.Type != QuestObjectiveType.GameObject) SetQuestObjectiveData(obj, curCastCount + addCastCount);
continue; SendQuestUpdateAddCredit(qInfo, guid, obj, (uint)(curCastCount + addCastCount));
if (!completedObjectiveInSequencedQuest && qInfo.HasSpecialFlag(QuestSpecialFlags.SequencedObjectives) && IsQuestObjectiveComplete(i, qInfo, obj))
int reqTarget = obj.ObjectID; completedObjectiveInSequencedQuest = true;
// other not this creature/GO related objectives
if (reqTarget != entry)
continue;
int curCastCount = GetQuestObjectiveData(qInfo, obj.StorageIndex);
if (curCastCount < obj.Amount)
{
SetQuestObjectiveData(obj, curCastCount + addCastCount);
SendQuestUpdateAddCredit(qInfo, guid, obj, (uint)(curCastCount + addCastCount));
}
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 (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)
@@ -2373,19 +2421,24 @@ namespace Game.Entities
if (questObjective.Type != QuestObjectiveType.CriteriaTree) if (questObjective.Type != QuestObjectiveType.CriteriaTree)
return; return;
if (GetQuestStatus(questObjective.QuestID) == QuestStatus.Incomplete) Quest quest = Global.ObjectMgr.GetQuestTemplate(questObjective.QuestID);
if (!IsQuestObjectiveComplete(FindQuestSlot(questObjective.QuestID), quest, questObjective))
{ {
SetQuestObjectiveData(questObjective, 1); SetQuestObjectiveData(questObjective, 1);
SendQuestUpdateAddCreditSimple(questObjective); SendQuestUpdateAddCreditSimple(questObjective);
if (CanCompleteQuest(questObjective.QuestID)) if (CanCompleteQuest(questObjective.QuestID))
CompleteQuest(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; ushort addTalkCount = 1;
bool completedObjectiveInSequencedQuest = false;
for (byte i = 0; i < SharedConst.MaxQuestLogSize; ++i) for (byte i = 0; i < SharedConst.MaxQuestLogSize; ++i)
{ {
uint questid = GetQuestSlotQuestId(i); uint questid = GetQuestSlotQuestId(i);
@@ -2393,63 +2446,74 @@ namespace Game.Entities
continue; continue;
Quest qInfo = Global.ObjectMgr.GetQuestTemplate(questid); Quest qInfo = Global.ObjectMgr.GetQuestTemplate(questid);
if (qInfo == null) if (qInfo == null || !qInfo.HasQuestObjectiveType(QuestObjectiveType.TalkTo))
continue; continue;
QuestStatusData q_status = m_QuestStatus[questid]; if (m_QuestStatus[questid].Status != QuestStatus.Incomplete)
if (q_status.Status == QuestStatus.Incomplete)
{
foreach (QuestObjective obj in qInfo.Objectives)
{
if (obj.Type != QuestObjectiveType.TalkTo)
continue;
int reqTarget = obj.ObjectID;
if (reqTarget == entry)
{
int curTalkCount = GetQuestObjectiveData(qInfo, obj.StorageIndex);
if (curTalkCount < obj.Amount)
{
SetQuestObjectiveData(obj, curTalkCount + addTalkCount);
SendQuestUpdateAddCredit(qInfo, guid, obj, (uint)(curTalkCount + addTalkCount));
}
if (CanCompleteQuest(questid))
CompleteQuest(questid);
// Quest can't have more than one objective for the same creature (code optimisation)
break;
}
}
}
}
}
public void MoneyChanged(ulong value)
{
for (byte i = 0; i < SharedConst.MaxQuestLogSize; ++i)
{
uint questid = GetQuestSlotQuestId(i);
if (questid == 0)
continue; continue;
Quest qInfo = Global.ObjectMgr.GetQuestTemplate(questid); if (GetGroup() && GetGroup().IsRaidGroup() && qInfo.IsAllowedInRaid(GetMap().GetDifficultyID()))
if (qInfo == null)
continue; continue;
foreach (QuestObjective obj in qInfo.Objectives) foreach (QuestObjective obj in qInfo.Objectives)
{ {
if (obj.Type != QuestObjectiveType.Money) if (obj.Type != QuestObjectiveType.TalkTo || !IsQuestObjectiveCompletable(i, qInfo, obj))
continue; continue;
QuestStatusData q_status = m_QuestStatus[questid]; if (obj.ObjectID != entry)
if (q_status.Status == QuestStatus.Incomplete) 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)
{
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 ((long)value >= obj.Amount)
{ {
if (CanCompleteQuest(questid)) if (CanCompleteQuest(questid))
CompleteQuest(questid); CompleteQuest(questid);
if (!completedObjectiveInSequencedQuest && qInfo.HasSpecialFlag(QuestSpecialFlags.SequencedObjectives))
completedObjectiveInSequencedQuest = true;
} }
} }
else if (q_status.Status == QuestStatus.Complete) else if (q_status.Status == QuestStatus.Complete)
@@ -2459,10 +2523,14 @@ namespace Game.Entities
} }
} }
} }
if (completedObjectiveInSequencedQuest)
UpdateForQuestWorldObjects();
} }
public void ReputationChanged(FactionRecord FactionRecord) public void ReputationChanged(FactionRecord FactionRecord, int change)
{ {
bool completedObjectiveInSequencedQuest = false;
for (byte i = 0; i < SharedConst.MaxQuestLogSize; ++i) for (byte i = 0; i < SharedConst.MaxQuestLogSize; ++i)
{ {
uint questid = GetQuestSlotQuestId(i); uint questid = GetQuestSlotQuestId(i);
@@ -2473,47 +2541,80 @@ namespace Game.Entities
{ {
QuestStatusData q_status = m_QuestStatus[questid]; 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) foreach (QuestObjective obj in qInfo.Objectives)
{ {
if (obj.ObjectID != FactionRecord.Id) if (obj.ObjectID != FactionRecord.Id || !IsQuestObjectiveCompletable(i, qInfo, obj))
continue; continue;
if (obj.Type == QuestObjectiveType.MinReputation) switch (obj.Type)
{ {
if (q_status.Status == QuestStatus.Incomplete) case QuestObjectiveType.MinReputation:
{ if (!IsQuestObjectiveComplete(i, qInfo, obj))
if (GetReputationMgr().GetReputation(FactionRecord) >= obj.Amount) {
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)) if (CanCompleteQuest(questid))
CompleteQuest(questid); CompleteQuest(questid);
}
else if (q_status.Status == QuestStatus.Complete) if (!completedObjectiveInSequencedQuest && qInfo.HasSpecialFlag(QuestSpecialFlags.SequencedObjectives) && IsQuestObjectiveComplete(i, qInfo, obj))
{ completedObjectiveInSequencedQuest = true;
if (GetReputationMgr().GetReputation(FactionRecord) < obj.Amount) }
IncompleteQuest(questid); break;
}
}
else if (obj.Type == QuestObjectiveType.MaxReputation)
{
if (q_status.Status == QuestStatus.Incomplete)
{
if (GetReputationMgr().GetReputation(FactionRecord) <= obj.Amount)
if (CanCompleteQuest(questid))
CompleteQuest(questid);
}
else if (q_status.Status == QuestStatus.Complete)
{
if (GetReputationMgr().GetReputation(FactionRecord) > obj.Amount)
IncompleteQuest(questid);
}
} }
} }
} }
} }
} }
if (completedObjectiveInSequencedQuest)
UpdateForQuestWorldObjects();
} }
void CurrencyChanged(uint currencyId, int change) void CurrencyChanged(uint currencyId, int change)
{ {
bool completedObjectiveInSequencedQuest = false;
for (byte i = 0; i < SharedConst.MaxQuestLogSize; ++i) for (byte i = 0; i < SharedConst.MaxQuestLogSize; ++i)
{ {
uint questid = GetQuestSlotQuestId(i); uint questid = GetQuestSlotQuestId(i);
@@ -2524,41 +2625,78 @@ namespace Game.Entities
if (qInfo == null) if (qInfo == null)
continue; 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) foreach (QuestObjective obj in qInfo.Objectives)
{ {
if (obj.ObjectID != currencyId) if (obj.ObjectID != currencyId || !IsQuestObjectiveCompletable(i, qInfo, obj))
continue; continue;
QuestStatusData q_status = m_QuestStatus[questid]; switch (obj.Type)
if (obj.Type == QuestObjectiveType.Currency || obj.Type == QuestObjectiveType.HaveCurrency)
{ {
long value = GetCurrency(currencyId); case QuestObjectiveType.Currency:
if (obj.Type == QuestObjectiveType.HaveCurrency) if (!IsQuestObjectiveComplete(i, qInfo, obj))
SetQuestObjectiveData(obj, (int)Math.Min(value, obj.Amount));
if (q_status.Status == QuestStatus.Incomplete)
{
if (value >= obj.Amount)
{ {
if (GetCurrency(currencyId) + change >= obj.Amount)
{
if (CanCompleteQuest(questid))
CompleteQuest(questid);
if (qInfo.HasSpecialFlag(QuestSpecialFlags.SequencedObjectives))
completedObjectiveInSequencedQuest = true;
}
}
else if (q_status.Status == QuestStatus.Complete)
{
if (GetCurrency(currencyId) + change < obj.Amount)
IncompleteQuest(questid);
}
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)) if (CanCompleteQuest(questid))
CompleteQuest(questid); CompleteQuest(questid);
if (!completedObjectiveInSequencedQuest && qInfo.HasSpecialFlag(QuestSpecialFlags.SequencedObjectives) && IsQuestObjectiveComplete(i, qInfo, obj))
completedObjectiveInSequencedQuest = true;
} }
} break;
else if (q_status.Status == QuestStatus.Complete)
{
if (value < obj.Amount)
IncompleteQuest(questid);
}
}
else if (obj.Type == QuestObjectiveType.ObtainCurrency && change > 0) // currency losses are not accounted for in this objective type
{
long currentProgress = GetQuestObjectiveData(qInfo, obj.StorageIndex);
SetQuestObjectiveData(obj, (int)Math.Max(Math.Min(currentProgress + change, obj.Amount), 0));
if (CanCompleteQuest(questid))
CompleteQuest(questid);
} }
} }
} }
if (completedObjectiveInSequencedQuest)
UpdateForQuestWorldObjects();
} }
public bool HasQuestForItem(uint itemid) public bool HasQuestForItem(uint itemid)
@@ -2588,7 +2726,7 @@ namespace Game.Entities
// This part for ReqItem drop // This part for ReqItem drop
foreach (QuestObjective obj in qInfo.Objectives) foreach (QuestObjective obj in qInfo.Objectives)
{ {
if (obj.Type == QuestObjectiveType.Item && itemid == obj.ObjectID && GetQuestObjectiveData(qInfo, obj.StorageIndex) < obj.Amount) if (obj.Type == QuestObjectiveType.Item && IsQuestObjectiveCompletable(i, qInfo, obj) && itemid == obj.ObjectID && GetQuestSlotObjectiveData(i, obj) < obj.Amount)
return true; return true;
} }
// This part - for ReqSource // This part - for ReqSource
@@ -2618,55 +2756,128 @@ namespace Game.Entities
return false; return false;
} }
public int GetQuestObjectiveData(Quest quest, sbyte storageIndex) public int GetQuestObjectiveData(QuestObjective objective)
{ {
if (storageIndex < 0) ushort slot = FindQuestSlot(objective.QuestID);
Log.outError(LogFilter.Player, "GetQuestObjectiveData: called for quest {0} with invalid StorageIndex {1} (objective data is not tracked)", quest.Id, storageIndex); if (slot >= SharedConst.MaxQuestLogSize)
return 0;
var status = m_QuestStatus.LookupByKey(quest.Id); return GetQuestSlotObjectiveData(slot, objective);
}
public void SetQuestObjectiveData(QuestObjective objective, int data)
{
if (objective.StorageIndex < 0)
{
Log.outError(LogFilter.Player, $"Player.SetQuestObjectiveData: called for quest {objective.QuestID} with invalid StorageIndex {objective.StorageIndex} (objective data is not tracked)");
return;
}
var status = m_QuestStatus.LookupByKey(objective.QuestID);
if (status == null) if (status == null)
return 0;
if (storageIndex >= status.ObjectiveData.Length)
{ {
Log.outError(LogFilter.Player, "GetQuestObjectiveData: player {0} ({1}) quest {2} out of range StorageIndex {3}", GetName(), GetGUID().ToString(), quest.Id, storageIndex); Log.outError(LogFilter.Player, $"Player.SetQuestObjectiveData: player '{GetName()}' ({GetGUID()}) doesn't have quest status data (QuestID: {objective.QuestID})");
return 0; return;
}
if (objective.StorageIndex >= SharedConst.MaxQuestCounts)
{
Log.outError(LogFilter.Player, $"Player.SetQuestObjectiveData: player '{GetName()}' ({GetGUID()}) quest {objective.QuestID} out of range StorageIndex {objective.StorageIndex}");
return;
} }
return status.ObjectiveData[storageIndex]; if (status.Slot >= SharedConst.MaxQuestLogSize)
} return;
bool IsQuestObjectiveProgressComplete(Quest quest) // No change
{ int oldData = GetQuestSlotObjectiveData(status.Slot, objective);
float progress = 0; if (oldData == data)
foreach (QuestObjective obj in quest.Objectives) return;
{
if (obj.Flags.HasAnyFlag(QuestObjectiveFlags.PartOfProgressBar))
{
progress += GetQuestObjectiveData(quest, obj.StorageIndex) * obj.ProgressBarWeight;
if (progress >= 100)
return true;
}
}
return false;
}
public bool IsQuestObjectiveComplete(QuestObjective objective)
{
Quest quest = Global.ObjectMgr.GetQuestTemplate(objective.QuestID); Quest quest = Global.ObjectMgr.GetQuestTemplate(objective.QuestID);
//ASSERT(quest); if (quest != null)
Global.ScriptMgr.OnQuestObjectiveChange(this, quest, objective, oldData, data);
// Add to save
m_QuestStatusSave[objective.QuestID] = QuestSaveType.Default;
// Update quest fields
if (!objective.IsStoringFlag())
SetQuestSlotCounter(status.Slot, (byte)objective.StorageIndex, (ushort)data);
else if (data != 0)
SetQuestSlotObjectiveFlag(status.Slot, objective.StorageIndex);
else
RemoveQuestSlotObjectiveFlag(status.Slot, objective.StorageIndex);
}
public bool IsQuestObjectiveCompletable(ushort slot, Quest quest, QuestObjective objective)
{
Cypher.Assert(objective.QuestID == quest.Id);
if (objective.Flags.HasAnyFlag(QuestObjectiveFlags.PartOfProgressBar))
{
// delegate check to actual progress bar objective
var progressBarObjective = quest.Objectives.Find(otherObjective => otherObjective.Type == QuestObjectiveType.ProgressBar && !otherObjective.Flags.HasAnyFlag(QuestObjectiveFlags.PartOfProgressBar));
if (progressBarObjective == null)
return false;
return IsQuestObjectiveCompletable(slot, quest, progressBarObjective) && !IsQuestObjectiveComplete(slot, quest, progressBarObjective);
}
int objectiveIndex = quest.Objectives.IndexOf(objective);
if (objectiveIndex == -1)
return true;
// check sequenced objectives
int previousIndex = objectiveIndex - 1;
bool objectiveSequenceSatisfied = true;
bool previousSequencedObjectiveComplete = false;
int previousSequencedObjectiveIndex = -1;
do
{
QuestObjective previousObjective = quest.Objectives[previousIndex];
if (previousObjective.Flags.HasAnyFlag(QuestObjectiveFlags.Sequenced))
{
previousSequencedObjectiveIndex = previousIndex;
previousSequencedObjectiveComplete = IsQuestObjectiveComplete(slot, quest, previousObjective);
break;
}
if (objectiveSequenceSatisfied)
objectiveSequenceSatisfied = IsQuestObjectiveComplete(slot, quest, previousObjective) || previousObjective.Flags.HasAnyFlag(QuestObjectiveFlags.Optional | QuestObjectiveFlags.PartOfProgressBar);
--previousIndex;
} while (previousIndex >= 0);
if (objective.Flags.HasAnyFlag(QuestObjectiveFlags.Sequenced))
{
if (previousSequencedObjectiveIndex == -1)
return objectiveSequenceSatisfied;
if (!previousSequencedObjectiveComplete || !objectiveSequenceSatisfied)
return false;
}
else if (!previousSequencedObjectiveComplete && previousSequencedObjectiveIndex != -1)
{
if (!IsQuestObjectiveCompletable(slot, quest, quest.Objectives[previousSequencedObjectiveIndex]))
return false;
}
return true;
}
public bool IsQuestObjectiveComplete(ushort slot, Quest quest, QuestObjective objective)
{
switch (objective.Type) switch (objective.Type)
{ {
case QuestObjectiveType.Monster: case QuestObjectiveType.Monster:
case QuestObjectiveType.Item: case QuestObjectiveType.Item:
case QuestObjectiveType.GameObject: case QuestObjectiveType.GameObject:
case QuestObjectiveType.PlayerKills:
case QuestObjectiveType.TalkTo: case QuestObjectiveType.TalkTo:
case QuestObjectiveType.PlayerKills:
case QuestObjectiveType.WinPvpPetBattles: case QuestObjectiveType.WinPvpPetBattles:
case QuestObjectiveType.HaveCurrency: case QuestObjectiveType.HaveCurrency:
case QuestObjectiveType.ObtainCurrency: case QuestObjectiveType.ObtainCurrency:
if (GetQuestObjectiveData(quest, objective.StorageIndex) < objective.Amount) case QuestObjectiveType.IncreaseReputation:
if (GetQuestSlotObjectiveData(slot, objective) < objective.Amount)
return false; return false;
break; break;
case QuestObjectiveType.MinReputation: case QuestObjectiveType.MinReputation:
@@ -2682,8 +2893,12 @@ namespace Game.Entities
return false; return false;
break; break;
case QuestObjectiveType.AreaTrigger: case QuestObjectiveType.AreaTrigger:
case QuestObjectiveType.WinPetBattleAgainstNpc:
case QuestObjectiveType.DefeatBattlePet:
case QuestObjectiveType.CriteriaTree: case QuestObjectiveType.CriteriaTree:
if (GetQuestObjectiveData(quest, objective.StorageIndex) == 0) case QuestObjectiveType.AreaTriggerEnter:
case QuestObjectiveType.AreaTriggerExit:
if (GetQuestSlotObjectiveData(slot, objective) == 0)
return false; return false;
break; break;
case QuestObjectiveType.LearnSpell: case QuestObjectiveType.LearnSpell:
@@ -2695,7 +2910,7 @@ namespace Game.Entities
return false; return false;
break; break;
case QuestObjectiveType.ProgressBar: case QuestObjectiveType.ProgressBar:
if (!IsQuestObjectiveProgressComplete(quest)) if (!IsQuestObjectiveProgressBarComplete(slot, quest))
return false; return false;
break; break;
default: default:
@@ -2707,53 +2922,20 @@ namespace Game.Entities
return true; return true;
} }
public void SetQuestObjectiveData(QuestObjective objective, int data) public bool IsQuestObjectiveProgressBarComplete(ushort slot, Quest quest)
{ {
if (objective.StorageIndex < 0) float progress = 0.0f;
foreach (QuestObjective obj in quest.Objectives)
{ {
Log.outError(LogFilter.Player, "SetQuestObjectiveData: called for quest {0} with invalid StorageIndex {1} (objective data is not tracked)", objective.QuestID, objective.StorageIndex); if (obj.Flags.HasAnyFlag(QuestObjectiveFlags.PartOfProgressBar))
return; {
progress += GetQuestSlotObjectiveData(slot, obj) * obj.ProgressBarWeight;
if (progress >= 100.0f)
return true;
}
} }
var status = m_QuestStatus.LookupByKey(objective.QuestID); return false;
if (status == null)
{
Log.outError(LogFilter.Player, "SetQuestObjectiveData: player {0} ({1}) doesn't have quest status data for quest {2}", GetName(), GetGUID().ToString(), objective.QuestID);
return;
}
if (objective.StorageIndex >= status.ObjectiveData.Length)
{
Log.outError(LogFilter.Player, "SetQuestObjectiveData: player {0} ({1}) quest {2} out of range StorageIndex {3}", GetName(), GetGUID().ToString(), objective.QuestID, objective.StorageIndex);
return;
}
// No change
int oldData = status.ObjectiveData[objective.StorageIndex];
if (oldData == data)
return;
Quest quest = Global.ObjectMgr.GetQuestTemplate(objective.QuestID);
if (quest != null)
Global.ScriptMgr.OnQuestObjectiveChange(this, quest, objective, oldData, data);
// Set data
status.ObjectiveData[objective.StorageIndex] = data;
// Add to save
m_QuestStatusSave[objective.QuestID] = QuestSaveType.Default;
// Update quest fields
ushort log_slot = FindQuestSlot(objective.QuestID);
if (log_slot < SharedConst.MaxQuestLogSize)
{
if (!objective.IsStoringFlag())
SetQuestSlotCounter(log_slot, (byte)objective.StorageIndex, (ushort)status.ObjectiveData[objective.StorageIndex]);
else if (data != 0)
SetQuestSlotObjectiveFlag(log_slot, objective.StorageIndex);
else
RemoveQuestSlotObjectiveFlag(log_slot, objective.StorageIndex);
}
} }
public void SendQuestComplete(Quest quest) public void SendQuestComplete(Quest quest)
@@ -2977,7 +3159,10 @@ namespace Game.Entities
if (obj.Type != QuestObjectiveType.GameObject) //skip non GO case if (obj.Type != QuestObjectiveType.GameObject) //skip non GO case
continue; continue;
if (GOId == obj.ObjectID && GetQuestObjectiveData(qInfo, obj.StorageIndex) < obj.Amount) if (!IsQuestObjectiveCompletable(i, qInfo, obj))
continue;
if (GOId == obj.ObjectID && GetQuestSlotObjectiveData(i, obj) < obj.Amount)
return true; return true;
} }
} }
+3 -3
View File
@@ -1204,6 +1204,8 @@ namespace Game.Entities
if (playerCurrency.state != PlayerCurrencyState.New) if (playerCurrency.state != PlayerCurrencyState.New)
playerCurrency.state = PlayerCurrencyState.Changed; playerCurrency.state = PlayerCurrencyState.Changed;
CurrencyChanged((uint)id, count);
playerCurrency.Quantity = (uint)newTotalCount; playerCurrency.Quantity = (uint)newTotalCount;
playerCurrency.WeeklyQuantity = (uint)newWeekCount; playerCurrency.WeeklyQuantity = (uint)newWeekCount;
playerCurrency.TrackedQuantity = (uint)newTrackedCount; playerCurrency.TrackedQuantity = (uint)newTrackedCount;
@@ -1211,8 +1213,6 @@ namespace Game.Entities
if (count > 0) if (count > 0)
UpdateCriteria(CriteriaTypes.Currency, (uint)id, (uint)count); UpdateCriteria(CriteriaTypes.Currency, (uint)id, (uint)count);
CurrencyChanged((uint)id, count);
_currencyStorage[(uint)id] = playerCurrency; _currencyStorage[(uint)id] = playerCurrency;
SetCurrency packet = new(); SetCurrency packet = new();
@@ -5076,8 +5076,8 @@ namespace Game.Entities
} }
public void SetMoney(ulong value) public void SetMoney(ulong value)
{ {
SetUpdateFieldValue(m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.Coinage), value);
MoneyChanged((uint)value); MoneyChanged((uint)value);
SetUpdateFieldValue(m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.Coinage), value);
UpdateCriteria(CriteriaTypes.HighestGoldValueOwned); UpdateCriteria(CriteriaTypes.HighestGoldValueOwned);
} }
+14 -3
View File
@@ -7083,20 +7083,23 @@ namespace Game
{ {
case QuestObjectiveType.Item: case QuestObjectiveType.Item:
if (GetItemTemplate((uint)obj.ObjectID) == null) if (GetItemTemplate((uint)obj.ObjectID) == null)
Log.outError(LogFilter.Sql, "Quest {0} objective {1} has non existing item entry {2}, quest can't be done.", qinfo.Id, obj.Id, obj.ObjectID); Log.outError(LogFilter.Sql, $"Quest {qinfo.Id} objective {obj.Id} has non existing item entry {obj.ObjectID}, quest can't be done.");
break; break;
case QuestObjectiveType.Monster: case QuestObjectiveType.Monster:
if (GetCreatureTemplate((uint)obj.ObjectID) == null) if (GetCreatureTemplate((uint)obj.ObjectID) == null)
Log.outError(LogFilter.Sql, "Quest {0} objective {1} has non existing creature entry {2}, quest can't be done.", qinfo.Id, obj.Id, obj.ObjectID); Log.outError(LogFilter.Sql, $"Quest {qinfo.Id} objective {obj.Id} has non existing creature entry {obj.ObjectID}, quest can't be done.");
break; break;
case QuestObjectiveType.GameObject: case QuestObjectiveType.GameObject:
if (GetGameObjectTemplate((uint)obj.ObjectID) == null) if (GetGameObjectTemplate((uint)obj.ObjectID) == null)
Log.outError(LogFilter.Sql, "Quest {0} objective {1} has non existing gameobject entry {2}, quest can't be done.", qinfo.Id, obj.Id, obj.ObjectID); Log.outError(LogFilter.Sql, $"Quest {qinfo.Id} objective {obj.Id} has non existing gameobject entry {obj.ObjectID}, quest can't be done.");
break; break;
case QuestObjectiveType.TalkTo: case QuestObjectiveType.TalkTo:
if (Global.ObjectMgr.GetCreatureTemplate((uint)obj.ObjectID) == null)
Log.outError(LogFilter.Sql, $"Quest {qinfo.Id} objective {obj.Id} has non existing creature entry {obj.ObjectID}, quest can't be done.");
break; break;
case QuestObjectiveType.MinReputation: case QuestObjectiveType.MinReputation:
case QuestObjectiveType.MaxReputation: case QuestObjectiveType.MaxReputation:
case QuestObjectiveType.IncreaseReputation:
if (!CliDB.FactionStorage.ContainsKey((uint)obj.ObjectID)) if (!CliDB.FactionStorage.ContainsKey((uint)obj.ObjectID))
Log.outError(LogFilter.Sql, "Quest {0} objective {1} has non existing faction id {2}", qinfo.Id, obj.Id, obj.ObjectID); Log.outError(LogFilter.Sql, "Quest {0} objective {1} has non existing faction id {2}", qinfo.Id, obj.Id, obj.ObjectID);
break; break;
@@ -7130,6 +7133,11 @@ namespace Game
break; break;
case QuestObjectiveType.AreaTrigger: case QuestObjectiveType.AreaTrigger:
if (!CliDB.AreaTriggerStorage.ContainsKey((uint)obj.ObjectID) && obj.ObjectID != -1) if (!CliDB.AreaTriggerStorage.ContainsKey((uint)obj.ObjectID) && obj.ObjectID != -1)
Log.outError(LogFilter.Sql, "Quest {0} objective {1} has non existing AreaTrigger.db2 id {2}", qinfo.Id, obj.Id, obj.ObjectID);
break;
case QuestObjectiveType.AreaTriggerEnter:
case QuestObjectiveType.AreaTriggerExit:
if (Global.AreaTriggerDataStorage.GetAreaTriggerTemplate(new AreaTriggerId((uint)obj.ObjectID, false)) == null && Global.AreaTriggerDataStorage.GetAreaTriggerTemplate(new AreaTriggerId((uint)obj.ObjectID, true)) != null)
Log.outError(LogFilter.Sql, "Quest {0} objective {1} has non existing areatrigger id {2}", qinfo.Id, obj.Id, obj.ObjectID); Log.outError(LogFilter.Sql, "Quest {0} objective {1} has non existing areatrigger id {2}", qinfo.Id, obj.Id, obj.ObjectID);
break; break;
case QuestObjectiveType.Money: case QuestObjectiveType.Money:
@@ -7139,6 +7147,9 @@ namespace Game
Log.outError(LogFilter.Sql, "Quest {0} objective {1} has unhandled type {2}", qinfo.Id, obj.Id, obj.Type); Log.outError(LogFilter.Sql, "Quest {0} objective {1} has unhandled type {2}", qinfo.Id, obj.Id, obj.Type);
break; break;
} }
if (obj.Flags.HasAnyFlag(QuestObjectiveFlags.Sequenced))
qinfo.SetSpecialFlag(QuestSpecialFlags.SequencedObjectives);
} }
for (var j = 0; j < SharedConst.QuestItemDropCount; j++) for (var j = 0; j < SharedConst.QuestItemDropCount; j++)
+9 -2
View File
@@ -204,17 +204,21 @@ namespace Game
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;
foreach (uint questId in quests) foreach (uint questId in quests)
{ {
Quest qInfo = Global.ObjectMgr.GetQuestTemplate(questId); Quest qInfo = Global.ObjectMgr.GetQuestTemplate(questId);
if (qInfo != null && player.GetQuestStatus(questId) == QuestStatus.Incomplete) ushort slot = player.FindQuestSlot(questId);
if (qInfo != null && slot < SharedConst.MaxQuestLogSize && player.GetQuestStatus(questId) == QuestStatus.Incomplete)
{ {
foreach (QuestObjective obj in qInfo.Objectives) foreach (QuestObjective obj in qInfo.Objectives)
{ {
if (obj.Type == QuestObjectiveType.AreaTrigger && !player.IsQuestObjectiveComplete(obj)) if (obj.Type == QuestObjectiveType.AreaTrigger && !player.IsQuestObjectiveComplete(slot, qInfo, obj))
{ {
player.SetQuestObjectiveData(obj, 1); player.SetQuestObjectiveData(obj, 1);
player.SendQuestUpdateAddCreditSimple(obj); player.SendQuestUpdateAddCreditSimple(obj);
if (qInfo.HasSpecialFlag(QuestSpecialFlags.SequencedObjectives))
completedObjectiveInSequencedQuest = true;
break; break;
} }
} }
@@ -223,6 +227,9 @@ namespace Game
player.CompleteQuest(questId); player.CompleteQuest(questId);
} }
} }
if (completedObjectiveInSequencedQuest)
player.UpdateForQuestWorldObjects();
} }
} }
+1 -1
View File
@@ -728,9 +728,9 @@ namespace Game
public class QuestStatusData public class QuestStatusData
{ {
public ushort Slot = SharedConst.MaxQuestLogSize;
public QuestStatus Status; public QuestStatus Status;
public uint Timer; public uint Timer;
public int[] ObjectiveData;
} }
public class QuestGreeting public class QuestGreeting
@@ -291,6 +291,13 @@ namespace Game
return false; return false;
} }
ushort slot = _owner.FindQuestSlot(objective.QuestID);
if (slot >= SharedConst.MaxQuestLogSize || !_owner.IsQuestObjectiveCompletable(slot, quest, objective))
{
Log.outTrace(LogFilter.Achievement, $"QuestObjectiveCriteriaMgr.CanUpdateCriteriaTree: (Id: {criteria.Id} Type {criteria.Entry.Type} Quest Objective {objective.Id}) Objective not completable");
return false;
}
return base.CanUpdateCriteriaTree(criteria, tree, referencePlayer); return base.CanUpdateCriteriaTree(criteria, tree, referencePlayer);
} }
+6 -2
View File
@@ -371,7 +371,11 @@ namespace Game
ReputationRank old_rank = ReputationToRank(factionState.Standing + BaseRep); ReputationRank old_rank = ReputationToRank(factionState.Standing + BaseRep);
ReputationRank new_rank = ReputationToRank(standing); ReputationRank new_rank = ReputationToRank(standing);
factionState.Standing = standing - BaseRep; int newStanding = standing - BaseRep;
_player.ReputationChanged(factionEntry, newStanding - factionState.Standing);
factionState.Standing = newStanding;
factionState.needSend = true; factionState.needSend = true;
factionState.needSave = true; factionState.needSave = true;
@@ -385,7 +389,7 @@ namespace Game
UpdateRankCounters(old_rank, new_rank); UpdateRankCounters(old_rank, new_rank);
_player.ReputationChanged(factionEntry);
_player.UpdateCriteria(CriteriaTypes.KnownFactions, factionEntry.Id); _player.UpdateCriteria(CriteriaTypes.KnownFactions, factionEntry.Id);
_player.UpdateCriteria(CriteriaTypes.GainReputation, factionEntry.Id); _player.UpdateCriteria(CriteriaTypes.GainReputation, factionEntry.Id);
_player.UpdateCriteria(CriteriaTypes.GainExaltedReputation, factionEntry.Id); _player.UpdateCriteria(CriteriaTypes.GainExaltedReputation, factionEntry.Id);
+2 -1
View File
@@ -120,7 +120,8 @@ namespace Scripts.Smart
// Called when a quest objective data change // Called when a quest objective data change
public override void OnQuestObjectiveChange(Player player, Quest quest, QuestObjective objective, int oldAmount, int newAmount) public override void OnQuestObjectiveChange(Player player, Quest quest, QuestObjective objective, int oldAmount, int newAmount)
{ {
if (player.IsQuestObjectiveComplete(objective)) ushort slot = player.FindQuestSlot(quest.Id);
if (slot < SharedConst.MaxQuestLogSize && player.IsQuestObjectiveComplete(slot, quest, objective))
{ {
SmartScript smartScript = new SmartScript(); SmartScript smartScript = new SmartScript();
smartScript.OnInitialize(quest); smartScript.OnInitialize(quest);