From 2f71dbfda0eb21aa26a4acb196662ef6de12aa2d Mon Sep 17 00:00:00 2001 From: Hondacrx Date: Thu, 8 Aug 2024 17:28:27 -0400 Subject: [PATCH] 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) --- Source/Framework/Constants/SmartAIConst.cs | 7 +- Source/Game/AI/SmartScripts/SmartAI.cs | 15 +- Source/Game/AI/SmartScripts/SmartAIManager.cs | 22 ++- Source/Game/AI/SmartScripts/SmartScript.cs | 162 ++++++++++++++++-- Source/Game/Entities/Player/Player.cs | 9 +- .../Generators/FleeingMovementGenerator.cs | 6 +- .../Generators/FlightPathMovementGenerator.cs | 4 +- .../Generators/FollowMovementGenerator.cs | 3 +- .../Generators/GenericMovementGenerator.cs | 4 +- .../Game/Movement/Generators/IdleMovement.cs | 4 +- .../Movement/Generators/MovementGenerators.cs | 5 +- .../Game/Movement/Generators/PointMovement.cs | 6 +- .../Generators/RandomMovementGenerator.cs | 3 +- .../Movement/Generators/WaypointMovement.cs | 6 +- Source/Game/Movement/MotionMaster.cs | 37 ++-- .../Game/Scripting/v2/ScriptActionResult.cs | 38 ++++ Source/Game/Scripting/v2/ScriptActions.cs | 75 ++++++++ Source/Game/Spells/Spell.cs | 8 +- 18 files changed, 335 insertions(+), 79 deletions(-) create mode 100644 Source/Game/Scripting/v2/ScriptActionResult.cs create mode 100644 Source/Game/Scripting/v2/ScriptActions.cs diff --git a/Source/Framework/Constants/SmartAIConst.cs b/Source/Framework/Constants/SmartAIConst.cs index 4d2fbae29..836885c82 100644 --- a/Source/Framework/Constants/SmartAIConst.cs +++ b/Source/Framework/Constants/SmartAIConst.cs @@ -88,14 +88,14 @@ namespace Framework.Constants Difficulty1_Deprecated = 0x04, //Event only occurs in instance difficulty 1 Difficulty2_Deprecated = 0x08, //Event only occurs in instance difficulty 2 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, DebugOnly = 0x80, //Event only occurs in debug build DontReset = 0x100, //Event will not reset in SmartScript.OnReset() WhileCharmed = 0x200, //Event occurs even if AI owner is charmed 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 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_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 - 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 diff --git a/Source/Game/AI/SmartScripts/SmartAI.cs b/Source/Game/AI/SmartScripts/SmartAI.cs index 82866764b..c48a7ba65 100644 --- a/Source/Game/AI/SmartScripts/SmartAI.cs +++ b/Source/Game/AI/SmartScripts/SmartAI.cs @@ -4,9 +4,8 @@ using Framework.Constants; using Game.Entities; using Game.Groups; -using Game.Maps; +using Game.Scripting.v2; using Game.Spells; -using System; using System.Collections.Generic; using System.Linq; @@ -70,7 +69,7 @@ namespace Game.AI 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 scriptResult = null) { if (HasEscortState(SmartEscortState.Escorting)) StopPath(); @@ -96,7 +95,7 @@ namespace Game.AI 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) @@ -562,7 +561,7 @@ namespace Game.AI if (me.GetMotionMaster().GetCurrentMovementGeneratorType(MovementSlot.Default) != MovementGeneratorType.Waypoint) if (me.GetWaypointPathId() != 0) me.GetMotionMaster().MovePath(me.GetWaypointPathId(), true); - + me.ResumeMovement(); } else if (formation.IsFormed()) @@ -627,7 +626,7 @@ namespace Game.AI { GetScript().ProcessEventsFor(SmartEvents.SpellHit, caster.ToUnit(), 0, 0, false, spellInfo, caster.ToGameObject()); } - + public override void SpellHitTarget(WorldObject target, SpellInfo spellInfo) { GetScript().ProcessEventsFor(SmartEvents.SpellHitTarget, target.ToUnit(), 0, 0, false, spellInfo, target.ToGameObject()); @@ -647,7 +646,7 @@ namespace Game.AI { GetScript().ProcessEventsFor(SmartEvents.OnSpellStart, null, 0, 0, false, spellInfo); } - + public override void DamageTaken(Unit attacker, ref uint damage, DamageEffectType damageType, SpellInfo spellInfo = null) { GetScript().ProcessEventsFor(SmartEvents.Damaged, attacker, damage); @@ -1139,7 +1138,7 @@ namespace Game.AI } public override void SetData(uint id, uint value) { SetData(id, value, null); } - + public void SetData(uint id, uint value, Unit invoker) { GetScript().ProcessEventsFor(SmartEvents.DataSet, invoker, id, value); diff --git a/Source/Game/AI/SmartScripts/SmartAIManager.cs b/Source/Game/AI/SmartScripts/SmartAIManager.cs index b7e39e812..34873b851 100644 --- a/Source/Game/AI/SmartScripts/SmartAIManager.cs +++ b/Source/Game/AI/SmartScripts/SmartAIManager.cs @@ -1,7 +1,6 @@ // Copyright (c) CypherCore All rights reserved. // 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.Database; using Game.DataStorage; @@ -12,7 +11,6 @@ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using static Game.AI.SmartAction; 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()); 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)) @@ -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"); } } + 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; } case SmartActions.CrossCast: @@ -1538,6 +1546,11 @@ namespace Game.AI 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; } case SmartActions.InvokerCast: @@ -1552,6 +1565,11 @@ namespace Game.AI case SmartActions.SelfCast: if (!IsSpellValid(e, e.Action.cast.spell)) 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; case SmartActions.CallAreaexploredoreventhappens: case SmartActions.CallGroupeventhappens: diff --git a/Source/Game/AI/SmartScripts/SmartScript.cs b/Source/Game/AI/SmartScripts/SmartScript.cs index 2143a9e19..4bfdf1d54 100644 --- a/Source/Game/AI/SmartScripts/SmartScript.cs +++ b/Source/Game/AI/SmartScripts/SmartScript.cs @@ -9,6 +9,7 @@ using Game.Groups; using Game.Maps; using Game.Misc; using Game.Movement; +using Game.Scripting.v2; using Game.Spells; using System; using System.Collections.Generic; @@ -30,6 +31,7 @@ namespace Game.AI List _installEvents = new(); List _timedActionList = new(); ObjectGuid mTimedActionListInvoker; + ActionBase mTimedActionWaitEvent; Creature _me; ObjectGuid _meOrigGUID; GameObject _go; @@ -185,27 +187,29 @@ namespace Game.AI _talkerEntry = talker.GetEntry(); _lastTextID = e.Action.talk.textGroupId; _textTimer = e.Action.talk.duration; - _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(e, [GameTime.Now() + TimeSpan.FromMilliseconds(duration)]); Log.outDebug(LogFilter.ScriptsAi, "SmartScript.ProcessAction: SMART_ACTION_TALK: talker: {0} (Guid: {1}), textGuid: {2}", talker.GetName(), talker.GetGUID().ToString(), _textGUID.ToString()); break; } case SmartActions.SimpleTalk: { + uint duration = 0; foreach (var target in targets) { 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) { 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}", target.GetName(), target.GetGUID().ToString(), e.Action.simpleTalk.textGroupId); } + mTimedActionWaitEvent = CreateTimedActionListWaitEventFor(e, [GameTime.Now() + TimeSpan.FromMilliseconds(duration)]); break; } case SmartActions.PlayEmote: @@ -439,6 +443,9 @@ namespace Game.AI } case SmartActions.Cast: { + if (targets.Empty()) + break; + if (e.Action.cast.targetsLimit > 0 && targets.Count > e.Action.cast.targetsLimit) targets.RandomResize(e.Action.cast.targetsLimit); @@ -454,6 +461,8 @@ namespace Game.AI args.TriggerFlags = TriggerCastFlags.FullMask; } + MultiActionResult waitEvent = CreateTimedActionListWaitEventFor>(e); + foreach (WorldObject target in targets) { 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; } + if (waitEvent != null) + { + args.SetScriptResult(ActionResult.GetResultSetter(waitEvent.CreateAndGetResult())); + args.SetScriptWaitsForSpellHit(e.Action.cast.castFlags.HasAnyFlag((uint)SmartCastFlags.WaitForHit)); + } SpellCastResult result = SpellCastResult.BadTargets; 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}"); } + 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 (failedSpellCast && !successfulSpellCast) { @@ -507,6 +524,8 @@ namespace Game.AI if (e.Action.cast.targetsLimit != 0) targets.RandomResize(e.Action.cast.targetsLimit); + MultiActionResult waitEvent = CreateTimedActionListWaitEventFor>(e); + CastSpellExtraArgs args = new(); 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))) continue; + if (waitEvent != null) + { + args.SetScriptResult(ActionResult.GetResultSetter(waitEvent.CreateAndGetResult())); + args.SetScriptWaitsForSpellHit(e.Action.cast.castFlags.HasAnyFlag((uint)SmartCastFlags.WaitForHit)); + } + if (e.Action.cast.castFlags.HasAnyFlag((uint)SmartCastFlags.InterruptPrevious) && target.IsUnit()) target.ToUnit().InterruptNonMeleeSpells(false); target.CastSpell(target, e.Action.cast.spell, args); } + + if (waitEvent != null && !waitEvent.Results.Empty()) + mTimedActionWaitEvent = waitEvent; break; } case SmartActions.InvokerCast: @@ -549,6 +577,8 @@ namespace Game.AI args.TriggerFlags = TriggerCastFlags.FullMask; } + MultiActionResult waitEvent = CreateTimedActionListWaitEventFor>(e); + foreach (var target in targets) { 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)) tempLastInvoker.InterruptNonMeleeSpells(false); + if (waitEvent != null) + { + args.SetScriptResult(ActionResult.GetResultSetter(waitEvent.CreateAndGetResult())); + args.SetScriptWaitsForSpellHit(e.Action.cast.castFlags.HasAnyFlag((uint)SmartCastFlags.WaitForHit)); + } + 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}"); } + + if (waitEvent != null && !waitEvent.Results.Empty()) + mTimedActionWaitEvent = waitEvent; break; } case SmartActions.ActivateGobject: @@ -1073,6 +1112,8 @@ namespace Game.AI } case SmartActions.MoveOffset: { + MultiActionResult waitEvent = CreateTimedActionListWaitEventFor>(e); + foreach (var target in targets) { 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 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; - target.ToCreature().GetMotionMaster().MovePoint(e.Action.moveOffset.PointId, x, y, z); + + ActionResultSetter scriptResult = null; + if (waitEvent != null) + scriptResult = ActionResult.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; } case SmartActions.SetVisibility: @@ -1294,7 +1343,13 @@ namespace Game.AI } } - _me.GetAI().StartPath(entry, repeat, unit); + ActionResult waitEvent = CreateTimedActionListWaitEventFor(e); + ActionResultSetter scriptResult = null; + if (waitEvent != null) + scriptResult = ActionResult.GetResultSetter(waitEvent); + + _me.GetAI().StartPath(entry, repeat, unit, 0, scriptResult); + mTimedActionWaitEvent = waitEvent; uint quest = e.Action.wpStart.quest; uint DespawnTime = e.Action.wpStart.despawnTime; @@ -1368,6 +1423,11 @@ namespace Game.AI if (!targets.Empty()) target = targets.SelectRandom(); + ActionResult waitEvent = CreateTimedActionListWaitEventFor(e); + ActionResultSetter scriptResult = null; + if (waitEvent != null) + scriptResult = ActionResult.GetResultSetter(waitEvent); + if (target != null) { float x, y, z; @@ -1375,6 +1435,7 @@ namespace Game.AI if (e.Action.moveToPos.contactDistance > 0) 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); + mTimedActionWaitEvent = waitEvent; } 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); } - _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; } case SmartActions.EnableTempGobj: @@ -1577,6 +1639,8 @@ namespace Game.AI List 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 waitEvent = CreateTimedActionListWaitEventFor>(e); + CastSpellExtraArgs args = new(); if (e.Action.crossCast.castFlags.HasAnyFlag((uint)SmartCastFlags.Triggered)) args.TriggerFlags = TriggerCastFlags.FullMask; @@ -1603,9 +1667,18 @@ namespace Game.AI interruptedSpell = true; } + if (waitEvent != null) + { + args.SetScriptResult(ActionResult.GetResultSetter(waitEvent.CreateAndGetResult())); + args.SetScriptWaitsForSpellHit(e.Action.cast.castFlags.HasAnyFlag((uint)SmartCastFlags.WaitForHit)); + } + casterUnit.CastSpell(target, e.Action.crossCast.spell, args); } } + + if (waitEvent != null && !waitEvent.Results.Empty()) + mTimedActionWaitEvent = waitEvent; break; } case SmartActions.CallRandomTimedActionlist: @@ -1694,9 +1767,24 @@ namespace Game.AI } case SmartActions.ActivateTaxi: { + MultiActionResult waitEvent = CreateTimedActionListWaitEventFor>(e); + foreach (var target in targets) + { if (IsPlayer(target)) - target.ToPlayer().ActivateTaxiPathTo(e.Action.taxi.id); + { + ActionResultSetter scriptResult = null; + if (waitEvent != null) + scriptResult = ActionResult.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; } case SmartActions.RandomMove: @@ -1813,13 +1901,20 @@ namespace Game.AI else if (e.GetTargetType() != SmartTargets.Position) break; + ActionResult waitEvent = CreateTimedActionListWaitEventFor(e); + ActionResultSetter actionResultSetter = null; + if (waitEvent != null) + actionResultSetter = ActionResult.GetResultSetter(waitEvent); + 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; - _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 - _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; } case SmartActions.GoSetLootState: @@ -1997,11 +2092,7 @@ namespace Game.AI } case SmartActions.StartClosestWaypoint: { - List waypoints = new(); - 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); + MultiActionResult waitEvent = CreateTimedActionListWaitEventFor>(e); float distanceToClosest = float.MaxValue; uint closestPathId = 0; @@ -2014,12 +2105,12 @@ namespace Game.AI { 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); if (path == null || path.Nodes.Empty()) continue; - foreach (var waypoint in path.Nodes) { float distanceToThisNode = creature.GetDistance(waypoint.X, waypoint.Y, waypoint.Z); @@ -2033,10 +2124,19 @@ namespace Game.AI } if (closestPathId != 0) - ((SmartAI)creature.GetAI()).StartPath(closestPathId, true, null, closestWaypointId); + { + ActionResultSetter actionResultSetter = null; + if (waitEvent != null) + actionResultSetter = ActionResult.GetResultSetter(waitEvent.CreateAndGetResult()); + + creature.GetAI().StartPath(closestPathId, true, null, closestWaypointId, actionResultSetter); + } } } } + + if (waitEvent != null && !waitEvent.Results.Empty()) + mTimedActionWaitEvent = waitEvent; break; } 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) return; + if (e.GetScriptType() == SmartScriptType.TimedActionlist && mTimedActionWaitEvent != null && !mTimedActionWaitEvent.IsReady()) + return; + if (e.Timer < diff) { // delay spell cast event if another spell is being casted @@ -4362,6 +4465,29 @@ namespace Game.AI } } } + + //template, typename...Args> + public static ActionResult CreateTimedActionListWaitEventFor(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)Activator.CreateInstance(typeof(ActionResult), args); + } + + public static ConcreteActionImpl CreateTimedActionListWaitEventFor(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 diff --git a/Source/Game/Entities/Player/Player.cs b/Source/Game/Entities/Player/Player.cs index bdfb3e9c8..d0dba06e9 100644 --- a/Source/Game/Entities/Player/Player.cs +++ b/Source/Game/Entities/Player/Player.cs @@ -7,7 +7,6 @@ using Framework.Dynamic; using Game.Achievements; using Game.AI; using Game.Arenas; -using Game.BattleFields; using Game.BattleGrounds; using Game.BattlePets; using Game.Chat; @@ -22,11 +21,11 @@ using Game.Misc; using Game.Miscellaneous; using Game.Networking; using Game.Networking.Packets; +using Game.Scripting.v2; using Game.Spells; using System; using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using static Global; namespace Game.Entities @@ -6921,7 +6920,7 @@ namespace Game.Entities public uint GetDeathTimer() { return m_deathTimer; } - public bool ActivateTaxiPathTo(List nodes, Creature npc = null, uint spellid = 0, uint preferredMountDisplay = 0, float? speed = null, TaskCompletionSource scriptResult = null) + public bool ActivateTaxiPathTo(List nodes, Creature npc = null, uint spellid = 0, uint preferredMountDisplay = 0, float? speed = null, ActionResultSetter scriptResult = null) { if (nodes.Count < 2) { @@ -7103,7 +7102,7 @@ namespace Game.Entities return true; } - public bool ActivateTaxiPathTo(uint taxi_path_id, uint spellid = 0, float? speed = null, TaskCompletionSource scriptResult = null) + public bool ActivateTaxiPathTo(uint taxi_path_id, uint spellid = 0, float? speed = null, ActionResultSetter scriptResult = null) { var entry = CliDB.TaxiPathStorage.LookupByKey(taxi_path_id); if (entry == null) @@ -7178,7 +7177,7 @@ namespace Game.Entities StartTaxiMovement(mountDisplayId, path, startNode, null, null); } - void StartTaxiMovement(uint mountDisplayId, uint path, uint pathNode, float? speed, TaskCompletionSource scriptResult) + void StartTaxiMovement(uint mountDisplayId, uint path, uint pathNode, float? speed, ActionResultSetter scriptResult) { // remove fake death RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.Interacting); diff --git a/Source/Game/Movement/Generators/FleeingMovementGenerator.cs b/Source/Game/Movement/Generators/FleeingMovementGenerator.cs index 2d11b4dfb..4da9d4f17 100644 --- a/Source/Game/Movement/Generators/FleeingMovementGenerator.cs +++ b/Source/Game/Movement/Generators/FleeingMovementGenerator.cs @@ -4,8 +4,8 @@ using Framework.Constants; using Game.AI; using Game.Entities; +using Game.Scripting.v2; using System; -using System.Threading.Tasks; namespace Game.Movement { @@ -14,7 +14,7 @@ namespace Game.Movement public const float MIN_QUIET_DISTANCE = 28.0f; public const float MAX_QUIET_DISTANCE = 43.0f; - public FleeingMovementGenerator(ObjectGuid fleeTargetGUID, TaskCompletionSource scriptResult = null) + public FleeingMovementGenerator(ObjectGuid fleeTargetGUID, ActionResultSetter scriptResult = null) { _fleeTargetGUID = fleeTargetGUID; _timer = new TimeTracker(); @@ -197,7 +197,7 @@ namespace Game.Movement public class TimedFleeingMovementGenerator : FleeingMovementGenerator { - public TimedFleeingMovementGenerator(ObjectGuid fleeTargetGUID, TimeSpan time, TaskCompletionSource scriptResult = null) : base(fleeTargetGUID, scriptResult) + public TimedFleeingMovementGenerator(ObjectGuid fleeTargetGUID, TimeSpan time, ActionResultSetter scriptResult = null) : base(fleeTargetGUID, scriptResult) { _totalFleeTime = new TimeTracker(time); } diff --git a/Source/Game/Movement/Generators/FlightPathMovementGenerator.cs b/Source/Game/Movement/Generators/FlightPathMovementGenerator.cs index 9440290bb..e8e119fe9 100644 --- a/Source/Game/Movement/Generators/FlightPathMovementGenerator.cs +++ b/Source/Game/Movement/Generators/FlightPathMovementGenerator.cs @@ -5,11 +5,11 @@ using Framework.Constants; using Game.DataStorage; using Game.Entities; using Game.Maps; +using Game.Scripting.v2; using System; using System.Collections.Generic; using System.Linq; using System.Numerics; -using System.Threading.Tasks; namespace Game.Movement { @@ -25,7 +25,7 @@ namespace Game.Movement int _currentNode; List _pointsForPathSwitch = new(); //! node indexes and costs where TaxiPath changes - public FlightPathMovementGenerator(float? speed, TaskCompletionSource scriptResult) + public FlightPathMovementGenerator(float? speed, ActionResultSetter scriptResult) { _speed = speed; Mode = MovementGeneratorMode.Default; diff --git a/Source/Game/Movement/Generators/FollowMovementGenerator.cs b/Source/Game/Movement/Generators/FollowMovementGenerator.cs index 35708086f..cac2ba95f 100644 --- a/Source/Game/Movement/Generators/FollowMovementGenerator.cs +++ b/Source/Game/Movement/Generators/FollowMovementGenerator.cs @@ -4,6 +4,7 @@ using Framework.Constants; using Game.AI; using Game.Entities; +using Game.Scripting.v2; using System; using System.Threading.Tasks; @@ -24,7 +25,7 @@ namespace Game.Movement AbstractFollower _abstractFollower; - public FollowMovementGenerator(Unit target, float range, ChaseAngle angle, TimeSpan? duration, TaskCompletionSource scriptResult = null) + public FollowMovementGenerator(Unit target, float range, ChaseAngle angle, TimeSpan? duration, ActionResultSetter scriptResult = null) { _abstractFollower = new AbstractFollower(target); _range = range; diff --git a/Source/Game/Movement/Generators/GenericMovementGenerator.cs b/Source/Game/Movement/Generators/GenericMovementGenerator.cs index a40f5f9bb..dc9a6280e 100644 --- a/Source/Game/Movement/Generators/GenericMovementGenerator.cs +++ b/Source/Game/Movement/Generators/GenericMovementGenerator.cs @@ -3,8 +3,8 @@ using Framework.Constants; using Game.Entities; +using Game.Scripting.v2; using System; -using System.Threading.Tasks; namespace Game.Movement { @@ -117,6 +117,6 @@ namespace Game.Movement public uint? ArrivalSpellId; public ObjectGuid? ArrivalSpellTarget; public TimeSpan? Duration; - public TaskCompletionSource ScriptResult; + public ActionResultSetter ScriptResult; } } diff --git a/Source/Game/Movement/Generators/IdleMovement.cs b/Source/Game/Movement/Generators/IdleMovement.cs index 9dace5aa8..2c88dfa63 100644 --- a/Source/Game/Movement/Generators/IdleMovement.cs +++ b/Source/Game/Movement/Generators/IdleMovement.cs @@ -3,8 +3,8 @@ using Framework.Constants; using Game.Entities; +using Game.Scripting.v2; using System; -using System.Threading.Tasks; namespace Game.Movement { @@ -57,7 +57,7 @@ namespace Game.Movement float? _totalTurnAngle; uint _diffSinceLastUpdate; - public RotateMovementGenerator(uint id, RotateDirection direction, TimeSpan? duration, float? turnSpeed, float? totalTurnAngle, TaskCompletionSource scriptResult) + public RotateMovementGenerator(uint id, RotateDirection direction, TimeSpan? duration, float? turnSpeed, float? totalTurnAngle, ActionResultSetter scriptResult) { _id = id; _direction = direction; diff --git a/Source/Game/Movement/Generators/MovementGenerators.cs b/Source/Game/Movement/Generators/MovementGenerators.cs index 6f5870624..805cf55b8 100644 --- a/Source/Game/Movement/Generators/MovementGenerators.cs +++ b/Source/Game/Movement/Generators/MovementGenerators.cs @@ -2,10 +2,9 @@ // Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information. using Framework.Constants; -using Game.AI; using Game.Entities; +using Game.Scripting.v2; using System; -using System.Threading.Tasks; namespace Game.Movement { @@ -15,7 +14,7 @@ namespace Game.Movement public MovementGeneratorPriority Priority; public MovementGeneratorFlags Flags; public UnitState BaseUnitState; - public TaskCompletionSource ScriptResult; + public ActionResultSetter ScriptResult; ~MovementGenerator() { diff --git a/Source/Game/Movement/Generators/PointMovement.cs b/Source/Game/Movement/Generators/PointMovement.cs index 4294317c7..03d04c9d7 100644 --- a/Source/Game/Movement/Generators/PointMovement.cs +++ b/Source/Game/Movement/Generators/PointMovement.cs @@ -3,8 +3,8 @@ using Framework.Constants; using Game.Entities; +using Game.Scripting.v2; using System; -using System.Threading.Tasks; namespace Game.Movement { @@ -22,7 +22,7 @@ namespace Game.Movement 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, - MovementWalkRunSpeedSelectionMode speedSelectionMode = MovementWalkRunSpeedSelectionMode.Default, float? closeEnoughDistance = null, TaskCompletionSource scriptResult = null) + MovementWalkRunSpeedSelectionMode speedSelectionMode = MovementWalkRunSpeedSelectionMode.Default, float? closeEnoughDistance = null, ActionResultSetter scriptResult = null) { _movementId = id; _destination = new Position(x, y, z); @@ -207,7 +207,7 @@ namespace Game.Movement } public uint GetId() { return _movementId; } - + public override MovementGeneratorType GetMovementGeneratorType() { return MovementGeneratorType.Point; diff --git a/Source/Game/Movement/Generators/RandomMovementGenerator.cs b/Source/Game/Movement/Generators/RandomMovementGenerator.cs index ced83f79c..652b51e3a 100644 --- a/Source/Game/Movement/Generators/RandomMovementGenerator.cs +++ b/Source/Game/Movement/Generators/RandomMovementGenerator.cs @@ -3,6 +3,7 @@ using Framework.Constants; using Game.Entities; +using Game.Scripting.v2; using System; using System.Threading.Tasks; @@ -17,7 +18,7 @@ namespace Game.Movement float _wanderDistance; uint _wanderSteps; - public RandomMovementGenerator(float spawnDist = 0.0f, TimeSpan? duration = null, TaskCompletionSource scriptResult = null) + public RandomMovementGenerator(float spawnDist = 0.0f, TimeSpan? duration = null, ActionResultSetter scriptResult = null) { _timer = new TimeTracker(); _reference = new(); diff --git a/Source/Game/Movement/Generators/WaypointMovement.cs b/Source/Game/Movement/Generators/WaypointMovement.cs index 454dd428c..d6c5c451a 100644 --- a/Source/Game/Movement/Generators/WaypointMovement.cs +++ b/Source/Game/Movement/Generators/WaypointMovement.cs @@ -4,10 +4,10 @@ using Framework.Constants; using Game.AI; using Game.Entities; +using Game.Scripting.v2; using System; using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; namespace Game.Movement { @@ -31,7 +31,7 @@ namespace Game.Movement bool _generatePath; 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 scriptResult = null) + (TimeSpan min, TimeSpan max)? waitTimeRangeAtPathEnd = null, float? wanderDistanceAtPathEnds = null, bool? followPathBackwardsFromEndToStart = null, bool generatePath = true, ActionResultSetter scriptResult = null) { _nextMoveTime = new TimeTracker(0); _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, - (TimeSpan min, TimeSpan max)? waitTimeRangeAtPathEnd = null, float? wanderDistanceAtPathEnds = null, bool? followPathBackwardsFromEndToStart = null, bool generatePath = true, TaskCompletionSource scriptResult = null) + (TimeSpan min, TimeSpan max)? waitTimeRangeAtPathEnd = null, float? wanderDistanceAtPathEnds = null, bool? followPathBackwardsFromEndToStart = null, bool generatePath = true, ActionResultSetter scriptResult = null) { _nextMoveTime = new TimeTracker(0); _repeating = repeating; diff --git a/Source/Game/Movement/MotionMaster.cs b/Source/Game/Movement/MotionMaster.cs index dbd28ad23..e66848c66 100644 --- a/Source/Game/Movement/MotionMaster.cs +++ b/Source/Game/Movement/MotionMaster.cs @@ -2,15 +2,14 @@ // Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information. using Framework.Constants; -using Framework.Dynamic; using Game.AI; using Game.DataStorage; using Game.Entities; +using Game.Scripting.v2; using System; using System.Collections.Generic; using System.Linq; using System.Numerics; -using System.Threading.Tasks; namespace Game.Movement { @@ -545,7 +544,7 @@ namespace Game.Movement 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 scriptResult = null) + public void MoveRandom(float wanderDistance = 0.0f, TimeSpan? duration = null, MovementSlot slot = MovementSlot.Default, ActionResultSetter scriptResult = null) { if (_owner.IsTypeId(TypeId.Unit)) Add(new RandomMovementGenerator(wanderDistance, duration, scriptResult), slot); @@ -553,12 +552,12 @@ namespace Game.Movement scriptResult.SetResult(MovementStopReason.Interrupted); } - public void MoveFollow(Unit target, float dist, float angle = 0.0f, TimeSpan? duration = null, MovementSlot slot = MovementSlot.Active, TaskCompletionSource scriptResult = null) + public void MoveFollow(Unit target, float dist, float angle = 0.0f, TimeSpan? duration = null, MovementSlot slot = MovementSlot.Active, ActionResultSetter scriptResult = null) { 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 scriptResult = null) + public void MoveFollow(Unit target, float dist, ChaseAngle angle, TimeSpan? duration = null, MovementSlot slot = MovementSlot.Active, ActionResultSetter scriptResult = null) { // Ignore movement request if target not exist if (target == null || target == _owner) @@ -589,7 +588,7 @@ namespace Game.Movement Add(new ConfusedMovementGenerator()); } - public void MoveFleeing(Unit enemy, TimeSpan time = default, TaskCompletionSource scriptResult = null) + public void MoveFleeing(Unit enemy, TimeSpan time = default, ActionResultSetter scriptResult = null) { if (enemy == null) { @@ -604,12 +603,12 @@ namespace Game.Movement 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 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 scriptResult = null) { 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 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 scriptResult = null) { 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)); @@ -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 scriptResult = null) + public void MoveLand(uint id, Position pos, uint? tierTransitionId = null, float? velocity = null, MovementWalkRunSpeedSelectionMode speedSelectionMode = MovementWalkRunSpeedSelectionMode.Default, ActionResultSetter scriptResult = null) { var initializer = (MoveSplineInit init) => { @@ -666,7 +665,7 @@ namespace Game.Movement 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 scriptResult = null) + public void MoveTakeoff(uint id, Position pos, uint? tierTransitionId = null, float? velocity = null, MovementWalkRunSpeedSelectionMode speedSelectionMode = MovementWalkRunSpeedSelectionMode.Default, ActionResultSetter scriptResult = null) { var initializer = (MoveSplineInit init) => { @@ -772,12 +771,12 @@ namespace Game.Movement 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 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 scriptResult = null) { 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 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 scriptResult = null) { 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) @@ -815,7 +814,7 @@ namespace Game.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 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 scriptResult = null) { Log.outDebug(LogFilter.Movement, $"MotionMaster.MoveJumpWithGravity: '{_owner.GetGUID()}', jumps to point Id: {id} ({pos})"); if (speedXY < 0.01f) @@ -853,7 +852,7 @@ namespace Game.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 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 scriptResult = null) { var initializer = (MoveSplineInit init) => { @@ -960,7 +959,7 @@ namespace Game.Movement Add(new SplineChainMovementGenerator(info)); } - public void MoveFall(uint id = 0, TaskCompletionSource scriptResult = null) + public void MoveFall(uint id = 0, ActionResultSetter scriptResult = null) { // 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); @@ -1019,7 +1018,7 @@ namespace Game.Movement 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 scriptResult = null) + public void MoveTaxiFlight(uint path, uint pathnode, float? speed = null, ActionResultSetter scriptResult = null) { 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, - (TimeSpan, TimeSpan)? waitTimeRangeAtPathEnd = null, float? wanderDistanceAtPathEnds = null, bool? followPathBackwardsFromEndToStart = null, bool generatePath = true, TaskCompletionSource scriptResult = null) + (TimeSpan, TimeSpan)? waitTimeRangeAtPathEnd = null, float? wanderDistanceAtPathEnds = null, bool? followPathBackwardsFromEndToStart = null, bool generatePath = true, ActionResultSetter scriptResult = null) { 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, - (TimeSpan, TimeSpan)? waitTimeRangeAtPathEnd = null, float? wanderDistanceAtPathEnds = null, bool? followPathBackwardsFromEndToStart = null, bool generatePath = true, TaskCompletionSource scriptResult = null) + (TimeSpan, TimeSpan)? waitTimeRangeAtPathEnd = null, float? wanderDistanceAtPathEnds = null, bool? followPathBackwardsFromEndToStart = null, bool generatePath = true, ActionResultSetter scriptResult = null) { 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); @@ -1082,7 +1081,7 @@ namespace Game.Movement /// How long should this movement last, infinite if not set /// How fast should the unit rotate, in radians per second. Uses unit's turn speed if not set /// Total angle of the entire movement, infinite if not set - public void MoveRotate(uint id, RotateDirection direction, TimeSpan? time = null, float? turnSpeed = null, float? totalTurnAngle = null, TaskCompletionSource scriptResult = null) + public void MoveRotate(uint id, RotateDirection direction, TimeSpan? time = null, float? turnSpeed = null, float? totalTurnAngle = null, ActionResultSetter scriptResult = null) { Log.outDebug(LogFilter.Movement, $"MotionMaster::MoveRotate: '{_owner.GetGUID()}', starts rotate (time: {time.GetValueOrDefault(TimeSpan.Zero)}ms, turnSpeed: {turnSpeed}, totalTurnAngle: {totalTurnAngle}, direction: {direction})"); diff --git a/Source/Game/Scripting/v2/ScriptActionResult.cs b/Source/Game/Scripting/v2/ScriptActionResult.cs new file mode 100644 index 000000000..36df21fb6 --- /dev/null +++ b/Source/Game/Scripting/v2/ScriptActionResult.cs @@ -0,0 +1,38 @@ +// Copyright (c) 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 + { + 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(); + } + } +} diff --git a/Source/Game/Scripting/v2/ScriptActions.cs b/Source/Game/Scripting/v2/ScriptActions.cs new file mode 100644 index 000000000..234915c1b --- /dev/null +++ b/Source/Game/Scripting/v2/ScriptActions.cs @@ -0,0 +1,75 @@ +// Copyright (c) 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 : ActionBase + { + T _result; + + public static ActionResultSetter GetResultSetter(ActionResult action) + { + T resultPtr = action._result; + return new ActionResultSetter(action, resultPtr); + } + } + + public class ActionResult : ActionBase + { + public static ActionResultSetter GetResultSetter(ActionResult action) + { + return new ActionResultSetter(action); + } + } + + public class MultiActionResult : ActionResult + { + public List> Results = new(); + + public ActionResult CreateAndGetResult() + { + ActionResult result = new(); + Results.Add(result); + return result; + } + + public override bool IsReady() + { + return Results.All(result => result.IsReady()); + } + } +} \ No newline at end of file diff --git a/Source/Game/Spells/Spell.cs b/Source/Game/Spells/Spell.cs index 90f6c613e..719a034ab 100644 --- a/Source/Game/Spells/Spell.cs +++ b/Source/Game/Spells/Spell.cs @@ -16,11 +16,11 @@ using Game.Miscellaneous; using Game.Movement; using Game.Networking.Packets; using Game.Scripting; +using Game.Scripting.v2; using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; -using System.Threading.Tasks; namespace Game.Spells { @@ -8267,7 +8267,7 @@ namespace Game.Spells public List m_appliedMods = new(); - public TaskCompletionSource m_scriptResult; + public ActionResultSetter m_scriptResult; public bool m_scriptWaitsForSpellHit; WorldObject m_caster; @@ -9551,7 +9551,7 @@ namespace Game.Spells public Dictionary SpellValueOverrides = new(); public object CustomArg; - public TaskCompletionSource ScriptResult; + public ActionResultSetter ScriptResult; public bool ScriptWaitsForSpellHit; public CastSpellExtraArgs() { } @@ -9656,7 +9656,7 @@ namespace Game.Spells return this; } - public CastSpellExtraArgs SetScriptResult(TaskCompletionSource scriptResult) + public CastSpellExtraArgs SetScriptResult(ActionResultSetter scriptResult) { ScriptResult = scriptResult; return this;