diff --git a/Source/Game/Entities/Object/WorldObject.cs b/Source/Game/Entities/Object/WorldObject.cs index 1b8b64900..07e194ccd 100644 --- a/Source/Game/Entities/Object/WorldObject.cs +++ b/Source/Game/Entities/Object/WorldObject.cs @@ -2497,6 +2497,8 @@ namespace Game.Entities } spell.m_customArg = args.CustomArg; + spell.m_scriptResult = args.ScriptResult; + spell.m_scriptWaitsForSpellHit = args.ScriptWaitsForSpellHit; return spell.Prepare(targets.Targets, args.TriggeringAura); } diff --git a/Source/Game/Entities/Player/Player.cs b/Source/Game/Entities/Player/Player.cs index a21511f3f..bdfb3e9c8 100644 --- a/Source/Game/Entities/Player/Player.cs +++ b/Source/Game/Entities/Player/Player.cs @@ -26,6 +26,7 @@ using Game.Spells; using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using static Global; namespace Game.Entities @@ -6920,7 +6921,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) + public bool ActivateTaxiPathTo(List nodes, Creature npc = null, uint spellid = 0, uint preferredMountDisplay = 0, float? speed = null, TaskCompletionSource scriptResult = null) { if (nodes.Count < 2) { @@ -7097,18 +7098,18 @@ namespace Game.Entities ModifyMoney(-firstcost); UpdateCriteria(CriteriaType.MoneySpentOnTaxis, firstcost); GetSession().SendActivateTaxiReply(); - StartTaxiMovement(mount_display_id, sourcepath, 0, speed); + StartTaxiMovement(mount_display_id, sourcepath, 0, speed, scriptResult); } return true; } - public bool ActivateTaxiPathTo(uint taxi_path_id, uint spellid = 0, float? speed = null) + public bool ActivateTaxiPathTo(uint taxi_path_id, uint spellid = 0, float? speed = null, TaskCompletionSource scriptResult = null) { var entry = CliDB.TaxiPathStorage.LookupByKey(taxi_path_id); if (entry == null) return false; - return ActivateTaxiPathTo([entry.FromTaxiNode, entry.ToTaxiNode], null, spellid, 0, speed); + return ActivateTaxiPathTo([entry.FromTaxiNode, entry.ToTaxiNode], null, spellid, 0, speed, scriptResult); } public void FinishTaxiFlight() @@ -7174,10 +7175,10 @@ namespace Game.Entities } } - StartTaxiMovement(mountDisplayId, path, startNode, null); + StartTaxiMovement(mountDisplayId, path, startNode, null, null); } - void StartTaxiMovement(uint mountDisplayId, uint path, uint pathNode, float? speed) + void StartTaxiMovement(uint mountDisplayId, uint path, uint pathNode, float? speed, TaskCompletionSource scriptResult) { // remove fake death RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.Interacting); @@ -7185,7 +7186,7 @@ namespace Game.Entities if (mountDisplayId != 0) Mount(mountDisplayId); - GetMotionMaster().MoveTaxiFlight(path, pathNode, speed); + GetMotionMaster().MoveTaxiFlight(path, pathNode, speed, scriptResult); } public bool GetsRecruitAFriendBonus(bool forXP) diff --git a/Source/Game/Movement/Generators/FleeingMovementGenerator.cs b/Source/Game/Movement/Generators/FleeingMovementGenerator.cs index c6cd39864..2d11b4dfb 100644 --- a/Source/Game/Movement/Generators/FleeingMovementGenerator.cs +++ b/Source/Game/Movement/Generators/FleeingMovementGenerator.cs @@ -5,6 +5,7 @@ using Framework.Constants; using Game.AI; using Game.Entities; using System; +using System.Threading.Tasks; namespace Game.Movement { @@ -13,15 +14,16 @@ namespace Game.Movement public const float MIN_QUIET_DISTANCE = 28.0f; public const float MAX_QUIET_DISTANCE = 43.0f; - public FleeingMovementGenerator(ObjectGuid fright) + public FleeingMovementGenerator(ObjectGuid fleeTargetGUID, TaskCompletionSource scriptResult = null) { - _fleeTargetGUID = fright; + _fleeTargetGUID = fleeTargetGUID; _timer = new TimeTracker(); Mode = MovementGeneratorMode.Default; Priority = MovementGeneratorPriority.Highest; Flags = MovementGeneratorFlags.InitializationPending; BaseUnitState = UnitState.Fleeing; + ScriptResult = scriptResult; } public override void Initialize(Unit owner) @@ -90,6 +92,9 @@ namespace Game.Movement else if (owner.IsPlayer()) owner.StopMoving(); } + + if (movementInform) + SetScriptResult(MovementStopReason.Finished); } void SetTargetLocation(Unit owner) @@ -192,7 +197,7 @@ namespace Game.Movement public class TimedFleeingMovementGenerator : FleeingMovementGenerator { - public TimedFleeingMovementGenerator(ObjectGuid fright, TimeSpan time) : base(fright) + public TimedFleeingMovementGenerator(ObjectGuid fleeTargetGUID, TimeSpan time, TaskCompletionSource scriptResult = null) : base(fleeTargetGUID, scriptResult) { _totalFleeTime = new TimeTracker(time); } @@ -228,6 +233,8 @@ namespace Game.Movement if (movementInform) { + SetScriptResult(MovementStopReason.Finished); + Creature ownerCreature = owner.ToCreature(); CreatureAI ai = ownerCreature != null ? ownerCreature.GetAI() : null; if (ai != null) diff --git a/Source/Game/Movement/Generators/FlightPathMovementGenerator.cs b/Source/Game/Movement/Generators/FlightPathMovementGenerator.cs index bf5068cb2..9440290bb 100644 --- a/Source/Game/Movement/Generators/FlightPathMovementGenerator.cs +++ b/Source/Game/Movement/Generators/FlightPathMovementGenerator.cs @@ -9,6 +9,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Numerics; +using System.Threading.Tasks; namespace Game.Movement { @@ -24,13 +25,14 @@ namespace Game.Movement int _currentNode; List _pointsForPathSwitch = new(); //! node indexes and costs where TaxiPath changes - public FlightPathMovementGenerator(float? speed) + public FlightPathMovementGenerator(float? speed, TaskCompletionSource scriptResult) { _speed = speed; Mode = MovementGeneratorMode.Default; Priority = MovementGeneratorPriority.Highest; Flags = MovementGeneratorFlags.InitializationPending; BaseUnitState = UnitState.InFlight; + ScriptResult = scriptResult; } public override void DoInitialize(Player owner) @@ -157,6 +159,9 @@ namespace Game.Movement } owner.RemovePlayerFlag(PlayerFlags.TaxiBenchmark); + + if (movementInform) + SetScriptResult(MovementStopReason.Finished); } uint GetPathAtMapEnd() diff --git a/Source/Game/Movement/Generators/FollowMovementGenerator.cs b/Source/Game/Movement/Generators/FollowMovementGenerator.cs index 7ac2478ef..35708086f 100644 --- a/Source/Game/Movement/Generators/FollowMovementGenerator.cs +++ b/Source/Game/Movement/Generators/FollowMovementGenerator.cs @@ -5,6 +5,7 @@ using Framework.Constants; using Game.AI; using Game.Entities; using System; +using System.Threading.Tasks; namespace Game.Movement { @@ -23,7 +24,7 @@ namespace Game.Movement AbstractFollower _abstractFollower; - public FollowMovementGenerator(Unit target, float range, ChaseAngle angle, TimeSpan? duration) + public FollowMovementGenerator(Unit target, float range, ChaseAngle angle, TimeSpan? duration, TaskCompletionSource scriptResult = null) { _abstractFollower = new AbstractFollower(target); _range = range; @@ -33,6 +34,7 @@ namespace Game.Movement Priority = MovementGeneratorPriority.Normal; Flags = MovementGeneratorFlags.InitializationPending; BaseUnitState = UnitState.Follow; + ScriptResult = scriptResult; if (duration.HasValue) _duration = new(duration.Value); @@ -186,6 +188,8 @@ namespace Game.Movement { owner.ClearUnitState(UnitState.FollowMove); UpdatePetSpeed(owner); + if (movementInform) + SetScriptResult(MovementStopReason.Finished); } } diff --git a/Source/Game/Movement/Generators/GenericMovementGenerator.cs b/Source/Game/Movement/Generators/GenericMovementGenerator.cs index 9af4215ca..a40f5f9bb 100644 --- a/Source/Game/Movement/Generators/GenericMovementGenerator.cs +++ b/Source/Game/Movement/Generators/GenericMovementGenerator.cs @@ -4,6 +4,7 @@ using Framework.Constants; using Game.Entities; using System; +using System.Threading.Tasks; namespace Game.Movement { @@ -38,6 +39,8 @@ namespace Game.Movement _duration = new(args.Duration.Value); _durationTracksSpline = false; } + + ScriptResult = args.ScriptResult; } public override void Initialize(Unit owner) @@ -99,6 +102,8 @@ namespace Game.Movement if (_arrivalSpellId != 0) owner.CastSpell(Global.ObjAccessor.GetUnit(owner, _arrivalSpellTargetGuid), _arrivalSpellId, true); + SetScriptResult(MovementStopReason.Finished); + Creature creature = owner.ToCreature(); if (creature != null && creature.GetAI() != null) creature.GetAI().MovementInform(_type, _pointId); @@ -112,5 +117,6 @@ namespace Game.Movement public uint? ArrivalSpellId; public ObjectGuid? ArrivalSpellTarget; public TimeSpan? Duration; + public TaskCompletionSource ScriptResult; } } diff --git a/Source/Game/Movement/Generators/IdleMovement.cs b/Source/Game/Movement/Generators/IdleMovement.cs index 4dae4cef0..9dace5aa8 100644 --- a/Source/Game/Movement/Generators/IdleMovement.cs +++ b/Source/Game/Movement/Generators/IdleMovement.cs @@ -4,6 +4,7 @@ using Framework.Constants; using Game.Entities; using System; +using System.Threading.Tasks; namespace Game.Movement { @@ -56,7 +57,7 @@ namespace Game.Movement float? _totalTurnAngle; uint _diffSinceLastUpdate; - public RotateMovementGenerator(uint id, RotateDirection direction, TimeSpan? duration, float? turnSpeed, float? totalTurnAngle) + public RotateMovementGenerator(uint id, RotateDirection direction, TimeSpan? duration, float? turnSpeed, float? totalTurnAngle, TaskCompletionSource scriptResult) { _id = id; _direction = direction; @@ -70,6 +71,7 @@ namespace Game.Movement Priority = MovementGeneratorPriority.Normal; Flags = MovementGeneratorFlags.InitializationPending; BaseUnitState = UnitState.Rotating; + ScriptResult = scriptResult; } public override void Initialize(Unit owner) @@ -142,8 +144,12 @@ namespace Game.Movement { AddFlag(MovementGeneratorFlags.Finalized); - if (movementInform && owner.IsCreature()) - owner.ToCreature().GetAI().MovementInform(MovementGeneratorType.Rotate, _id); + if (movementInform) + { + SetScriptResult(MovementStopReason.Finished); + if (owner.IsCreature()) + owner.ToCreature().GetAI().MovementInform(MovementGeneratorType.Rotate, _id); + } } public override MovementGeneratorType GetMovementGeneratorType() { return MovementGeneratorType.Rotate; } diff --git a/Source/Game/Movement/Generators/MovementGenerators.cs b/Source/Game/Movement/Generators/MovementGenerators.cs index 9224de5a1..58fb4567f 100644 --- a/Source/Game/Movement/Generators/MovementGenerators.cs +++ b/Source/Game/Movement/Generators/MovementGenerators.cs @@ -5,6 +5,7 @@ using Framework.Constants; using Game.AI; using Game.Entities; using System; +using System.Threading.Tasks; namespace Game.Movement { @@ -14,6 +15,7 @@ namespace Game.Movement public MovementGeneratorPriority Priority; public MovementGeneratorFlags Flags; public UnitState BaseUnitState; + public TaskCompletionSource ScriptResult; // on top first update public virtual void Initialize(Unit owner) { } @@ -68,8 +70,17 @@ namespace Game.Movement { return $"Mode: {Mode} Priority: {Priority} Flags: {Flags} BaseUniteState: {BaseUnitState}"; } + + public void SetScriptResult(MovementStopReason reason) + { + if (ScriptResult != null) + { + ScriptResult.SetResult(reason); + ScriptResult = null; + } + } } - + public abstract class MovementGeneratorMedium : MovementGenerator where T : Unit { public override void Initialize(Unit owner) diff --git a/Source/Game/Movement/Generators/PointMovement.cs b/Source/Game/Movement/Generators/PointMovement.cs index ae1a9c403..4294317c7 100644 --- a/Source/Game/Movement/Generators/PointMovement.cs +++ b/Source/Game/Movement/Generators/PointMovement.cs @@ -4,6 +4,7 @@ using Framework.Constants; using Game.Entities; using System; +using System.Threading.Tasks; namespace Game.Movement { @@ -21,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) + MovementWalkRunSpeedSelectionMode speedSelectionMode = MovementWalkRunSpeedSelectionMode.Default, float? closeEnoughDistance = null, TaskCompletionSource scriptResult = null) { _movementId = id; _destination = new Position(x, y, z); @@ -37,6 +38,7 @@ namespace Game.Movement Priority = MovementGeneratorPriority.Normal; Flags = MovementGeneratorFlags.InitializationPending; BaseUnitState = UnitState.Roaming; + ScriptResult = scriptResult; } public override void Initialize(Unit owner) @@ -192,6 +194,8 @@ namespace Game.Movement public void MovementInform(Unit owner) { + SetScriptResult(MovementStopReason.Finished); + // deliver EVENT_CHARGE to scripts, EVENT_CHARGE_PREPATH is just internal implementation detail of this movement generator uint movementId = _movementId == EventId.ChargePrepath ? EventId.Charge : _movementId; owner.ToCreature()?.GetAI()?.MovementInform(MovementGeneratorType.Point, movementId); diff --git a/Source/Game/Movement/Generators/RandomMovementGenerator.cs b/Source/Game/Movement/Generators/RandomMovementGenerator.cs index 44ffcb7a5..ced83f79c 100644 --- a/Source/Game/Movement/Generators/RandomMovementGenerator.cs +++ b/Source/Game/Movement/Generators/RandomMovementGenerator.cs @@ -4,6 +4,7 @@ using Framework.Constants; using Game.Entities; using System; +using System.Threading.Tasks; namespace Game.Movement { @@ -16,7 +17,7 @@ namespace Game.Movement float _wanderDistance; uint _wanderSteps; - public RandomMovementGenerator(float spawnDist = 0.0f, TimeSpan? duration = null) + public RandomMovementGenerator(float spawnDist = 0.0f, TimeSpan? duration = null, TaskCompletionSource scriptResult = null) { _timer = new TimeTracker(); _reference = new(); @@ -26,6 +27,8 @@ namespace Game.Movement Priority = MovementGeneratorPriority.Normal; Flags = MovementGeneratorFlags.InitializationPending; BaseUnitState = UnitState.Roaming; + ScriptResult = scriptResult; + if (duration.HasValue) _duration = new TimeTracker(duration.Value); } @@ -112,8 +115,11 @@ namespace Game.Movement } if (movementInform && HasFlag(MovementGeneratorFlags.InformEnabled)) + { + SetScriptResult(MovementStopReason.Finished); if (owner.IsAIEnabled()) owner.GetAI().MovementInform(MovementGeneratorType.Random, 0); + } } public override void Pause(uint timer) diff --git a/Source/Game/Movement/Generators/WaypointMovement.cs b/Source/Game/Movement/Generators/WaypointMovement.cs index 3dc4071a1..454dd428c 100644 --- a/Source/Game/Movement/Generators/WaypointMovement.cs +++ b/Source/Game/Movement/Generators/WaypointMovement.cs @@ -7,6 +7,7 @@ using Game.Entities; using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; namespace Game.Movement { @@ -30,8 +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) - + (TimeSpan min, TimeSpan max)? waitTimeRangeAtPathEnd = null, float? wanderDistanceAtPathEnds = null, bool? followPathBackwardsFromEndToStart = null, bool generatePath = true, TaskCompletionSource scriptResult = null) { _nextMoveTime = new TimeTracker(0); _pathId = pathId; @@ -49,13 +49,14 @@ namespace Game.Movement Priority = MovementGeneratorPriority.Normal; Flags = MovementGeneratorFlags.InitializationPending; BaseUnitState = UnitState.Roaming; + ScriptResult = scriptResult; if (duration.HasValue) _duration = new(duration.Value); } 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) + (TimeSpan min, TimeSpan max)? waitTimeRangeAtPathEnd = null, float? wanderDistanceAtPathEnds = null, bool? followPathBackwardsFromEndToStart = null, bool generatePath = true, TaskCompletionSource scriptResult = null) { _nextMoveTime = new TimeTracker(0); _repeating = repeating; @@ -72,6 +73,7 @@ namespace Game.Movement Priority = MovementGeneratorPriority.Normal; Flags = MovementGeneratorFlags.InitializationPending; BaseUnitState = UnitState.Roaming; + ScriptResult = scriptResult; if (duration.HasValue) _duration = new(duration.Value); @@ -176,6 +178,9 @@ namespace Game.Movement { RemoveFlag(MovementGeneratorFlags.Transitory); AddFlag(MovementGeneratorFlags.InformEnabled); + AddFlag(MovementGeneratorFlags.Finalized); + owner.UpdateCurrentWaypointInfo(0, 0); + SetScriptResult(MovementStopReason.Finished); return false; } } @@ -268,6 +273,9 @@ namespace Game.Movement // TODO: Research if this modification is needed, which most likely isnt owner.SetWalk(false); } + + if (movementInform) + SetScriptResult(MovementStopReason.Finished); } public void MovementInform(Creature owner) @@ -367,6 +375,8 @@ namespace Game.Movement CreatureAI ai = owner.GetAI(); if (ai != null) ai.WaypointPathEnded(currentWaypoint.Id, _path.Id); + + SetScriptResult(MovementStopReason.Finished); return; } } @@ -406,7 +416,7 @@ namespace Game.Movement init.SetAnimation(AnimTier.Ground); break; case WaypointMoveType.Takeoff: - init.SetAnimation(AnimTier.Hover); + init.SetAnimation(AnimTier.Fly); break; case WaypointMoveType.Run: init.SetWalk(false); diff --git a/Source/Game/Movement/MotionMaster.cs b/Source/Game/Movement/MotionMaster.cs index 701c0d79e..23b160964 100644 --- a/Source/Game/Movement/MotionMaster.cs +++ b/Source/Game/Movement/MotionMaster.cs @@ -10,6 +10,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Numerics; +using System.Threading.Tasks; namespace Game.Movement { @@ -544,21 +545,30 @@ 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) + public void MoveRandom(float wanderDistance = 0.0f, TimeSpan? duration = null, MovementSlot slot = MovementSlot.Default, TaskCompletionSource scriptResult = null) { if (_owner.IsTypeId(TypeId.Unit)) - Add(new RandomMovementGenerator(wanderDistance, duration), slot); + Add(new RandomMovementGenerator(wanderDistance, duration, scriptResult), slot); + else if (scriptResult != null) + scriptResult.SetResult(MovementStopReason.Interrupted); } - public void MoveFollow(Unit target, float dist, float angle = 0.0f, TimeSpan? duration = null, MovementSlot slot = MovementSlot.Active) { MoveFollow(target, dist, new ChaseAngle(angle), duration, slot); } + public void MoveFollow(Unit target, float dist, float angle = 0.0f, TimeSpan? duration = null, MovementSlot slot = MovementSlot.Active, TaskCompletionSource 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) + public void MoveFollow(Unit target, float dist, ChaseAngle angle, TimeSpan? duration = null, MovementSlot slot = MovementSlot.Active, TaskCompletionSource scriptResult = null) { // Ignore movement request if target not exist if (target == null || target == _owner) + { + if (scriptResult != null) + scriptResult.SetResult(MovementStopReason.Interrupted); return; + } - Add(new FollowMovementGenerator(target, dist, angle, duration), slot); + Add(new FollowMovementGenerator(target, dist, angle, duration, scriptResult), slot); } public void MoveChase(Unit target, float dist, float angle = 0.0f) { MoveChase(target, new ChaseRange(dist), new ChaseAngle(angle)); } @@ -579,26 +589,30 @@ namespace Game.Movement Add(new ConfusedMovementGenerator()); } - public void MoveFleeing(Unit enemy, TimeSpan time = default) + public void MoveFleeing(Unit enemy, TimeSpan time = default, TaskCompletionSource scriptResult = null) { if (enemy == null) + { + if (scriptResult != null) + scriptResult.SetResult(MovementStopReason.Interrupted); return; + } if (_owner.IsCreature() && time > TimeSpan.Zero) - Add(new TimedFleeingMovementGenerator(enemy.GetGUID(), time)); + Add(new TimedFleeingMovementGenerator(enemy.GetGUID(), time, scriptResult)); else - Add(new FleeingMovementGenerator(enemy.GetGUID())); + 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) + 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) { - MovePoint(id, pos.posX, pos.posY, pos.posZ, generatePath, finalOrient, speed, speedSelectionMode, closeEnoughDistance); + 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) + 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) { 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)); + Add(new PointMovementGenerator(id, x, y, z, generatePath, speed, finalOrient, null, null, speedSelectionMode, closeEnoughDistance, scriptResult)); } public void MoveCloserAndStop(uint id, Unit target, float distance) @@ -625,12 +639,12 @@ namespace Game.Movement } } - public void MoveLand(uint id, Position pos, uint? tierTransitionId = null, float? velocity = null, MovementWalkRunSpeedSelectionMode speedSelectionMode = MovementWalkRunSpeedSelectionMode.Default) + public void MoveLand(uint id, Position pos, uint? tierTransitionId = null, float? velocity = null, MovementWalkRunSpeedSelectionMode speedSelectionMode = MovementWalkRunSpeedSelectionMode.Default, TaskCompletionSource scriptResult = null) { var initializer = (MoveSplineInit init) => { init.MoveTo(pos, false); - init.SetAnimation(AnimTier.Ground, tierTransitionId.GetValueOrDefault(0)); + init.SetAnimation(AnimTier.Ground, tierTransitionId.GetValueOrDefault(1)); switch (speedSelectionMode) { case MovementWalkRunSpeedSelectionMode.ForceRun: @@ -647,15 +661,15 @@ namespace Game.Movement init.SetVelocity(velocity.Value); }; - Add(new GenericMovementGenerator(initializer, MovementGeneratorType.Effect, id)); + 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) + public void MoveTakeoff(uint id, Position pos, uint? tierTransitionId = null, float? velocity = null, MovementWalkRunSpeedSelectionMode speedSelectionMode = MovementWalkRunSpeedSelectionMode.Default, TaskCompletionSource scriptResult = null) { var initializer = (MoveSplineInit init) => { init.MoveTo(pos, false); - init.SetAnimation(AnimTier.Hover, tierTransitionId.GetValueOrDefault(0)); + init.SetAnimation(AnimTier.Hover, tierTransitionId.GetValueOrDefault(15)); switch (speedSelectionMode) { case MovementWalkRunSpeedSelectionMode.ForceRun: @@ -672,7 +686,7 @@ namespace Game.Movement init.SetVelocity(velocity.Value); }; - Add(new GenericMovementGenerator(initializer, MovementGeneratorType.Effect, id)); + Add(new GenericMovementGenerator(initializer, MovementGeneratorType.Effect, id, new() { ScriptResult = scriptResult })); } public void MoveCharge(float x, float y, float z, float speed = SPEED_CHARGE, uint id = EventId.Charge, bool generatePath = false, Unit target = null, SpellEffectExtraData spellEffectExtraData = null) @@ -755,16 +769,20 @@ 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) + 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) { - MoveJump(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), speedXY, speedZ, id, hasOrientation, arrivalCast, spellEffectExtraData); + 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) + 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) { Log.outDebug(LogFilter.Server, "Unit ({0}) jump to point (X: {1} Y: {2} Z: {3})", _owner.GetGUID().ToString(), x, y, z); if (speedXY < 0.01f) + { + if (scriptResult != null) + scriptResult.SetResult(MovementStopReason.Interrupted); return; + } float moveTimeHalf = (float)(speedZ / gravity); float max_height = -MoveSpline.ComputeFallElevation(moveTimeHalf, false, -speedZ); @@ -788,17 +806,21 @@ namespace Game.Movement arrivalSpellTargetGuid = arrivalCast.Target; } - GenericMovementGenerator movement = new(initializer, MovementGeneratorType.Effect, id, new GenericMovementGeneratorArgs() { ArrivalSpellId = arrivalSpellId, ArrivalSpellTarget = arrivalSpellTargetGuid }); + GenericMovementGenerator movement = new(initializer, MovementGeneratorType.Effect, id, new GenericMovementGeneratorArgs() { ArrivalSpellId = arrivalSpellId, ArrivalSpellTarget = arrivalSpellTargetGuid, ScriptResult = scriptResult }); movement.Priority = MovementGeneratorPriority.Highest; movement.BaseUnitState = UnitState.Jumping; Add(movement); } - public void MoveJumpWithGravity(Position pos, float speedXY, float gravity, uint id = EventId.Jump, bool hasOrientation = false, JumpArrivalCastArgs arrivalCast = null, SpellEffectExtraData spellEffectExtraData = null) + 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) { Log.outDebug(LogFilter.Movement, $"MotionMaster.MoveJumpWithGravity: '{_owner.GetGUID()}', jumps to point Id: {id} ({pos})"); if (speedXY < 0.01f) + { + if (scriptResult != null) + scriptResult.SetResult(MovementStopReason.Interrupted); return; + } var initializer = (MoveSplineInit init) => { @@ -821,14 +843,14 @@ namespace Game.Movement arrivalSpellTargetGuid = arrivalCast.Target; } - GenericMovementGenerator movement = new GenericMovementGenerator(initializer, MovementGeneratorType.Effect, id, new GenericMovementGeneratorArgs() { ArrivalSpellId = arrivalSpellId, ArrivalSpellTarget = arrivalSpellTargetGuid }); + GenericMovementGenerator movement = new GenericMovementGenerator(initializer, MovementGeneratorType.Effect, id, new GenericMovementGeneratorArgs() { ArrivalSpellId = arrivalSpellId, ArrivalSpellTarget = arrivalSpellTargetGuid, ScriptResult = scriptResult }); movement.Priority = MovementGeneratorPriority.Highest; movement.BaseUnitState = UnitState.Jumping; movement.AddFlag(MovementGeneratorFlags.PersistOnDeath); 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) + 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) { var initializer = (MoveSplineInit init) => { @@ -879,7 +901,7 @@ namespace Game.Movement init.SetVelocity(speed.Value); }; - Add(new GenericMovementGenerator(initializer, MovementGeneratorType.Effect, 0, new GenericMovementGeneratorArgs() { Duration = duration })); + Add(new GenericMovementGenerator(initializer, MovementGeneratorType.Effect, 0, new GenericMovementGeneratorArgs() { Duration = duration, ScriptResult = scriptResult })); } public void MoveSmoothPath(uint pointId, Vector3[] pathPoints, int pathSize, bool walk = false, bool fly = false) @@ -935,7 +957,7 @@ namespace Game.Movement Add(new SplineChainMovementGenerator(info)); } - public void MoveFall(uint id = 0) + public void MoveFall(uint id = 0, TaskCompletionSource 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); @@ -965,7 +987,7 @@ namespace Game.Movement init.SetFall(); }; - GenericMovementGenerator movement = new(initializer, MovementGeneratorType.Effect, id); + GenericMovementGenerator movement = new(initializer, MovementGeneratorType.Effect, id, new() { ScriptResult = scriptResult }); movement.Priority = MovementGeneratorPriority.Highest; Add(movement); } @@ -994,7 +1016,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) + public void MoveTaxiFlight(uint path, uint pathnode, float? speed = null, TaskCompletionSource scriptResult = null) { if (_owner.IsTypeId(TypeId.Player)) { @@ -1006,7 +1028,7 @@ namespace Game.Movement bool hasExisting = HasMovementGenerator(gen => gen.GetMovementGeneratorType() == MovementGeneratorType.Flight); Cypher.Assert(!hasExisting, "Duplicate flight path movement generator"); - FlightPathMovementGenerator movement = new(speed); + FlightPathMovementGenerator movement = new(speed, scriptResult); movement.LoadPath(_owner.ToPlayer()); Add(movement); } @@ -1029,20 +1051,24 @@ namespace Game.Movement } public void MovePath(uint pathId, bool repeatable, 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) + (TimeSpan, TimeSpan)? waitTimeRangeAtPathEnd = null, float? wanderDistanceAtPathEnds = null, bool? followPathBackwardsFromEndToStart = null, bool generatePath = true, TaskCompletionSource scriptResult = null) { if (pathId == 0) + { + if (scriptResult != null) + scriptResult.SetResult(MovementStopReason.Interrupted); return; + } Log.outDebug(LogFilter.Movement, $"MotionMaster::MovePath: '{_owner.GetGUID()}', starts moving over path Id: {pathId} (repeatable: {repeatable})"); - Add(new WaypointMovementGenerator(pathId, repeatable, duration, speed, speedSelectionMode, waitTimeRangeAtPathEnd, wanderDistanceAtPathEnds, followPathBackwardsFromEndToStart, generatePath), MovementSlot.Default); + Add(new WaypointMovementGenerator(pathId, repeatable, duration, speed, speedSelectionMode, waitTimeRangeAtPathEnd, wanderDistanceAtPathEnds, followPathBackwardsFromEndToStart, generatePath, scriptResult), MovementSlot.Default); } public void MovePath(WaypointPath path, bool repeatable, 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) + (TimeSpan, TimeSpan)? waitTimeRangeAtPathEnd = null, float? wanderDistanceAtPathEnds = null, bool? followPathBackwardsFromEndToStart = null, bool generatePath = true, TaskCompletionSource 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), MovementSlot.Default); + Add(new WaypointMovementGenerator(path, repeatable, duration, speed, speedSelectionMode, waitTimeRangeAtPathEnd, wanderDistanceAtPathEnds, followPathBackwardsFromEndToStart, generatePath, scriptResult), MovementSlot.Default); } /// @@ -1053,11 +1079,11 @@ 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) + public void MoveRotate(uint id, RotateDirection direction, TimeSpan? time = null, float? turnSpeed = null, float? totalTurnAngle = null, TaskCompletionSource scriptResult = null) { Log.outDebug(LogFilter.Movement, $"MotionMaster::MoveRotate: '{_owner.GetGUID()}', starts rotate (time: {time.GetValueOrDefault(TimeSpan.Zero)}ms, turnSpeed: {turnSpeed}, totalTurnAngle: {totalTurnAngle}, direction: {direction})"); - Add(new RotateMovementGenerator(id, direction, time, turnSpeed, totalTurnAngle)); + Add(new RotateMovementGenerator(id, direction, time, turnSpeed, totalTurnAngle, scriptResult)); } public void MoveFormation(Unit leader, float range, float angle, uint point1, uint point2) diff --git a/Source/Game/Spells/Spell.cs b/Source/Game/Spells/Spell.cs index cf30a7cce..f70d30928 100644 --- a/Source/Game/Spells/Spell.cs +++ b/Source/Game/Spells/Spell.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; +using System.Threading.Tasks; namespace Game.Spells { @@ -3000,6 +3001,9 @@ namespace Game.Spells HandleImmediate(); } + if (m_scriptResult != null && !m_scriptWaitsForSpellHit) + m_scriptResult.SetResult(SpellCastResult.SpellCastOk); + CallScriptAfterCastHandlers(); var spell_triggered = Global.SpellMgr.GetSpellLinked(SpellLinkedType.Cast, m_spellInfo.Id); @@ -3438,6 +3442,9 @@ namespace Game.Spells m_spellState = SpellState.Finished; + if (m_scriptResult != null && (m_scriptWaitsForSpellHit || result != SpellCastResult.SpellCastOk)) + m_scriptResult.SetResult(result); + if (m_caster == null) return; @@ -8225,6 +8232,9 @@ namespace Game.Spells public List m_appliedMods = new(); + public TaskCompletionSource m_scriptResult; + public bool m_scriptWaitsForSpellHit; + WorldObject m_caster; public SpellValue m_spellValue; ObjectGuid m_originalCasterGUID; @@ -9516,6 +9526,9 @@ namespace Game.Spells public Dictionary SpellValueOverrides = new(); public object CustomArg; + public TaskCompletionSource ScriptResult; + public bool ScriptWaitsForSpellHit; + public CastSpellExtraArgs() { } public CastSpellExtraArgs(bool triggered) @@ -9618,6 +9631,17 @@ namespace Game.Spells return this; } + public CastSpellExtraArgs SetScriptResult(TaskCompletionSource scriptResult) + { + ScriptResult = scriptResult; + return this; + } + public CastSpellExtraArgs SetScriptWaitsForSpellHit(bool scriptWaitsForSpellHit) + { + ScriptWaitsForSpellHit = scriptWaitsForSpellHit; + return this; + } + public static implicit operator CastSpellExtraArgs(bool triggered) => new CastSpellExtraArgs(triggered); public static implicit operator CastSpellExtraArgs(TriggerCastFlags trigger) => new CastSpellExtraArgs(trigger);