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
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
+7 -8
View File
@@ -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<MovementStopReason> 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);
+20 -2
View File
@@ -1,7 +1,6 @@
// 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 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:
+144 -18
View File
@@ -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<SmartScriptHolder> _installEvents = new();
List<SmartScriptHolder> _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<WaitAction>(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<WaitAction>(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<SpellCastResult> waitEvent = CreateTimedActionListWaitEventFor<MultiActionResult<SpellCastResult>>(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<SpellCastResult>.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<SpellCastResult> waitEvent = CreateTimedActionListWaitEventFor<MultiActionResult<SpellCastResult>>(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<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())
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<SpellCastResult> waitEvent = CreateTimedActionListWaitEventFor<MultiActionResult<SpellCastResult>>(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<SpellCastResult>.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<MovementStopReason> waitEvent = CreateTimedActionListWaitEventFor<MultiActionResult<MovementStopReason>>(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<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;
}
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 DespawnTime = e.Action.wpStart.despawnTime;
@@ -1368,6 +1423,11 @@ namespace Game.AI
if (!targets.Empty())
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)
{
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<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();
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<SpellCastResult>.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<MovementStopReason> waitEvent = CreateTimedActionListWaitEventFor<MultiActionResult<MovementStopReason>>(e);
foreach (var target in targets)
{
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;
}
case SmartActions.RandomMove:
@@ -1813,13 +1901,20 @@ namespace Game.AI
else if (e.GetTargetType() != SmartTargets.Position)
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)
{
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<uint> 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<MovementStopReason> waitEvent = CreateTimedActionListWaitEventFor<MultiActionResult<MovementStopReason>>(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<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;
}
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 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
+4 -5
View File
@@ -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<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)
{
@@ -7103,7 +7102,7 @@ namespace Game.Entities
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);
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<MovementStopReason> scriptResult)
void StartTaxiMovement(uint mountDisplayId, uint path, uint pathNode, float? speed, ActionResultSetter<MovementStopReason> scriptResult)
{
// remove fake death
RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.Interacting);
@@ -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<MovementStopReason> scriptResult = null)
public FleeingMovementGenerator(ObjectGuid fleeTargetGUID, ActionResultSetter<MovementStopReason> 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<MovementStopReason> scriptResult = null) : base(fleeTargetGUID, scriptResult)
public TimedFleeingMovementGenerator(ObjectGuid fleeTargetGUID, TimeSpan time, ActionResultSetter<MovementStopReason> scriptResult = null) : base(fleeTargetGUID, scriptResult)
{
_totalFleeTime = new TimeTracker(time);
}
@@ -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<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;
Mode = MovementGeneratorMode.Default;
@@ -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<MovementStopReason> scriptResult = null)
public FollowMovementGenerator(Unit target, float range, ChaseAngle angle, TimeSpan? duration, ActionResultSetter<MovementStopReason> scriptResult = null)
{
_abstractFollower = new AbstractFollower(target);
_range = range;
@@ -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<MovementStopReason> ScriptResult;
public ActionResultSetter<MovementStopReason> ScriptResult;
}
}
@@ -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<MovementStopReason> scriptResult)
public RotateMovementGenerator(uint id, RotateDirection direction, TimeSpan? duration, float? turnSpeed, float? totalTurnAngle, ActionResultSetter<MovementStopReason> scriptResult)
{
_id = id;
_direction = direction;
@@ -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<MovementStopReason> ScriptResult;
public ActionResultSetter<MovementStopReason> ScriptResult;
~MovementGenerator()
{
@@ -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<MovementStopReason> scriptResult = null)
MovementWalkRunSpeedSelectionMode speedSelectionMode = MovementWalkRunSpeedSelectionMode.Default, float? closeEnoughDistance = null, ActionResultSetter<MovementStopReason> 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;
@@ -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<MovementStopReason> scriptResult = null)
public RandomMovementGenerator(float spawnDist = 0.0f, TimeSpan? duration = null, ActionResultSetter<MovementStopReason> scriptResult = null)
{
_timer = new TimeTracker();
_reference = new();
@@ -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<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);
_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<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);
_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.
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<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))
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<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);
}
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
if (target == null || target == _owner)
@@ -589,7 +588,7 @@ namespace Game.Movement
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)
{
@@ -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<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);
}
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})");
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) =>
{
@@ -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<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) =>
{
@@ -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<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);
}
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);
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<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})");
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<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) =>
{
@@ -960,7 +959,7 @@ namespace Game.Movement
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
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<MovementStopReason> scriptResult = null)
public void MoveTaxiFlight(uint path, uint pathnode, float? speed = null, ActionResultSetter<MovementStopReason> 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<MovementStopReason> scriptResult = null)
(TimeSpan, TimeSpan)? waitTimeRangeAtPathEnd = null, float? wanderDistanceAtPathEnds = null, bool? followPathBackwardsFromEndToStart = null, bool generatePath = true, ActionResultSetter<MovementStopReason> 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<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})");
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="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>
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})");
@@ -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.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<Aura> m_appliedMods = new();
public TaskCompletionSource<SpellCastResult> m_scriptResult;
public ActionResultSetter<SpellCastResult> m_scriptResult;
public bool m_scriptWaitsForSpellHit;
WorldObject m_caster;
@@ -9551,7 +9551,7 @@ namespace Game.Spells
public Dictionary<SpellValueMod, int> SpellValueOverrides = new();
public object CustomArg;
public TaskCompletionSource<SpellCastResult> ScriptResult;
public ActionResultSetter<SpellCastResult> ScriptResult;
public bool ScriptWaitsForSpellHit;
public CastSpellExtraArgs() { }
@@ -9656,7 +9656,7 @@ namespace Game.Spells
return this;
}
public CastSpellExtraArgs SetScriptResult(TaskCompletionSource<SpellCastResult> scriptResult)
public CastSpellExtraArgs SetScriptResult(ActionResultSetter<SpellCastResult> scriptResult)
{
ScriptResult = scriptResult;
return this;