public static int GetDuration(Skill skill) { int maxActionMoveCancelStartTime = 0; int maxAnimationDuration = 0; for (int i = 0; i < skill.Actions.Count; i++) { if (skill.Actions[i].MoveCancelStartTime > maxActionMoveCancelStartTime) maxActionMoveCancelStartTime = skill.Actions[i].MoveCancelStartTime; if (skill.Actions[i].StageList == null) continue; for (int j = 0; j < skill.Actions[i].StageList.Count; j++) { for (int k = 0; k < skill.Actions[i].StageList[j].AnimationList.Count; k++) { var anim = skill.Actions[i].StageList[j].AnimationList[k]; if (anim.Animation.Duraction > maxAnimationDuration) maxAnimationDuration = anim.Animation.Duraction; if (anim.Duration > maxAnimationDuration) maxAnimationDuration = anim.Duration; } } } return (int) ((maxActionMoveCancelStartTime != 0 ? maxActionMoveCancelStartTime : maxAnimationDuration) / skill.TimeRate); }
public static int GetAttackDistance(Skill skill) { if (skill.TargetingList == null) return 0; int result = 0; for (int i = 0; i < skill.TargetingList.Count; i++) { if (skill.TargetingList[i].AreaList == null) continue; for (int j = 0; j < skill.TargetingList[i].AreaList.Count; j++) { int distance = (int) (skill.TargetingList[i].AreaList[j].MaxRadius + skill.TargetingList[i].AreaList[j].OffsetDistance); if (distance > result) result = distance; } } return result; }
public void Reset() { Damage.Clear(); Hate.Clear(); SelectedSkill = null; }
public void Action() { if (SkillsCount == 0) return; if (Npc.Attack != null && !Npc.Attack.IsFinished) return; lock (Lock) { Creature target = null; int maxHate = int.MinValue; foreach (var hate in Hate) { if (hate.Key.Position.DistanceTo(Npc.Position) > 1500 || hate.Key.LifeStats.IsDead()) { Damage.Remove(hate.Key); Hate.Remove(hate.Key); return; } if (hate.Value > maxHate) { target = hate.Key; maxHate = hate.Value; } } Npc.Target = target; } if (Npc.Target == null) return; if (SelectedSkill == null) { if (RandomUtilities.Random().Next(0, 1000) < 250) { Global.SkillEngine.UseSkill(Npc, MoveSkills[RandomUtilities.Random().Next(0, MoveSkills.Count)]); return; } SelectedSkill = AttackSkills[RandomUtilities.Random().Next(0, AttackSkills.Count)]; } int distance = SeUtils.GetAttackDistance(SelectedSkill); if (Npc.Position.DistanceTo(Npc.Target.Position.X, Npc.Target.Position.Y) > distance + 30) { long now = RandomUtilities.GetCurrentMilliseconds(); if (now > LastMoveUts + 1000) { LastMoveUts = now; ((NpcAi) Npc.Ai).MoveController.MoveTo(Npc.Target.Position, distance); } return; } ((NpcAi) Npc.Ai).MoveController.Stop(); Global.SkillEngine.UseSkill(Npc, SelectedSkill); SelectedSkill = null; }
private async void ProcessArea(Creature creature, Skill skill, Targeting targeting, TargetingArea area, Projectile projectile = null) { try { bool isProjectileSkill = skill.Type == SkillType.Projectile || skill.Type == SkillType.Userslug; int skillId = creature.Attack.Args.SkillId; if (isProjectileSkill) skillId += 20; if (targeting.Time > 0) await Task.Delay((int) (targeting.Time/skill.TimeRate)); int elapsed = targeting.Time; Player player = creature as Player; do { try { if (creature.LifeStats.IsDead()) return; if (area.DropItem != null) creature.Instance.AddDrop(new Item { Owner = player, ItemId = (int) area.DropItem, Count = 1, Position = Geom.ForwardPosition(creature.Position, 40), Instance = player.Instance, }); Point3D center = projectile != null ? projectile.Position.ToPoint3D() : Geom.GetNormal(creature.Position.Heading) .Multiple(area.OffsetDistance) .Add(creature.Position); int count = 0; List<Creature> targets = creature.Attack.Args.Targets.Count > 0 ? creature.Attack.Args.Targets : VisibleService.FindTargets(creature, center, projectile != null ? projectile.AttackDistance : area.MaxRadius, area.Type); foreach (Creature target in targets) { if (target != creature //Ignore checks for self-target && !isProjectileSkill && !creature.Attack.Args.IsItemSkill) { if (center.DistanceTo(target.Position) < area.MinRadius - 40) continue; if (center.DistanceTo(target.Position) > area.MaxRadius) continue; short diff = Geom.GetAngleDiff(creature.Attack.Args.StartPosition.Heading, Geom.GetHeading(center, target.Position)); //diff from 0 to 180 //area.RangeAngel from 0 to 360 if (diff * 2 > (creature.Attack.Args.IsTargetAttack ? 90 : Math.Abs(area.RangeAngle) + 10)) continue; } if (skill.TotalAtk > 0) { int damage = SeUtils.CalculateDamage(creature, target, skill.TotalAtk*area.Effect.Atk); AttackResult result = new AttackResult { AttackType = AttackType.Normal, AttackUid = creature.Attack.UID, Damage = damage, Target = target, }; result.AngleDif = Geom.GetAngleDiff(creature.Attack.Args.StartPosition.Heading, result.Target.Position.Heading); SeUtils.UpdateAttackResult(creature, result); if (result.AttackType == AttackType.Block) VisibleService.Send(target, new SpAttackShowBlock(target, skillId)); VisibleService.Send(target, new SpAttackResult(creature, skillId, result)); AiLogic.OnAttack(creature, target); AiLogic.OnAttacked(target, creature, result.Damage); if (target is Player && ((Player)target).Duel != null && player != null && ((Player)target).Duel.Equals(player.Duel) && target.LifeStats.GetHpDiffResult(damage) < 1) DuelService.FinishDuel(player); else CreatureLogic.HpChanged(target, target.LifeStats.MinusHp(result.Damage)); } if (area.Effect.HpDiff > 0) { AttackResult result = new AttackResult {HpDiff = area.Effect.HpDiff, Target = target}; PassivityProcessor.OnHeal(player, result); if(target is Player) PassivityProcessor.OnHealed((Player)target, result); CreatureLogic.HpChanged(target, target.LifeStats.PlusHp(result.HpDiff), creature); } if (area.Effect.MpDiff > 0) CreatureLogic.MpChanged(target, target.LifeStats.PlusMp(area.Effect.MpDiff), creature); if (area.Effect.AbnormalityOnCommon != null) for (int i = 0; i < area.Effect.AbnormalityOnCommon.Count; i++) AbnormalityProcessor.AddAbnormality(target, area.Effect.AbnormalityOnCommon[i], creature); if (player != null) { DuelService.ProcessDamage(player); //MP regen on combo skill if (skill.Id/10000 == 1 && player.GameStats.CombatMpRegen > 0) { CreatureLogic.MpChanged(player, player.LifeStats.PlusMp( player.MaxMp*player.GameStats.CombatMpRegen/200)); } } if (++count == area.MaxCount) break; } } catch (Exception ex) { Logger.WriteLine(LogState.Exception,"SkillEngine: ProcessAreaExc: " + ex); } if (targeting.Interval > 0) { await Task.Delay((int) (targeting.Interval/skill.TimeRate)); elapsed += targeting.Interval; } } while (targeting.Interval > 0 && elapsed < targeting.Until); } catch (Exception ex) { Logger.WriteLine(LogState.Exception,"SkillEngine: ProcessArea: " + ex); } }
private void ProcessTargets(Creature creature, Skill skill) { if (skill.TargetingList == null) return; for (int i = 0; i < skill.TargetingList.Count; i++) { if (skill.TargetingList[i].ProjectileSkillList != null && skill.TargetingList[i].ProjectileSkillList.Count > 0) { for (int k = 0; k < skill.TargetingList[i].ProjectileSkillList.Count; k++) { ProjectileSkill projectileSkill = skill.TargetingList[i].ProjectileSkillList[k]; try { new DelayedAction(() => ProcessProjectileSkillList(creature, projectileSkill), (int) (skill.TargetingList[i].Time/skill.TimeRate)); } catch (Exception ex) { Logger.WriteLine(LogState.Exception,"SkillEngine: ProcessTargets: " + ex); } } } if (skill.TargetingList[i].AreaList == null) continue; for (int j = 0; j < skill.TargetingList[i].AreaList.Count; j++) { if (skill.TargetingList[i].AreaList[j].Type == TargetingAreaType.PvP) continue; ProcessArea(creature, skill, skill.TargetingList[i], skill.TargetingList[i].AreaList[j]); } } }
private void ProcessProjectileTargets(Creature creature, Skill skill, Projectile projectile) { if (skill.ProjectileData.With(data => data.TargetingList) == null) return; skill.ProjectileData.TargetingList.Map(targeting => targeting.AreaList.Map(area => { if (area.Type == TargetingAreaType.PvP) return; ProcessArea(creature, skill, targeting, area, projectile); })); }
private async void ProcessMove(Creature creature, Skill skill) { bool checkIntersections = creature is Player && skill.Type != SkillType.Evade; AnimSeq anim = null; skill.Actions.Map(action => action.StageList.Map(stage => stage.AnimationList.Map(animation => { if (anim != null) return; if (animation.With(a => a.Animation).With(a => a.Distance).Get(d => d[6], 0f) < 1) return; anim = animation; }))); if (anim == null) return; short heading = (short)(creature.Position.Heading + anim.Animation.Dir); int stepTime = (int)((anim.Animation.Duraction / skill.TimeRate) / 7); float movedDistance = 0f; for (int l = 0; l < 7; l++) { await Task.Delay(stepTime); if (creature.LifeStats.IsDead()) return; float stepDistance = anim.Animation.Distance[l] * anim.RootMotionXYRate - movedDistance; if (stepDistance <= 0.0) continue; movedDistance += stepDistance; Point3D moved = Geom.GetNormal(heading).Multiple(stepDistance); if (checkIntersections && anim.Animation.Dir != 32768) { float koef = SeUtils.CheckIntersections(creature, heading, moved, stepDistance); moved.X *= koef; moved.Y *= koef; } creature.Position.X += moved.X; creature.Position.Y += moved.Y; } }
public virtual void OnUseSkill(Skill skill) { }
private void ProcessSkill(Creature creature, List<UseSkillArgs> argsList, Skill skill) { creature.Attack = new Attack(creature, argsList[0], () => GlobalLogic.AttackStageEnd(creature), () => GlobalLogic.AttackFinished(creature)); VisibleService.Send(creature, new SpAttack(creature, creature.Attack)); VisibleService.Send(creature, new SpAttackDestination(creature, creature.Attack)); ProcessStages(creature, skill); ProcessMove(creature, skill); AiLogic.OnUseSkill(creature, skill); ProcessTargets(creature, skill); }
private void ProcessSkill(Creature creature, UseSkillArgs args, Skill skill, Projectile projectile = null) { bool isProjectileSkill = skill.Type == SkillType.Projectile || skill.Type == SkillType.Userslug; if (!isProjectileSkill) { if (skill.ChargingStageList == null || skill.ChargingStageList.ChargeStageList.Count == 0) { if (skill.Precondition.Cost.Hp > 0) CreatureLogic.HpChanged(creature, creature.LifeStats.MinusHp(skill.Precondition.Cost.Hp)); if (skill.Precondition.Cost.Mp > 0) CreatureLogic.MpChanged(creature, creature.LifeStats.MinusMp(skill.Precondition.Cost.Mp)); } if (!args.IsDelaySkill || args.IsDelayStart) { if (args.TargetPosition.IsNull()) { double angle = args.StartPosition.Heading*Math.PI/32768; args.StartPosition.CopyTo(args.TargetPosition); args.TargetPosition.X += 100*(float) Math.Cos(angle); args.TargetPosition.Y += 100*(float) Math.Sin(angle); } // ReSharper disable ImplicitlyCapturedClosure creature.Attack = new Attack(creature, args, () => GlobalLogic.AttackStageEnd(creature), () => GlobalLogic.AttackFinished(creature)); // ReSharper restore ImplicitlyCapturedClosure VisibleService.Send(creature, new SpAttack(creature, creature.Attack)); VisibleService.Send(creature, new SpAttackDestination(creature, creature.Attack)); if (!args.IsDelaySkill) ProcessStages(creature, skill); else { Player player = creature as Player; if (player != null && skill.BaseId == 20100 && (player.PlayerData.Class == PlayerClass.Berserker || player.PlayerData.Class == PlayerClass.Lancer)) { player.EffectsImpact.ResetChanges(player); //Set IsBlockFrontAttacks } } ProcessMove(creature, skill); } } else { creature.Attack.Args.IsTargetAttack = args.IsTargetAttack; creature.Attack.Args.Targets = args.Targets; ProcessProjectileTargets(creature, skill, projectile); } AiLogic.OnUseSkill(creature, skill); if (skill.ChargingStageList != null) { if (args.IsDelayStart) { int uid = creature.Attack.UID; ThreadPool.QueueUserWorkItem( o => { Thread.Sleep(750); for (int i = 1; i < skill.ChargingStageList.ChargeStageList.Count; i++) { if (creature.Attack.UID != uid) return; creature.Attack.NextStage(); if (i != 3) Thread.Sleep(750); } }); } } else ProcessTargets(creature, skill); }
private bool CheckRequirements(Player player, Skill skill) { if (player.PlayerMount != 0 //|| !player.Skills.Contains(skill.BaseId) || player.LifeStats.Hp < skill.Precondition.Cost.Hp ) { SystemMessages.YouCannotUseThatSkillAtTheMoment.Send(player.Connection); return false; } if (skill.Precondition.CoolTime > 0) { long cooldownUtc = 0; if (!player.SkillCooldowns.ContainsKey(skill.Id)) player.SkillCooldowns.Add(skill.Id, 0); else cooldownUtc = player.SkillCooldowns[skill.Id]; long now = RandomUtilities.GetCurrentMilliseconds(); if (cooldownUtc > now) return false; player.SkillCooldowns[skill.Id] = now + skill.Precondition.CoolTime; } if (player.LifeStats.Mp < skill.Precondition.Cost.Mp) { SystemMessages.NotEnoughMp.Send(player.Connection); return false; } return true; }
public void UseSkill(Npc npc, Skill skill) { if (npc.Target == null) return; npc.Position.Heading = Geom.GetHeading(npc.Position, npc.Target.Position); ProcessSkill(npc, new UseSkillArgs { IsTargetAttack = false, SkillId = skill.Id + 0x40000000 + (npc.NpcTemplate.HuntingZoneId << 16), StartPosition = npc.Position.Clone(), }, skill); }
public static void OnUseSkill(Creature creature, Skill skill) { creature.Ai.OnUseSkill(skill); }
public Projectile(Creature.Creature owner, ProjectileSkill projectileSkill) { Player.Player player = owner as Player.Player; Npc.Npc npc = owner as Npc.Npc; Parent = owner; if (npc != null) { TargetPosition = new WorldPosition(); npc.Target.Position.CopyTo(TargetPosition); } else TargetPosition = owner.Attack.Args.TargetPosition; Position = new WorldPosition { Heading = owner.Position.Heading, X = owner.Position.X, Y = owner.Position.Y, Z = owner.Position.Z + projectileSkill.DetachHeight, }; double angle = Position.Heading * Math.PI / 32768; Position.X += projectileSkill.DetachDistance * (float)Math.Cos(angle); Position.Y += projectileSkill.DetachDistance * (float)Math.Sin(angle); Instance = owner.Instance; ProjectileSkill = projectileSkill; GameStats = new CreatureBaseStats {HpBase = 1}; if (player != null) { Skill = Data.Skills[0][player.TemplateId][ProjectileSkill.Id]; SkillId = Skill.Id; } else if (npc != null) { Skill = Data.Skills[npc.NpcTemplate.HuntingZoneId][npc.NpcTemplate.Id][ProjectileSkill.Id]; SkillId = Skill.Id + 0x40000000 + (npc.NpcTemplate.HuntingZoneId << 16); } Lifetime = Skill.ProjectileData.LifeTime != 0 ? Skill.ProjectileData.LifeTime : 1000; if (projectileSkill.FlyingDistance <= 0f) TargetPosition = null; else if (Skill != null) { if (TargetPosition.IsNull()) { TargetPosition = Position.Clone(); TargetPosition.X += projectileSkill.FlyingDistance * (float)Math.Cos(angle); TargetPosition.Y += projectileSkill.FlyingDistance * (float)Math.Sin(angle); } Speed = (int) (projectileSkill.FlyingDistance*1000/Lifetime); } if (Skill != null) { if (Skill.TargetingList != null) { for (int i = 0; i < Skill.TargetingList.Count; i++) { if (Skill.TargetingList[i].AreaList == null) continue; for (int j = 0; j < Skill.TargetingList[i].AreaList.Count; j++) { if (Skill.TargetingList[i].AreaList[j].MaxRadius > AttackDistance) { AttackDistance = Skill.TargetingList[i].AreaList[j].MaxRadius; return; } } } } } }
private void ProcessStages(Creature creature, Skill skill) { int time = 0; List<int> durations = new List<int>(); skill.Actions.Map(action => action.StageList.Map(stage => { stage.AnimationList.Map(anim => { time += (int) (Math.Max(anim.Duration, anim.Animation.Get(a => a.Duraction, 0))/skill.TimeRate); }); durations.Add(time); })); for (int i = 0; i < durations.Count - 1; i++) new DelayedAction(creature.Attack.NextStage, durations[i]); new DelayedAction(creature.Attack.Finish, time); }
private void PatchArunsVitae(Skill skill) { skill.TargetingList.Last().AreaList.Last().DropItem = 8007 + skill.Level; }