diff --git a/src/DiIiS-NA/D3-GameServer/GSSystem/AISystem/Brains/MonsterBrain.cs b/src/DiIiS-NA/D3-GameServer/GSSystem/AISystem/Brains/MonsterBrain.cs index 6ea3910..f6f783d 100644 --- a/src/DiIiS-NA/D3-GameServer/GSSystem/AISystem/Brains/MonsterBrain.cs +++ b/src/DiIiS-NA/D3-GameServer/GSSystem/AISystem/Brains/MonsterBrain.cs @@ -21,456 +21,525 @@ using DiIiS_NA.GameServer.MessageSystem; namespace DiIiS_NA.GameServer.GSSystem.AISystem.Brains { - public class MonsterBrain : Brain - { - private new readonly Logger Logger; - // list of power SNOs that are defined for the monster - public Dictionary PresetPowers { get; private set; } + public class MonsterBrain : Brain + { + private new readonly Logger _logger; - private TickTimer _powerDelay; + // list of power SNOs that are defined for the monster + public Dictionary PresetPowers { get; private set; } - public struct Cooldown - { - public TickTimer CooldownTimer; - public float CooldownTime; - } + private TickTimer _powerDelay; - private bool _warnedNoPowers; - private Actor _target { get; set; } - private int _mpqPowerCount; - private bool Feared = false; + public struct Cooldown + { + public TickTimer CooldownTimer; + public float CooldownTime; + } - public Actor AttackedBy = null; - public TickTimer TimeoutAttacked = null; + private bool _warnedNoPowers; + private Actor Target { get; set; } + private int _mpqPowerCount; + private bool _feared = false; - public Actor PriorityTarget = null; + public Actor AttackedBy = null; + public TickTimer TimeoutAttacked = null; - public MonsterBrain(Actor body) - : base(body) - { - Logger = LogManager.CreateLogger(GetType().Name); - - PresetPowers = new Dictionary(); + public Actor PriorityTarget = null; - // build list of powers defined in monster mpq data - if (body.ActorData.MonsterSNO <= 0) - { - Logger.Warn($"$[red]${GetType().Name}$[/]$ - Monster \"{body.SNO}\" has no monster SNO"); - return; - } - var monsterData = (DiIiS_NA.Core.MPQ.FileFormats.Monster)MPQStorage.Data.Assets[SNOGroup.Monster][body.ActorData.MonsterSNO].Data; - _mpqPowerCount = monsterData.SkillDeclarations.Count(e => e.SNOPower != -1); - for (int i = 0; i < monsterData.SkillDeclarations.Length; i++) - { - if (monsterData.SkillDeclarations[i].SNOPower == -1) continue; - if (PowerLoader.HasImplementationForPowerSNO(monsterData.SkillDeclarations[i].SNOPower)) - { - var cooldownTime = monsterData.MonsterSkillDeclarations[i].Timer / 10f; - PresetPowers.Add(monsterData.SkillDeclarations[i].SNOPower, new Cooldown { CooldownTimer = null, CooldownTime = cooldownTime }); - } - } + public MonsterBrain(Actor body) + : base(body) + { + _logger = LogManager.CreateLogger(GetType().Name); - if (monsterData.SkillDeclarations.All(s => s.SNOPower != 30592)) - PresetPowers.Add(30592, new Cooldown { CooldownTimer = null, CooldownTime = 0f }); //hack for dummy mobs without powers - } + PresetPowers = new Dictionary(); - public override void Think(int tickCounter) - { - switch (Body.SNO) - { - case ActorSno._uber_siegebreakerdemon: - case ActorSno._a4dun_garden_corruption_monster: - case ActorSno._a4dun_garden_hellportal_pillar: - case ActorSno._belialvoiceover: - return; - } + // build list of powers defined in monster mpq data + if (body.ActorData.MonsterSNO <= 0) + { + _logger.Warn($"$[red]${GetType().Name}$[/]$ - Monster \"{body.SNO}\" has no monster SNO"); + return; + } - if (Body.Hidden) - return; + var monsterData = + (DiIiS_NA.Core.MPQ.FileFormats.Monster)MPQStorage.Data.Assets[SNOGroup.Monster][ + body.ActorData.MonsterSNO].Data; + _mpqPowerCount = monsterData.SkillDeclarations.Count(e => e.SNOPower != -1); + for (int i = 0; i < monsterData.SkillDeclarations.Length; i++) + { + if (monsterData.SkillDeclarations[i].SNOPower == -1) continue; + if (!PowerLoader.HasImplementationForPowerSNO(monsterData.SkillDeclarations[i].SNOPower)) continue; + var cooldownTime = monsterData.MonsterSkillDeclarations[i].Timer / 10f; + PresetPowers.Add(monsterData.SkillDeclarations[i].SNOPower, + new Cooldown { CooldownTimer = null, CooldownTime = cooldownTime }); + } - if (CurrentAction != null && PriorityTarget != null && PriorityTarget.Attributes[GameAttributes.Is_Helper] == true) - { - PriorityTarget = null; - CurrentAction.Cancel(tickCounter); - CurrentAction = null; - return; - } + if (monsterData.SkillDeclarations.All(s => s.SNOPower != 30592)) + PresetPowers.Add(30592, + new Cooldown { CooldownTimer = null, CooldownTime = 0f }); //hack for dummy mobs without powers + } - if (tickCounter % 60 != 0) return; - - if (Body is NPC) return; + public override void Think(int tickCounter) + { + switch (Body.SNO) + { + case ActorSno._uber_siegebreakerdemon: + case ActorSno._a4dun_garden_corruption_monster: + case ActorSno._a4dun_garden_hellportal_pillar: + case ActorSno._belialvoiceover: + return; + } - if (!Body.Visible || Body.Dead) return; + if (Body.Hidden) + return; - if (Body.World.Game.Paused) return; - if (Body.Attributes[GameAttributes.Disabled]) return; + if (CurrentAction != null && PriorityTarget != null && + PriorityTarget.Attributes[GameAttributes.Is_Helper] == true) + { + PriorityTarget = null; + CurrentAction.Cancel(tickCounter); + CurrentAction = null; + return; + } - if (Body.Attributes[GameAttributes.Frozen] || - Body.Attributes[GameAttributes.Stunned] || - Body.Attributes[GameAttributes.Blind] || - Body.Attributes[GameAttributes.Webbed] || - Body.Disable || - Body.World.BuffManager.GetFirstBuff(Body) != null || - Body.World.BuffManager.GetFirstBuff(Body) != null) - { - if (CurrentAction != null) - { - CurrentAction.Cancel(tickCounter); - CurrentAction = null; - } - _powerDelay = null; + if (tickCounter % 60 != 0) return; - return; - } + if (Body is NPC) return; - if (Body.Attributes[GameAttributes.Feared]) - { - if (!Feared || CurrentAction == null) - { - if (CurrentAction != null) - { - CurrentAction.Cancel(tickCounter); - CurrentAction = null; - } - Feared = true; - CurrentAction = new MoveToPointWithPathfindAction( - Body, - PowerContext.RandomDirection(Body.Position, 3f, 8f) - ); - return; - } + if (!Body.Visible || Body.Dead) return; - return; - } + if (Body.World.Game.Paused) return; + if (Body.Attributes[GameAttributes.Disabled]) return; - Feared = false; + if (Body.Attributes[GameAttributes.Frozen] || + Body.Attributes[GameAttributes.Stunned] || + Body.Attributes[GameAttributes.Blind] || + Body.Attributes[GameAttributes.Webbed] || + Body.Disable || + Body.World.BuffManager.GetFirstBuff(Body) != null || + Body.World.BuffManager.GetFirstBuff(Body) != null) + { + if (CurrentAction != null) + { + CurrentAction.Cancel(tickCounter); + CurrentAction = null; + } - if (CurrentAction == null) - { - _powerDelay ??= new SecondsTickTimer(Body.World.Game, 1.0f); - if (AttackedBy != null || Body.GetObjectsInRange(50f).Count != 0) - { - if (_powerDelay.TimedOut) - { - _powerDelay = new SecondsTickTimer(Body.World.Game, 1.0f); + _powerDelay = null; - if (AttackedBy != null) - PriorityTarget = AttackedBy; + return; + } - if (PriorityTarget == null) - { - Actor[] targets; + if (Body.Attributes[GameAttributes.Feared]) + { + if (_feared && CurrentAction != null) return; + if (CurrentAction != null) + { + CurrentAction.Cancel(tickCounter); + CurrentAction = null; + } - if (Body.Attributes[GameAttributes.Team_Override] == 1) - targets = Body.GetObjectsInRange(60f) - .Where(p => !p.Dead) - .OrderBy((monster) => PowerMath.Distance2D(monster.Position, Body.Position)) - .ToArray(); - else - targets = Body.GetActorsInRange(50f) - .Where(p => ((p is Player) && !p.Dead && p.Attributes[GameAttributes.Loading] == false && p.Attributes[GameAttributes.Is_Helper] == false && p.World.BuffManager.GetFirstBuff(p) == null) - || ((p is Minion) && !p.Dead && p.Attributes[GameAttributes.Is_Helper] == false) - || (p is DesctructibleLootContainer && p.SNO.IsDoorOrBarricade()) - || ((p is Hireling) && !p.Dead) - ) - .OrderBy((actor) => PowerMath.Distance2D(actor.Position, Body.Position)) - .ToArray(); + _feared = true; + CurrentAction = new MoveToPointWithPathfindAction( + Body, + PowerContext.RandomDirection(Body.Position, 3f, 8f) + ); + return; + } - if (targets.Length == 0) return; - - _target = targets.First(); - } - else - _target = PriorityTarget; + _feared = false; - int powerToUse = PickPowerToUse(); - if (powerToUse > 0) - { - PowerScript power = PowerLoader.CreateImplementationForPowerSNO(powerToUse); - power.User = Body; - float attackRange = Body.ActorData.Cylinder.Ax2 + (power.EvalTag(PowerKeys.AttackRadius) > 0f ? (powerToUse == 30592 ? 10f : Math.Min((float)power.EvalTag(PowerKeys.AttackRadius), 35f)) : 35f); - float targetDistance = PowerMath.Distance2D(_target.Position, Body.Position); - if (targetDistance < attackRange + _target.ActorData.Cylinder.Ax2) - { - if (Body.WalkSpeed != 0) - Body.TranslateFacing(_target.Position, false); + if (CurrentAction != null) return; + _powerDelay ??= new SecondsTickTimer(Body.World.Game, 1.0f); + // Check if the character has been attacked or if there are any players within 50 units range + if (AttackedBy != null || Body.GetObjectsInRange(50f).Count != 0) + { + // If the power delay hasn't timed out, return + if (!_powerDelay.TimedOut) return; - CurrentAction = new PowerAction(Body, powerToUse, _target); + // Reset the power delay + _powerDelay = new SecondsTickTimer(Body.World.Game, 1.0f); - if (power is SummoningSkill) - PresetPowers[powerToUse] = new Cooldown { CooldownTimer = null, CooldownTime = (Body is Boss ? 15f : 7f) }; + // If the character has been attacked, set the attacker as the priority target + if (AttackedBy != null) + PriorityTarget = AttackedBy; - if (power is MonsterAffixSkill monsterAffixSkill) - PresetPowers[powerToUse] = new Cooldown { CooldownTimer = null, CooldownTime = monsterAffixSkill.CooldownTime }; + // If there's no defined priority target, start a search + if (PriorityTarget == null) + { + Actor[] targets; - if (PresetPowers[powerToUse].CooldownTime > 0f) - PresetPowers[powerToUse] = new Cooldown { CooldownTimer = new SecondsTickTimer(Body.World.Game, PresetPowers[powerToUse].CooldownTime), CooldownTime = PresetPowers[powerToUse].CooldownTime }; + // If the character is part of a team, search for alive monsters within a range of 60 units and order them by distance + if (Body.Attributes[GameAttributes.Team_Override] == 1) + targets = Body.GetObjectsInRange(60f) + .Where(p => !p.Dead) + .OrderBy((monster) => PowerMath.Distance2D(monster.Position, Body.Position)) + .ToArray(); + else + // Otherwise, search for different types of actors including players, minions, destructible loot containers, or hirelings that are alive, not loading and not helpers, and order them by distance + targets = Body.GetActorsInRange(50f) + .Where(p => ((p is Player) && !p.Dead && p.Attributes[GameAttributes.Loading] == false && + p.Attributes[GameAttributes.Is_Helper] == false && + p.World.BuffManager.GetFirstBuff(p) == null) + || ((p is Minion) && !p.Dead && p.Attributes[GameAttributes.Is_Helper] == false) + || (p is DesctructibleLootContainer && p.SNO.IsDoorOrBarricade()) + || ((p is Hireling) && !p.Dead) + ) + .OrderBy((actor) => PowerMath.Distance2D(actor.Position, Body.Position)) + .ToArray(); - if (powerToUse is 96925 or 223284) - PresetPowers[powerToUse] = new Cooldown { CooldownTimer = new SecondsTickTimer(Body.World.Game, 10f), CooldownTime = 10f }; - } - else if (Body.WalkSpeed != 0) - { - if (Body.SNO.IsWoodwraithOrWasp()) - { - Logger.Trace($"{GetType().Name} $[underline white]${nameof(MoveToPointAction)}$[/]$ to target $[white]${_target.ActorType}$[/]$ [{_target.Position}]"); - CurrentAction = new MoveToPointAction( - Body, _target.Position - ); - } - else - { - Logger.Trace($"{GetType().Name} {nameof(MoveToTargetWithPathfindAction)} to target [{_target.ActorType}] {_target.SNO.ToString()}"); - CurrentAction = new MoveToTargetWithPathfindAction( - Body, - _target, - attackRange + _target.ActorData.Cylinder.Ax2, - powerToUse - ); - } - } - else - { - powerToUse = Body.SNO switch - { - ActorSno._a1dun_leor_firewall2 => 223284, - _ => powerToUse - }; - CurrentAction = new PowerAction(Body, powerToUse, _target); + // If there are no targets, return + if (targets.Length == 0) return; - if (power is SummoningSkill) - PresetPowers[powerToUse] = new Cooldown { CooldownTimer = null, CooldownTime = (Body is Boss ? 15f : 7f) }; + // Set the first found target as the target + Target = targets.First(); + } + else + // If there is a priority target, set it as the target + Target = PriorityTarget; - if (power is MonsterAffixSkill) - PresetPowers[powerToUse] = new Cooldown { CooldownTimer = null, CooldownTime = (power as MonsterAffixSkill).CooldownTime }; + int powerToUse = PickPowerToUse(); + if (powerToUse <= 0) return; + PowerScript power = PowerLoader.CreateImplementationForPowerSNO(powerToUse); + power.User = Body; + float attackRange = Body.ActorData.Cylinder.Ax2 + (power.EvalTag(PowerKeys.AttackRadius) > 0f + ? (powerToUse == 30592 ? 10f : Math.Min((float)power.EvalTag(PowerKeys.AttackRadius), 35f)) + : 35f); + float targetDistance = PowerMath.Distance2D(Target.Position, Body.Position); + if (targetDistance < attackRange + Target.ActorData.Cylinder.Ax2) + { + if (Body.WalkSpeed != 0) + Body.TranslateFacing(Target.Position, false); - if (PresetPowers[powerToUse].CooldownTime > 0f) - PresetPowers[powerToUse] = new Cooldown { CooldownTimer = new SecondsTickTimer(Body.World.Game, PresetPowers[powerToUse].CooldownTime), CooldownTime = PresetPowers[powerToUse].CooldownTime }; + CurrentAction = new PowerAction(Body, powerToUse, Target); - if (powerToUse == 96925 || - powerToUse == 223284) - PresetPowers[powerToUse] = new Cooldown { CooldownTimer = new SecondsTickTimer(Body.World.Game, 10f), CooldownTime = 10f }; - } - } - } - } + PresetPowers[powerToUse] = power switch + { + SummoningSkill => new Cooldown + { + CooldownTimer = null, CooldownTime = (Body is Boss ? 15f : 7f) + }, + MonsterAffixSkill monsterAffixSkill => new Cooldown + { + CooldownTimer = null, CooldownTime = monsterAffixSkill.CooldownTime + }, + _ => PresetPowers[powerToUse] + }; - else if (Body.GetObjectsInRange(50f).Count != 0) - { - if (_powerDelay.TimedOut) - { - _powerDelay = new SecondsTickTimer(Body.World.Game, 1.0f); + if (PresetPowers[powerToUse].CooldownTime > 0f) + PresetPowers[powerToUse] = new Cooldown + { + CooldownTimer = + new SecondsTickTimer(Body.World.Game, PresetPowers[powerToUse].CooldownTime), + CooldownTime = PresetPowers[powerToUse].CooldownTime + }; - if (AttackedBy != null) - PriorityTarget = AttackedBy; + if (powerToUse is 96925 or 223284) + PresetPowers[powerToUse] = new Cooldown + { CooldownTimer = new SecondsTickTimer(Body.World.Game, 10f), CooldownTime = 10f }; + } + else if (Body.WalkSpeed != 0) + { + if (Body.SNO.IsWoodwraithOrWasp()) + { + _logger.Trace( + $"{GetType().Name} $[underline white]${nameof(MoveToPointAction)}$[/]$ to target $[white]${Target.ActorType}$[/]$ [{Target.Position}]"); + CurrentAction = new MoveToPointAction( + Body, Target.Position + ); + } + else + { + _logger.Trace( + $"{GetType().Name} {nameof(MoveToTargetWithPathfindAction)} to target [{Target.ActorType}] {Target.SNO.ToString()}"); + CurrentAction = new MoveToTargetWithPathfindAction( + Body, + Target, + attackRange + Target.ActorData.Cylinder.Ax2, + powerToUse + ); + } + } + else + { + powerToUse = Body.SNO switch + { + ActorSno._a1dun_leor_firewall2 => 223284, + _ => powerToUse + }; + CurrentAction = new PowerAction(Body, powerToUse, Target); - if (PriorityTarget == null) - { - var targets = Body.GetActorsInRange(50f) - .Where(p => ((p is LorathNahr_NPC) && !p.Dead) - || ((p is CaptainRumford) && !p.Dead) - || (p is DesctructibleLootContainer && p.SNO.IsDoorOrBarricade()) - || ((p is Cain) && !p.Dead)) - .OrderBy((actor) => PowerMath.Distance2D(actor.Position, Body.Position)) - .ToArray(); + PresetPowers[powerToUse] = power switch + { + SummoningSkill => new Cooldown + { + CooldownTimer = null, CooldownTime = (Body is Boss ? 15f : 7f) + }, + MonsterAffixSkill skill => new Cooldown + { + CooldownTimer = null, CooldownTime = skill.CooldownTime + }, + _ => PresetPowers[powerToUse] + }; - if (targets.Length == 0) - { - targets = Body.GetActorsInRange(20f) - .Where(p => ((p is Monster) && !p.Dead) - || ((p is CaptainRumford) && !p.Dead) - ) - .OrderBy((actor) => PowerMath.Distance2D(actor.Position, Body.Position)) - .ToArray(); + if (PresetPowers[powerToUse].CooldownTime > 0f) + PresetPowers[powerToUse] = new Cooldown + { + CooldownTimer = + new SecondsTickTimer(Body.World.Game, PresetPowers[powerToUse].CooldownTime), + CooldownTime = PresetPowers[powerToUse].CooldownTime + }; + + if (powerToUse is 96925 or 223284) + PresetPowers[powerToUse] = new Cooldown + { CooldownTimer = new SecondsTickTimer(Body.World.Game, 10f), CooldownTime = 10f }; + } + } + + else if (Body.GetObjectsInRange(50f).Count != 0) + { + if (!_powerDelay.TimedOut) return; + _powerDelay = new SecondsTickTimer(Body.World.Game, 1.0f); + + if (AttackedBy != null) + PriorityTarget = AttackedBy; + + if (PriorityTarget == null) + { + var targets = Body.GetActorsInRange(50f) + .Where(p => ((p is LorathNahr_NPC) && !p.Dead) + || ((p is CaptainRumford) && !p.Dead) + || (p is DesctructibleLootContainer && p.SNO.IsDoorOrBarricade()) + || ((p is Cain) && !p.Dead)) + .OrderBy((actor) => PowerMath.Distance2D(actor.Position, Body.Position)) + .ToArray(); + + if (targets.Length == 0) + { + targets = Body.GetActorsInRange(20f) + .Where(p => ((p is Monster) && !p.Dead) + || ((p is CaptainRumford) && !p.Dead) + ) + .OrderBy((actor) => PowerMath.Distance2D(actor.Position, Body.Position)) + .ToArray(); - if (targets.Length == 0) - return; + if (targets.Length == 0) + return; - foreach (var monsterActor in targets.Where(tar => _target == null)) - if (monsterActor is Monster { Brain: MonsterBrain brain } monster && monsterActor != Body) - if (brain.AttackedBy != null) - _target = brain.AttackedBy; - } - else - { - _target = targets.First(); - } - foreach (var tar in targets) - if (tar is DesctructibleLootContainer && tar.SNO.IsDoorOrBarricade() && tar.SNO != ActorSno._trout_wagon_barricade) - { _target = tar; break; } - } - else - _target = PriorityTarget; + foreach (var monsterActor in targets.Where(tar => Target == null)) + if (monsterActor is Monster { Brain: MonsterBrain brain } monster && monsterActor != Body) + if (brain.AttackedBy != null) + Target = brain.AttackedBy; + } + else + { + Target = targets.First(); + } - int powerToUse = PickPowerToUse(); - if (powerToUse > 0) - { - PowerScript power = PowerLoader.CreateImplementationForPowerSNO(powerToUse); - power.User = Body; - if (_target == null) - { - /* - if (!this.Body.ActorSNO.Name.ToLower().Contains("woodwraith") && - !this.Body.ActorSNO.Name.ToLower().Contains("wasp")) - if (this.Body.Quality < 2) - { - this.CurrentAction = new MoveToPointWithPathfindAction(this.Body, RandomPosibleDirection(this.Body.CheckPointPosition, 3f, 8f, this.Body.World)); - return; - } - else - //*/ - return; - } - float attackRange = Body.ActorData.Cylinder.Ax2 + (power.EvalTag(PowerKeys.AttackRadius) > 0f ? (powerToUse == 30592 ? 10f : Math.Min((float)power.EvalTag(PowerKeys.AttackRadius), 35f)) : 35f); - float targetDistance = PowerMath.Distance2D(_target.Position, Body.Position); - if (targetDistance < attackRange + _target.ActorData.Cylinder.Ax2) - { - if (Body.WalkSpeed != 0) - Body.TranslateFacing(_target.Position, false); //columns and other non-walkable shit can't turn + foreach (var tar in targets) + if (tar is DesctructibleLootContainer && tar.SNO.IsDoorOrBarricade() && + tar.SNO != ActorSno._trout_wagon_barricade) + { + Target = tar; + break; + } + } + else + Target = PriorityTarget; - - Logger.Trace($"{GetType().Name} {nameof(PowerAction)} to target [{_target.ActorType}] {_target.SNO.ToString()}"); - // Logger.Trace("PowerAction to target"); - CurrentAction = new PowerAction(Body, powerToUse, _target); + int powerToUse = PickPowerToUse(); + if (powerToUse > 0) + { + PowerScript power = PowerLoader.CreateImplementationForPowerSNO(powerToUse); + power.User = Body; + if (Target == null) + { + /* + if (!this.Body.ActorSNO.Name.ToLower().Contains("woodwraith") && + !this.Body.ActorSNO.Name.ToLower().Contains("wasp")) + if (this.Body.Quality < 2) + { + this.CurrentAction = new MoveToPointWithPathfindAction(this.Body, RandomPosibleDirection(this.Body.CheckPointPosition, 3f, 8f, this.Body.World)); + return; + } + else + //*/ + return; + } - if (power is SummoningSkill) - PresetPowers[powerToUse] = new Cooldown { CooldownTimer = null, CooldownTime = (Body is Boss ? 15f : 7f) }; + float attackRange = Body.ActorData.Cylinder.Ax2 + (power.EvalTag(PowerKeys.AttackRadius) > 0f + ? (powerToUse == 30592 ? 10f : Math.Min((float)power.EvalTag(PowerKeys.AttackRadius), 35f)) + : 35f); + float targetDistance = PowerMath.Distance2D(Target.Position, Body.Position); + if (targetDistance < attackRange + Target.ActorData.Cylinder.Ax2) + { + if (Body.WalkSpeed != 0) + Body.TranslateFacing(Target.Position, + false); //columns and other non-walkable shit can't turn - if (power is MonsterAffixSkill monsterSkill) - PresetPowers[powerToUse] = new Cooldown { CooldownTimer = null, CooldownTime = monsterSkill.CooldownTime }; - if (PresetPowers[powerToUse].CooldownTime > 0f) - PresetPowers[powerToUse] = new Cooldown { CooldownTimer = new SecondsTickTimer(Body.World.Game, PresetPowers[powerToUse].CooldownTime), CooldownTime = PresetPowers[powerToUse].CooldownTime }; - } - else if (Body.WalkSpeed != 0) - { - if (Body.SNO.IsWoodwraithOrWasp()) - { - Logger.Trace($"{GetType().Name} {nameof(MoveToPointAction)} to target [{_target.Position}]"); + _logger.Trace( + $"{GetType().Name} {nameof(PowerAction)} to target [{Target.ActorType}] {Target.SNO.ToString()}"); + // Logger.Trace("PowerAction to target"); + CurrentAction = new PowerAction(Body, powerToUse, Target); - CurrentAction = new MoveToPointAction( - Body, _target.Position - ); - } - else - { - Logger.Trace($"{GetType().Name} {nameof(MoveToTargetWithPathfindAction)} to target [{_target.ActorType}] {_target.SNO.ToString()}"); + if (power is SummoningSkill) + PresetPowers[powerToUse] = new Cooldown + { CooldownTimer = null, CooldownTime = (Body is Boss ? 15f : 7f) }; - CurrentAction = new MoveToTargetWithPathfindAction( - Body, - //( - _target,// + MovementHelpers.GetMovementPosition( - //new Vector3D(0, 0, 0), - //this.Body.WalkSpeed, - //MovementHelpers.GetFacingAngle(_target.Position, this.Body.Position), - //6 - //) - //) - attackRange + _target.ActorData.Cylinder.Ax2, - powerToUse - ); - } - } - } - } - } + if (power is MonsterAffixSkill monsterSkill) + PresetPowers[powerToUse] = new Cooldown + { CooldownTimer = null, CooldownTime = monsterSkill.CooldownTime }; - else - { - //Logger.Trace("No enemies in range, return to master"); - if (Body.Position != Body.CheckPointPosition) - CurrentAction = new MoveToPointWithPathfindAction(Body, Body.CheckPointPosition); - } - } - } - public static Core.Types.Math.Vector3D RandomPossibleDirection(Core.Types.Math.Vector3D position, float minRadius, float maxRadius, MapSystem.World world) - { - float angle = (float)(FastRandom.Instance.NextDouble() * Math.PI * 2); - float radius = minRadius + (float)FastRandom.Instance.NextDouble() * (maxRadius - minRadius); - Core.Types.Math.Vector3D point = null; - int tryC = 0; - while (tryC < 100) - { - //break; - point = new Core.Types.Math.Vector3D(position.X + (float)Math.Cos(angle) * radius, - position.Y + (float)Math.Sin(angle) * radius, - position.Z); - if (world.CheckLocationForFlag(point, DiIiS_NA.Core.MPQ.FileFormats.Scene.NavCellFlags.AllowWalk)) - break; - tryC++; - } - return point; - } + if (PresetPowers[powerToUse].CooldownTime > 0f) + PresetPowers[powerToUse] = new Cooldown + { + CooldownTimer = new SecondsTickTimer(Body.World.Game, + PresetPowers[powerToUse].CooldownTime), + CooldownTime = PresetPowers[powerToUse].CooldownTime + }; + } + else if (Body.WalkSpeed != 0) + { + if (Body.SNO.IsWoodwraithOrWasp()) + { + _logger.Trace( + $"{GetType().Name} {nameof(MoveToPointAction)} to target [{Target.Position}]"); - public void FastAttack(Actor target, int skillSNO) - { - PowerScript power = PowerLoader.CreateImplementationForPowerSNO(skillSNO); - power.User = Body; - if (Body.WalkSpeed != 0) - Body.TranslateFacing(target.Position, false); //columns and other non-walkable shit can't turn - Logger.Trace($"{GetType().Name} {nameof(FastAttack)} {nameof(PowerAction)} to target [{_target.ActorType}] {_target.SNO.ToString()}"); - CurrentAction = new PowerAction(Body, skillSNO, target); + CurrentAction = new MoveToPointAction( + Body, Target.Position + ); + } + else + { + _logger.Trace( + $"{GetType().Name} {nameof(MoveToTargetWithPathfindAction)} to target [{Target.ActorType}] {Target.SNO.ToString()}"); - if (power is SummoningSkill) - PresetPowers[skillSNO] = new Cooldown { CooldownTimer = null, CooldownTime = (Body is Boss ? 15f : 7f) }; + CurrentAction = new MoveToTargetWithPathfindAction( + Body, + //( + Target, // + MovementHelpers.GetMovementPosition( + //new Vector3D(0, 0, 0), + //this.Body.WalkSpeed, + //MovementHelpers.GetFacingAngle(_target.Position, this.Body.Position), + //6 + //) + //) + attackRange + Target.ActorData.Cylinder.Ax2, + powerToUse + ); + } + } + } + } - if (power is MonsterAffixSkill monsterAffixSkill) - PresetPowers[skillSNO] = new Cooldown { CooldownTimer = null, CooldownTime = monsterAffixSkill.CooldownTime }; + else + { + //Logger.Trace("No enemies in range, return to master"); + if (Body.Position != Body.CheckPointPosition) + CurrentAction = new MoveToPointWithPathfindAction(Body, Body.CheckPointPosition); + } + } - if (PresetPowers[skillSNO].CooldownTime > 0f) - PresetPowers[skillSNO] = new Cooldown { CooldownTimer = new SecondsTickTimer(Body.World.Game, PresetPowers[skillSNO].CooldownTime), CooldownTime = PresetPowers[skillSNO].CooldownTime }; - } + public static Core.Types.Math.Vector3D RandomPossibleDirection(Core.Types.Math.Vector3D position, + float minRadius, float maxRadius, MapSystem.World world) + { + float angle = (float)(FastRandom.Instance.NextDouble() * Math.PI * 2); + float radius = minRadius + (float)FastRandom.Instance.NextDouble() * (maxRadius - minRadius); + Core.Types.Math.Vector3D point = null; + int tryC = 0; + while (tryC < 100) + { + //break; + point = new Core.Types.Math.Vector3D(position.X + (float)Math.Cos(angle) * radius, + position.Y + (float)Math.Sin(angle) * radius, + position.Z); + if (world.CheckLocationForFlag(point, DiIiS_NA.Core.MPQ.FileFormats.Scene.NavCellFlags.AllowWalk)) + break; + tryC++; + } - protected virtual int PickPowerToUse() - { - if (!_warnedNoPowers && PresetPowers.Count == 0) - { - Logger.Warn($"Monster $[red]$\"{Body.Name}\"$[/]$ has no usable powers. {_mpqPowerCount} are defined in mpq data."); - _warnedNoPowers = true; - return -1; - } + return point; + } - // randomly used an implemented power - if (PresetPowers.Count <= 0) return -1; - - //int power = this.PresetPowers[RandomHelper.Next(this.PresetPowers.Count)].Key; - var availablePowers = PresetPowers.Where(p => (p.Value.CooldownTimer == null || p.Value.CooldownTimer.TimedOut) && PowerLoader.HasImplementationForPowerSNO(p.Key)).Select(p => p.Key).ToList(); - if (availablePowers.Where(p => p != 30592).TryPickRandom(out var selectedPower)) - { - return selectedPower; - } + public void FastAttack(Actor target, int skillSno) + { + PowerScript power = PowerLoader.CreateImplementationForPowerSNO(skillSno); + power.User = Body; + if (Body.WalkSpeed != 0) + Body.TranslateFacing(target.Position, false); //columns and other non-walkable shit can't turn + _logger.Trace( + $"{GetType().Name} {nameof(FastAttack)} {nameof(PowerAction)} to target [{Target.ActorType}] {Target.SNO.ToString()}"); + CurrentAction = new PowerAction(Body, skillSno, target); - if (availablePowers.Contains(30592)) - return 30592; // melee attack + if (power is SummoningSkill) + PresetPowers[skillSno] = new Cooldown + { CooldownTimer = null, CooldownTime = (Body is Boss ? 15f : 7f) }; - // no usable power - return -1; - } + if (power is MonsterAffixSkill monsterAffixSkill) + PresetPowers[skillSno] = new Cooldown + { CooldownTimer = null, CooldownTime = monsterAffixSkill.CooldownTime }; - public void AddPresetPower(int powerSNO) - { - if (PresetPowers.ContainsKey(powerSNO)) - { - Logger.Debug($"Monster $[red]$\"{Body.Name}\"$[/]$ already has power {powerSNO}."); - // Logger.MethodTrace("power sno {0} already defined for monster \"{1}\"", - //powerSNO, this.Body.ActorSNO.Name); - return; - } + if (PresetPowers[skillSno].CooldownTime > 0f) + PresetPowers[skillSno] = new Cooldown + { + CooldownTimer = new SecondsTickTimer(Body.World.Game, PresetPowers[skillSno].CooldownTime), + CooldownTime = PresetPowers[skillSno].CooldownTime + }; + } - PresetPowers.Add(powerSNO, - PresetPowers.ContainsKey(30592) //if can cast melee - ? new Cooldown { CooldownTimer = null, CooldownTime = 5f } - : new Cooldown - { CooldownTimer = null, CooldownTime = 1f + (float)FastRandom.Instance.NextDouble() }); - } + protected virtual int PickPowerToUse() + { + if (!_warnedNoPowers && PresetPowers.Count == 0) + { + _logger.Warn( + $"Monster $[red]$\"{Body.Name}\"$[/]$ has no usable powers. {_mpqPowerCount} are defined in mpq data."); + _warnedNoPowers = true; + return -1; + } - public void RemovePresetPower(int powerSNO) - { - if (PresetPowers.ContainsKey(powerSNO)) - { - PresetPowers.Remove(powerSNO); - } - } - } -} + // randomly used an implemented power + if (PresetPowers.Count <= 0) return -1; + + //int power = this.PresetPowers[RandomHelper.Next(this.PresetPowers.Count)].Key; + var availablePowers = PresetPowers + .Where(p => (p.Value.CooldownTimer == null || p.Value.CooldownTimer.TimedOut) && + PowerLoader.HasImplementationForPowerSNO(p.Key)).Select(p => p.Key).ToList(); + if (availablePowers.Where(p => p != 30592).TryPickRandom(out var selectedPower)) + { + return selectedPower; + } + + if (availablePowers.Contains(30592)) + return 30592; // melee attack + + // no usable power + return -1; + } + + public void AddPresetPower(int powerSno) + { + if (PresetPowers.ContainsKey(powerSno)) + { + _logger.Debug($"Monster $[red]$\"{Body.Name}\"$[/]$ already has power {powerSno}."); + // Logger.MethodTrace("power sno {0} already defined for monster \"{1}\"", + //powerSNO, this.Body.ActorSNO.Name); + return; + } + + PresetPowers.Add(powerSno, + PresetPowers.ContainsKey(30592) //if can cast melee + ? new Cooldown { CooldownTimer = null, CooldownTime = 5f } + : new Cooldown + { CooldownTimer = null, CooldownTime = 1f + (float)FastRandom.Instance.NextDouble() }); + } + + public void RemovePresetPower(int powerSno) + { + if (PresetPowers.ContainsKey(powerSno)) + { + PresetPowers.Remove(powerSno); + } + } + } +} \ No newline at end of file