Core/SAI: Implement waiting for actions on action list to finish before continuing the action list

Port From (https://github.com/TrinityCore/TrinityCore/commit/5dfac0ef142c1b59e41c51ab2cca48083be4cb9e)
This commit is contained in:
Hondacrx
2024-08-08 17:28:27 -04:00
parent 0b1f2fe34e
commit 2f71dbfda0
18 changed files with 335 additions and 79 deletions
+4 -3
View File
@@ -88,14 +88,14 @@ namespace Framework.Constants
Difficulty1_Deprecated = 0x04, //Event only occurs in instance difficulty 1 Difficulty1_Deprecated = 0x04, //Event only occurs in instance difficulty 1
Difficulty2_Deprecated = 0x08, //Event only occurs in instance difficulty 2 Difficulty2_Deprecated = 0x08, //Event only occurs in instance difficulty 2
Difficulty3_Deprecated = 0x10, //Event only occurs in instance difficulty 3 Difficulty3_Deprecated = 0x10, //Event only occurs in instance difficulty 3
Reserved5 = 0x20, ActionlistWaits = 0x20, // Timed action list will wait for action from this event to finish before moving on to next action
Reserved6 = 0x40, Reserved6 = 0x40,
DebugOnly = 0x80, //Event only occurs in debug build DebugOnly = 0x80, //Event only occurs in debug build
DontReset = 0x100, //Event will not reset in SmartScript.OnReset() DontReset = 0x100, //Event will not reset in SmartScript.OnReset()
WhileCharmed = 0x200, //Event occurs even if AI owner is charmed WhileCharmed = 0x200, //Event occurs even if AI owner is charmed
Deprecated = (Difficulty0_Deprecated | Difficulty1_Deprecated | Difficulty2_Deprecated | Difficulty3_Deprecated), Deprecated = (Difficulty0_Deprecated | Difficulty1_Deprecated | Difficulty2_Deprecated | Difficulty3_Deprecated),
All = (NotRepeatable | Deprecated | Reserved5 | Reserved6 | DebugOnly | DontReset | WhileCharmed), All = (NotRepeatable | Deprecated | ActionlistWaits | Reserved6 | DebugOnly | DontReset | WhileCharmed),
// Temp flags, used only at runtime, never stored in DB // Temp flags, used only at runtime, never stored in DB
TempIgnoreChanceRoll = 0x40000000, //Event occurs no matter what roll_chance_i(e.event.event_chance) returns. TempIgnoreChanceRoll = 0x40000000, //Event occurs no matter what roll_chance_i(e.event.event_chance) returns.
@@ -127,7 +127,8 @@ namespace Framework.Constants
//CAST_NO_MELEE_IF_OOM = 0x08, //Prevents creature from entering melee if out of mana or out of range //CAST_NO_MELEE_IF_OOM = 0x08, //Prevents creature from entering melee if out of mana or out of range
//CAST_FORCE_TARGET_SELF = 0x10, //Forces the target to cast this spell on itself //CAST_FORCE_TARGET_SELF = 0x10, //Forces the target to cast this spell on itself
AuraNotPresent = 0x20, //Only casts the spell if the target does not have an aura from the spell AuraNotPresent = 0x20, //Only casts the spell if the target does not have an aura from the spell
CombatMove = 0x40 //Prevents combat movement if cast successful. Allows movement on range, OOM, LOS CombatMove = 0x40, //Prevents combat movement if cast successful. Allows movement on range, OOM, LOS
WaitForHit = 0x80
} }
public enum SmartActionSummonCreatureFlags public enum SmartActionSummonCreatureFlags
+3 -4
View File
@@ -4,9 +4,8 @@
using Framework.Constants; using Framework.Constants;
using Game.Entities; using Game.Entities;
using Game.Groups; using Game.Groups;
using Game.Maps; using Game.Scripting.v2;
using Game.Spells; using Game.Spells;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -70,7 +69,7 @@ namespace Game.AI
return !_isCharmed; return !_isCharmed;
} }
public void StartPath(uint pathId = 0, bool repeat = false, Unit invoker = null, uint nodeId = 0) public void StartPath(uint pathId = 0, bool repeat = false, Unit invoker = null, uint nodeId = 0, ActionResultSetter<MovementStopReason> scriptResult = null)
{ {
if (HasEscortState(SmartEscortState.Escorting)) if (HasEscortState(SmartEscortState.Escorting))
StopPath(); StopPath();
@@ -96,7 +95,7 @@ namespace Game.AI
me.ReplaceAllNpcFlags(NPCFlags.None); me.ReplaceAllNpcFlags(NPCFlags.None);
} }
me.GetMotionMaster().MovePath(path, _repeatWaypointPath); me.GetMotionMaster().MovePath(path, _repeatWaypointPath, null, null, MovementWalkRunSpeedSelectionMode.Default, null, null, null, true, scriptResult);
} }
WaypointPath LoadPath(uint entry) WaypointPath LoadPath(uint entry)
+20 -2
View File
@@ -1,7 +1,6 @@
// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved. // Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information. // Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using Framework.Collections;
using Framework.Constants; using Framework.Constants;
using Framework.Database; using Framework.Database;
using Game.DataStorage; using Game.DataStorage;
@@ -12,7 +11,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using static Game.AI.SmartAction;
namespace Game.AI namespace Game.AI
{ {
@@ -1369,6 +1367,11 @@ namespace Game.AI
Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Not handled event_type({0}), Entry {1} SourceType {2} Event {3} Action {4}, skipped.", e.GetEventType(), e.EntryOrGuid, e.GetScriptType(), e.EventId, e.GetActionType()); Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Not handled event_type({0}), Entry {1} SourceType {2} Event {3} Action {4}, skipped.", e.GetEventType(), e.EntryOrGuid, e.GetScriptType(), e.EventId, e.GetActionType());
return false; return false;
} }
if (e.Event.event_flags.HasAnyFlag(SmartEventFlags.ActionlistWaits))
{
Log.outError(LogFilter.Sql, "SmartAIMgr: {e}, uses SMART_EVENT_FLAG_ACTIONLIST_WAITS but is not part of a timed actionlist.");
return false;
}
} }
if (!CheckUnusedEventParams(e)) if (!CheckUnusedEventParams(e))
@@ -1506,6 +1509,11 @@ namespace Game.AI
Log.outError(LogFilter.Sql, $"SmartAIMgr: {e} Effect: SPELL_EFFECT_KILL_CREDIT: (SpellId: {e.Action.cast.spell} targetA: {spellEffectInfo.TargetA.GetTarget()} - targetB: {spellEffectInfo.TargetB.GetTarget()}) has invalid target for this Action"); Log.outError(LogFilter.Sql, $"SmartAIMgr: {e} Effect: SPELL_EFFECT_KILL_CREDIT: (SpellId: {e.Action.cast.spell} targetA: {spellEffectInfo.TargetA.GetTarget()} - targetB: {spellEffectInfo.TargetB.GetTarget()}) has invalid target for this Action");
} }
} }
if (e.Action.cast.castFlags.HasAnyFlag((uint)SmartCastFlags.WaitForHit) && !e.Event.event_flags.HasAnyFlag(SmartEventFlags.ActionlistWaits))
{
Log.outError(LogFilter.Sql, "SmartAIMgr: {e} uses SMARTCAST_WAIT_FOR_HIT but is not part of actionlist event that has SMART_EVENT_FLAG_ACTIONLIST_WAITS");
return false;
}
break; break;
} }
case SmartActions.CrossCast: case SmartActions.CrossCast:
@@ -1538,6 +1546,11 @@ namespace Game.AI
return false; return false;
} }
} }
if (e.Action.crossCast.castFlags.HasAnyFlag((uint)SmartCastFlags.WaitForHit) && !e.Event.event_flags.HasAnyFlag(SmartEventFlags.ActionlistWaits))
{
Log.outError(LogFilter.Sql, "SmartAIMgr: {e} uses SMARTCAST_WAIT_FOR_HIT but is not part of actionlist event that has SMART_EVENT_FLAG_ACTIONLIST_WAITS");
return false;
}
break; break;
} }
case SmartActions.InvokerCast: case SmartActions.InvokerCast:
@@ -1552,6 +1565,11 @@ namespace Game.AI
case SmartActions.SelfCast: case SmartActions.SelfCast:
if (!IsSpellValid(e, e.Action.cast.spell)) if (!IsSpellValid(e, e.Action.cast.spell))
return false; return false;
if (e.Action.cast.castFlags.HasAnyFlag((uint)SmartCastFlags.WaitForHit) && !e.Event.event_flags.HasAnyFlag(SmartEventFlags.ActionlistWaits))
{
Log.outError(LogFilter.Sql, "SmartAIMgr: {e} uses SMARTCAST_WAIT_FOR_HIT but is not part of actionlist event that has SMART_EVENT_FLAG_ACTIONLIST_WAITS");
return false;
}
break; break;
case SmartActions.CallAreaexploredoreventhappens: case SmartActions.CallAreaexploredoreventhappens:
case SmartActions.CallGroupeventhappens: case SmartActions.CallGroupeventhappens:
+144 -18
View File
@@ -9,6 +9,7 @@ using Game.Groups;
using Game.Maps; using Game.Maps;
using Game.Misc; using Game.Misc;
using Game.Movement; using Game.Movement;
using Game.Scripting.v2;
using Game.Spells; using Game.Spells;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -30,6 +31,7 @@ namespace Game.AI
List<SmartScriptHolder> _installEvents = new(); List<SmartScriptHolder> _installEvents = new();
List<SmartScriptHolder> _timedActionList = new(); List<SmartScriptHolder> _timedActionList = new();
ObjectGuid mTimedActionListInvoker; ObjectGuid mTimedActionListInvoker;
ActionBase mTimedActionWaitEvent;
Creature _me; Creature _me;
ObjectGuid _meOrigGUID; ObjectGuid _meOrigGUID;
GameObject _go; GameObject _go;
@@ -185,27 +187,29 @@ namespace Game.AI
_talkerEntry = talker.GetEntry(); _talkerEntry = talker.GetEntry();
_lastTextID = e.Action.talk.textGroupId; _lastTextID = e.Action.talk.textGroupId;
_textTimer = e.Action.talk.duration; _textTimer = e.Action.talk.duration;
_useTextTimer = true; _useTextTimer = true;
Global.CreatureTextMgr.SendChat(talker, (byte)e.Action.talk.textGroupId, talkTarget); uint duration = Global.CreatureTextMgr.SendChat(talker, (byte)e.Action.talk.textGroupId, talkTarget);
mTimedActionWaitEvent = CreateTimedActionListWaitEventFor<WaitAction>(e, [GameTime.Now() + TimeSpan.FromMilliseconds(duration)]);
Log.outDebug(LogFilter.ScriptsAi, "SmartScript.ProcessAction: SMART_ACTION_TALK: talker: {0} (Guid: {1}), textGuid: {2}", Log.outDebug(LogFilter.ScriptsAi, "SmartScript.ProcessAction: SMART_ACTION_TALK: talker: {0} (Guid: {1}), textGuid: {2}",
talker.GetName(), talker.GetGUID().ToString(), _textGUID.ToString()); talker.GetName(), talker.GetGUID().ToString(), _textGUID.ToString());
break; break;
} }
case SmartActions.SimpleTalk: case SmartActions.SimpleTalk:
{ {
uint duration = 0;
foreach (var target in targets) foreach (var target in targets)
{ {
if (IsCreature(target)) if (IsCreature(target))
Global.CreatureTextMgr.SendChat(target.ToCreature(), (byte)e.Action.simpleTalk.textGroupId, IsPlayer(GetLastInvoker()) ? GetLastInvoker() : null); duration = Math.Max(Global.CreatureTextMgr.SendChat(target.ToCreature(), (byte)e.Action.simpleTalk.textGroupId, IsPlayer(GetLastInvoker()) ? GetLastInvoker() : null), duration);
else if (IsPlayer(target) && _me != null) else if (IsPlayer(target) && _me != null)
{ {
Unit templastInvoker = GetLastInvoker(); Unit templastInvoker = GetLastInvoker();
Global.CreatureTextMgr.SendChat(_me, (byte)e.Action.simpleTalk.textGroupId, IsPlayer(templastInvoker) ? templastInvoker : null, ChatMsg.Addon, Language.Addon, CreatureTextRange.Normal, 0, SoundKitPlayType.Normal, Team.Other, false, target.ToPlayer()); duration = Math.Max(Global.CreatureTextMgr.SendChat(_me, (byte)e.Action.simpleTalk.textGroupId, IsPlayer(templastInvoker) ? templastInvoker : null, ChatMsg.Addon, Language.Addon, CreatureTextRange.Normal, 0, SoundKitPlayType.Normal, Team.Other, false, target.ToPlayer()), duration);
} }
Log.outDebug(LogFilter.ScriptsAi, "SmartScript.ProcessAction. SMART_ACTION_SIMPLE_TALK: talker: {0} (GuidLow: {1}), textGroupId: {2}", Log.outDebug(LogFilter.ScriptsAi, "SmartScript.ProcessAction. SMART_ACTION_SIMPLE_TALK: talker: {0} (GuidLow: {1}), textGroupId: {2}",
target.GetName(), target.GetGUID().ToString(), e.Action.simpleTalk.textGroupId); target.GetName(), target.GetGUID().ToString(), e.Action.simpleTalk.textGroupId);
} }
mTimedActionWaitEvent = CreateTimedActionListWaitEventFor<WaitAction>(e, [GameTime.Now() + TimeSpan.FromMilliseconds(duration)]);
break; break;
} }
case SmartActions.PlayEmote: case SmartActions.PlayEmote:
@@ -439,6 +443,9 @@ namespace Game.AI
} }
case SmartActions.Cast: case SmartActions.Cast:
{ {
if (targets.Empty())
break;
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);
@@ -454,6 +461,8 @@ namespace Game.AI
args.TriggerFlags = TriggerCastFlags.FullMask; args.TriggerFlags = TriggerCastFlags.FullMask;
} }
MultiActionResult<SpellCastResult> waitEvent = CreateTimedActionListWaitEventFor<MultiActionResult<SpellCastResult>>(e);
foreach (WorldObject target in targets) foreach (WorldObject target in targets)
{ {
if (e.Action.cast.castFlags.HasAnyFlag((uint)SmartCastFlags.AuraNotPresent) && (!target.IsUnit() || target.ToUnit().HasAura(e.Action.cast.spell))) if (e.Action.cast.castFlags.HasAnyFlag((uint)SmartCastFlags.AuraNotPresent) && (!target.IsUnit() || target.ToUnit().HasAura(e.Action.cast.spell)))
@@ -462,6 +471,11 @@ namespace Game.AI
continue; continue;
} }
if (waitEvent != null)
{
args.SetScriptResult(ActionResult<SpellCastResult>.GetResultSetter(waitEvent.CreateAndGetResult()));
args.SetScriptWaitsForSpellHit(e.Action.cast.castFlags.HasAnyFlag((uint)SmartCastFlags.WaitForHit));
}
SpellCastResult result = SpellCastResult.BadTargets; SpellCastResult result = SpellCastResult.BadTargets;
if (_me != null) if (_me != null)
@@ -489,6 +503,9 @@ namespace Game.AI
Log.outDebug(LogFilter.ScriptsAi, $"SmartScript::ProcessAction:: SMART_ACTION_CAST:: {(_me != null ? _me.GetGUID() : _go.GetGUID())} casts spell {e.Action.cast.spell} on target {target.GetGUID()} with castflags {e.Action.cast.castFlags}"); Log.outDebug(LogFilter.ScriptsAi, $"SmartScript::ProcessAction:: SMART_ACTION_CAST:: {(_me != null ? _me.GetGUID() : _go.GetGUID())} casts spell {e.Action.cast.spell} on target {target.GetGUID()} with castflags {e.Action.cast.castFlags}");
} }
if (waitEvent != null && !waitEvent.Results.Empty())
mTimedActionWaitEvent = waitEvent;
// If there is at least 1 failed cast and no successful casts at all, retry again on next loop // If there is at least 1 failed cast and no successful casts at all, retry again on next loop
if (failedSpellCast && !successfulSpellCast) if (failedSpellCast && !successfulSpellCast)
{ {
@@ -507,6 +524,8 @@ namespace Game.AI
if (e.Action.cast.targetsLimit != 0) if (e.Action.cast.targetsLimit != 0)
targets.RandomResize(e.Action.cast.targetsLimit); targets.RandomResize(e.Action.cast.targetsLimit);
MultiActionResult<SpellCastResult> waitEvent = CreateTimedActionListWaitEventFor<MultiActionResult<SpellCastResult>>(e);
CastSpellExtraArgs args = new(); CastSpellExtraArgs args = new();
if (e.Action.cast.castFlags.HasAnyFlag((uint)SmartCastFlags.Triggered)) if (e.Action.cast.castFlags.HasAnyFlag((uint)SmartCastFlags.Triggered))
{ {
@@ -521,11 +540,20 @@ namespace Game.AI
if (e.Action.cast.castFlags.HasAnyFlag((uint)SmartCastFlags.AuraNotPresent) && (!target.IsUnit() || target.ToUnit().HasAura(e.Action.cast.spell))) if (e.Action.cast.castFlags.HasAnyFlag((uint)SmartCastFlags.AuraNotPresent) && (!target.IsUnit() || target.ToUnit().HasAura(e.Action.cast.spell)))
continue; continue;
if (waitEvent != null)
{
args.SetScriptResult(ActionResult<SpellCastResult>.GetResultSetter(waitEvent.CreateAndGetResult()));
args.SetScriptWaitsForSpellHit(e.Action.cast.castFlags.HasAnyFlag((uint)SmartCastFlags.WaitForHit));
}
if (e.Action.cast.castFlags.HasAnyFlag((uint)SmartCastFlags.InterruptPrevious) && target.IsUnit()) if (e.Action.cast.castFlags.HasAnyFlag((uint)SmartCastFlags.InterruptPrevious) && target.IsUnit())
target.ToUnit().InterruptNonMeleeSpells(false); target.ToUnit().InterruptNonMeleeSpells(false);
target.CastSpell(target, e.Action.cast.spell, args); target.CastSpell(target, e.Action.cast.spell, args);
} }
if (waitEvent != null && !waitEvent.Results.Empty())
mTimedActionWaitEvent = waitEvent;
break; break;
} }
case SmartActions.InvokerCast: case SmartActions.InvokerCast:
@@ -549,6 +577,8 @@ namespace Game.AI
args.TriggerFlags = TriggerCastFlags.FullMask; args.TriggerFlags = TriggerCastFlags.FullMask;
} }
MultiActionResult<SpellCastResult> waitEvent = CreateTimedActionListWaitEventFor<MultiActionResult<SpellCastResult>>(e);
foreach (var target in targets) foreach (var target in targets)
{ {
if (e.Action.cast.castFlags.HasAnyFlag((uint)SmartCastFlags.AuraNotPresent) && (!target.IsUnit() || target.ToUnit().HasAura(e.Action.cast.spell))) if (e.Action.cast.castFlags.HasAnyFlag((uint)SmartCastFlags.AuraNotPresent) && (!target.IsUnit() || target.ToUnit().HasAura(e.Action.cast.spell)))
@@ -560,9 +590,18 @@ namespace Game.AI
if (e.Action.cast.castFlags.HasAnyFlag((uint)SmartCastFlags.InterruptPrevious)) if (e.Action.cast.castFlags.HasAnyFlag((uint)SmartCastFlags.InterruptPrevious))
tempLastInvoker.InterruptNonMeleeSpells(false); tempLastInvoker.InterruptNonMeleeSpells(false);
if (waitEvent != null)
{
args.SetScriptResult(ActionResult<SpellCastResult>.GetResultSetter(waitEvent.CreateAndGetResult()));
args.SetScriptWaitsForSpellHit(e.Action.cast.castFlags.HasAnyFlag((uint)SmartCastFlags.WaitForHit));
}
tempLastInvoker.CastSpell(target, e.Action.cast.spell, args); tempLastInvoker.CastSpell(target, e.Action.cast.spell, args);
Log.outDebug(LogFilter.ScriptsAi, $"SmartScript::ProcessAction:: SMART_ACTION_INVOKER_CAST: Invoker {tempLastInvoker.GetGUID()} casts spell {e.Action.cast.spell} on target {target.GetGUID()} with castflags {e.Action.cast.castFlags}"); Log.outDebug(LogFilter.ScriptsAi, $"SmartScript::ProcessAction:: SMART_ACTION_INVOKER_CAST: Invoker {tempLastInvoker.GetGUID()} casts spell {e.Action.cast.spell} on target {target.GetGUID()} with castflags {e.Action.cast.castFlags}");
} }
if (waitEvent != null && !waitEvent.Results.Empty())
mTimedActionWaitEvent = waitEvent;
break; break;
} }
case SmartActions.ActivateGobject: case SmartActions.ActivateGobject:
@@ -1073,6 +1112,8 @@ namespace Game.AI
} }
case SmartActions.MoveOffset: case SmartActions.MoveOffset:
{ {
MultiActionResult<MovementStopReason> waitEvent = CreateTimedActionListWaitEventFor<MultiActionResult<MovementStopReason>>(e);
foreach (var target in targets) foreach (var target in targets)
{ {
if (!IsCreature(target)) if (!IsCreature(target))
@@ -1088,8 +1129,16 @@ namespace Game.AI
float x = (float)(pos.GetPositionX() + (Math.Cos(o - (Math.PI / 2)) * e.Target.x) + (Math.Cos(o) * e.Target.y)); float x = (float)(pos.GetPositionX() + (Math.Cos(o - (Math.PI / 2)) * e.Target.x) + (Math.Cos(o) * e.Target.y));
float y = (float)(pos.GetPositionY() + (Math.Sin(o - (Math.PI / 2)) * e.Target.x) + (Math.Sin(o) * e.Target.y)); float y = (float)(pos.GetPositionY() + (Math.Sin(o - (Math.PI / 2)) * e.Target.x) + (Math.Sin(o) * e.Target.y));
float z = pos.GetPositionZ() + e.Target.z; float z = pos.GetPositionZ() + e.Target.z;
target.ToCreature().GetMotionMaster().MovePoint(e.Action.moveOffset.PointId, x, y, z);
ActionResultSetter<MovementStopReason> scriptResult = null;
if (waitEvent != null)
scriptResult = ActionResult<MovementStopReason>.GetResultSetter(waitEvent.CreateAndGetResult());
target.ToCreature().GetMotionMaster().MovePoint(e.Action.moveOffset.PointId, x, y, z, true, null, null, MovementWalkRunSpeedSelectionMode.Default, null, scriptResult);
} }
if (waitEvent != null && !waitEvent.Results.Empty())
mTimedActionWaitEvent = waitEvent;
break; break;
} }
case SmartActions.SetVisibility: case SmartActions.SetVisibility:
@@ -1294,7 +1343,13 @@ namespace Game.AI
} }
} }
_me.GetAI<SmartAI>().StartPath(entry, repeat, unit); ActionResult<MovementStopReason> waitEvent = CreateTimedActionListWaitEventFor<MovementStopReason, ActionResult>(e);
ActionResultSetter<MovementStopReason> scriptResult = null;
if (waitEvent != null)
scriptResult = ActionResult<MovementStopReason>.GetResultSetter(waitEvent);
_me.GetAI<SmartAI>().StartPath(entry, repeat, unit, 0, scriptResult);
mTimedActionWaitEvent = waitEvent;
uint quest = e.Action.wpStart.quest; uint quest = e.Action.wpStart.quest;
uint DespawnTime = e.Action.wpStart.despawnTime; uint DespawnTime = e.Action.wpStart.despawnTime;
@@ -1368,6 +1423,11 @@ namespace Game.AI
if (!targets.Empty()) if (!targets.Empty())
target = targets.SelectRandom(); target = targets.SelectRandom();
ActionResult<MovementStopReason> waitEvent = CreateTimedActionListWaitEventFor<MovementStopReason, ActionResult>(e);
ActionResultSetter<MovementStopReason> scriptResult = null;
if (waitEvent != null)
scriptResult = ActionResult<MovementStopReason>.GetResultSetter(waitEvent);
if (target != null) if (target != null)
{ {
float x, y, z; float x, y, z;
@@ -1375,6 +1435,7 @@ namespace Game.AI
if (e.Action.moveToPos.contactDistance > 0) if (e.Action.moveToPos.contactDistance > 0)
target.GetContactPoint(_me, out x, out y, out z, e.Action.moveToPos.contactDistance); target.GetContactPoint(_me, out x, out y, out z, e.Action.moveToPos.contactDistance);
_me.GetMotionMaster().MovePoint(e.Action.moveToPos.pointId, x + e.Target.x, y + e.Target.y, z + e.Target.z, e.Action.moveToPos.disablePathfinding == 0); _me.GetMotionMaster().MovePoint(e.Action.moveToPos.pointId, x + e.Target.x, y + e.Target.y, z + e.Target.z, e.Action.moveToPos.disablePathfinding == 0);
mTimedActionWaitEvent = waitEvent;
} }
if (e.GetTargetType() != SmartTargets.Position) if (e.GetTargetType() != SmartTargets.Position)
@@ -1388,7 +1449,8 @@ namespace Game.AI
trans.CalculatePassengerPosition(ref dest.posX, ref dest.posY, ref dest.posZ, ref dest.Orientation); trans.CalculatePassengerPosition(ref dest.posX, ref dest.posY, ref dest.posZ, ref dest.Orientation);
} }
_me.GetMotionMaster().MovePoint(e.Action.moveToPos.pointId, dest, e.Action.moveToPos.disablePathfinding == 0); _me.GetMotionMaster().MovePoint(e.Action.moveToPos.pointId, dest, e.Action.moveToPos.disablePathfinding == 0, null, null, MovementWalkRunSpeedSelectionMode.Default, null, scriptResult);
mTimedActionWaitEvent = waitEvent;
break; break;
} }
case SmartActions.EnableTempGobj: case SmartActions.EnableTempGobj:
@@ -1577,6 +1639,8 @@ namespace Game.AI
List<WorldObject> casters = GetTargets(CreateSmartEvent(SmartEvents.UpdateIc, 0, 0, 0, 0, 0, 0, SmartActions.None, 0, 0, 0, 0, 0, 0, 0, (SmartTargets)e.Action.crossCast.targetType, e.Action.crossCast.targetParam1, e.Action.crossCast.targetParam2, e.Action.crossCast.targetParam3, e.Action.crossCast.targetParam4, e.Action.param_string, 0), unit); List<WorldObject> casters = GetTargets(CreateSmartEvent(SmartEvents.UpdateIc, 0, 0, 0, 0, 0, 0, SmartActions.None, 0, 0, 0, 0, 0, 0, 0, (SmartTargets)e.Action.crossCast.targetType, e.Action.crossCast.targetParam1, e.Action.crossCast.targetParam2, e.Action.crossCast.targetParam3, e.Action.crossCast.targetParam4, e.Action.param_string, 0), unit);
MultiActionResult<SpellCastResult> waitEvent = CreateTimedActionListWaitEventFor<MultiActionResult<SpellCastResult>>(e);
CastSpellExtraArgs args = new(); CastSpellExtraArgs args = new();
if (e.Action.crossCast.castFlags.HasAnyFlag((uint)SmartCastFlags.Triggered)) if (e.Action.crossCast.castFlags.HasAnyFlag((uint)SmartCastFlags.Triggered))
args.TriggerFlags = TriggerCastFlags.FullMask; args.TriggerFlags = TriggerCastFlags.FullMask;
@@ -1603,9 +1667,18 @@ namespace Game.AI
interruptedSpell = true; interruptedSpell = true;
} }
if (waitEvent != null)
{
args.SetScriptResult(ActionResult<SpellCastResult>.GetResultSetter(waitEvent.CreateAndGetResult()));
args.SetScriptWaitsForSpellHit(e.Action.cast.castFlags.HasAnyFlag((uint)SmartCastFlags.WaitForHit));
}
casterUnit.CastSpell(target, e.Action.crossCast.spell, args); casterUnit.CastSpell(target, e.Action.crossCast.spell, args);
} }
} }
if (waitEvent != null && !waitEvent.Results.Empty())
mTimedActionWaitEvent = waitEvent;
break; break;
} }
case SmartActions.CallRandomTimedActionlist: case SmartActions.CallRandomTimedActionlist:
@@ -1694,9 +1767,24 @@ namespace Game.AI
} }
case SmartActions.ActivateTaxi: case SmartActions.ActivateTaxi:
{ {
MultiActionResult<MovementStopReason> waitEvent = CreateTimedActionListWaitEventFor<MultiActionResult<MovementStopReason>>(e);
foreach (var target in targets) foreach (var target in targets)
{
if (IsPlayer(target)) if (IsPlayer(target))
target.ToPlayer().ActivateTaxiPathTo(e.Action.taxi.id); {
ActionResultSetter<MovementStopReason> scriptResult = null;
if (waitEvent != null)
scriptResult = ActionResult<MovementStopReason>.GetResultSetter(waitEvent.CreateAndGetResult());
if (!target.ToPlayer().ActivateTaxiPathTo(e.Action.taxi.id, 0, null, scriptResult))
if (scriptResult != null)
scriptResult.SetResult(MovementStopReason.Interrupted);
}
}
if (waitEvent != null && !waitEvent.Results.Empty())
mTimedActionWaitEvent = waitEvent;
break; break;
} }
case SmartActions.RandomMove: case SmartActions.RandomMove:
@@ -1813,13 +1901,20 @@ namespace Game.AI
else if (e.GetTargetType() != SmartTargets.Position) else if (e.GetTargetType() != SmartTargets.Position)
break; break;
ActionResult<MovementStopReason> waitEvent = CreateTimedActionListWaitEventFor<MovementStopReason, ActionResult>(e);
ActionResultSetter<MovementStopReason> actionResultSetter = null;
if (waitEvent != null)
actionResultSetter = ActionResult<MovementStopReason>.GetResultSetter(waitEvent);
if (e.Action.jump.Gravity != 0 || e.Action.jump.UseDefaultGravity != 0) if (e.Action.jump.Gravity != 0 || e.Action.jump.UseDefaultGravity != 0)
{ {
float gravity = e.Action.jump.UseDefaultGravity != 0 ? (float)MotionMaster.gravity : e.Action.jump.Gravity; float gravity = e.Action.jump.UseDefaultGravity != 0 ? (float)MotionMaster.gravity : e.Action.jump.Gravity;
_me.GetMotionMaster().MoveJumpWithGravity(pos, e.Action.jump.SpeedXY, gravity, e.Action.jump.PointId); _me.GetMotionMaster().MoveJumpWithGravity(pos, e.Action.jump.SpeedXY, gravity, e.Action.jump.PointId, false, null, null, actionResultSetter);
} }
else else
_me.GetMotionMaster().MoveJump(pos, e.Action.jump.SpeedXY, e.Action.jump.SpeedZ, e.Action.jump.PointId); _me.GetMotionMaster().MoveJump(pos, e.Action.jump.SpeedXY, e.Action.jump.SpeedZ, e.Action.jump.PointId, false, null, null, actionResultSetter);
mTimedActionWaitEvent = waitEvent;
break; break;
} }
case SmartActions.GoSetLootState: case SmartActions.GoSetLootState:
@@ -1997,11 +2092,7 @@ namespace Game.AI
} }
case SmartActions.StartClosestWaypoint: case SmartActions.StartClosestWaypoint:
{ {
List<uint> waypoints = new(); MultiActionResult<MovementStopReason> waitEvent = CreateTimedActionListWaitEventFor<MultiActionResult<MovementStopReason>>(e);
var closestWaypointFromList = e.Action.closestWaypointFromList;
foreach (var id in new[] { closestWaypointFromList.wp1, closestWaypointFromList.wp2, closestWaypointFromList.wp3, closestWaypointFromList.wp4, closestWaypointFromList.wp5, closestWaypointFromList.wp6 })
if (id != 0)
waypoints.Add(id);
float distanceToClosest = float.MaxValue; float distanceToClosest = float.MaxValue;
uint closestPathId = 0; uint closestPathId = 0;
@@ -2014,12 +2105,12 @@ namespace Game.AI
{ {
if (IsSmart(creature)) if (IsSmart(creature))
{ {
foreach (uint pathId in waypoints) var closestWaypointFromList = e.Action.closestWaypointFromList;
foreach (uint pathId in new[] { closestWaypointFromList.wp1, closestWaypointFromList.wp2, closestWaypointFromList.wp3, closestWaypointFromList.wp4, closestWaypointFromList.wp5, closestWaypointFromList.wp6 })
{ {
WaypointPath path = Global.WaypointMgr.GetPath(pathId); WaypointPath path = Global.WaypointMgr.GetPath(pathId);
if (path == null || path.Nodes.Empty()) if (path == null || path.Nodes.Empty())
continue; continue;
foreach (var waypoint in path.Nodes) foreach (var waypoint in path.Nodes)
{ {
float distanceToThisNode = creature.GetDistance(waypoint.X, waypoint.Y, waypoint.Z); float distanceToThisNode = creature.GetDistance(waypoint.X, waypoint.Y, waypoint.Z);
@@ -2033,10 +2124,19 @@ namespace Game.AI
} }
if (closestPathId != 0) if (closestPathId != 0)
((SmartAI)creature.GetAI()).StartPath(closestPathId, true, null, closestWaypointId); {
ActionResultSetter<MovementStopReason> actionResultSetter = null;
if (waitEvent != null)
actionResultSetter = ActionResult<MovementStopReason>.GetResultSetter(waitEvent.CreateAndGetResult());
creature.GetAI<SmartAI>().StartPath(closestPathId, true, null, closestWaypointId, actionResultSetter);
}
} }
} }
} }
if (waitEvent != null && !waitEvent.Results.Empty())
mTimedActionWaitEvent = waitEvent;
break; break;
} }
case SmartActions.RandomSound: case SmartActions.RandomSound:
@@ -3614,6 +3714,9 @@ namespace Game.AI
if (e.GetEventType() == SmartEvents.UpdateOoc && (_me != null && _me.IsEngaged()))//can be used with me=NULL (go script) if (e.GetEventType() == SmartEvents.UpdateOoc && (_me != null && _me.IsEngaged()))//can be used with me=NULL (go script)
return; return;
if (e.GetScriptType() == SmartScriptType.TimedActionlist && mTimedActionWaitEvent != null && !mTimedActionWaitEvent.IsReady())
return;
if (e.Timer < diff) if (e.Timer < diff)
{ {
// delay spell cast event if another spell is being casted // delay spell cast event if another spell is being casted
@@ -4362,6 +4465,29 @@ namespace Game.AI
} }
} }
} }
//template<typename Result, typename ConcreteActionImpl = Scripting::v2::ActionResult<Result>, typename...Args>
public static ActionResult<Result> CreateTimedActionListWaitEventFor<Result, ConcreteActionImpl>(SmartScriptHolder e, object[] args = null) where ConcreteActionImpl : ActionBase
{
if (e.GetScriptType() != SmartScriptType.TimedActionlist)
return null;
if (!e.Event.event_flags.HasFlag(SmartEventFlags.ActionlistWaits))
return null;
return (ActionResult<Result>)Activator.CreateInstance(typeof(ActionResult<Result>), args);
}
public static ConcreteActionImpl CreateTimedActionListWaitEventFor<ConcreteActionImpl>(SmartScriptHolder e, object[] args = null) where ConcreteActionImpl : ActionBase
{
if (e.GetScriptType() != SmartScriptType.TimedActionlist)
return null;
if (!e.Event.event_flags.HasFlag(SmartEventFlags.ActionlistWaits))
return null;
return (ConcreteActionImpl)Activator.CreateInstance(typeof(ConcreteActionImpl), args);
}
} }
class ObjectGuidList class ObjectGuidList
+4 -5
View File
@@ -7,7 +7,6 @@ using Framework.Dynamic;
using Game.Achievements; using Game.Achievements;
using Game.AI; using Game.AI;
using Game.Arenas; using Game.Arenas;
using Game.BattleFields;
using Game.BattleGrounds; using Game.BattleGrounds;
using Game.BattlePets; using Game.BattlePets;
using Game.Chat; using Game.Chat;
@@ -22,11 +21,11 @@ using Game.Misc;
using Game.Miscellaneous; using Game.Miscellaneous;
using Game.Networking; using Game.Networking;
using Game.Networking.Packets; using Game.Networking.Packets;
using Game.Scripting.v2;
using Game.Spells; using Game.Spells;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using static Global; using static Global;
namespace Game.Entities namespace Game.Entities
@@ -6921,7 +6920,7 @@ namespace Game.Entities
public uint GetDeathTimer() { return m_deathTimer; } public uint GetDeathTimer() { return m_deathTimer; }
public bool ActivateTaxiPathTo(List<uint> nodes, Creature npc = null, uint spellid = 0, uint preferredMountDisplay = 0, float? speed = null, TaskCompletionSource<MovementStopReason> scriptResult = null) public bool ActivateTaxiPathTo(List<uint> nodes, Creature npc = null, uint spellid = 0, uint preferredMountDisplay = 0, float? speed = null, ActionResultSetter<MovementStopReason> scriptResult = null)
{ {
if (nodes.Count < 2) if (nodes.Count < 2)
{ {
@@ -7103,7 +7102,7 @@ namespace Game.Entities
return true; return true;
} }
public bool ActivateTaxiPathTo(uint taxi_path_id, uint spellid = 0, float? speed = null, TaskCompletionSource<MovementStopReason> scriptResult = null) public bool ActivateTaxiPathTo(uint taxi_path_id, uint spellid = 0, float? speed = null, ActionResultSetter<MovementStopReason> scriptResult = null)
{ {
var entry = CliDB.TaxiPathStorage.LookupByKey(taxi_path_id); var entry = CliDB.TaxiPathStorage.LookupByKey(taxi_path_id);
if (entry == null) if (entry == null)
@@ -7178,7 +7177,7 @@ namespace Game.Entities
StartTaxiMovement(mountDisplayId, path, startNode, null, null); StartTaxiMovement(mountDisplayId, path, startNode, null, null);
} }
void StartTaxiMovement(uint mountDisplayId, uint path, uint pathNode, float? speed, TaskCompletionSource<MovementStopReason> scriptResult) void StartTaxiMovement(uint mountDisplayId, uint path, uint pathNode, float? speed, ActionResultSetter<MovementStopReason> scriptResult)
{ {
// remove fake death // remove fake death
RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.Interacting); RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.Interacting);
@@ -4,8 +4,8 @@
using Framework.Constants; using Framework.Constants;
using Game.AI; using Game.AI;
using Game.Entities; using Game.Entities;
using Game.Scripting.v2;
using System; using System;
using System.Threading.Tasks;
namespace Game.Movement namespace Game.Movement
{ {
@@ -14,7 +14,7 @@ namespace Game.Movement
public const float MIN_QUIET_DISTANCE = 28.0f; public const float MIN_QUIET_DISTANCE = 28.0f;
public const float MAX_QUIET_DISTANCE = 43.0f; public const float MAX_QUIET_DISTANCE = 43.0f;
public FleeingMovementGenerator(ObjectGuid fleeTargetGUID, TaskCompletionSource<MovementStopReason> scriptResult = null) public FleeingMovementGenerator(ObjectGuid fleeTargetGUID, ActionResultSetter<MovementStopReason> scriptResult = null)
{ {
_fleeTargetGUID = fleeTargetGUID; _fleeTargetGUID = fleeTargetGUID;
_timer = new TimeTracker(); _timer = new TimeTracker();
@@ -197,7 +197,7 @@ namespace Game.Movement
public class TimedFleeingMovementGenerator : FleeingMovementGenerator public class TimedFleeingMovementGenerator : FleeingMovementGenerator
{ {
public TimedFleeingMovementGenerator(ObjectGuid fleeTargetGUID, TimeSpan time, TaskCompletionSource<MovementStopReason> scriptResult = null) : base(fleeTargetGUID, scriptResult) public TimedFleeingMovementGenerator(ObjectGuid fleeTargetGUID, TimeSpan time, ActionResultSetter<MovementStopReason> scriptResult = null) : base(fleeTargetGUID, scriptResult)
{ {
_totalFleeTime = new TimeTracker(time); _totalFleeTime = new TimeTracker(time);
} }
@@ -5,11 +5,11 @@ using Framework.Constants;
using Game.DataStorage; using Game.DataStorage;
using Game.Entities; using Game.Entities;
using Game.Maps; using Game.Maps;
using Game.Scripting.v2;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using System.Threading.Tasks;
namespace Game.Movement namespace Game.Movement
{ {
@@ -25,7 +25,7 @@ namespace Game.Movement
int _currentNode; int _currentNode;
List<TaxiNodeChangeInfo> _pointsForPathSwitch = new(); //! node indexes and costs where TaxiPath changes List<TaxiNodeChangeInfo> _pointsForPathSwitch = new(); //! node indexes and costs where TaxiPath changes
public FlightPathMovementGenerator(float? speed, TaskCompletionSource<MovementStopReason> scriptResult) public FlightPathMovementGenerator(float? speed, ActionResultSetter<MovementStopReason> scriptResult)
{ {
_speed = speed; _speed = speed;
Mode = MovementGeneratorMode.Default; Mode = MovementGeneratorMode.Default;
@@ -4,6 +4,7 @@
using Framework.Constants; using Framework.Constants;
using Game.AI; using Game.AI;
using Game.Entities; using Game.Entities;
using Game.Scripting.v2;
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -24,7 +25,7 @@ namespace Game.Movement
AbstractFollower _abstractFollower; AbstractFollower _abstractFollower;
public FollowMovementGenerator(Unit target, float range, ChaseAngle angle, TimeSpan? duration, TaskCompletionSource<MovementStopReason> scriptResult = null) public FollowMovementGenerator(Unit target, float range, ChaseAngle angle, TimeSpan? duration, ActionResultSetter<MovementStopReason> scriptResult = null)
{ {
_abstractFollower = new AbstractFollower(target); _abstractFollower = new AbstractFollower(target);
_range = range; _range = range;
@@ -3,8 +3,8 @@
using Framework.Constants; using Framework.Constants;
using Game.Entities; using Game.Entities;
using Game.Scripting.v2;
using System; using System;
using System.Threading.Tasks;
namespace Game.Movement namespace Game.Movement
{ {
@@ -117,6 +117,6 @@ namespace Game.Movement
public uint? ArrivalSpellId; public uint? ArrivalSpellId;
public ObjectGuid? ArrivalSpellTarget; public ObjectGuid? ArrivalSpellTarget;
public TimeSpan? Duration; public TimeSpan? Duration;
public TaskCompletionSource<MovementStopReason> ScriptResult; public ActionResultSetter<MovementStopReason> ScriptResult;
} }
} }
@@ -3,8 +3,8 @@
using Framework.Constants; using Framework.Constants;
using Game.Entities; using Game.Entities;
using Game.Scripting.v2;
using System; using System;
using System.Threading.Tasks;
namespace Game.Movement namespace Game.Movement
{ {
@@ -57,7 +57,7 @@ namespace Game.Movement
float? _totalTurnAngle; float? _totalTurnAngle;
uint _diffSinceLastUpdate; uint _diffSinceLastUpdate;
public RotateMovementGenerator(uint id, RotateDirection direction, TimeSpan? duration, float? turnSpeed, float? totalTurnAngle, TaskCompletionSource<MovementStopReason> scriptResult) public RotateMovementGenerator(uint id, RotateDirection direction, TimeSpan? duration, float? turnSpeed, float? totalTurnAngle, ActionResultSetter<MovementStopReason> scriptResult)
{ {
_id = id; _id = id;
_direction = direction; _direction = direction;
@@ -2,10 +2,9 @@
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information. // Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using Framework.Constants; using Framework.Constants;
using Game.AI;
using Game.Entities; using Game.Entities;
using Game.Scripting.v2;
using System; using System;
using System.Threading.Tasks;
namespace Game.Movement namespace Game.Movement
{ {
@@ -15,7 +14,7 @@ namespace Game.Movement
public MovementGeneratorPriority Priority; public MovementGeneratorPriority Priority;
public MovementGeneratorFlags Flags; public MovementGeneratorFlags Flags;
public UnitState BaseUnitState; public UnitState BaseUnitState;
public TaskCompletionSource<MovementStopReason> ScriptResult; public ActionResultSetter<MovementStopReason> ScriptResult;
~MovementGenerator() ~MovementGenerator()
{ {
@@ -3,8 +3,8 @@
using Framework.Constants; using Framework.Constants;
using Game.Entities; using Game.Entities;
using Game.Scripting.v2;
using System; using System;
using System.Threading.Tasks;
namespace Game.Movement namespace Game.Movement
{ {
@@ -22,7 +22,7 @@ namespace Game.Movement
float? _closeEnoughDistance; float? _closeEnoughDistance;
public PointMovementGenerator(uint id, float x, float y, float z, bool generatePath, float? speed = null, float? finalOrient = null, Unit faceTarget = null, SpellEffectExtraData spellEffectExtraData = null, public PointMovementGenerator(uint id, float x, float y, float z, bool generatePath, float? speed = null, float? finalOrient = null, Unit faceTarget = null, SpellEffectExtraData spellEffectExtraData = null,
MovementWalkRunSpeedSelectionMode speedSelectionMode = MovementWalkRunSpeedSelectionMode.Default, float? closeEnoughDistance = null, TaskCompletionSource<MovementStopReason> scriptResult = null) MovementWalkRunSpeedSelectionMode speedSelectionMode = MovementWalkRunSpeedSelectionMode.Default, float? closeEnoughDistance = null, ActionResultSetter<MovementStopReason> scriptResult = null)
{ {
_movementId = id; _movementId = id;
_destination = new Position(x, y, z); _destination = new Position(x, y, z);
@@ -3,6 +3,7 @@
using Framework.Constants; using Framework.Constants;
using Game.Entities; using Game.Entities;
using Game.Scripting.v2;
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -17,7 +18,7 @@ namespace Game.Movement
float _wanderDistance; float _wanderDistance;
uint _wanderSteps; uint _wanderSteps;
public RandomMovementGenerator(float spawnDist = 0.0f, TimeSpan? duration = null, TaskCompletionSource<MovementStopReason> scriptResult = null) public RandomMovementGenerator(float spawnDist = 0.0f, TimeSpan? duration = null, ActionResultSetter<MovementStopReason> scriptResult = null)
{ {
_timer = new TimeTracker(); _timer = new TimeTracker();
_reference = new(); _reference = new();
@@ -4,10 +4,10 @@
using Framework.Constants; using Framework.Constants;
using Game.AI; using Game.AI;
using Game.Entities; using Game.Entities;
using Game.Scripting.v2;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
namespace Game.Movement namespace Game.Movement
{ {
@@ -31,7 +31,7 @@ namespace Game.Movement
bool _generatePath; bool _generatePath;
public WaypointMovementGenerator(uint pathId = 0, bool repeating = true, TimeSpan? duration = null, float? speed = null, MovementWalkRunSpeedSelectionMode speedSelectionMode = MovementWalkRunSpeedSelectionMode.Default, public WaypointMovementGenerator(uint pathId = 0, bool repeating = true, TimeSpan? duration = null, float? speed = null, MovementWalkRunSpeedSelectionMode speedSelectionMode = MovementWalkRunSpeedSelectionMode.Default,
(TimeSpan min, TimeSpan max)? waitTimeRangeAtPathEnd = null, float? wanderDistanceAtPathEnds = null, bool? followPathBackwardsFromEndToStart = null, bool generatePath = true, TaskCompletionSource<MovementStopReason> scriptResult = null) (TimeSpan min, TimeSpan max)? waitTimeRangeAtPathEnd = null, float? wanderDistanceAtPathEnds = null, bool? followPathBackwardsFromEndToStart = null, bool generatePath = true, ActionResultSetter<MovementStopReason> scriptResult = null)
{ {
_nextMoveTime = new TimeTracker(0); _nextMoveTime = new TimeTracker(0);
_pathId = pathId; _pathId = pathId;
@@ -56,7 +56,7 @@ namespace Game.Movement
} }
public WaypointMovementGenerator(WaypointPath path, bool repeating = true, TimeSpan? duration = null, float? speed = null, MovementWalkRunSpeedSelectionMode speedSelectionMode = MovementWalkRunSpeedSelectionMode.Default, public WaypointMovementGenerator(WaypointPath path, bool repeating = true, TimeSpan? duration = null, float? speed = null, MovementWalkRunSpeedSelectionMode speedSelectionMode = MovementWalkRunSpeedSelectionMode.Default,
(TimeSpan min, TimeSpan max)? waitTimeRangeAtPathEnd = null, float? wanderDistanceAtPathEnds = null, bool? followPathBackwardsFromEndToStart = null, bool generatePath = true, TaskCompletionSource<MovementStopReason> scriptResult = null) (TimeSpan min, TimeSpan max)? waitTimeRangeAtPathEnd = null, float? wanderDistanceAtPathEnds = null, bool? followPathBackwardsFromEndToStart = null, bool generatePath = true, ActionResultSetter<MovementStopReason> scriptResult = null)
{ {
_nextMoveTime = new TimeTracker(0); _nextMoveTime = new TimeTracker(0);
_repeating = repeating; _repeating = repeating;
+18 -19
View File
@@ -2,15 +2,14 @@
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information. // Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using Framework.Constants; using Framework.Constants;
using Framework.Dynamic;
using Game.AI; using Game.AI;
using Game.DataStorage; using Game.DataStorage;
using Game.Entities; using Game.Entities;
using Game.Scripting.v2;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using System.Threading.Tasks;
namespace Game.Movement namespace Game.Movement
{ {
@@ -545,7 +544,7 @@ namespace Game.Movement
Add(new FollowMovementGenerator(target, SharedConst.PetFollowDist, new ChaseAngle(SharedConst.PetFollowAngle), null)); Add(new FollowMovementGenerator(target, SharedConst.PetFollowDist, new ChaseAngle(SharedConst.PetFollowAngle), null));
} }
public void MoveRandom(float wanderDistance = 0.0f, TimeSpan? duration = null, MovementSlot slot = MovementSlot.Default, TaskCompletionSource<MovementStopReason> scriptResult = null) public void MoveRandom(float wanderDistance = 0.0f, TimeSpan? duration = null, MovementSlot slot = MovementSlot.Default, ActionResultSetter<MovementStopReason> scriptResult = null)
{ {
if (_owner.IsTypeId(TypeId.Unit)) if (_owner.IsTypeId(TypeId.Unit))
Add(new RandomMovementGenerator(wanderDistance, duration, scriptResult), slot); Add(new RandomMovementGenerator(wanderDistance, duration, scriptResult), slot);
@@ -553,12 +552,12 @@ namespace Game.Movement
scriptResult.SetResult(MovementStopReason.Interrupted); scriptResult.SetResult(MovementStopReason.Interrupted);
} }
public void MoveFollow(Unit target, float dist, float angle = 0.0f, TimeSpan? duration = null, MovementSlot slot = MovementSlot.Active, TaskCompletionSource<MovementStopReason> scriptResult = null) public void MoveFollow(Unit target, float dist, float angle = 0.0f, TimeSpan? duration = null, MovementSlot slot = MovementSlot.Active, ActionResultSetter<MovementStopReason> scriptResult = null)
{ {
MoveFollow(target, dist, new ChaseAngle(angle), duration, slot, scriptResult); MoveFollow(target, dist, new ChaseAngle(angle), duration, slot, scriptResult);
} }
public void MoveFollow(Unit target, float dist, ChaseAngle angle, TimeSpan? duration = null, MovementSlot slot = MovementSlot.Active, TaskCompletionSource<MovementStopReason> scriptResult = null) public void MoveFollow(Unit target, float dist, ChaseAngle angle, TimeSpan? duration = null, MovementSlot slot = MovementSlot.Active, ActionResultSetter<MovementStopReason> scriptResult = null)
{ {
// Ignore movement request if target not exist // Ignore movement request if target not exist
if (target == null || target == _owner) if (target == null || target == _owner)
@@ -589,7 +588,7 @@ namespace Game.Movement
Add(new ConfusedMovementGenerator<Creature>()); Add(new ConfusedMovementGenerator<Creature>());
} }
public void MoveFleeing(Unit enemy, TimeSpan time = default, TaskCompletionSource<MovementStopReason> scriptResult = null) public void MoveFleeing(Unit enemy, TimeSpan time = default, ActionResultSetter<MovementStopReason> scriptResult = null)
{ {
if (enemy == null) if (enemy == null)
{ {
@@ -604,12 +603,12 @@ namespace Game.Movement
Add(new FleeingMovementGenerator(enemy.GetGUID(), scriptResult)); Add(new FleeingMovementGenerator(enemy.GetGUID(), scriptResult));
} }
public void MovePoint(uint id, Position pos, bool generatePath = true, float? finalOrient = null, float? speed = null, MovementWalkRunSpeedSelectionMode speedSelectionMode = MovementWalkRunSpeedSelectionMode.Default, float? closeEnoughDistance = null, TaskCompletionSource<MovementStopReason> scriptResult = null) public void MovePoint(uint id, Position pos, bool generatePath = true, float? finalOrient = null, float? speed = null, MovementWalkRunSpeedSelectionMode speedSelectionMode = MovementWalkRunSpeedSelectionMode.Default, float? closeEnoughDistance = null, ActionResultSetter<MovementStopReason> scriptResult = null)
{ {
MovePoint(id, pos.posX, pos.posY, pos.posZ, generatePath, finalOrient, speed, speedSelectionMode, closeEnoughDistance, scriptResult); MovePoint(id, pos.posX, pos.posY, pos.posZ, generatePath, finalOrient, speed, speedSelectionMode, closeEnoughDistance, scriptResult);
} }
public void MovePoint(uint id, float x, float y, float z, bool generatePath = true, float? finalOrient = null, float? speed = null, MovementWalkRunSpeedSelectionMode speedSelectionMode = MovementWalkRunSpeedSelectionMode.Default, float? closeEnoughDistance = null, TaskCompletionSource<MovementStopReason> scriptResult = null) public void MovePoint(uint id, float x, float y, float z, bool generatePath = true, float? finalOrient = null, float? speed = null, MovementWalkRunSpeedSelectionMode speedSelectionMode = MovementWalkRunSpeedSelectionMode.Default, float? closeEnoughDistance = null, ActionResultSetter<MovementStopReason> scriptResult = null)
{ {
Log.outDebug(LogFilter.Movement, $"MotionMaster::MovePoint: '{_owner.GetGUID()}', targeted point Id: {id} (X: {x}, Y: {y}, Z: {z})"); Log.outDebug(LogFilter.Movement, $"MotionMaster::MovePoint: '{_owner.GetGUID()}', targeted point Id: {id} (X: {x}, Y: {y}, Z: {z})");
Add(new PointMovementGenerator(id, x, y, z, generatePath, speed, finalOrient, null, null, speedSelectionMode, closeEnoughDistance, scriptResult)); Add(new PointMovementGenerator(id, x, y, z, generatePath, speed, finalOrient, null, null, speedSelectionMode, closeEnoughDistance, scriptResult));
@@ -639,7 +638,7 @@ namespace Game.Movement
} }
} }
public void MoveLand(uint id, Position pos, uint? tierTransitionId = null, float? velocity = null, MovementWalkRunSpeedSelectionMode speedSelectionMode = MovementWalkRunSpeedSelectionMode.Default, TaskCompletionSource<MovementStopReason> scriptResult = null) public void MoveLand(uint id, Position pos, uint? tierTransitionId = null, float? velocity = null, MovementWalkRunSpeedSelectionMode speedSelectionMode = MovementWalkRunSpeedSelectionMode.Default, ActionResultSetter<MovementStopReason> scriptResult = null)
{ {
var initializer = (MoveSplineInit init) => var initializer = (MoveSplineInit init) =>
{ {
@@ -666,7 +665,7 @@ namespace Game.Movement
Add(new GenericMovementGenerator(initializer, MovementGeneratorType.Effect, id, new() { ScriptResult = scriptResult })); Add(new GenericMovementGenerator(initializer, MovementGeneratorType.Effect, id, new() { ScriptResult = scriptResult }));
} }
public void MoveTakeoff(uint id, Position pos, uint? tierTransitionId = null, float? velocity = null, MovementWalkRunSpeedSelectionMode speedSelectionMode = MovementWalkRunSpeedSelectionMode.Default, TaskCompletionSource<MovementStopReason> scriptResult = null) public void MoveTakeoff(uint id, Position pos, uint? tierTransitionId = null, float? velocity = null, MovementWalkRunSpeedSelectionMode speedSelectionMode = MovementWalkRunSpeedSelectionMode.Default, ActionResultSetter<MovementStopReason> scriptResult = null)
{ {
var initializer = (MoveSplineInit init) => var initializer = (MoveSplineInit init) =>
{ {
@@ -772,12 +771,12 @@ namespace Game.Movement
MoveJump(x, y, z, 0.0f, speedXY, speedZ); MoveJump(x, y, z, 0.0f, speedXY, speedZ);
} }
public void MoveJump(Position pos, float speedXY, float speedZ, uint id = EventId.Jump, bool hasOrientation = false, JumpArrivalCastArgs arrivalCast = null, SpellEffectExtraData spellEffectExtraData = null, TaskCompletionSource<MovementStopReason> scriptResult = null) public void MoveJump(Position pos, float speedXY, float speedZ, uint id = EventId.Jump, bool hasOrientation = false, JumpArrivalCastArgs arrivalCast = null, SpellEffectExtraData spellEffectExtraData = null, ActionResultSetter<MovementStopReason> scriptResult = null)
{ {
MoveJump(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), speedXY, speedZ, id, hasOrientation, arrivalCast, spellEffectExtraData, scriptResult); MoveJump(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), speedXY, speedZ, id, hasOrientation, arrivalCast, spellEffectExtraData, scriptResult);
} }
public void MoveJump(float x, float y, float z, float o, float speedXY, float speedZ, uint id = EventId.Jump, bool hasOrientation = false, JumpArrivalCastArgs arrivalCast = null, SpellEffectExtraData spellEffectExtraData = null, TaskCompletionSource<MovementStopReason> scriptResult = null) public void MoveJump(float x, float y, float z, float o, float speedXY, float speedZ, uint id = EventId.Jump, bool hasOrientation = false, JumpArrivalCastArgs arrivalCast = null, SpellEffectExtraData spellEffectExtraData = null, ActionResultSetter<MovementStopReason> scriptResult = null)
{ {
Log.outDebug(LogFilter.Server, "Unit ({0}) jump to point (X: {1} Y: {2} Z: {3})", _owner.GetGUID().ToString(), x, y, z); Log.outDebug(LogFilter.Server, "Unit ({0}) jump to point (X: {1} Y: {2} Z: {3})", _owner.GetGUID().ToString(), x, y, z);
if (speedXY < 0.01f) if (speedXY < 0.01f)
@@ -815,7 +814,7 @@ namespace Game.Movement
Add(movement); Add(movement);
} }
public void MoveJumpWithGravity(Position pos, float speedXY, float gravity, uint id = EventId.Jump, bool hasOrientation = false, JumpArrivalCastArgs arrivalCast = null, SpellEffectExtraData spellEffectExtraData = null, TaskCompletionSource<MovementStopReason> scriptResult = null) public void MoveJumpWithGravity(Position pos, float speedXY, float gravity, uint id = EventId.Jump, bool hasOrientation = false, JumpArrivalCastArgs arrivalCast = null, SpellEffectExtraData spellEffectExtraData = null, ActionResultSetter<MovementStopReason> scriptResult = null)
{ {
Log.outDebug(LogFilter.Movement, $"MotionMaster.MoveJumpWithGravity: '{_owner.GetGUID()}', jumps to point Id: {id} ({pos})"); Log.outDebug(LogFilter.Movement, $"MotionMaster.MoveJumpWithGravity: '{_owner.GetGUID()}', jumps to point Id: {id} ({pos})");
if (speedXY < 0.01f) if (speedXY < 0.01f)
@@ -853,7 +852,7 @@ namespace Game.Movement
Add(movement); Add(movement);
} }
public void MoveCirclePath(float x, float y, float z, float radius, bool clockwise, byte stepCount, TimeSpan? duration = null, float? speed = null, MovementWalkRunSpeedSelectionMode speedSelectionMode = MovementWalkRunSpeedSelectionMode.Default, TaskCompletionSource<MovementStopReason> scriptResult = null) public void MoveCirclePath(float x, float y, float z, float radius, bool clockwise, byte stepCount, TimeSpan? duration = null, float? speed = null, MovementWalkRunSpeedSelectionMode speedSelectionMode = MovementWalkRunSpeedSelectionMode.Default, ActionResultSetter<MovementStopReason> scriptResult = null)
{ {
var initializer = (MoveSplineInit init) => var initializer = (MoveSplineInit init) =>
{ {
@@ -960,7 +959,7 @@ namespace Game.Movement
Add(new SplineChainMovementGenerator(info)); Add(new SplineChainMovementGenerator(info));
} }
public void MoveFall(uint id = 0, TaskCompletionSource<MovementStopReason> scriptResult = null) public void MoveFall(uint id = 0, ActionResultSetter<MovementStopReason> scriptResult = null)
{ {
// Use larger distance for vmap height search than in most other cases // Use larger distance for vmap height search than in most other cases
float tz = _owner.GetMapHeight(_owner.GetPositionX(), _owner.GetPositionY(), _owner.GetPositionZ(), true, MapConst.MaxFallDistance); float tz = _owner.GetMapHeight(_owner.GetPositionX(), _owner.GetPositionY(), _owner.GetPositionZ(), true, MapConst.MaxFallDistance);
@@ -1019,7 +1018,7 @@ namespace Game.Movement
Log.outError(LogFilter.Server, $"MotionMaster::MoveSeekAssistanceDistract: {_owner.GetGUID()} attempted to call distract after assistance"); Log.outError(LogFilter.Server, $"MotionMaster::MoveSeekAssistanceDistract: {_owner.GetGUID()} attempted to call distract after assistance");
} }
public void MoveTaxiFlight(uint path, uint pathnode, float? speed = null, TaskCompletionSource<MovementStopReason> scriptResult = null) public void MoveTaxiFlight(uint path, uint pathnode, float? speed = null, ActionResultSetter<MovementStopReason> scriptResult = null)
{ {
if (_owner.IsTypeId(TypeId.Player)) if (_owner.IsTypeId(TypeId.Player))
{ {
@@ -1054,7 +1053,7 @@ namespace Game.Movement
} }
public void MovePath(uint pathId, bool repeatable, TimeSpan? duration = null, float? speed = null, MovementWalkRunSpeedSelectionMode speedSelectionMode = MovementWalkRunSpeedSelectionMode.Default, public void MovePath(uint pathId, bool repeatable, TimeSpan? duration = null, float? speed = null, MovementWalkRunSpeedSelectionMode speedSelectionMode = MovementWalkRunSpeedSelectionMode.Default,
(TimeSpan, TimeSpan)? waitTimeRangeAtPathEnd = null, float? wanderDistanceAtPathEnds = null, bool? followPathBackwardsFromEndToStart = null, bool generatePath = true, TaskCompletionSource<MovementStopReason> scriptResult = null) (TimeSpan, TimeSpan)? waitTimeRangeAtPathEnd = null, float? wanderDistanceAtPathEnds = null, bool? followPathBackwardsFromEndToStart = null, bool generatePath = true, ActionResultSetter<MovementStopReason> scriptResult = null)
{ {
if (pathId == 0) if (pathId == 0)
{ {
@@ -1068,7 +1067,7 @@ namespace Game.Movement
} }
public void MovePath(WaypointPath path, bool repeatable, TimeSpan? duration = null, float? speed = null, MovementWalkRunSpeedSelectionMode speedSelectionMode = MovementWalkRunSpeedSelectionMode.Default, public void MovePath(WaypointPath path, bool repeatable, TimeSpan? duration = null, float? speed = null, MovementWalkRunSpeedSelectionMode speedSelectionMode = MovementWalkRunSpeedSelectionMode.Default,
(TimeSpan, TimeSpan)? waitTimeRangeAtPathEnd = null, float? wanderDistanceAtPathEnds = null, bool? followPathBackwardsFromEndToStart = null, bool generatePath = true, TaskCompletionSource<MovementStopReason> scriptResult = null) (TimeSpan, TimeSpan)? waitTimeRangeAtPathEnd = null, float? wanderDistanceAtPathEnds = null, bool? followPathBackwardsFromEndToStart = null, bool generatePath = true, ActionResultSetter<MovementStopReason> scriptResult = null)
{ {
Log.outDebug(LogFilter.Movement, $"MotionMaster::MovePath: '{_owner.GetGUID()}', starts moving over path Id: {path.Id} (repeatable: {repeatable})"); Log.outDebug(LogFilter.Movement, $"MotionMaster::MovePath: '{_owner.GetGUID()}', starts moving over path Id: {path.Id} (repeatable: {repeatable})");
Add(new WaypointMovementGenerator(path, repeatable, duration, speed, speedSelectionMode, waitTimeRangeAtPathEnd, wanderDistanceAtPathEnds, followPathBackwardsFromEndToStart, generatePath, scriptResult), MovementSlot.Default); Add(new WaypointMovementGenerator(path, repeatable, duration, speed, speedSelectionMode, waitTimeRangeAtPathEnd, wanderDistanceAtPathEnds, followPathBackwardsFromEndToStart, generatePath, scriptResult), MovementSlot.Default);
@@ -1082,7 +1081,7 @@ namespace Game.Movement
/// <param name="time">How long should this movement last, infinite if not set</param> /// <param name="time">How long should this movement last, infinite if not set</param>
/// <param name="turnSpeed">How fast should the unit rotate, in radians per second. Uses unit's turn speed if not set</param> /// <param name="turnSpeed">How fast should the unit rotate, in radians per second. Uses unit's turn speed if not set</param>
/// <param name="totalTurnAngle">Total angle of the entire movement, infinite if not set</param> /// <param name="totalTurnAngle">Total angle of the entire movement, infinite if not set</param>
public void MoveRotate(uint id, RotateDirection direction, TimeSpan? time = null, float? turnSpeed = null, float? totalTurnAngle = null, TaskCompletionSource<MovementStopReason> scriptResult = null) public void MoveRotate(uint id, RotateDirection direction, TimeSpan? time = null, float? turnSpeed = null, float? totalTurnAngle = null, ActionResultSetter<MovementStopReason> scriptResult = null)
{ {
Log.outDebug(LogFilter.Movement, $"MotionMaster::MoveRotate: '{_owner.GetGUID()}', starts rotate (time: {time.GetValueOrDefault(TimeSpan.Zero)}ms, turnSpeed: {turnSpeed}, totalTurnAngle: {totalTurnAngle}, direction: {direction})"); Log.outDebug(LogFilter.Movement, $"MotionMaster::MoveRotate: '{_owner.GetGUID()}', starts rotate (time: {time.GetValueOrDefault(TimeSpan.Zero)}ms, turnSpeed: {turnSpeed}, totalTurnAngle: {totalTurnAngle}, direction: {direction})");
@@ -0,0 +1,38 @@
// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
namespace Game.Scripting.v2
{
public class ActionResultSetter<T>
{
ActionBase _action;
T _result;
public ActionResultSetter(ActionBase action, T result)
{
_action = action;
_result = result;
}
public void SetResult(T result)
{
_result = result;
_action.MarkCompleted();
}
}
public class ActionResultSetter
{
ActionBase _action;
public ActionResultSetter(ActionBase action)
{
_action = action;
}
public void SetResult()
{
_action.MarkCompleted();
}
}
}
+75
View File
@@ -0,0 +1,75 @@
// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Linq;
namespace Game.Scripting.v2
{
public class ActionBase
{
bool _isReady;
public virtual bool IsReady()
{
return _isReady;
}
public void MarkCompleted()
{
_isReady = true;
}
}
public class WaitAction : ActionBase
{
DateTime _waitEnd;
public WaitAction(DateTime waitEnd)
{
_waitEnd = waitEnd;
}
public override bool IsReady()
{
return _waitEnd <= GameTime.Now();
}
}
public class ActionResult<T> : ActionBase
{
T _result;
public static ActionResultSetter<T> GetResultSetter(ActionResult<T> action)
{
T resultPtr = action._result;
return new ActionResultSetter<T>(action, resultPtr);
}
}
public class ActionResult : ActionBase
{
public static ActionResultSetter GetResultSetter(ActionResult action)
{
return new ActionResultSetter(action);
}
}
public class MultiActionResult<InnerResult> : ActionResult
{
public List<ActionResult<InnerResult>> Results = new();
public ActionResult<InnerResult> CreateAndGetResult()
{
ActionResult<InnerResult> result = new();
Results.Add(result);
return result;
}
public override bool IsReady()
{
return Results.All(result => result.IsReady());
}
}
}
+4 -4
View File
@@ -16,11 +16,11 @@ using Game.Miscellaneous;
using Game.Movement; using Game.Movement;
using Game.Networking.Packets; using Game.Networking.Packets;
using Game.Scripting; using Game.Scripting;
using Game.Scripting.v2;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading.Tasks;
namespace Game.Spells namespace Game.Spells
{ {
@@ -8267,7 +8267,7 @@ namespace Game.Spells
public List<Aura> m_appliedMods = new(); public List<Aura> m_appliedMods = new();
public TaskCompletionSource<SpellCastResult> m_scriptResult; public ActionResultSetter<SpellCastResult> m_scriptResult;
public bool m_scriptWaitsForSpellHit; public bool m_scriptWaitsForSpellHit;
WorldObject m_caster; WorldObject m_caster;
@@ -9551,7 +9551,7 @@ namespace Game.Spells
public Dictionary<SpellValueMod, int> SpellValueOverrides = new(); public Dictionary<SpellValueMod, int> SpellValueOverrides = new();
public object CustomArg; public object CustomArg;
public TaskCompletionSource<SpellCastResult> ScriptResult; public ActionResultSetter<SpellCastResult> ScriptResult;
public bool ScriptWaitsForSpellHit; public bool ScriptWaitsForSpellHit;
public CastSpellExtraArgs() { } public CastSpellExtraArgs() { }
@@ -9656,7 +9656,7 @@ namespace Game.Spells
return this; return this;
} }
public CastSpellExtraArgs SetScriptResult(TaskCompletionSource<SpellCastResult> scriptResult) public CastSpellExtraArgs SetScriptResult(ActionResultSetter<SpellCastResult> scriptResult)
{ {
ScriptResult = scriptResult; ScriptResult = scriptResult;
return this; return this;