Core/Quests: Quest flag fixups

* Update flag names
* Implemented QUEST_FLAGS_COMPLETION_NO_DEATH and QUEST_FLAGS_FAIL_ON_LOGOUT
* Started using QUEST_FLAGS_COMPLETION_EVENT and QUEST_FLAGS_COMPLETION_AREA_TRIGGER instead of a custom SpeclalFlag
* Renamed Quest::IsAutoComplete to Quest::IsTurnIn to better describe what it means (a quest that can be turned in without accepting it to quest log)
* Implemented QUEST_FLAGS_UPDATE_PHASESHIFT and removed forced phaseshift updates on every quest status change
* Implemented QUEST_FLAGS_LAUNCH_GOSSIP_ACCEPT - reopens gossip menu with questgiver
Port From (https://github.com/TrinityCore/TrinityCore/commit/4d4c7e68935df9ca40bd5539d602ac4e779f53d5)
This commit is contained in:
hondacrx
2023-06-13 08:52:22 -04:00
parent 7d6b5fbe76
commit e6432f8c97
12 changed files with 205 additions and 192 deletions
+29 -25
View File
@@ -74,11 +74,15 @@ namespace Framework.Constants
public enum QuestType
{
AutoComplete= 0,
Disabled = 1,
TurnIn= 0,
WithMaxLevel = 1,
Normal = 2,
Task = 3,
Max = 4
MaxDBAllowedQuestTypes = 4,
// values used in quest menu packets
InProgress = 4,
TaskInProgress = 5
}
public enum QuestInfos
@@ -428,37 +432,37 @@ namespace Framework.Constants
public enum QuestFlags : uint
{
None = 0x00,
StayAlive = 0x01, // Not Used Currently
PartyAccept = 0x02, // Not Used Currently. If Player In Party, All Players That Can Accept This Quest Will Receive Confirmation Box To Accept Quest Cmsg_Quest_Confirm_Accept/Smsg_Quest_Confirm_Accept
Exploration = 0x04, // Not Used Currently
CompletionNoDeath = 0x01,
CompletionEvent = 0x02,
CompletionAreaTrigger = 0x04,
Sharable = 0x08, // Can Be Shared: Player.Cansharequest()
HasCondition = 0x10, // Not Used Currently
HideRewardPoi = 0x20, // Not Used Currently: Unsure Of Content
Raid = 0x40, // Can be completed while in raid
HideRewardPoi = 0x20, // Hides questgiver turn-in minimap icon
RaidGroupOk = 0x40, // Can be completed while in raid
WarModeRewardsOptIn = 0x80, // Not Used Currently
NoMoneyFromXp = 0x100, // Not Used Currently: Experience Is Not Converted To Gold At Max Level
HiddenRewards = 0x200, // Items And Money Rewarded Only Sent In Smsg_Questgiver_Offer_Reward (Not In Smsg_Questgiver_Quest_Details Or In Client Quest Log(Smsg_Quest_Query_Response))
Tracking = 0x400, // These Quests Are Automatically Rewarded On Quest Complete And They Will Never Appear In Quest Log Client Side.
NoMoneyForXp = 0x100, // Experience is not converted to gold at max level
HideReward = 0x200, // Items and money rewarded only sent in SMSG_QUESTGIVER_OFFER_REWARD (not in SMSG_QUEST_GIVER_QUEST_DETAILS or in client quest log(SMSG_QUEST_QUERY_RESPONSE))
TrackingEvent = 0x400, // These quests are automatically rewarded on quest complete and they will never appear in quest log client side.
DeprecateReputation = 0x800, // Not Used Currently
Daily = 0x1000, // Used To Know Quest Is Daily One
Pvp = 0x2000, // Having This Quest In Log Forces Pvp Flag
Unavailable = 0x4000, // Used On Quests That Are Not Generically Available
Deprecated = 0x4000, // Used on quests that are not generally available
Weekly = 0x8000,
AutoComplete = 0x10000, // Quests with this flag player submit automatically by special button in player gui
DisplayItemInTracker = 0x20000, // Displays Usable Item In Quest Tracker
ObjText = 0x40000, // Use Objective Text As Complete Text
DisableCompletionText = 0x40000, // Use Objective Text As Complete Text
AutoAccept = 0x80000, // The client recognizes this flag as auto-accept.
PlayerCastOnAccept = 0x100000,
PlayerCastOnComplete = 0x200000,
UpdatePhaseShift = 0x400000,
PlayerCastAccept = 0x100000,
PlayerCastComplete = 0x200000,
UpdatePhaseshift = 0x400000,
SorWhitelist = 0x800000,
LaunchGossipComplete = 0x1000000,
RemoveExtraGetItems = 0x2000000,
HideUntilDiscovered = 0x4000000,
RemoveSurplusItems = 0x2000000, // Remove all items from inventory that have the same id as the objective, not just the amount required by quest
WellKnown = 0x04000000,
PortraitInQuestLog = 0x8000000,
ShowItemWhenCompleted = 0x10000000,
LaunchGossipAccept = 0x20000000,
ItemsGlowWhenDone = 0x40000000,
ItemsGlowWhenComplete = 0x40000000,
FailOnLogout = 0x80000000
}
@@ -524,14 +528,14 @@ namespace Framework.Constants
{
None = 0x00,
// Flags For Set Specialflags In Db If Required But Used Only At Server
Repeatable = 0x001,
ExplorationOrEvent = 0x002, // If Required Area Explore, Spell Spell_Effect_Quest_Complete Casting, Table `*_Script` Command Script_Command_Quest_Explored Use, Set From Script)
AutoAccept = 0x004, // Quest Is To Be Auto-Accepted.
DfQuest = 0x008, // Quest Is Used By Dungeon Finder.
Monthly = 0x010, // Quest Is Reset At The Begining Of The Month
Repeatable = 0x001, // Set by 1 in SpecialFlags from DB
AutoPushToParty = 0x002, // Set by 2 in SpecialFlags from DB will make quest be pushed to entire party when one member accepts it
AutoAccept = 0x004, // Set by 4 in SpecialFlags in DB if the quest is to be auto-accepted.
DfQuest = 0x008, // Set by 8 in SpecialFlags in DB if the quest is used by Dungeon Finder.
Monthly = 0x010, // Set by 16 in SpecialFlags in DB if the quest is reset at the begining of the month
// Room For More Custom Flags
DbAllowed = Repeatable | ExplorationOrEvent | AutoAccept | DfQuest | Monthly,
DbAllowed = Repeatable | AutoAccept | DfQuest | Monthly,
SequencedObjectives = 0x20 // Internal flag computed only
}
@@ -1461,9 +1461,9 @@ namespace Game.AI
Quest qid = Global.ObjectMgr.GetQuestTemplate(e.Action.quest.questId);
if (qid != null)
{
if (!qid.HasSpecialFlag(QuestSpecialFlags.ExplorationOrEvent))
if (!qid.HasFlag(QuestFlags.CompletionEvent) && !qid.HasFlag(QuestFlags.CompletionAreaTrigger))
{
Log.outError(LogFilter.ScriptsAi, $"SmartAIMgr: {e} SpecialFlags for Quest entry {e.Action.quest.questId} does not include FLAGS_EXPLORATION_OR_EVENT(2), skipped.");
Log.outError(LogFilter.ScriptsAi, $"SmartAIMgr: {e} Flags for Quest entry {e.Action.quest.questId} does not include QUEST_FLAGS_COMPLETION_EVENT or QUEST_FLAGS_COMPLETION_AREA_TRIGGER, skipped.");
return false;
}
}
+2 -2
View File
@@ -282,7 +282,7 @@ namespace Game.Misc
gossipText.QuestType = item.QuestIcon;
gossipText.QuestFlags = (uint)quest.Flags;
gossipText.QuestFlagsEx = (uint)quest.FlagsEx;
gossipText.Repeatable = quest.IsAutoComplete() && quest.IsRepeatable() && !quest.IsDailyOrWeekly() && !quest.IsMonthly();
gossipText.Repeatable = quest.IsTurnIn() && quest.IsRepeatable() && !quest.IsDailyOrWeekly() && !quest.IsMonthly();
gossipText.QuestTitle = quest.LogTitle;
Locale locale = _session.GetSessionDbLocaleIndex();
@@ -374,7 +374,7 @@ namespace Game.Misc
text.QuestType = questMenuItem.QuestIcon;
text.QuestFlags = (uint)quest.Flags;
text.QuestFlagsEx = (uint)quest.FlagsEx;
text.Repeatable = quest.IsAutoComplete() && quest.IsRepeatable() && !quest.IsDailyOrWeekly() && !quest.IsMonthly();
text.Repeatable = quest.IsTurnIn() && quest.IsRepeatable() && !quest.IsDailyOrWeekly() && !quest.IsMonthly();
text.QuestTitle = quest.LogTitle;
if (localeConstant != Locale.enUS)
+87 -64
View File
@@ -310,9 +310,9 @@ namespace Game.Entities
if (!CanTakeQuest(quest, false))
continue;
if (quest.IsAutoComplete() && (!quest.IsRepeatable() || quest.IsDaily() || quest.IsWeekly() || quest.IsMonthly()))
if (quest.IsTurnIn() && (!quest.IsRepeatable() || quest.IsDaily() || quest.IsWeekly() || quest.IsMonthly()))
qm.AddMenuItem(questId, 0);
else if (quest.IsAutoComplete())
else if (quest.IsTurnIn())
qm.AddMenuItem(questId, 4);
else if (GetQuestStatus(questId) == QuestStatus.None)
qm.AddMenuItem(questId, 2);
@@ -347,9 +347,9 @@ namespace Game.Entities
if (quest.IsAutoAccept() && CanAddQuest(quest, true) && CanTakeQuest(quest, true))
AddQuestAndCheckCompletion(quest, source);
if (quest.IsAutoComplete() && quest.IsRepeatable() && !quest.IsDailyOrWeekly() && !quest.IsMonthly())
if (quest.IsTurnIn() && quest.IsRepeatable() && !quest.IsDailyOrWeekly() && !quest.IsMonthly())
PlayerTalkClass.SendQuestGiverRequestItems(quest, source.GetGUID(), CanCompleteRepeatableQuest(quest), true);
else if (quest.IsAutoComplete() && !quest.IsDailyOrWeekly() && !quest.IsMonthly())
else if (quest.IsTurnIn() && !quest.IsDailyOrWeekly() && !quest.IsMonthly())
PlayerTalkClass.SendQuestGiverRequestItems(quest, source.GetGUID(), CanRewardQuest(quest, false), true);
else
PlayerTalkClass.SendQuestGiverQuestDetails(quest, source.GetGUID(), true, false);
@@ -376,7 +376,9 @@ namespace Game.Entities
if (questGiver == this)
{
Cypher.Assert(quest.HasFlag(QuestFlags.AutoComplete));
if (!quest.HasFlag(QuestFlags.AutoComplete))
return null;
return Global.ObjectMgr.GetQuestTemplate(nextQuestID);
}
@@ -453,7 +455,7 @@ namespace Game.Entities
return false; // not allow re-complete quest
// auto complete quest
if (qInfo.IsAutoComplete() && CanTakeQuest(qInfo, false))
if (qInfo.IsTurnIn() && CanTakeQuest(qInfo, false))
return true;
var q_status = m_QuestStatus.LookupByKey(questId);
@@ -474,7 +476,7 @@ namespace Game.Entities
}
}
if (qInfo.HasSpecialFlag(QuestSpecialFlags.ExplorationOrEvent) && !q_status.Explored)
if ((qInfo.HasFlag(QuestFlags.CompletionEvent) || qInfo.HasFlag(QuestFlags.CompletionAreaTrigger)) && !q_status.Explored)
return false;
if (qInfo.LimitTime != 0 && q_status.Timer == 0)
@@ -512,7 +514,7 @@ namespace Game.Entities
return false;
// not auto complete quest and not completed quest (only cheating case, then ignore without message)
if (!quest.IsDFQuest() && !quest.IsAutoComplete() && GetQuestStatus(quest.Id) != QuestStatus.Complete)
if (!quest.IsDFQuest() && !quest.IsTurnIn() && GetQuestStatus(quest.Id) != QuestStatus.Complete)
return false;
// daily quest can't be rewarded (25 daily quest already completed)
@@ -784,7 +786,7 @@ namespace Game.Entities
{
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(quest.SourceSpellID, GetMap().GetDifficultyID());
Unit caster = this;
if (questGiver != null && questGiver.IsTypeMask(TypeMask.Unit) && !quest.HasFlag(QuestFlags.PlayerCastOnAccept) && !spellInfo.HasTargetType(Targets.UnitCaster) && !spellInfo.HasTargetType(Targets.DestCasterSummon))
if (questGiver != null && questGiver.IsTypeMask(TypeMask.Unit) && !quest.HasFlag(QuestFlags.PlayerCastAccept) && !spellInfo.HasTargetType(Targets.UnitCaster) && !spellInfo.HasTargetType(Targets.DestCasterSummon))
{
Unit unit = questGiver.ToUnit();
if (unit != null)
@@ -819,7 +821,7 @@ namespace Game.Entities
Quest qInfo = Global.ObjectMgr.GetQuestTemplate(quest_id);
if (qInfo != null)
if (qInfo.HasFlag(QuestFlags.Tracking))
if (qInfo.HasFlag(QuestFlags.TrackingEvent))
RewardQuest(qInfo, LootItemType.Item, 0, this, false);
}
}
@@ -943,8 +945,13 @@ namespace Game.Entities
switch (obj.Type)
{
case QuestObjectiveType.Item:
DestroyItemCount((uint)obj.ObjectID, (uint)obj.Amount, true);
{
int amountToDestroy = obj.Amount;
if (quest.HasFlag(QuestFlags.RemoveSurplusItems))
amountToDestroy = int.MaxValue;
DestroyItemCount((uint)obj.ObjectID, (uint)amountToDestroy, true);
break;
}
case QuestObjectiveType.Currency:
RemoveCurrency((uint)obj.ObjectID, obj.Amount, CurrencyDestroyReason.QuestTurnin);
break;
@@ -958,7 +965,9 @@ namespace Game.Entities
if (quest.ItemDrop[i] != 0)
{
uint count = quest.ItemDropQuantity[i];
DestroyItemCount(quest.ItemDrop[i], count != 0 ? count : 9999, true);
if (count == 0)
count = uint.MaxValue;
DestroyItemCount(quest.ItemDrop[i], count, true);
}
}
}
@@ -1117,7 +1126,7 @@ namespace Game.Entities
{
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(quest.RewardSpell, GetMap().GetDifficultyID());
Unit caster = this;
if (questGiver != null && questGiver.IsTypeMask(TypeMask.Unit) && !quest.HasFlag(QuestFlags.PlayerCastOnComplete) && !spellInfo.HasTargetType(Targets.UnitCaster))
if (questGiver != null && questGiver.IsTypeMask(TypeMask.Unit) && !quest.HasFlag(QuestFlags.PlayerCastComplete) && !spellInfo.HasTargetType(Targets.UnitCaster))
{
Unit unit = questGiver.ToUnit();
if (unit != null)
@@ -1137,7 +1146,7 @@ namespace Game.Entities
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(displaySpell.SpellId, GetMap().GetDifficultyID());
Unit caster = this;
if (questGiver && questGiver.IsTypeMask(TypeMask.Unit) && !quest.HasFlag(QuestFlags.PlayerCastOnComplete) && !spellInfo.HasTargetType(Targets.UnitCaster))
if (questGiver && questGiver.IsTypeMask(TypeMask.Unit) && !quest.HasFlag(QuestFlags.PlayerCastComplete) && !spellInfo.HasTargetType(Targets.UnitCaster))
{
Unit unit = questGiver.ToUnit();
if (unit)
@@ -1170,69 +1179,69 @@ namespace Game.Entities
SendQuestGiverStatusMultiple();
bool conditionChanged = SendQuestUpdate(questId, false);
SendQuestUpdate(questId);
bool updateVisibility = false;
if (quest.HasFlag(QuestFlags.UpdatePhaseshift))
updateVisibility = PhasingHandler.OnConditionChange(this, false);
//lets remove flag for delayed teleports
SetCanDelayTeleport(false);
bool canHaveNextQuest = !quest.HasFlag(QuestFlags.AutoComplete) ? questGiver != null && !questGiver.IsPlayer() : true;
if (canHaveNextQuest)
switch (questGiver.GetTypeId())
{
switch (questGiver.GetTypeId())
case TypeId.Unit:
case TypeId.Player:
{
case TypeId.Unit:
case TypeId.Player:
//For AutoSubmition was added plr case there as it almost same exclute AI script cases.
// Send next quest
Quest nextQuest = GetNextQuest(questGiver, quest);
if (nextQuest != null)
{
//For AutoSubmition was added plr case there as it almost same exclute AI script cases.
// Send next quest
Quest nextQuest = GetNextQuest(questGiver, quest);
if (nextQuest != null)
// Only send the quest to the player if the conditions are met
if (CanTakeQuest(nextQuest, false))
{
// Only send the quest to the player if the conditions are met
if (CanTakeQuest(nextQuest, false))
{
if (nextQuest.IsAutoAccept() && CanAddQuest(nextQuest, true))
AddQuestAndCheckCompletion(nextQuest, questGiver);
if (nextQuest.IsAutoAccept() && CanAddQuest(nextQuest, true))
AddQuestAndCheckCompletion(nextQuest, questGiver);
PlayerTalkClass.SendQuestGiverQuestDetails(nextQuest, questGiver.GetGUID(), true, false);
}
PlayerTalkClass.SendQuestGiverQuestDetails(nextQuest, questGiver.GetGUID(), true, false);
}
PlayerTalkClass.ClearMenus();
Creature creatureQGiver = questGiver.ToCreature();
if (creatureQGiver != null)
creatureQGiver.GetAI().OnQuestReward(this, quest, rewardType, rewardId);
break;
}
case TypeId.GameObject:
{
// Send next quest
Quest nextQuest = GetNextQuest(questGiver, quest);
if (nextQuest != null)
{
// Only send the quest to the player if the conditions are met
if (CanTakeQuest(nextQuest, false))
{
if (nextQuest.IsAutoAccept() && CanAddQuest(nextQuest, true))
AddQuestAndCheckCompletion(nextQuest, questGiver);
PlayerTalkClass.SendQuestGiverQuestDetails(nextQuest, questGiver.GetGUID(), true, false);
}
}
PlayerTalkClass.ClearMenus();
questGiver.ToGameObject()?.GetAI()?.OnQuestReward(this, quest, rewardType, rewardId);
break;
}
default:
break;
PlayerTalkClass.ClearMenus();
Creature creatureQGiver = questGiver.ToCreature();
if (creatureQGiver != null)
creatureQGiver.GetAI().OnQuestReward(this, quest, rewardType, rewardId);
break;
}
}
case TypeId.GameObject:
{
// Send next quest
Quest nextQuest = GetNextQuest(questGiver, quest);
if (nextQuest != null)
{
// Only send the quest to the player if the conditions are met
if (CanTakeQuest(nextQuest, false))
{
if (nextQuest.IsAutoAccept() && CanAddQuest(nextQuest, true))
AddQuestAndCheckCompletion(nextQuest, questGiver);
PlayerTalkClass.SendQuestGiverQuestDetails(nextQuest, questGiver.GetGUID(), true, false);
}
}
PlayerTalkClass.ClearMenus();
questGiver.ToGameObject()?.GetAI()?.OnQuestReward(this, quest, rewardType, rewardId);
break;
}
default:
break;
}
Global.ScriptMgr.OnQuestStatusChange(this, questId);
Global.ScriptMgr.OnQuestStatusChange(this, quest, oldStatus, QuestStatus.Rewarded);
if (conditionChanged)
if (updateVisibility)
UpdateObjectVisibility();
}
@@ -1299,6 +1308,21 @@ namespace Game.Entities
}
}
public void FailQuestsWithFlag(QuestFlags flag)
{
for (ushort slot = 0; slot < SharedConst.MaxQuestLogSize; ++slot)
{
uint questId = GetQuestSlotQuestId(slot);
if (questId == 0)
continue;
Quest quest = Global.ObjectMgr.GetQuestTemplate(questId);
if (quest != null)
if (quest.HasFlag(flag))
FailQuest(questId);
}
}
public void AbandonQuest(uint questId)
{
Quest quest = Global.ObjectMgr.GetQuestTemplate(questId);
@@ -1865,7 +1889,7 @@ namespace Game.Entities
QuestStatus oldStatus = m_QuestStatus[questId].Status;
m_QuestStatus[questId].Status = status;
if (!quest.IsAutoComplete())
if (!quest.IsTurnIn())
m_QuestStatusSave[questId] = QuestSaveType.Default;
Global.ScriptMgr.OnQuestStatusChange(this, questId);
@@ -1922,7 +1946,7 @@ namespace Game.Entities
SendQuestUpdate(questId);
}
bool SendQuestUpdate(uint questId, bool updateVisiblity = true)
void SendQuestUpdate(uint questId)
{
var saBounds = Global.SpellMgr.GetSpellAreaForQuestMapBounds(questId);
if (!saBounds.Empty())
@@ -1970,7 +1994,6 @@ namespace Game.Entities
}
UpdateVisibleGameobjectsOrSpellClicks();
return PhasingHandler.OnConditionChange(this, updateVisiblity);
}
public QuestGiverStatus GetQuestDialogStatus(WorldObject questgiver)
@@ -2042,7 +2065,7 @@ namespace Game.Entities
break;
}
if (quest.IsAutoComplete() && CanTakeQuest(quest, false) && quest.IsRepeatable() && !quest.IsDailyOrWeekly() && !quest.IsMonthly())
if (quest.IsTurnIn() && CanTakeQuest(quest, false) && quest.IsRepeatable() && !quest.IsDailyOrWeekly() && !quest.IsMonthly())
{
if (GetLevel() <= (GetQuestLevel(quest) + WorldConfig.GetIntValue(WorldCfg.QuestLowLevelHideDiff)))
result |= QuestGiverStatus.RepeatableTurnin;
+2
View File
@@ -650,6 +650,8 @@ namespace Game.Entities
InitializeSelfResurrectionSpells();
FailQuestsWithFlag(QuestFlags.CompletionNoDeath);
UpdateCriteria(CriteriaType.DieOnMap, 1);
UpdateCriteria(CriteriaType.DieAnywhere, 1);
UpdateCriteria(CriteriaType.DieInInstance, 1);
+16 -52
View File
@@ -1214,15 +1214,12 @@ namespace Game
continue;
}
if (!quest.HasSpecialFlag(QuestSpecialFlags.ExplorationOrEvent))
if (!quest.HasFlag(QuestFlags.CompletionEvent) && !quest.HasFlag(QuestFlags.CompletionAreaTrigger))
{
Log.outError(LogFilter.Sql, "Table `{0}` has quest (ID: {1}) in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id {2}, but quest not have flag QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT in quest flags. Script command or quest flags wrong. Quest modified to require objective.",
Log.outError(LogFilter.Sql, "Table `{0}` has quest (ID: {1}) in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id {2}, but quest not have QUEST_FLAGS_COMPLETION_EVENT or QUEST_FLAGS_COMPLETION_AREA_TRIGGER in quest flags. Script command will do nothing.",
tableName, tmp.QuestExplored.QuestID, tmp.id);
// this will prevent quest completing without objective
quest.SetSpecialFlag(QuestSpecialFlags.ExplorationOrEvent);
// continue; - quest objective requirement set and command can be allowed
continue;
}
if (tmp.QuestExplored.Distance > SharedConst.DefaultVisibilityDistance)
@@ -7249,7 +7246,7 @@ namespace Game
// additional quest integrity checks (GO, creaturetemplate and itemtemplate must be loaded already)
if (qinfo.Type >= QuestType.Max)
if (qinfo.Type >= QuestType.MaxDBAllowedQuestTypes)
Log.outError(LogFilter.Sql, "Quest {0} has `Method` = {1}, expected values are 0, 1 or 2.", qinfo.Id, qinfo.Type);
if (Convert.ToBoolean(qinfo.SpecialFlags & ~QuestSpecialFlags.DbAllowed))
@@ -7292,7 +7289,7 @@ namespace Game
}
}
if (Convert.ToBoolean(qinfo.Flags & QuestFlags.Tracking))
if (Convert.ToBoolean(qinfo.Flags & QuestFlags.TrackingEvent))
{
// at auto-reward can be rewarded only RewardChoiceItemId[0]
for (int j = 1; j < qinfo.RewardChoiceItemId.Length; ++j)
@@ -7878,35 +7875,7 @@ namespace Game
}
}
// check QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT for spell with SPELL_EFFECT_QUEST_COMPLETE
foreach (SpellNameRecord spellNameEntry in CliDB.SpellNameStorage.Values)
{
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(spellNameEntry.Id, Difficulty.None);
if (spellInfo == null)
continue;
foreach (var spellEffectInfo in spellInfo.GetEffects())
{
if (spellEffectInfo.Effect != SpellEffectName.QuestComplete)
continue;
uint questId = (uint)spellEffectInfo.MiscValue;
Quest quest = GetQuestTemplate(questId);
// some quest referenced in spells not exist (outdated spells)
if (quest == null)
continue;
if (!quest.HasSpecialFlag(QuestSpecialFlags.ExplorationOrEvent))
{
Log.outError(LogFilter.Sql, "Spell (id: {0}) have SPELL_EFFECT_QUEST_COMPLETE for quest {1}, but quest not have flag QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT. " +
"Quest flags must be fixed, quest modified to enable objective.", spellInfo.Id, questId);
// this will prevent quest completing without objective
quest.SetSpecialFlag(QuestSpecialFlags.ExplorationOrEvent);
}
}
}
// don't check spells with SPELL_EFFECT_QUEST_COMPLETE, a lot of invalid db2 data
// Make all paragon reward quests repeatable
foreach (ParagonReputationRecord paragonReputation in CliDB.ParagonReputationStorage.Values)
@@ -8122,35 +8091,30 @@ namespace Game
{
++count;
uint trigger_ID = result.Read<uint>(0);
uint quest_ID = result.Read<uint>(1);
uint triggerId = result.Read<uint>(0);
uint questId = result.Read<uint>(1);
AreaTriggerRecord atEntry = CliDB.AreaTriggerStorage.LookupByKey(trigger_ID);
AreaTriggerRecord atEntry = CliDB.AreaTriggerStorage.LookupByKey(triggerId);
if (atEntry == null)
{
Log.outError(LogFilter.Sql, "Area trigger (ID:{0}) does not exist in `AreaTrigger.dbc`.", trigger_ID);
Log.outError(LogFilter.Sql, "Area trigger (ID:{0}) does not exist in `AreaTrigger.dbc`.", triggerId);
continue;
}
Quest quest = GetQuestTemplate(quest_ID);
Quest quest = GetQuestTemplate(questId);
if (quest == null)
{
Log.outError(LogFilter.Sql, "Table `areatrigger_involvedrelation` has record (id: {0}) for not existing quest {1}", trigger_ID, quest_ID);
Log.outError(LogFilter.Sql, "Table `areatrigger_involvedrelation` has record (id: {0}) for not existing quest {1}", triggerId, questId);
continue;
}
if (!quest.HasSpecialFlag(QuestSpecialFlags.ExplorationOrEvent))
if (!quest.HasFlag(QuestFlags.CompletionAreaTrigger) && !quest.HasQuestObjectiveType(QuestObjectiveType.AreaTrigger))
{
Log.outError(LogFilter.Sql, "Table `areatrigger_involvedrelation` has record (id: {0}) for not quest {1}, but quest not have flag QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT. Trigger or quest flags must be fixed, quest modified to require objective.", trigger_ID, quest_ID);
// this will prevent quest completing without objective
quest.SetSpecialFlag(QuestSpecialFlags.ExplorationOrEvent);
// continue; - quest modified to required objective and trigger can be allowed.
Log.outError(LogFilter.Sql, $"Table `areatrigger_involvedrelation` has record (id: {triggerId}) for not quest {questId}, but quest not have flag QUEST_FLAGS_COMPLETION_AREA_TRIGGER and no objective with type QUEST_OBJECTIVE_AREATRIGGER. Trigger is obsolete, skipped.");
continue;
}
_questAreaTriggerStorage.Add(trigger_ID, quest_ID);
_questAreaTriggerStorage.Add(triggerId, questId);
} while (result.NextRow());
+2 -1
View File
@@ -220,7 +220,8 @@ namespace Game
break;
}
player.AreaExploredOrEventHappens(questId);
if (qInfo.HasFlag(QuestFlags.CompletionAreaTrigger))
player.AreaExploredOrEventHappens(questId);
if (player.CanCompleteQuest(questId))
player.CompleteQuest(questId);
+45 -29
View File
@@ -148,7 +148,7 @@ namespace Game
{
_player.AddQuestAndCheckCompletion(quest, obj);
if (quest.HasFlag(QuestFlags.PartyAccept))
if (quest.IsPushedToPartyOnAccept())
{
var group = _player.GetGroup();
if (group)
@@ -175,6 +175,26 @@ namespace Game
_player.PlayerTalkClass.SendCloseGossip();
if (quest.HasFlag(QuestFlags.LaunchGossipAccept))
{
void launchGossip(WorldObject worldObject)
{
_player.PlayerTalkClass.ClearMenus();
_player.PrepareGossipMenu(worldObject, _player.GetGossipMenuForSource(worldObject), true);
_player.SendPreparedGossip(worldObject);
}
Creature creature = obj.ToCreature();
if (creature != null)
launchGossip(creature);
else
{
GameObject go = obj.ToGameObject();
if (go != null)
launchGossip(go);
}
}
return;
}
}
@@ -202,7 +222,7 @@ namespace Game
if (quest.IsAutoAccept() && GetPlayer().CanAddQuest(quest, true))
GetPlayer().AddQuestAndCheckCompletion(quest, obj);
if (quest.IsAutoComplete())
if (quest.IsTurnIn())
GetPlayer().PlayerTalkClass.SendQuestGiverRequestItems(quest, obj.GetGUID(), GetPlayer().CanCompleteQuest(quest.Id), true);
else
GetPlayer().PlayerTalkClass.SendQuestGiverQuestDetails(quest, obj.GetGUID(), true, false);
@@ -331,7 +351,7 @@ namespace Game
}
if ((!GetPlayer().CanSeeStartQuest(quest) && GetPlayer().GetQuestStatus(packet.QuestID) == QuestStatus.None) ||
(GetPlayer().GetQuestStatus(packet.QuestID) != QuestStatus.Complete && !quest.IsAutoComplete()))
(GetPlayer().GetQuestStatus(packet.QuestID) != QuestStatus.Complete && !quest.IsTurnIn()))
{
Log.outError(LogFilter.Network, "Error in QuestStatus.Complete: player {0} ({1}) tried to complete quest {2}, but is not allowed to do so (possible packet-hacking or high latency)",
GetPlayer().GetName(), GetPlayer().GetGUID().ToString(), packet.QuestID);
@@ -422,35 +442,34 @@ namespace Game
[WorldPacketHandler(ClientOpcodes.QuestConfirmAccept)]
void HandleQuestConfirmAccept(QuestConfirmAccept packet)
{
if (_player.GetSharedQuestID() != packet.QuestID)
return;
_player.ClearQuestSharingInfo();
Quest quest = Global.ObjectMgr.GetQuestTemplate(packet.QuestID);
if (quest != null)
{
if (!quest.HasFlag(QuestFlags.PartyAccept))
return;
if (quest == null)
return;
Player originalPlayer = Global.ObjAccessor.FindPlayer(GetPlayer().GetPlayerSharingQuest());
if (originalPlayer == null)
return;
Player originalPlayer = Global.ObjAccessor.FindPlayer(GetPlayer().GetPlayerSharingQuest());
if (originalPlayer == null)
return;
if (!GetPlayer().IsInSameRaidWith(originalPlayer))
return;
if (!_player.IsInSameRaidWith(originalPlayer))
return;
if (!originalPlayer.IsActiveQuest(packet.QuestID))
return;
if (!originalPlayer.IsActiveQuest(packet.QuestID))
return;
if (!GetPlayer().CanTakeQuest(quest, true))
return;
if (!_player.CanTakeQuest(quest, true))
return;
if (GetPlayer().CanAddQuest(quest, true))
{
GetPlayer().AddQuestAndCheckCompletion(quest, null); // NULL, this prevent DB script from duplicate running
if (!_player.CanAddQuest(quest, true))
return;
if (quest.SourceSpellID > 0)
_player.CastSpell(_player, quest.SourceSpellID, true);
}
}
_player.AddQuestAndCheckCompletion(quest, null); // NULL, this prevent DB script from duplicate running
GetPlayer().ClearQuestSharingInfo();
if (quest.SourceSpellID > 0)
_player.CastSpell(_player, quest.SourceSpellID, true);
}
[WorldPacketHandler(ClientOpcodes.QuestGiverCompleteQuest, Processing = PacketProcessing.Inplace)]
@@ -462,9 +481,6 @@ namespace Game
if (quest == null)
return;
if (autoCompleteMode && !quest.HasFlag(QuestFlags.AutoComplete))
return;
WorldObject obj;
if (autoCompleteMode)
obj = GetPlayer();
@@ -474,7 +490,7 @@ namespace Game
if (!obj)
return;
if (!autoCompleteMode)
if (!quest.HasFlag(QuestFlags.AutoComplete))
{
if (!obj.HasInvolvedQuest(packet.QuestID))
return;
@@ -666,7 +682,7 @@ namespace Game
sender.SendPushToPartyResponse(receiver, QuestPushReason.Success);
if ((quest.IsAutoComplete() && quest.IsRepeatable() && !quest.IsDailyOrWeekly()) || quest.HasFlag(QuestFlags.AutoComplete))
if (quest.IsTurnIn() && quest.IsRepeatable() && !quest.IsDailyOrWeekly())
receiver.PlayerTalkClass.SendQuestGiverRequestItems(quest, sender.GetGUID(), receiver.CanCompleteRepeatableQuest(quest), true);
else
{
+15 -14
View File
@@ -462,7 +462,7 @@ namespace Game
{
return (uint)(MaxMoneyValue() * WorldConfig.GetFloatValue(WorldCfg.RateMoneyQuest));
}
public QuestTagType? GetQuestTag()
{
QuestInfoRecord questInfo = CliDB.QuestInfoStorage.LookupByKey(QuestInfoID);
@@ -471,7 +471,7 @@ namespace Game
return null;
}
public void BuildQuestRewards(QuestRewards rewards, Player player)
{
rewards.ChoiceItemCount = GetRewChoiceItemsCount();
@@ -494,7 +494,7 @@ namespace Game
if (++displaySpellIndex >= rewards.SpellCompletionDisplayID.Length)
break;
}
rewards.SpellCompletionID = RewardSpell;
rewards.SkillLineID = RewardSkillId;
rewards.NumSkillUps = RewardSkillPoints;
@@ -532,7 +532,7 @@ namespace Game
public uint GetRewMoneyMaxLevel()
{
// If Quest has flag to not give money on max level, it's 0
if (HasFlag(QuestFlags.NoMoneyFromXp))
if (HasFlag(QuestFlags.NoMoneyForXp))
return 0;
// Else, return the rewarded copper sum modified by the rate
@@ -544,9 +544,9 @@ namespace Game
return !WorldConfig.GetBoolValue(WorldCfg.QuestIgnoreAutoAccept) && HasFlag(QuestFlags.AutoAccept);
}
public bool IsAutoComplete()
public bool IsTurnIn()
{
return !WorldConfig.GetBoolValue(WorldCfg.QuestIgnoreAutoComplete) && Type == QuestType.AutoComplete;
return !WorldConfig.GetBoolValue(WorldCfg.QuestIgnoreAutoComplete) && Type == QuestType.TurnIn;
}
public bool IsRaidQuest(Difficulty difficulty)
@@ -563,7 +563,7 @@ namespace Game
break;
}
if (Flags.HasAnyFlag(QuestFlags.Raid))
if (Flags.HasAnyFlag(QuestFlags.RaidGroupOk))
return true;
return false;
@@ -655,7 +655,7 @@ namespace Game
response.Info.RewardXPDifficulty = RewardXPDifficulty;
response.Info.RewardXPMultiplier = RewardXPMultiplier;
if (!HasFlag(QuestFlags.HiddenRewards))
if (!HasFlag(QuestFlags.HideReward))
response.Info.RewardMoney = (int)(player != null ? player.GetQuestMoneyReward(this) : GetMaxMoneyReward());
response.Info.RewardMoneyDifficulty = RewardMoneyDifficulty;
@@ -699,7 +699,7 @@ namespace Game
response.Info.ItemDropQuantity[i] = (int)ItemDropQuantity[i];
}
if (!HasFlag(QuestFlags.HiddenRewards))
if (!HasFlag(QuestFlags.HideReward))
{
for (byte i = 0; i < SharedConst.QuestRewardItemCount; ++i)
{
@@ -780,12 +780,12 @@ namespace Game
public void SetSpecialFlag(QuestSpecialFlags flag) { SpecialFlags |= flag; }
public bool HasQuestObjectiveType(QuestObjectiveType type) { return _usedQuestObjectiveTypes[(int)type]; }
public bool IsAutoPush() { return HasFlagEx(QuestFlagsEx.AutoPush); }
public bool IsWorldQuest() { return HasFlagEx(QuestFlagsEx.IsWorldQuest);}
public bool IsAutoPush() { return HasFlagEx(QuestFlagsEx.AutoPush); }
public bool IsWorldQuest() { return HasFlagEx(QuestFlagsEx.IsWorldQuest); }
// Possibly deprecated flag
public bool IsUnavailable() { return HasFlag(QuestFlags.Unavailable); }
public bool IsUnavailable() { return HasFlag(QuestFlags.Deprecated); }
// table data accessors:
public bool IsRepeatable() { return SpecialFlags.HasAnyFlag(QuestSpecialFlags.Repeatable); }
@@ -800,7 +800,8 @@ namespace Game
}
public bool IsDailyOrWeekly() { return Flags.HasAnyFlag(QuestFlags.Daily | QuestFlags.Weekly); }
public bool IsDFQuest() { return SpecialFlags.HasAnyFlag(QuestSpecialFlags.DfQuest); }
public bool IsPushedToPartyOnAccept() { return HasSpecialFlag(QuestSpecialFlags.AutoPushToParty); }
public uint GetRewChoiceItemsCount() { return _rewChoiceItemsCount; }
public uint GetRewItemsCount() { return _rewItemsCount; }
public uint GetRewCurrencyCount() { return _rewCurrencyCount; }
+2
View File
@@ -153,6 +153,8 @@ namespace Game
// Clear whisper whitelist
_player.ClearWhisperWhiteList();
_player.FailQuestsWithFlag(QuestFlags.FailOnLogout);
// empty buyback items and save the player in the database
// some save parts only correctly work in case player present in map/player_lists (pets, etc)
if (save)
+2 -2
View File
@@ -71,10 +71,10 @@ namespace Game.Spells
_triggeredCastFlags = triggerFlags;
if (info.HasAttribute(SpellAttr2.DoNotReportSpellFailure))
_triggeredCastFlags = _triggeredCastFlags | TriggerCastFlags.DontReportCastError;
_triggeredCastFlags |= TriggerCastFlags.DontReportCastError;
if (m_spellInfo.HasAttribute(SpellAttr4.AllowCastWhileCasting))
_triggeredCastFlags = _triggeredCastFlags | TriggerCastFlags.IgnoreCastInProgress;
_triggeredCastFlags |= TriggerCastFlags.IgnoreCastInProgress;
m_castItemLevel = -1;
+1 -1
View File
@@ -3362,7 +3362,7 @@ namespace Game.Spells
ushort logSlot = player.FindQuestSlot(questId);
if (logSlot < SharedConst.MaxQuestLogSize)
player.AreaExploredOrEventHappens(questId);
else if (quest.HasFlag(QuestFlags.Tracking)) // Check if the quest is used as a serverside flag.
else if (quest.HasFlag(QuestFlags.TrackingEvent)) // Check if the quest is used as a serverside flag.
player.SetRewardedQuest(questId); // If so, set status to rewarded without broadcasting it to client.
}
}