public void DoAttack(SkillArg arg) { List <SkillAffectedActor> affected = arg.AffectedActors; List <ActorExt> targets = new List <ActorExt>(); #region Target Selection if (!arg.Caster.Status.Dead && !arg.Caster.Status.Down && !arg.Caster.Status.Invincible) { if (arg.CastMode == SkillCastMode.Coordinate) { Map.Map map = MapManager.Instance.GetMap(arg.Caster.MapInstanceID); foreach (Actor i in map.GetActorsAroundArea(arg.X, arg.Y, arg.Z, arg.Skill.BaseData.NoTargetRange > 0 ? arg.Skill.BaseData.NoTargetRange : arg.Skill.BaseData.CastRangeMax)) { if (NPC.FactionRelationFactory.Instance[arg.Caster.Faction][((ActorExt)i).Faction] != Relations.Friendly) { targets.Add((ActorExt)i); } } } else { switch (arg.Skill.BaseData.SkillType) { case SkillType.Single: if (!arg.Target.Status.Dead || arg.Target.Status.Recovering) { int dir = arg.Dir.DirectionRelativeToTarget(arg.Caster.X, arg.Caster.Y, arg.Target.X, arg.Target.Y); if (dir <= arg.Skill.BaseData.NoTargetAngle) { targets.Add(arg.Target); } } break; case SkillType.NoTarget: { Map.Map map = MapManager.Instance.GetMap(arg.Caster.MapInstanceID); foreach (Actor i in map.GetActorsAroundActor(arg.Caster, arg.Skill.BaseData.NoTargetRange > 0 ? arg.Skill.BaseData.NoTargetRange : arg.Skill.BaseData.CastRangeMax, false)) { if (i.ActorType == ActorType.CORPSE || i.ActorType == ActorType.ITEM) { continue; } if (arg.Skill.BaseData.NoTargetType == NoTargetTypes.Angular) { if (arg.Dir.DirectionRelativeToTarget(arg.Caster.X, arg.Caster.Y, i.X, i.Y) <= arg.Skill.BaseData.NoTargetAngle && NPC.FactionRelationFactory.Instance[arg.Caster.Faction][((ActorExt)i).Faction] != Relations.Friendly) { targets.Add((ActorExt)i); } } else { float degree = arg.Caster.DirectionRelativeToTarget(i); float distance = arg.Caster.DistanceToActor(i); Degree deg = degree; float width = (float)(distance * Math.Sin(deg.InRadians())); float length = (float)(distance * Math.Cos(deg.InRadians())); if (width <= arg.Skill.BaseData.NoTargetWidth && NPC.FactionRelationFactory.Instance[arg.Caster.Faction][((ActorExt)i).Faction] != Relations.Friendly && degree < 90) { targets.Add((ActorExt)i); } } } } break; case SkillType.Self: //Logger.Log.Debug("Do attack on self casting, really wanted?"); targets.Add(arg.Caster); break; } } } #endregion #region Attack Action foreach (ActorExt i in targets) { SkillAffectedActor res = new SkillAffectedActor(); SkillAffectedActor bonus = null; res.Result = CalcAttackResult(arg.Caster, i, arg.CastMode == SkillCastMode.Coordinate || arg.Skill.BaseData.SkillType == SkillType.NoTarget, arg.Skill.BaseData.CastRangeMax); res.Target = i; if (res.Result != SkillAttackResult.Avoid && res.Result != SkillAttackResult.Miss) { switch (res.Result) { case SkillAttackResult.Counter: { if (i.Tasks.TryGetValue("CounterEnemy", out Task task)) { SkillHandlers.KungfuMaster.Additions.CounterEnemy counter = task as SkillHandlers.KungfuMaster.Additions.CounterEnemy; counter.Deactivate(); SkillArg arg2 = new SkillArg(); if (i is ActorPC pc && pc.Skills.TryGetValue(counter.CounterSkillID, out Skill skill)) { if (pc.Job == Job.KungfuMaster) { Interlocked.Add(ref pc.MP, 3); if (pc.MP > pc.MaxMP) { Interlocked.Exchange(ref pc.MP, pc.MaxMP); } pc.Client().SendPlayerMP(); } arg2.Skill = skill; arg2.Caster = i; arg2.Target = arg.Caster; arg2.SkillSession = (byte)Global.Random.Next(0, 255); arg2.Dir = i.Dir; SkillCast(arg2); } } if (i.Tasks.TryGetValue("WoodBlock", out task)) { SkillHandlers.Assassin.Additions.WoodBlock counter = task as SkillHandlers.Assassin.Additions.WoodBlock; counter.Deactivate(); SkillArg arg2 = new SkillArg(); if (i is ActorPC pc && pc.Skills.TryGetValue(counter.CounterSkillID, out Skill skill)) { arg2.Skill = skill; arg2.Caster = i; arg2.Target = arg.Caster; arg2.SkillSession = (byte)Global.Random.Next(0, 255); arg2.Dir = i.Dir; SkillCast(arg2); } } } break; case SkillAttackResult.TotalParry: #region TotalParry { if (i is ActorPC pc) { if (pc.Job == Job.BladeMaster && pc.MP < pc.MaxMP) { Interlocked.Increment(ref pc.MP); pc.Client().SendPlayerMP(); } } } #endregion break; default: #region default attacking { int dmg = CalcDamage(arg, i, out int bonusCount, out int bonusDmg, out uint bonusAdditionID); switch (res.Result) { case SkillAttackResult.Critical: dmg = (int)(dmg * 1.2f); bonusDmg = (int)(bonusDmg * 1.2f); break; case SkillAttackResult.Parry: dmg = (int)(dmg * 0.3f); bonusDmg = (int)(bonusDmg * 0.3f); break; } res.Damage = dmg; ApplyDamage(arg.Caster, i, dmg); if (bonusDmg > 0) { bonus = new SkillAffectedActor() { Damage = bonusDmg, Target = i, BonusAdditionID = bonusAdditionID }; ApplyDamage(arg.Caster, i, bonusDmg); } if (i.EventHandler is BNSActorEventHandler handler) { handler.OnSkillDamage(arg, res.Result, dmg, bonusCount); } if (i is ActorPC pc) { Network.Client.GameSession client = pc.Client(); client?.ChangeCombatStatus(true); if (pc.Tasks.TryGetValue("CombatStatusTask", out Task task)) { task.DueTime = 30000; task.Activate(); } else { Tasks.Player.CombatStatusTask ct = new Tasks.Player.CombatStatusTask(30000, pc); pc.Tasks["CombatStatusTask"] = ct; ct.Activate(); } } } #endregion break; } } affected.Add(res); if (bonus != null) { affected.Add(bonus); } } #endregion }