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
|
||||
Kill = 0x200, // 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
|
||||
|
||||
@@ -1996,7 +1996,7 @@ namespace Game.Entities
|
||||
}
|
||||
void _SaveSeasonalQuestStatus(SQLTransaction trans)
|
||||
{
|
||||
if (!m_SeasonalQuestChanged || m_seasonalquests.Empty())
|
||||
if (!m_SeasonalQuestChanged)
|
||||
return;
|
||||
|
||||
// we don't need transactions here.
|
||||
@@ -2004,6 +2004,11 @@ namespace Game.Entities
|
||||
stmt.AddValue(0, GetGUID().GetCounter());
|
||||
trans.Append(stmt);
|
||||
|
||||
m_SeasonalQuestChanged = false;
|
||||
|
||||
if (m_seasonalquests.Empty())
|
||||
return;
|
||||
|
||||
foreach (var iter in m_seasonalquests)
|
||||
{
|
||||
stmt = DB.Characters.GetPreparedStatement(CharStatements.INS_CHARACTER_QUESTSTATUS_SEASONAL);
|
||||
@@ -2012,8 +2017,6 @@ namespace Game.Entities
|
||||
stmt.AddValue(2, iter.Key);
|
||||
trans.Append(stmt);
|
||||
}
|
||||
|
||||
m_SeasonalQuestChanged = false;
|
||||
}
|
||||
void _SaveMonthlyQuestStatus(SQLTransaction trans)
|
||||
{
|
||||
|
||||
@@ -462,7 +462,7 @@ namespace Game.Entities
|
||||
if (qInfo == null)
|
||||
return false;
|
||||
|
||||
if (!qInfo.IsRepeatable() && m_RewardedQuests.Contains(quest_id))
|
||||
if (!qInfo.IsRepeatable() && GetQuestRewardStatus(quest_id))
|
||||
return false; // not allow re-complete quest
|
||||
|
||||
// auto complete quest
|
||||
@@ -838,10 +838,10 @@ namespace Game.Entities
|
||||
|
||||
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
|
||||
if (rewarded && !quest.IsDFQuest())
|
||||
if (rewarded)
|
||||
return 0;
|
||||
|
||||
uint XP = (uint)(quest.XPValue(this) * WorldConfig.GetFloatValue(WorldCfg.RateXpQuest));
|
||||
@@ -995,7 +995,7 @@ namespace Game.Entities
|
||||
SendNewItem(item, quest.RewardItemCount[i], true, false);
|
||||
}
|
||||
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())
|
||||
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);
|
||||
|
||||
// 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.CompleteQuest, quest.Id);
|
||||
|
||||
// make full db save
|
||||
SaveToDB(false);
|
||||
|
||||
uint questBit = Global.DB2Mgr.GetQuestUniqueBitFlag(quest_id);
|
||||
if (questBit != 0)
|
||||
SetQuestCompletedBit(questBit, true);
|
||||
@@ -1159,13 +1157,15 @@ namespace Game.Entities
|
||||
Quest quest = Global.ObjectMgr.GetQuestTemplate(questId);
|
||||
if (quest != null)
|
||||
{
|
||||
// Already complete quests shouldn't turn failed.
|
||||
if (GetQuestStatus(questId) == QuestStatus.Complete && !quest.HasSpecialFlag(QuestSpecialFlags.Timed))
|
||||
return;
|
||||
QuestStatus qStatus = GetQuestStatus(questId);
|
||||
|
||||
// You can't fail a quest if you don't have it, or if it's already rewarded.
|
||||
if (GetQuestStatus(questId) == QuestStatus.None || GetQuestStatus(questId) == QuestStatus.Rewarded)
|
||||
return;
|
||||
// we can only fail incomplete quest or...
|
||||
if (qStatus != QuestStatus.Incomplete)
|
||||
{
|
||||
// completed timed quest with no requirements
|
||||
if (qStatus != QuestStatus.Complete || !quest.HasSpecialFlag(QuestSpecialFlags.Timed) || !quest.HasSpecialFlag(QuestSpecialFlags.CompletedAtStart))
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
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)
|
||||
{
|
||||
@@ -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
|
||||
if (!qInfo.IsRepeatable())
|
||||
return m_RewardedQuests.Contains(quest_id);
|
||||
return IsQuestRewarded(quest_id);
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1722,15 +1722,8 @@ namespace Game.Entities
|
||||
if (questStatusData != null)
|
||||
return questStatusData.Status;
|
||||
|
||||
Quest quest = Global.ObjectMgr.GetQuestTemplate(questId);
|
||||
if (quest != null)
|
||||
{
|
||||
if (quest.IsSeasonal() && !quest.IsRepeatable())
|
||||
return SatisfyQuestSeasonal(quest, false) ? QuestStatus.None : QuestStatus.Rewarded;
|
||||
|
||||
if (!quest.IsRepeatable() && IsQuestRewarded(questId))
|
||||
return QuestStatus.Rewarded;
|
||||
}
|
||||
if (GetQuestRewardStatus(questId))
|
||||
return QuestStatus.Rewarded;
|
||||
}
|
||||
return QuestStatus.None;
|
||||
}
|
||||
@@ -1738,7 +1731,22 @@ namespace Game.Entities
|
||||
public bool CanShareQuest(uint 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)
|
||||
@@ -1786,6 +1794,18 @@ namespace Game.Entities
|
||||
if (questBit != 0)
|
||||
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)
|
||||
SendQuestUpdate(questId);
|
||||
}
|
||||
@@ -2036,8 +2056,10 @@ namespace Game.Entities
|
||||
{
|
||||
q_status.Explored = true;
|
||||
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))
|
||||
|
||||
@@ -7114,6 +7114,27 @@ namespace Game
|
||||
_exclusiveQuestGroups.Add(qinfo.ExclusiveGroup, qinfo.Id);
|
||||
if (qinfo.LimitTime != 0)
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user