Various quest system fixes (seasonal quests, timed quests and more)
Port From (https://github.com/TrinityCore/TrinityCore/commit/16cf95654f35f6bb563e82608476d8f53837bd06)
This commit is contained in:
@@ -338,7 +338,8 @@ namespace Framework.Constants
|
|||||||
Speakto = 0x100, // Internal Flag Computed Only
|
Speakto = 0x100, // Internal Flag Computed Only
|
||||||
Kill = 0x200, // Internal Flag Computed Only
|
Kill = 0x200, // Internal Flag Computed Only
|
||||||
Timed = 0x400, // Internal Flag Computed Only
|
Timed = 0x400, // Internal Flag Computed Only
|
||||||
PlayerKill = 0x800 // Internal Flag Computed Only
|
PlayerKill = 0x800, // Internal Flag Computed Only
|
||||||
|
CompletedAtStart = 0x1000 // Internal flag computed only
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum QuestSaveType
|
public enum QuestSaveType
|
||||||
|
|||||||
@@ -1996,7 +1996,7 @@ namespace Game.Entities
|
|||||||
}
|
}
|
||||||
void _SaveSeasonalQuestStatus(SQLTransaction trans)
|
void _SaveSeasonalQuestStatus(SQLTransaction trans)
|
||||||
{
|
{
|
||||||
if (!m_SeasonalQuestChanged || m_seasonalquests.Empty())
|
if (!m_SeasonalQuestChanged)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// we don't need transactions here.
|
// we don't need transactions here.
|
||||||
@@ -2004,6 +2004,11 @@ namespace Game.Entities
|
|||||||
stmt.AddValue(0, GetGUID().GetCounter());
|
stmt.AddValue(0, GetGUID().GetCounter());
|
||||||
trans.Append(stmt);
|
trans.Append(stmt);
|
||||||
|
|
||||||
|
m_SeasonalQuestChanged = false;
|
||||||
|
|
||||||
|
if (m_seasonalquests.Empty())
|
||||||
|
return;
|
||||||
|
|
||||||
foreach (var iter in m_seasonalquests)
|
foreach (var iter in m_seasonalquests)
|
||||||
{
|
{
|
||||||
stmt = DB.Characters.GetPreparedStatement(CharStatements.INS_CHARACTER_QUESTSTATUS_SEASONAL);
|
stmt = DB.Characters.GetPreparedStatement(CharStatements.INS_CHARACTER_QUESTSTATUS_SEASONAL);
|
||||||
@@ -2012,8 +2017,6 @@ namespace Game.Entities
|
|||||||
stmt.AddValue(2, iter.Key);
|
stmt.AddValue(2, iter.Key);
|
||||||
trans.Append(stmt);
|
trans.Append(stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_SeasonalQuestChanged = false;
|
|
||||||
}
|
}
|
||||||
void _SaveMonthlyQuestStatus(SQLTransaction trans)
|
void _SaveMonthlyQuestStatus(SQLTransaction trans)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -462,7 +462,7 @@ namespace Game.Entities
|
|||||||
if (qInfo == null)
|
if (qInfo == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!qInfo.IsRepeatable() && m_RewardedQuests.Contains(quest_id))
|
if (!qInfo.IsRepeatable() && GetQuestRewardStatus(quest_id))
|
||||||
return false; // not allow re-complete quest
|
return false; // not allow re-complete quest
|
||||||
|
|
||||||
// auto complete quest
|
// auto complete quest
|
||||||
@@ -838,10 +838,10 @@ namespace Game.Entities
|
|||||||
|
|
||||||
public uint GetQuestXPReward(Quest quest)
|
public uint GetQuestXPReward(Quest quest)
|
||||||
{
|
{
|
||||||
bool rewarded = m_RewardedQuests.Contains(quest.Id);
|
bool rewarded = IsQuestRewarded(quest.Id) && !quest.IsDFQuest();
|
||||||
|
|
||||||
// Not give XP in case already completed once repeatable quest
|
// Not give XP in case already completed once repeatable quest
|
||||||
if (rewarded && !quest.IsDFQuest())
|
if (rewarded)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
uint XP = (uint)(quest.XPValue(this) * WorldConfig.GetFloatValue(WorldCfg.RateXpQuest));
|
uint XP = (uint)(quest.XPValue(this) * WorldConfig.GetFloatValue(WorldCfg.RateXpQuest));
|
||||||
@@ -995,7 +995,7 @@ namespace Game.Entities
|
|||||||
SendNewItem(item, quest.RewardItemCount[i], true, false);
|
SendNewItem(item, quest.RewardItemCount[i], true, false);
|
||||||
}
|
}
|
||||||
else if (quest.IsDFQuest())
|
else if (quest.IsDFQuest())
|
||||||
SendItemRetrievalMail(quest.RewardItemId[i], quest.RewardItemCount[i], ItemContext.QuestReward);
|
SendItemRetrievalMail(itemId, quest.RewardItemCount[i], ItemContext.QuestReward);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1081,11 +1081,6 @@ namespace Game.Entities
|
|||||||
if (quest.CanIncreaseRewardedQuestCounters())
|
if (quest.CanIncreaseRewardedQuestCounters())
|
||||||
SetRewardedQuest(quest_id);
|
SetRewardedQuest(quest_id);
|
||||||
|
|
||||||
// StoreNewItem, mail reward, etc. save data directly to the database
|
|
||||||
// to prevent exploitable data desynchronisation we save the quest status to the database too
|
|
||||||
// (to prevent rewarding this quest another time while rewards were already given out)
|
|
||||||
_SaveQuestStatus(null);
|
|
||||||
|
|
||||||
SendQuestReward(quest, questGiver?.ToCreature(), XP, !announce);
|
SendQuestReward(quest, questGiver?.ToCreature(), XP, !announce);
|
||||||
|
|
||||||
// cast spells after mark quest complete (some spells have quest completed state requirements in spell_area data)
|
// cast spells after mark quest complete (some spells have quest completed state requirements in spell_area data)
|
||||||
@@ -1128,6 +1123,9 @@ namespace Game.Entities
|
|||||||
UpdateCriteria(CriteriaTypes.CompleteQuestCount);
|
UpdateCriteria(CriteriaTypes.CompleteQuestCount);
|
||||||
UpdateCriteria(CriteriaTypes.CompleteQuest, quest.Id);
|
UpdateCriteria(CriteriaTypes.CompleteQuest, quest.Id);
|
||||||
|
|
||||||
|
// make full db save
|
||||||
|
SaveToDB(false);
|
||||||
|
|
||||||
uint questBit = Global.DB2Mgr.GetQuestUniqueBitFlag(quest_id);
|
uint questBit = Global.DB2Mgr.GetQuestUniqueBitFlag(quest_id);
|
||||||
if (questBit != 0)
|
if (questBit != 0)
|
||||||
SetQuestCompletedBit(questBit, true);
|
SetQuestCompletedBit(questBit, true);
|
||||||
@@ -1159,13 +1157,15 @@ namespace Game.Entities
|
|||||||
Quest quest = Global.ObjectMgr.GetQuestTemplate(questId);
|
Quest quest = Global.ObjectMgr.GetQuestTemplate(questId);
|
||||||
if (quest != null)
|
if (quest != null)
|
||||||
{
|
{
|
||||||
// Already complete quests shouldn't turn failed.
|
QuestStatus qStatus = GetQuestStatus(questId);
|
||||||
if (GetQuestStatus(questId) == QuestStatus.Complete && !quest.HasSpecialFlag(QuestSpecialFlags.Timed))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// You can't fail a quest if you don't have it, or if it's already rewarded.
|
// we can only fail incomplete quest or...
|
||||||
if (GetQuestStatus(questId) == QuestStatus.None || GetQuestStatus(questId) == QuestStatus.Rewarded)
|
if (qStatus != QuestStatus.Incomplete)
|
||||||
return;
|
{
|
||||||
|
// completed timed quest with no requirements
|
||||||
|
if (qStatus != QuestStatus.Complete || !quest.HasSpecialFlag(QuestSpecialFlags.Timed) || !quest.HasSpecialFlag(QuestSpecialFlags.CompletedAtStart))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
SetQuestStatus(questId, QuestStatus.Failed);
|
SetQuestStatus(questId, QuestStatus.Failed);
|
||||||
|
|
||||||
@@ -1548,7 +1548,7 @@ namespace Game.Entities
|
|||||||
}
|
}
|
||||||
|
|
||||||
// alternative quest already started or completed - but don't check rewarded states if both are repeatable
|
// alternative quest already started or completed - but don't check rewarded states if both are repeatable
|
||||||
if (GetQuestStatus(exclude_Id) != QuestStatus.None || (!(qInfo.IsRepeatable() && Nquest.IsRepeatable()) && m_RewardedQuests.Contains(exclude_Id)))
|
if (GetQuestStatus(exclude_Id) != QuestStatus.None || (!(qInfo.IsRepeatable() && Nquest.IsRepeatable()) && GetQuestRewardStatus(exclude_Id)))
|
||||||
{
|
{
|
||||||
if (msg)
|
if (msg)
|
||||||
{
|
{
|
||||||
@@ -1707,7 +1707,7 @@ namespace Game.Entities
|
|||||||
|
|
||||||
// for repeatable quests: rewarded field is set after first reward only to prevent getting XP more than once
|
// for repeatable quests: rewarded field is set after first reward only to prevent getting XP more than once
|
||||||
if (!qInfo.IsRepeatable())
|
if (!qInfo.IsRepeatable())
|
||||||
return m_RewardedQuests.Contains(quest_id);
|
return IsQuestRewarded(quest_id);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1722,15 +1722,8 @@ namespace Game.Entities
|
|||||||
if (questStatusData != null)
|
if (questStatusData != null)
|
||||||
return questStatusData.Status;
|
return questStatusData.Status;
|
||||||
|
|
||||||
Quest quest = Global.ObjectMgr.GetQuestTemplate(questId);
|
if (GetQuestRewardStatus(questId))
|
||||||
if (quest != null)
|
return QuestStatus.Rewarded;
|
||||||
{
|
|
||||||
if (quest.IsSeasonal() && !quest.IsRepeatable())
|
|
||||||
return SatisfyQuestSeasonal(quest, false) ? QuestStatus.None : QuestStatus.Rewarded;
|
|
||||||
|
|
||||||
if (!quest.IsRepeatable() && IsQuestRewarded(questId))
|
|
||||||
return QuestStatus.Rewarded;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return QuestStatus.None;
|
return QuestStatus.None;
|
||||||
}
|
}
|
||||||
@@ -1738,7 +1731,22 @@ namespace Game.Entities
|
|||||||
public bool CanShareQuest(uint quest_id)
|
public bool CanShareQuest(uint quest_id)
|
||||||
{
|
{
|
||||||
Quest qInfo = Global.ObjectMgr.GetQuestTemplate(quest_id);
|
Quest qInfo = Global.ObjectMgr.GetQuestTemplate(quest_id);
|
||||||
return qInfo != null && qInfo.HasFlag(QuestFlags.Sharable) && IsActiveQuest(quest_id);
|
if (qInfo != null && qInfo.HasFlag(QuestFlags.Sharable))
|
||||||
|
{
|
||||||
|
var questStatusData = m_QuestStatus.LookupByKey(quest_id);
|
||||||
|
if (questStatusData != null)
|
||||||
|
{
|
||||||
|
if (questStatusData.Status != QuestStatus.Incomplete)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// in pool and not currently available (wintergrasp weekly, dalaran weekly) - can't share
|
||||||
|
if (Global.PoolMgr.IsPartOfAPool<Quest>(quest_id) != 0 && !Global.PoolMgr.IsSpawnedObject<Quest>(quest_id))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetQuestStatus(uint questId, QuestStatus status, bool update = true)
|
public void SetQuestStatus(uint questId, QuestStatus status, bool update = true)
|
||||||
@@ -1786,6 +1794,18 @@ namespace Game.Entities
|
|||||||
if (questBit != 0)
|
if (questBit != 0)
|
||||||
SetQuestCompletedBit(questBit, false);
|
SetQuestCompletedBit(questBit, false);
|
||||||
|
|
||||||
|
// Remove seasonal quest also
|
||||||
|
Quest qInfo = Global.ObjectMgr.GetQuestTemplate(questId);
|
||||||
|
if (qInfo.IsSeasonal())
|
||||||
|
{
|
||||||
|
ushort eventId = qInfo.GetEventIdForQuest();
|
||||||
|
if (m_seasonalquests.ContainsKey(eventId))
|
||||||
|
{
|
||||||
|
m_seasonalquests.Remove(eventId, questId);
|
||||||
|
m_SeasonalQuestChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (update)
|
if (update)
|
||||||
SendQuestUpdate(questId);
|
SendQuestUpdate(questId);
|
||||||
}
|
}
|
||||||
@@ -2036,8 +2056,10 @@ namespace Game.Entities
|
|||||||
{
|
{
|
||||||
q_status.Explored = true;
|
q_status.Explored = true;
|
||||||
m_QuestStatusSave[questId] = QUEST_DEFAULT_SAVE_TYPE;
|
m_QuestStatusSave[questId] = QUEST_DEFAULT_SAVE_TYPE;
|
||||||
SetQuestSlotState(log_slot, QUEST_STATE_COMPLETE);
|
|
||||||
SendQuestComplete(questId);
|
// if we cannot complete quest send exploration succeded (to mark exploration on client)
|
||||||
|
if (!CanCompleteQuest(questId))
|
||||||
|
SendQuestComplete(questId)
|
||||||
}*/
|
}*/
|
||||||
}
|
}
|
||||||
if (CanCompleteQuest(questId))
|
if (CanCompleteQuest(questId))
|
||||||
|
|||||||
@@ -7114,6 +7114,27 @@ namespace Game
|
|||||||
_exclusiveQuestGroups.Add(qinfo.ExclusiveGroup, qinfo.Id);
|
_exclusiveQuestGroups.Add(qinfo.ExclusiveGroup, qinfo.Id);
|
||||||
if (qinfo.LimitTime != 0)
|
if (qinfo.LimitTime != 0)
|
||||||
qinfo.SetSpecialFlag(QuestSpecialFlags.Timed);
|
qinfo.SetSpecialFlag(QuestSpecialFlags.Timed);
|
||||||
|
|
||||||
|
// Special flag to determine if quest is completed from the start, used to determine if we can fail timed quest if it is completed
|
||||||
|
if (!qinfo.HasSpecialFlag(QuestSpecialFlags.Kill | QuestSpecialFlags.Cast | QuestSpecialFlags.Speakto | QuestSpecialFlags.ExplorationOrEvent))
|
||||||
|
{
|
||||||
|
bool addFlag = true;
|
||||||
|
if (qinfo.HasSpecialFlag(QuestSpecialFlags.Deliver))
|
||||||
|
{
|
||||||
|
foreach (QuestObjective obj in qinfo.Objectives)
|
||||||
|
{
|
||||||
|
if (obj.Type == QuestObjectiveType.Item)
|
||||||
|
if (obj.ObjectID != qinfo.SourceItemId || obj.Amount > qinfo.SourceItemIdCount)
|
||||||
|
{
|
||||||
|
addFlag = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addFlag)
|
||||||
|
qinfo.SetSpecialFlag(QuestSpecialFlags.CompletedAtStart);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT for spell with SPELL_EFFECT_QUEST_COMPLETE
|
// check QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT for spell with SPELL_EFFECT_QUEST_COMPLETE
|
||||||
|
|||||||
Reference in New Issue
Block a user