Core/Quests: Quest improvements
Port From (https://github.com/TrinityCore/TrinityCore/commit/91b91b502f30f781c8dea77dec4ce2f469b89744)
This commit is contained in:
@@ -498,6 +498,8 @@ namespace Framework.Constants
|
||||
// Room For More Custom Flags
|
||||
|
||||
DbAllowed = Repeatable | ExplorationOrEvent | AutoAccept | DfQuest | Monthly,
|
||||
|
||||
SequencedObjectives = 0x20 // Internal flag computed only
|
||||
}
|
||||
|
||||
public enum QuestSaveType
|
||||
|
||||
@@ -1689,7 +1689,12 @@ namespace Game.Achievements
|
||||
if (objective == null)
|
||||
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;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -365,7 +365,15 @@ namespace Game.Conditions
|
||||
if (questObj == null)
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -2123,7 +2123,9 @@ namespace Game
|
||||
if (condition.QuestKillID != 0)
|
||||
{
|
||||
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];
|
||||
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]);
|
||||
|
||||
if (questObjective != null)
|
||||
results[i] = player.GetQuestObjectiveData(quest, questObjective.StorageIndex) >= questObjective.Amount;
|
||||
results[i] = player.GetQuestSlotObjectiveData(questSlot, questObjective) >= questObjective.Amount;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -709,6 +709,7 @@ namespace Game.Entities
|
||||
// add to quest log
|
||||
if (slot < SharedConst.MaxQuestLogSize && questStatusData.Status != QuestStatus.None)
|
||||
{
|
||||
questStatusData.Slot = slot;
|
||||
SetQuestSlot(slot, quest_id, (uint)quest_time); // cast can't be helped
|
||||
|
||||
if (questStatusData.Status == QuestStatus.Complete)
|
||||
@@ -719,14 +720,6 @@ namespace Game.Entities
|
||||
++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;
|
||||
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);
|
||||
|
||||
Quest quest = Global.ObjectMgr.GetQuestTemplate(questID);
|
||||
ushort slot = FindQuestSlot(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);
|
||||
if (objectiveIndex < questStatusData.ObjectiveData.Length && objectiveItr != null)
|
||||
var objective = quest.Objectives.FirstOrDefault(objective => objective.StorageIndex == storageIndex);
|
||||
if (objective != null)
|
||||
{
|
||||
int data = result.Read<int>(2);
|
||||
questStatusData.ObjectiveData[objectiveIndex] = data;
|
||||
if (!objectiveItr.IsStoringFlag())
|
||||
SetQuestSlotCounter(slot, objectiveIndex, (ushort)data);
|
||||
if (!objective.IsStoringFlag())
|
||||
SetQuestSlotCounter(questStatusData.Slot, storageIndex, (ushort)data);
|
||||
else if (data != 0)
|
||||
SetQuestSlotObjectiveFlag(slot, (sbyte)objectiveIndex);
|
||||
SetQuestSlotObjectiveFlag(questStatusData.Slot, (sbyte)storageIndex);
|
||||
}
|
||||
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
|
||||
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());
|
||||
}
|
||||
@@ -1943,7 +1934,7 @@ namespace Game.Entities
|
||||
if (!isTransaction)
|
||||
trans = new SQLTransaction();
|
||||
|
||||
PreparedStatement stmt = null;
|
||||
PreparedStatement stmt;
|
||||
bool keepAbandoned = !Global.WorldMgr.GetCleaningFlags().HasAnyFlag(CleaningFlags.Queststatus);
|
||||
|
||||
foreach (var save in m_QuestStatusSave)
|
||||
@@ -1961,13 +1952,24 @@ namespace Game.Entities
|
||||
trans.Append(stmt);
|
||||
|
||||
// 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.AddValue(0, GetGUID().GetCounter());
|
||||
stmt.AddValue(1, save.Key);
|
||||
stmt.AddValue(2, i);
|
||||
stmt.AddValue(3, data.ObjectiveData[i]);
|
||||
stmt.AddValue(2, obj.StorageIndex);
|
||||
stmt.AddValue(3, count);
|
||||
trans.Append(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -487,7 +487,7 @@ namespace Game.Entities
|
||||
{
|
||||
if (!obj.Flags.HasAnyFlag(QuestObjectiveFlags.Optional) && !obj.Flags.HasAnyFlag(QuestObjectiveFlags.PartOfProgressBar))
|
||||
{
|
||||
if (!IsQuestObjectiveComplete(obj))
|
||||
if (!IsQuestObjectiveComplete(q_status.Slot, qInfo, obj))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -740,14 +740,8 @@ namespace Game.Entities
|
||||
// check for repeatable quests status reset
|
||||
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);
|
||||
AdjustQuestObjectiveProgress(quest);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
questStatusData.Slot = log_slot;
|
||||
SetQuestSlot(log_slot, quest_id, qtime);
|
||||
|
||||
AdjustQuestReqItemCount(quest);
|
||||
|
||||
m_QuestStatusSave[quest_id] = QuestSaveType.Default;
|
||||
|
||||
StartCriteriaTimer(CriteriaTimedTypes.Quest, quest_id);
|
||||
@@ -1960,14 +1953,18 @@ namespace Game.Entities
|
||||
if (qInfo == null)
|
||||
return 0;
|
||||
|
||||
ushort slot = FindQuestSlot(quest_id);
|
||||
if (slot >= SharedConst.MaxQuestLogSize)
|
||||
return 0;
|
||||
|
||||
foreach (QuestObjective obj in qInfo.Objectives)
|
||||
if (obj.ObjectID == entry)
|
||||
return (ushort)GetQuestObjectiveData(qInfo, obj.StorageIndex);
|
||||
return (ushort)GetQuestSlotObjectiveData(slot, obj);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void AdjustQuestReqItemCount(Quest quest)
|
||||
public void AdjustQuestObjectiveProgress(Quest quest)
|
||||
{
|
||||
// adjust progress of quest objectives that rely on external counters, like items
|
||||
if (quest.HasQuestObjectiveType(QuestObjectiveType.Item))
|
||||
@@ -1980,6 +1977,12 @@ namespace Game.Entities
|
||||
uint curItemCount = GetItemCount((uint)obj.ObjectID, true);
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (obj.Type != QuestObjectiveType.Item)
|
||||
if (obj.Type != QuestObjectiveType.Item || !IsQuestObjectiveCompletable(i, qInfo, obj))
|
||||
continue;
|
||||
|
||||
int reqItem = obj.ObjectID;
|
||||
if (reqItem == entry)
|
||||
if (obj.ObjectID != entry)
|
||||
continue;
|
||||
|
||||
int reqItemCount = obj.Amount;
|
||||
int curItemCount = GetQuestSlotObjectiveData(i, obj);
|
||||
if (curItemCount < reqItemCount)
|
||||
{
|
||||
int reqItemCount = obj.Amount;
|
||||
int curItemCount = GetQuestObjectiveData(qInfo, obj.StorageIndex);
|
||||
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;
|
||||
int newItemCount = (int)Math.Min(curItemCount + count, reqItemCount);
|
||||
SetQuestObjectiveData(obj, newItemCount);
|
||||
}
|
||||
|
||||
if (CanCompleteQuest(questid))
|
||||
CompleteQuest(questid);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateForQuestWorldObjects();
|
||||
}
|
||||
|
||||
@@ -2190,29 +2217,29 @@ namespace Game.Entities
|
||||
|
||||
foreach (QuestObjective obj in qInfo.Objectives)
|
||||
{
|
||||
if (obj.Type != QuestObjectiveType.Item)
|
||||
if (obj.Type != QuestObjectiveType.Item || !IsQuestObjectiveCompletable(i, qInfo, obj))
|
||||
continue;
|
||||
|
||||
int reqItem = obj.ObjectID;
|
||||
if (reqItem == entry)
|
||||
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)
|
||||
{
|
||||
uint reqItemCount = (uint)obj.Amount;
|
||||
int curItemCount = GetQuestObjectiveData(qInfo, obj.StorageIndex);
|
||||
|
||||
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;
|
||||
SetQuestObjectiveData(obj, newItemCount);
|
||||
IncompleteQuest(questid);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateForQuestWorldObjects();
|
||||
}
|
||||
|
||||
@@ -2243,6 +2270,7 @@ 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);
|
||||
@@ -2253,40 +2281,47 @@ namespace Game.Entities
|
||||
if (qInfo == null || !qInfo.HasQuestObjectiveType(QuestObjectiveType.Monster))
|
||||
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())))
|
||||
if (m_QuestStatus[questid].Status != QuestStatus.Incomplete)
|
||||
continue;
|
||||
|
||||
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)
|
||||
continue;
|
||||
|
||||
int reqkill = obj.ObjectID;
|
||||
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;
|
||||
}
|
||||
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()
|
||||
{
|
||||
ushort addKillCount = 1;
|
||||
|
||||
bool completedObjectiveInSequencedQuest = false;
|
||||
for (byte i = 0; i < SharedConst.MaxQuestLogSize; ++i)
|
||||
{
|
||||
uint questid = GetQuestSlotQuestId(i);
|
||||
@@ -2303,14 +2338,16 @@ namespace Game.Entities
|
||||
{
|
||||
foreach (QuestObjective obj in qInfo.Objectives)
|
||||
{
|
||||
if (obj.Type != QuestObjectiveType.PlayerKills)
|
||||
if (obj.Type != QuestObjectiveType.PlayerKills || !IsQuestObjectiveCompletable(i, qInfo, obj))
|
||||
continue;
|
||||
|
||||
int curKillCount = GetQuestObjectiveData(qInfo, obj.StorageIndex);
|
||||
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))
|
||||
@@ -2321,11 +2358,15 @@ namespace Game.Entities
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (completedObjectiveInSequencedQuest)
|
||||
UpdateForQuestWorldObjects();
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -2333,39 +2374,46 @@ namespace Game.Entities
|
||||
continue;
|
||||
|
||||
Quest qInfo = Global.ObjectMgr.GetQuestTemplate(questid);
|
||||
if (qInfo == null)
|
||||
if (qInfo == null || !qInfo.HasQuestObjectiveType(QuestObjectiveType.GameObject))
|
||||
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)
|
||||
continue;
|
||||
|
||||
int reqTarget = obj.ObjectID;
|
||||
|
||||
// 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;
|
||||
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)
|
||||
@@ -2373,19 +2421,24 @@ namespace Game.Entities
|
||||
if (questObjective.Type != QuestObjectiveType.CriteriaTree)
|
||||
return;
|
||||
|
||||
if (GetQuestStatus(questObjective.QuestID) == QuestStatus.Incomplete)
|
||||
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)
|
||||
{
|
||||
ushort addTalkCount = 1;
|
||||
bool completedObjectiveInSequencedQuest = false;
|
||||
for (byte i = 0; i < SharedConst.MaxQuestLogSize; ++i)
|
||||
{
|
||||
uint questid = GetQuestSlotQuestId(i);
|
||||
@@ -2393,63 +2446,74 @@ namespace Game.Entities
|
||||
continue;
|
||||
|
||||
Quest qInfo = Global.ObjectMgr.GetQuestTemplate(questid);
|
||||
if (qInfo == null)
|
||||
if (qInfo == null || !qInfo.HasQuestObjectiveType(QuestObjectiveType.TalkTo))
|
||||
continue;
|
||||
|
||||
QuestStatusData q_status = m_QuestStatus[questid];
|
||||
|
||||
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)
|
||||
if (m_QuestStatus[questid].Status != QuestStatus.Incomplete)
|
||||
continue;
|
||||
|
||||
Quest qInfo = Global.ObjectMgr.GetQuestTemplate(questid);
|
||||
if (qInfo == null)
|
||||
if (GetGroup() && GetGroup().IsRaidGroup() && qInfo.IsAllowedInRaid(GetMap().GetDifficultyID()))
|
||||
continue;
|
||||
|
||||
foreach (QuestObjective obj in qInfo.Objectives)
|
||||
{
|
||||
if (obj.Type != QuestObjectiveType.Money)
|
||||
if (obj.Type != QuestObjectiveType.TalkTo || !IsQuestObjectiveCompletable(i, qInfo, obj))
|
||||
continue;
|
||||
|
||||
QuestStatusData q_status = m_QuestStatus[questid];
|
||||
if (q_status.Status == QuestStatus.Incomplete)
|
||||
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)
|
||||
{
|
||||
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)
|
||||
@@ -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)
|
||||
{
|
||||
uint questid = GetQuestSlotQuestId(i);
|
||||
@@ -2473,47 +2541,80 @@ namespace Game.Entities
|
||||
{
|
||||
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)
|
||||
if (obj.ObjectID != FactionRecord.Id || !IsQuestObjectiveCompletable(i, qInfo, obj))
|
||||
continue;
|
||||
|
||||
if (obj.Type == QuestObjectiveType.MinReputation)
|
||||
switch (obj.Type)
|
||||
{
|
||||
if (q_status.Status == QuestStatus.Incomplete)
|
||||
{
|
||||
if (GetReputationMgr().GetReputation(FactionRecord) >= obj.Amount)
|
||||
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);
|
||||
}
|
||||
else if (q_status.Status == QuestStatus.Complete)
|
||||
{
|
||||
if (GetReputationMgr().GetReputation(FactionRecord) < obj.Amount)
|
||||
IncompleteQuest(questid);
|
||||
}
|
||||
}
|
||||
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 && qInfo.HasSpecialFlag(QuestSpecialFlags.SequencedObjectives) && IsQuestObjectiveComplete(i, qInfo, obj))
|
||||
completedObjectiveInSequencedQuest = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (completedObjectiveInSequencedQuest)
|
||||
UpdateForQuestWorldObjects();
|
||||
}
|
||||
|
||||
void CurrencyChanged(uint currencyId, int change)
|
||||
{
|
||||
bool completedObjectiveInSequencedQuest = false;
|
||||
for (byte i = 0; i < SharedConst.MaxQuestLogSize; ++i)
|
||||
{
|
||||
uint questid = GetQuestSlotQuestId(i);
|
||||
@@ -2524,41 +2625,78 @@ namespace Game.Entities
|
||||
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)
|
||||
if (obj.ObjectID != currencyId || !IsQuestObjectiveCompletable(i, qInfo, obj))
|
||||
continue;
|
||||
|
||||
QuestStatusData q_status = m_QuestStatus[questid];
|
||||
if (obj.Type == QuestObjectiveType.Currency || obj.Type == QuestObjectiveType.HaveCurrency)
|
||||
switch (obj.Type)
|
||||
{
|
||||
long value = GetCurrency(currencyId);
|
||||
if (obj.Type == QuestObjectiveType.HaveCurrency)
|
||||
SetQuestObjectiveData(obj, (int)Math.Min(value, obj.Amount));
|
||||
|
||||
if (q_status.Status == QuestStatus.Incomplete)
|
||||
{
|
||||
if (value >= obj.Amount)
|
||||
case QuestObjectiveType.Currency:
|
||||
if (!IsQuestObjectiveComplete(i, qInfo, obj))
|
||||
{
|
||||
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))
|
||||
CompleteQuest(questid);
|
||||
|
||||
if (!completedObjectiveInSequencedQuest && qInfo.HasSpecialFlag(QuestSpecialFlags.SequencedObjectives) && IsQuestObjectiveComplete(i, qInfo, obj))
|
||||
completedObjectiveInSequencedQuest = true;
|
||||
}
|
||||
}
|
||||
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);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (completedObjectiveInSequencedQuest)
|
||||
UpdateForQuestWorldObjects();
|
||||
}
|
||||
|
||||
public bool HasQuestForItem(uint itemid)
|
||||
@@ -2588,7 +2726,7 @@ namespace Game.Entities
|
||||
// This part for ReqItem drop
|
||||
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;
|
||||
}
|
||||
// This part - for ReqSource
|
||||
@@ -2618,55 +2756,128 @@ namespace Game.Entities
|
||||
return false;
|
||||
}
|
||||
|
||||
public int GetQuestObjectiveData(Quest quest, sbyte storageIndex)
|
||||
public int GetQuestObjectiveData(QuestObjective objective)
|
||||
{
|
||||
if (storageIndex < 0)
|
||||
Log.outError(LogFilter.Player, "GetQuestObjectiveData: called for quest {0} with invalid StorageIndex {1} (objective data is not tracked)", quest.Id, storageIndex);
|
||||
ushort slot = FindQuestSlot(objective.QuestID);
|
||||
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)
|
||||
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);
|
||||
return 0;
|
||||
Log.outError(LogFilter.Player, $"Player.SetQuestObjectiveData: player '{GetName()}' ({GetGUID()}) doesn't have quest status data (QuestID: {objective.QuestID})");
|
||||
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)
|
||||
{
|
||||
float progress = 0;
|
||||
foreach (QuestObjective obj in quest.Objectives)
|
||||
{
|
||||
if (obj.Flags.HasAnyFlag(QuestObjectiveFlags.PartOfProgressBar))
|
||||
{
|
||||
progress += GetQuestObjectiveData(quest, obj.StorageIndex) * obj.ProgressBarWeight;
|
||||
if (progress >= 100)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// No change
|
||||
int oldData = GetQuestSlotObjectiveData(status.Slot, objective);
|
||||
if (oldData == data)
|
||||
return;
|
||||
|
||||
public bool IsQuestObjectiveComplete(QuestObjective objective)
|
||||
{
|
||||
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)
|
||||
{
|
||||
case QuestObjectiveType.Monster:
|
||||
case QuestObjectiveType.Item:
|
||||
case QuestObjectiveType.GameObject:
|
||||
case QuestObjectiveType.PlayerKills:
|
||||
case QuestObjectiveType.TalkTo:
|
||||
case QuestObjectiveType.PlayerKills:
|
||||
case QuestObjectiveType.WinPvpPetBattles:
|
||||
case QuestObjectiveType.HaveCurrency:
|
||||
case QuestObjectiveType.ObtainCurrency:
|
||||
if (GetQuestObjectiveData(quest, objective.StorageIndex) < objective.Amount)
|
||||
case QuestObjectiveType.IncreaseReputation:
|
||||
if (GetQuestSlotObjectiveData(slot, objective) < objective.Amount)
|
||||
return false;
|
||||
break;
|
||||
case QuestObjectiveType.MinReputation:
|
||||
@@ -2682,8 +2893,12 @@ namespace Game.Entities
|
||||
return false;
|
||||
break;
|
||||
case QuestObjectiveType.AreaTrigger:
|
||||
case QuestObjectiveType.WinPetBattleAgainstNpc:
|
||||
case QuestObjectiveType.DefeatBattlePet:
|
||||
case QuestObjectiveType.CriteriaTree:
|
||||
if (GetQuestObjectiveData(quest, objective.StorageIndex) == 0)
|
||||
case QuestObjectiveType.AreaTriggerEnter:
|
||||
case QuestObjectiveType.AreaTriggerExit:
|
||||
if (GetQuestSlotObjectiveData(slot, objective) == 0)
|
||||
return false;
|
||||
break;
|
||||
case QuestObjectiveType.LearnSpell:
|
||||
@@ -2695,7 +2910,7 @@ namespace Game.Entities
|
||||
return false;
|
||||
break;
|
||||
case QuestObjectiveType.ProgressBar:
|
||||
if (!IsQuestObjectiveProgressComplete(quest))
|
||||
if (!IsQuestObjectiveProgressBarComplete(slot, quest))
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
@@ -2707,53 +2922,20 @@ namespace Game.Entities
|
||||
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);
|
||||
return;
|
||||
if (obj.Flags.HasAnyFlag(QuestObjectiveFlags.PartOfProgressBar))
|
||||
{
|
||||
progress += GetQuestSlotObjectiveData(slot, obj) * obj.ProgressBarWeight;
|
||||
if (progress >= 100.0f)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
var status = m_QuestStatus.LookupByKey(objective.QuestID);
|
||||
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);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void SendQuestComplete(Quest quest)
|
||||
@@ -2977,7 +3159,10 @@ namespace Game.Entities
|
||||
if (obj.Type != QuestObjectiveType.GameObject) //skip non GO case
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1204,6 +1204,8 @@ namespace Game.Entities
|
||||
if (playerCurrency.state != PlayerCurrencyState.New)
|
||||
playerCurrency.state = PlayerCurrencyState.Changed;
|
||||
|
||||
CurrencyChanged((uint)id, count);
|
||||
|
||||
playerCurrency.Quantity = (uint)newTotalCount;
|
||||
playerCurrency.WeeklyQuantity = (uint)newWeekCount;
|
||||
playerCurrency.TrackedQuantity = (uint)newTrackedCount;
|
||||
@@ -1211,8 +1213,6 @@ namespace Game.Entities
|
||||
if (count > 0)
|
||||
UpdateCriteria(CriteriaTypes.Currency, (uint)id, (uint)count);
|
||||
|
||||
CurrencyChanged((uint)id, count);
|
||||
|
||||
_currencyStorage[(uint)id] = playerCurrency;
|
||||
|
||||
SetCurrency packet = new();
|
||||
@@ -5076,8 +5076,8 @@ namespace Game.Entities
|
||||
}
|
||||
public void SetMoney(ulong value)
|
||||
{
|
||||
SetUpdateFieldValue(m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.Coinage), value);
|
||||
MoneyChanged((uint)value);
|
||||
SetUpdateFieldValue(m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.Coinage), value);
|
||||
UpdateCriteria(CriteriaTypes.HighestGoldValueOwned);
|
||||
}
|
||||
|
||||
|
||||
@@ -7083,20 +7083,23 @@ namespace Game
|
||||
{
|
||||
case QuestObjectiveType.Item:
|
||||
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;
|
||||
case QuestObjectiveType.Monster:
|
||||
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;
|
||||
case QuestObjectiveType.GameObject:
|
||||
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;
|
||||
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;
|
||||
case QuestObjectiveType.MinReputation:
|
||||
case QuestObjectiveType.MaxReputation:
|
||||
case QuestObjectiveType.IncreaseReputation:
|
||||
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);
|
||||
break;
|
||||
@@ -7130,6 +7133,11 @@ namespace Game
|
||||
break;
|
||||
case QuestObjectiveType.AreaTrigger:
|
||||
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);
|
||||
break;
|
||||
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);
|
||||
break;
|
||||
}
|
||||
|
||||
if (obj.Flags.HasAnyFlag(QuestObjectiveFlags.Sequenced))
|
||||
qinfo.SetSpecialFlag(QuestSpecialFlags.SequencedObjectives);
|
||||
}
|
||||
|
||||
for (var j = 0; j < SharedConst.QuestItemDropCount; j++)
|
||||
|
||||
@@ -204,17 +204,21 @@ namespace Game
|
||||
List<uint> quests = Global.ObjectMgr.GetQuestsForAreaTrigger(packet.AreaTriggerID);
|
||||
if (quests != null)
|
||||
{
|
||||
bool completedObjectiveInSequencedQuest = false;
|
||||
foreach (uint questId in quests)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (obj.Type == QuestObjectiveType.AreaTrigger && !player.IsQuestObjectiveComplete(obj))
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -223,6 +227,9 @@ namespace Game
|
||||
player.CompleteQuest(questId);
|
||||
}
|
||||
}
|
||||
|
||||
if (completedObjectiveInSequencedQuest)
|
||||
player.UpdateForQuestWorldObjects();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -728,9 +728,9 @@ namespace Game
|
||||
|
||||
public class QuestStatusData
|
||||
{
|
||||
public ushort Slot = SharedConst.MaxQuestLogSize;
|
||||
public QuestStatus Status;
|
||||
public uint Timer;
|
||||
public int[] ObjectiveData;
|
||||
}
|
||||
|
||||
public class QuestGreeting
|
||||
|
||||
@@ -291,6 +291,13 @@ namespace Game
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -371,7 +371,11 @@ namespace Game
|
||||
ReputationRank old_rank = ReputationToRank(factionState.Standing + BaseRep);
|
||||
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.needSave = true;
|
||||
|
||||
@@ -385,7 +389,7 @@ namespace Game
|
||||
|
||||
UpdateRankCounters(old_rank, new_rank);
|
||||
|
||||
_player.ReputationChanged(factionEntry);
|
||||
|
||||
_player.UpdateCriteria(CriteriaTypes.KnownFactions, factionEntry.Id);
|
||||
_player.UpdateCriteria(CriteriaTypes.GainReputation, factionEntry.Id);
|
||||
_player.UpdateCriteria(CriteriaTypes.GainExaltedReputation, factionEntry.Id);
|
||||
|
||||
@@ -120,7 +120,8 @@ namespace Scripts.Smart
|
||||
// Called when a quest objective data change
|
||||
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.OnInitialize(quest);
|
||||
|
||||
Reference in New Issue
Block a user