Core/SAI: Spell casts that cannot be executed because the unit is currently casting another spell will be retried asap with priority over other events

Port From (https://github.com/TrinityCore/TrinityCore/commit/f2ee365da43f851181c8a486e10325a95b75c55d)
This commit is contained in:
hondacrx
2022-03-04 17:20:01 -05:00
parent 1670c09454
commit 82389dbaeb
2 changed files with 75 additions and 4 deletions
+19 -1
View File
@@ -1907,8 +1907,10 @@ namespace Game.AI
}; };
} }
public class SmartScriptHolder public class SmartScriptHolder : IComparer<SmartScriptHolder>
{ {
public const uint DefaultPriority = uint.MaxValue;
public int EntryOrGuid; public int EntryOrGuid;
public SmartScriptType SourceType; public SmartScriptType SourceType;
public uint EventId; public uint EventId;
@@ -1917,6 +1919,7 @@ namespace Game.AI
public SmartAction Action; public SmartAction Action;
public SmartTarget Target; public SmartTarget Target;
public uint Timer; public uint Timer;
public uint Priority;
public bool Active; public bool Active;
public bool RunOnce; public bool RunOnce;
public bool EnableTimed; public bool EnableTimed;
@@ -1946,6 +1949,21 @@ namespace Game.AI
{ {
return $"Entry {EntryOrGuid} SourceType {GetScriptType()} Event {EventId} Action {GetActionType()}"; return $"Entry {EntryOrGuid} SourceType {GetScriptType()} Event {EventId} Action {GetActionType()}";
} }
public int Compare(SmartScriptHolder left, SmartScriptHolder right)
{
int result = left.Priority.CompareTo(right.Priority);
if (result == 0)
result = left.EntryOrGuid.CompareTo(right.EntryOrGuid);
if (result == 0)
result = left.SourceType.CompareTo(right.SourceType);
if (result == 0)
result = left.EventId.CompareTo(right.EventId);
if (result == 0)
result = left.Link.CompareTo(right.Link);
return result;
}
} }
[StructLayout(LayoutKind.Explicit)] [StructLayout(LayoutKind.Explicit)]
+56 -3
View File
@@ -61,6 +61,8 @@ namespace Game.AI
ObjectGuid _textGUID; ObjectGuid _textGUID;
uint _talkerEntry; uint _talkerEntry;
bool _useTextTimer; bool _useTextTimer;
uint _currentPriority;
bool _eventSortingRequired;
Dictionary<uint, ObjectGuidList> _storedTargets = new(); Dictionary<uint, ObjectGuidList> _storedTargets = new();
@@ -95,9 +97,16 @@ namespace Game.AI
InitTimer(holder); InitTimer(holder);
holder.RunOnce = false; holder.RunOnce = false;
} }
if (holder.Priority != SmartScriptHolder.DefaultPriority)
{
holder.Priority = SmartScriptHolder.DefaultPriority;
_eventSortingRequired = true;
}
} }
ProcessEventsFor(SmartEvents.Reset); ProcessEventsFor(SmartEvents.Reset);
LastInvoker = ObjectGuid.Empty; LastInvoker.Clear();
} }
public void ProcessEventsFor(SmartEvents e, Unit unit = null, uint var0 = 0, uint var1 = 0, bool bvar = false, SpellInfo spell = null, GameObject gob = null, string varString = "") public void ProcessEventsFor(SmartEvents e, Unit unit = null, uint var0 = 0, uint var1 = 0, bool bvar = false, SpellInfo spell = null, GameObject gob = null, string varString = "")
@@ -427,6 +436,9 @@ namespace Game.AI
if (e.Action.cast.targetsLimit > 0 && targets.Count > e.Action.cast.targetsLimit) if (e.Action.cast.targetsLimit > 0 && targets.Count > e.Action.cast.targetsLimit)
targets.RandomResize(e.Action.cast.targetsLimit); targets.RandomResize(e.Action.cast.targetsLimit);
bool failedSpellCast = false;
bool successfulSpellCast = false;
foreach (var target in targets) foreach (var target in targets)
{ {
if (_go != null) if (_go != null)
@@ -458,7 +470,9 @@ namespace Game.AI
((SmartAI)_me.GetAI()).SetCombatMove(spellCastFailed, true); ((SmartAI)_me.GetAI()).SetCombatMove(spellCastFailed, true);
if (spellCastFailed) if (spellCastFailed)
RecalcTimer(e, 500, 500); failedSpellCast = true;
else
successfulSpellCast = true;
} }
else if (_go) else if (_go)
_go.CastSpell(target.ToUnit(), e.Action.cast.spell, new CastSpellExtraArgs(triggerFlag)); _go.CastSpell(target.ToUnit(), e.Action.cast.spell, new CastSpellExtraArgs(triggerFlag));
@@ -467,6 +481,11 @@ namespace Game.AI
Log.outDebug(LogFilter.ScriptsAi, "Spell {0} not casted because it has flag SMARTCAST_AURA_NOT_PRESENT and the target (Guid: {1} Entry: {2} Type: {3}) already has the aura", Log.outDebug(LogFilter.ScriptsAi, "Spell {0} not casted because it has flag SMARTCAST_AURA_NOT_PRESENT and the target (Guid: {1} Entry: {2} Type: {3}) already has the aura",
e.Action.cast.spell, target.GetGUID(), target.GetEntry(), target.GetTypeId()); e.Action.cast.spell, target.GetGUID(), target.GetEntry(), target.GetTypeId());
} }
// If there is at least 1 failed cast and no successful casts at all, retry again on next loop
if (failedSpellCast && !successfulSpellCast)
RaisePriority(e);
break; break;
} }
case SmartActions.SelfCast: case SmartActions.SelfCast:
@@ -3779,7 +3798,7 @@ namespace Game.AI
{ {
if (_me != null && _me.HasUnitState(UnitState.Casting)) if (_me != null && _me.HasUnitState(UnitState.Casting))
{ {
e.Timer = 1; RaisePriority(e);
return; return;
} }
} }
@@ -3839,6 +3858,18 @@ namespace Game.AI
break; break;
} }
} }
if (e.Priority != SmartScriptHolder.DefaultPriority)
{
// Reset priority to default one only if the event hasn't been rescheduled again to next loop
if (e.Timer > 1)
{
// Re-sort events if this was moved to the top of the queue
_eventSortingRequired = true;
// Reset priority to default one
e.Priority = SmartScriptHolder.DefaultPriority;
}
}
} }
else else
{ {
@@ -3879,6 +3910,12 @@ namespace Game.AI
InstallEvents();//before UpdateTimers InstallEvents();//before UpdateTimers
if (_eventSortingRequired)
{
SortEvents(_events);
_eventSortingRequired = false;
}
foreach (var holder in _events) foreach (var holder in _events)
UpdateTimer(holder, diff); UpdateTimer(holder, diff);
@@ -3926,6 +3963,22 @@ namespace Game.AI
} }
} }
void SortEvents(List<SmartScriptHolder> events)
{
events.Sort();
}
void RaisePriority(SmartScriptHolder e)
{
e.Timer = 1;
// Change priority only if it's set to default, otherwise keep the current order of events
if (e.Priority == SmartScriptHolder.DefaultPriority)
{
e.Priority = _currentPriority++;
_eventSortingRequired = true;
}
}
void FillScript(List<SmartScriptHolder> e, WorldObject obj, AreaTriggerRecord at, SceneTemplate scene, Quest quest) void FillScript(List<SmartScriptHolder> e, WorldObject obj, AreaTriggerRecord at, SceneTemplate scene, Quest quest)
{ {
if (e.Empty()) if (e.Empty())