diff --git a/Source/Framework/Constants/SmartAIConst.cs b/Source/Framework/Constants/SmartAIConst.cs index 836885c82..bb10efaa6 100644 --- a/Source/Framework/Constants/SmartAIConst.cs +++ b/Source/Framework/Constants/SmartAIConst.cs @@ -251,7 +251,7 @@ namespace Framework.Constants SummonCreature = 12, // Creatureid, Summontype, Duration In Ms, Storageid, Attackinvoker, flags(SmartActionSummonCreatureFlags) ThreatSinglePct = 13, // Threat% ThreatAllPct = 14, // Threat% - CallAreaexploredoreventhappens = 15, // Questid + CallAreaexploredoreventhappens = 15, // UNUSED, DO NOT REUSE SetIngamePhaseGroup = 16, // phaseGroupId, apply SetEmoteState = 17, // Emoteid SetUnitFlag = 18, // UNUSED, DO NOT REUSE @@ -262,7 +262,7 @@ namespace Framework.Constants IncEventPhase = 23, // Value (May Be Negative To Decrement Phase, Should Not Be 0) Evade = 24, // No Params FleeForAssist = 25, // With Emote - CallGroupeventhappens = 26, // Questid + CallGroupeventhappens = 26, // UNUSED, DO NOT REUSE CombatStop = 27, // RemoveAurasFromSpell = 28, // Spellid, 0 Removes All Auras Follow = 29, // Distance (0 = Default), Angle (0 = Default), Endcreatureentry, Credit, Credittype (0monsterkill, 1event) @@ -388,6 +388,7 @@ namespace Framework.Constants BecomePersonalCloneForPlayer = 149, // summonType 1-8, duration in ms TriggerGameEvent = 150, // eventId, useSaiTargetAsGameEventSource DoAction = 151, + CompleteQuest = 152, // QuestId. Regular quests with objectives can't be completed with this action (only quests with QUEST_FLAGS_COMPLETION_EVENT, QUEST_FLAGS_COMPLETION_AREA_TRIGGER or QUEST_FLAGS_TRACKING_EVENT) End } diff --git a/Source/Game/AI/SmartScripts/SmartAIManager.cs b/Source/Game/AI/SmartScripts/SmartAIManager.cs index 3f99624b7..ecf0c894c 100644 --- a/Source/Game/AI/SmartScripts/SmartAIManager.cs +++ b/Source/Game/AI/SmartScripts/SmartAIManager.cs @@ -284,7 +284,6 @@ namespace Game.AI } break; case SmartEvents.VictimCasting: - case SmartEvents.IsBehindTarget: if (temp.Event.minMaxRepeat.min == 0 && temp.Event.minMaxRepeat.max == 0 && !temp.Event.event_flags.HasAnyFlag(SmartEventFlags.NotRepeatable) && temp.SourceType != SmartScriptType.TimedActionlist) { temp.Event.event_flags |= SmartEventFlags.NotRepeatable; @@ -696,7 +695,6 @@ namespace Game.AI SmartActions.SummonCreature => Marshal.SizeOf(typeof(SmartAction.SummonCreature)), SmartActions.ThreatSinglePct => Marshal.SizeOf(typeof(SmartAction.ThreatPCT)), SmartActions.ThreatAllPct => Marshal.SizeOf(typeof(SmartAction.ThreatPCT)), - SmartActions.CallAreaexploredoreventhappens => Marshal.SizeOf(typeof(SmartAction.Quest)), SmartActions.SetIngamePhaseGroup => Marshal.SizeOf(typeof(SmartAction.IngamePhaseGroup)), SmartActions.SetEmoteState => Marshal.SizeOf(typeof(SmartAction.Emote)), SmartActions.AutoAttack => Marshal.SizeOf(typeof(SmartAction.AutoAttack)), @@ -705,7 +703,6 @@ namespace Game.AI SmartActions.IncEventPhase => Marshal.SizeOf(typeof(SmartAction.IncEventPhase)), SmartActions.Evade => Marshal.SizeOf(typeof(SmartAction.Evade)), SmartActions.FleeForAssist => Marshal.SizeOf(typeof(SmartAction.FleeAssist)), - SmartActions.CallGroupeventhappens => Marshal.SizeOf(typeof(SmartAction.Quest)), SmartActions.CombatStop => 0, SmartActions.RemoveAurasFromSpell => Marshal.SizeOf(typeof(SmartAction.RemoveAura)), SmartActions.Follow => Marshal.SizeOf(typeof(SmartAction.Follow)), @@ -816,6 +813,7 @@ namespace Game.AI SmartActions.BecomePersonalCloneForPlayer => Marshal.SizeOf(typeof(SmartAction.BecomePersonalClone)), SmartActions.TriggerGameEvent => Marshal.SizeOf(typeof(SmartAction.TriggerGameEvent)), SmartActions.DoAction => Marshal.SizeOf(typeof(SmartAction.DoAction)), + SmartActions.CompleteQuest => Marshal.SizeOf(typeof(SmartAction.Quest)), _ => Marshal.SizeOf(typeof(SmartAction.Raw)), }; @@ -1572,23 +1570,6 @@ namespace Game.AI return false; } break; - case SmartActions.CallAreaexploredoreventhappens: - case SmartActions.CallGroupeventhappens: - Quest qid = Global.ObjectMgr.GetQuestTemplate(e.Action.quest.questId); - if (qid != null) - { - if (!qid.HasFlag(QuestFlags.CompletionEvent) && !qid.HasFlag(QuestFlags.CompletionAreaTrigger)) - { - 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; - } - } - else - { - Log.outError(LogFilter.ScriptsAi, $"SmartAIMgr: {e} uses non-existent Quest entry {e.Action.quest.questId}, skipped."); - return false; - } - break; case SmartActions.SetEventPhase: if (e.Action.setEventPhase.phase >= (uint)SmartPhase.Max) { @@ -2215,7 +2196,25 @@ namespace Game.AI TC_SAI_IS_BOOLEAN_VALID(e, e.Action.triggerGameEvent.useSaiTargetAsGameEventSource); break; } + case SmartActions.CompleteQuest: + Quest quest = Global.ObjectMgr.GetQuestTemplate(e.Action.quest.questId); + if (quest != null) + { + if (!quest.HasFlag(QuestFlags.CompletionEvent) && !quest.HasFlag(QuestFlags.CompletionAreaTrigger) && !quest.HasFlag(QuestFlags.TrackingEvent)) + { + Log.outError(LogFilter.Sql, $"SmartAIMgr: {e} Flags for Quest entry {e.Action.quest.questId} does not include QUEST_FLAGS_COMPLETION_EVENT or QUEST_FLAGS_COMPLETION_AREA_TRIGGER or QUEST_FLAGS_TRACKING_EVENT, skipped."); + return false; + } + } + else + { + Log.outError(LogFilter.Sql, $"SmartAIMgr: {e} uses non-existent Quest entry {e.Action.quest.questId}, skipped."); + return false; + } + break; // No longer supported + case SmartActions.CallAreaexploredoreventhappens: + case SmartActions.CallGroupeventhappens: case SmartActions.SetUnitFlag: case SmartActions.RemoveUnitFlag: case SmartActions.InstallAITemplate: diff --git a/Source/Game/AI/SmartScripts/SmartScript.cs b/Source/Game/AI/SmartScripts/SmartScript.cs index 6d92307fd..23163026a 100644 --- a/Source/Game/AI/SmartScripts/SmartScript.cs +++ b/Source/Game/AI/SmartScripts/SmartScript.cs @@ -413,34 +413,6 @@ namespace Game.AI } break; } - case SmartActions.CallAreaexploredoreventhappens: - { - foreach (var target in targets) - { - // Special handling for vehicles - if (IsUnit(target)) - { - Vehicle vehicle = target.ToUnit().GetVehicleKit(); - if (vehicle != null) - { - foreach (var seat in vehicle.Seats) - { - Player player = Global.ObjAccessor.GetPlayer(target, seat.Value.Passenger.Guid); - if (player != null) - player.AreaExploredOrEventHappens(e.Action.quest.questId); - } - } - } - - if (IsPlayer(target)) - { - target.ToPlayer().AreaExploredOrEventHappens(e.Action.quest.questId); - Log.outDebug(LogFilter.ScriptsAi, "SmartScript.ProcessAction. SMART_ACTION_CALL_AREAEXPLOREDOREVENTHAPPENS: {0} credited quest {1}", - target.GetGUID().ToString(), e.Action.quest.questId); - } - } - break; - } case SmartActions.Cast: { if (targets.Empty()) @@ -714,33 +686,6 @@ namespace Game.AI Log.outDebug(LogFilter.ScriptsAi, "SmartScript.ProcessAction. SMART_ACTION_FLEE_FOR_ASSIST: Creature {0} DoFleeToGetAssistance", _me.GetGUID().ToString()); break; } - case SmartActions.CallGroupeventhappens: - { - if (unit == null) - break; - - // If invoker was pet or charm - Player playerCharmed = unit.GetCharmerOrOwnerPlayerOrPlayerItself(); - if (playerCharmed != null && GetBaseObject() != null) - { - playerCharmed.GroupEventHappens(e.Action.quest.questId, GetBaseObject()); - Log.outDebug(LogFilter.ScriptsAi, "SmartScript.ProcessAction: SMART_ACTION_CALL_GROUPEVENTHAPPENS: Player {0}, group credit for quest {1}", - unit.GetGUID().ToString(), e.Action.quest.questId); - } - - // Special handling for vehicles - Vehicle vehicle = unit.GetVehicleKit(); - if (vehicle != null) - { - foreach (var seat in vehicle.Seats) - { - Player passenger = Global.ObjAccessor.GetPlayer(unit, seat.Value.Passenger.Guid); - if (passenger != null) - passenger.GroupEventHappens(e.Action.quest.questId, GetBaseObject()); - } - } - break; - } case SmartActions.CombatStop: { if (_me == null) @@ -2586,6 +2531,34 @@ namespace Game.AI break; } + case SmartActions.CompleteQuest: + { + uint questId = e.Action.quest.questId; + Quest quest = Global.ObjectMgr.GetQuestTemplate(questId); + if (quest == null) + break; + + foreach (WorldObject target in targets) + { + Player player = target?.ToPlayer(); + if (player == null) + continue; + + QuestStatus questStatus = player.GetQuestStatus(questId); + if (questStatus == QuestStatus.Rewarded) + continue; + + if (quest.HasFlag(QuestFlags.CompletionEvent) || quest.HasFlag(QuestFlags.CompletionAreaTrigger)) + { + if (questStatus == QuestStatus.Incomplete) + player.AreaExploredOrEventHappens(questId); + } + else if (quest.HasFlag(QuestFlags.TrackingEvent)) // Check if the quest is used as a serverside flag + player.CompleteQuest(questId); + } + + break; + } default: Log.outError(LogFilter.Sql, "SmartScript.ProcessAction: Entry {0} SourceType {1}, Event {2}, Unhandled Action type {3}", e.EntryOrGuid, e.GetScriptType(), e.EventId, e.GetActionType()); break; diff --git a/Source/Game/Spells/SpellEffects.cs b/Source/Game/Spells/SpellEffects.cs index d86065976..3a039ff32 100644 --- a/Source/Game/Spells/SpellEffects.cs +++ b/Source/Game/Spells/SpellEffects.cs @@ -3374,11 +3374,17 @@ namespace Game.Spells if (quest == null) return; - ushort logSlot = player.FindQuestSlot(questId); - if (logSlot < SharedConst.MaxQuestLogSize) - player.AreaExploredOrEventHappens(questId); - 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. + QuestStatus questStatus = player.GetQuestStatus(questId); + if (questStatus == QuestStatus.Rewarded) + return; + + if (quest.HasFlag(QuestFlags.CompletionEvent) || quest.HasFlag(QuestFlags.CompletionAreaTrigger)) + { + if (questStatus == QuestStatus.Incomplete) + player.AreaExploredOrEventHappens(questId); + } + else if (quest.HasFlag(QuestFlags.TrackingEvent)) // Check if the quest is used as a serverside flag + player.CompleteQuest(questId); } }