/// <summary> /// Handles knock back/stun/death. /// </summary> protected virtual void HandleKnockBack(Creature attacker, Creature target, TargetAction tAction) { if (target.IsDead) { tAction.Set(TargetOptions.FinishingKnockDown); attacker.Shove(target, KnockbackDistance); } else { // If knocked down, instant recovery, // if repeat hit, knock down, // otherwise potential knock back. if (target.IsKnockedDown) { tAction.Stun = 0; } else if (target.IsUnstable) { tAction.Set(TargetOptions.KnockDown); } else { target.Stability -= StabilityReduction; if (target.IsUnstable) { tAction.Set(TargetOptions.KnockBack); attacker.Shove(target, KnockbackDistance); } } } }
/// <summary> /// Handles knock back/stun/death. /// </summary> protected override void HandleKnockBack(Creature attacker, Creature target, TargetAction tAction, bool overcharge) { attacker.Shove(target, KnockbackDistance); if (target.IsDead) tAction.Set(TargetOptions.FinishingKnockDown); else tAction.Set(TargetOptions.KnockDown); }
/// <summary> /// Handles explosion. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="fireballProp"></param> private void Impact(Creature attacker, Skill skill, Prop fireballProp) { var regionId = attacker.RegionId; var propPos = fireballProp.GetPosition(); var targetLocation = new Location(regionId, propPos); var targets = attacker.GetTargetableCreaturesAround(propPos, ExplosionRadius); var aAction = new AttackerAction(CombatActionType.SpecialHit, attacker, targetLocation.ToLocationId(), skill.Info.Id); aAction.Set(AttackerOptions.UseEffect); aAction.PropId = fireballProp.EntityId; var cap = new CombatActionPack(attacker, skill.Info.Id, aAction); foreach (var target in targets) { target.StopMove(); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result | TargetOptions.KnockDown); tAction.Stun = TargetStun; tAction.Delay = 200; tAction.EffectFlags = EffectFlags.SpecialRangeHit; cap.Add(tAction); // Damage var damage = this.GetDamage(attacker, skill); // Elements damage *= this.GetElementalDamageMultiplier(attacker, target); // Critical Hit var critChance = attacker.GetTotalCritChance(target.Protection, true); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Reduce damage SkillHelper.HandleMagicDefenseProtection(target, ref damage); SkillHelper.HandleConditions(attacker, target, ref damage); ManaShield.Handle(target, ref damage, tAction); ManaDeflector.Handle(attacker, target, ref damage, tAction); // Deal damage if (damage > 0) target.TakeDamage(tAction.Damage = damage, attacker); target.Aggro(attacker); // Knockback target.Stability = Creature.MinStability; target.GetShoved(fireballProp, KnockbackDistance); if (target.IsDead) tAction.Set(TargetOptions.FinishingKnockDown); this.Train(skill, tAction); } cap.Handle(); }
/// <summary> /// Handles knock back/stun/death. /// </summary> protected override void HandleKnockBack(Creature attacker, Creature target, TargetAction tAction) { attacker.Shove(target, KnockbackDistance); if (target.IsDead) { tAction.Set(TargetOptions.FinishingKnockDown); } else { tAction.Set(TargetOptions.KnockDown); } }
/// <summary> /// Checks if attacker has Critical Hit and applies crit bonus /// by chance. Also sets the target action's critical option if a /// crit happens. /// </summary> /// <param name="attacker"></param> /// <param name="critChance"></param> /// <param name="damage"></param> /// <param name="tAction"></param> public static void Handle(Creature attacker, float critChance, ref float damage, TargetAction tAction, bool bypassNoviceCheck = false) { // Check if attacker actually has critical hit var critSkill = attacker.Skills.Get(SkillId.CriticalHit); if (critSkill == null || (critSkill.Info.Rank == SkillRank.Novice && !bypassNoviceCheck)) { return; } // Cap crit chance at 30% critChance = Math2.Clamp(0, 30, critChance); // Cancel if crit doesn't happen if (RandomProvider.Get().NextDouble() * 100 >= critChance) { return; } // Add crit bonus var bonus = critSkill.RankData.Var1 / 100f; damage = damage + (damage * bonus); // Set target option tAction.Set(TargetOptions.Critical); }
/// <summary> /// Bolt specific use code. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="target"></param> protected override void UseSkillOnTarget(Creature attacker, Skill skill, Creature mainTarget) { // Create actions var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, skill.Info.Id, mainTarget.EntityId); aAction.Set(AttackerOptions.Result); var cap = new CombatActionPack(attacker, skill.Info.Id, aAction); var targets = new List<Creature>(); targets.Add(mainTarget); targets.AddRange(mainTarget.Region.GetCreaturesInRange(mainTarget.GetPosition(), SplashRange).Where(a => a != mainTarget && attacker.CanTarget(a) && attacker.CanAttack(a))); // Damage var damage = this.GetDamage(attacker, skill); var max = Math.Min(targets.Count, skill.Stacks); for (int i = 0; i < max; ++i) { var target = targets[i]; var targetDamage = damage; target.StopMove(); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result); tAction.Stun = TargetStun; // Full damage for the first target, -10% for every subsequent one. targetDamage -= (targetDamage * 0.1f) * i; // Reduce damage var maxDamage = damage; //Damage without Defense and Protection // Reduce damage Defense.Handle(aAction, tAction); SkillHelper.HandleMagicDefenseProtection(target, ref targetDamage); ManaShield.Handle(target, ref targetDamage, tAction, maxDamage, true); // Deal damage if (targetDamage > 0) target.TakeDamage(tAction.Damage = targetDamage, attacker); if (target == mainTarget) target.Aggro(attacker); // Death/Knockback this.HandleKnockBack(attacker, target, tAction); cap.Add(tAction); } // Override stun set by defense aAction.Stun = AttackerStun; Send.Effect(attacker, Effect.UseMagic, EffectSkillName); Send.SkillUseStun(attacker, skill.Info.Id, aAction.Stun, 1); this.BeforeHandlingPack(attacker, skill); cap.Handle(); }
/// <summary> /// Bolt specific use code. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="target"></param> protected override void UseSkillOnTarget(Creature attacker, Skill skill, Creature target) { attacker.StopMove(); target.StopMove(); // Create actions var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, target.EntityId); aAction.Set(AttackerOptions.Result); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result); tAction.Stun = TargetStun; var cap = new CombatActionPack(attacker, skill.Info.Id, aAction, tAction); // Damage var damage = this.GetDamage(attacker, skill); // Elements damage *= this.GetElementalDamageMultiplier(attacker, target); // Reduce damage SkillHelper.HandleMagicDefenseProtection(target, ref damage); ManaShield.Handle(target, ref damage, tAction); ManaDeflector.Handle(attacker, target, ref damage, tAction); // Deal damage if (damage > 0) target.TakeDamage(tAction.Damage = damage, attacker); target.Aggro(attacker); // Knock down on deadly if (target.Conditions.Has(ConditionsA.Deadly)) { tAction.Set(TargetOptions.KnockDown); tAction.Stun = TargetStun; } // Death/Knockback attacker.Shove(target, KnockbackDistance); if (target.IsDead) tAction.Set(TargetOptions.FinishingKnockDown); else tAction.Set(TargetOptions.KnockDown); // Override stun set by defense aAction.Stun = AttackerStun; Send.Effect(attacker, Effect.UseMagic, EffectSkillName); Send.SkillUseStun(attacker, skill.Info.Id, aAction.Stun, 1); skill.Stacks = 0; cap.Handle(); }
/// <summary> /// Bolt specific use code. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="target"></param> protected virtual void UseSkillOnTarget(Creature attacker, Skill skill, Creature target) { target.StopMove(); // Create actions var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, skill.Info.Id, target.EntityId); aAction.Set(AttackerOptions.Result); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result); tAction.Stun = TargetStun; var cap = new CombatActionPack(attacker, skill.Info.Id, aAction, tAction); // Damage var damage = this.GetDamage(attacker, skill); // Reduce damage if (this.Defendable) { Defense.Handle(aAction, tAction, ref damage); } SkillHelper.HandleMagicDefenseProtection(target, ref damage); ManaShield.Handle(target, ref damage, tAction); // Deal damage if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); } target.Aggro(attacker); // Death/Knockback this.HandleKnockBack(attacker, target, tAction); // Override stun set by defense aAction.Stun = AttackerStun; Send.Effect(attacker, Effect.UseMagic, EffectSkillName); Send.SkillUseStun(attacker, skill.Info.Id, aAction.Stun, 1); this.BeforeHandlingPack(attacker, skill); cap.Handle(); }
/// <summary> /// Checks if attacker has Critical Hit and applies crit bonus /// by chance. Also sets the target action's critical option if a /// crit happens. /// </summary> /// <param name="attacker"></param> /// <param name="critChance"></param> /// <param name="damage"></param> /// <param name="tAction"></param> public static void Handle(Creature attacker, float critChance, ref float damage, TargetAction tAction, bool bypassNoviceCheck = false) { // Check if attacker actually has critical hit var critSkill = attacker.Skills.Get(SkillId.CriticalHit); if (critSkill == null || (critSkill.Info.Rank == SkillRank.Novice && !bypassNoviceCheck)) return; // Cancel if crit doesn't happen if (RandomProvider.Get().Next(100) >= critChance) return; // Add crit bonus var bonus = critSkill.RankData.Var1 / 100f; damage = damage + (damage * bonus); // Set target option tAction.Set(TargetOptions.Critical); }
/// <summary> /// Uses the skill. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetEntityId"></param> /// <returns></returns> public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { // Get target var target = attacker.Region.GetCreature(targetEntityId); if (target == null) return CombatSkillResult.InvalidTarget; // Actions var cap = new CombatActionPack(attacker, skill.Info.Id); var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, skill.Info.Id, targetEntityId); aAction.Set(AttackerOptions.Result); aAction.Stun = AttackerStun; cap.Add(aAction); // Hit by chance var chance = attacker.AimMeter.GetAimChance(target); var rnd = RandomProvider.Get(); if (rnd.NextDouble() * 100 < chance) { target.StopMove(); aAction.Set(AttackerOptions.KnockBackHit2); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result | TargetOptions.CleanHit); tAction.Stun = TargetStun; cap.Add(tAction); // TODO: Splash damage // Damage var damage = this.GetDamage(attacker, skill); // More damage with fire arrow if (attacker.Temp.FireArrow) damage *= FireBonus; // Critical Hit var critChance = attacker.GetRightCritChance(target.Protection); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Defense Defense.Handle(aAction, tAction, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Deal with it! if (damage > 0) target.TakeDamage(tAction.Damage = damage, attacker); // TODO: We have to calculate knockback distance right // TODO: Target with Defense and shield shouldn't be knocked back attacker.Shove(target, KnockBackDistance); // Aggro target.Aggro(attacker); tAction.Set(TargetOptions.KnockDownFinish); if (target.IsDead) { aAction.Set(AttackerOptions.KnockBackHit1); tAction.Set(TargetOptions.Finished); } } else { aAction.Set(AttackerOptions.Missed); } // Reduce arrows if (attacker.Magazine != null && !ChannelServer.Instance.Conf.World.InfiniteArrows) attacker.Inventory.Decrement(attacker.Magazine); // Disable fire arrow effect if (attacker.Temp.FireArrow) Send.Effect(attacker, Effect.FireArrow, false); // "Cancels" the skill // 800 = old load time? == aAction.Stun? Varies? Doesn't seem to be a stun. Send.SkillUse(attacker, skill.Info.Id, 800, 1); cap.Handle(); return CombatSkillResult.Okay; }
/// <summary> /// Uses WM, attacking targets. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetAreaId"></param> /// <param name="unkInt1"></param> /// <param name="unkInt2"></param> public void Use(Creature attacker, Skill skill, long targetAreaId, int unkInt1, int unkInt2) { var range = this.GetRange(attacker, skill); var targets = attacker.GetTargetableCreaturesInRange(range, true); // Check targets if (targets.Count == 0) { Send.Notice(attacker, Localization.Get("There isn't a target nearby to use that on.")); Send.SkillUseSilentCancel(attacker); return; } // Create actions var cap = new CombatActionPack(attacker, skill.Info.Id); var aAction = new AttackerAction(CombatActionType.SpecialHit, attacker, skill.Info.Id, targetAreaId); aAction.Set(AttackerOptions.Result); cap.Add(aAction); var survived = new List<Creature>(); foreach (var target in targets) { target.StopMove(); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Delay = 300; // Usually 300, sometimes 350? // Calculate damage var damage = attacker.GetRndTotalDamage(); damage *= skill.RankData.Var1 / 100f; // Handle skills and reductions CriticalHit.Handle(attacker, attacker.GetTotalCritChance(0), ref damage, tAction); SkillHelper.HandleDefenseProtection(target, ref damage); Defense.Handle(aAction, tAction, ref damage); ManaShield.Handle(target, ref damage, tAction); // Clean Hit if not defended nor critical if (tAction.SkillId != SkillId.Defense && !tAction.Has(TargetOptions.Critical)) tAction.Set(TargetOptions.CleanHit); // Take damage if any is left if (damage > 0) target.TakeDamage(tAction.Damage = damage, attacker); // Finish if dead, knock down if not defended if (target.IsDead) tAction.Set(TargetOptions.KnockDownFinish); else if (tAction.SkillId != SkillId.Defense) tAction.Set(TargetOptions.KnockDown); // Anger Management if (!target.IsDead) survived.Add(target); // Stun & knock back aAction.Stun = CombatMastery.GetAttackerStun(attacker.AverageKnockCount, attacker.AverageAttackSpeed, true); if (tAction.SkillId != SkillId.Defense) { tAction.Stun = CombatMastery.GetTargetStun(attacker.AverageKnockCount, attacker.AverageAttackSpeed, true); target.Stability = Creature.MinStability; attacker.Shove(target, KnockbackDistance); } // Add action cap.Add(tAction); } // Only select a random aggro if there is no aggro yet, // WM only aggroes one target at a time. if (survived.Count != 0 && attacker.Region.CountAggro(attacker) < 1) { var rnd = RandomProvider.Get(); var aggroTarget = survived.Random(); aggroTarget.Aggro(attacker); } // Reduce life in old combat system if (!AuraData.FeaturesDb.IsEnabled("CombatSystemRenewal")) { var amount = (attacker.LifeMax < 10 ? 2 : attacker.LifeMax / 10); attacker.ModifyLife(-amount); // TODO: Invincibility } // Spin it~ Send.UseMotion(attacker, 8, 4); cap.Handle(); Send.SkillUse(attacker, skill.Info.Id, targetAreaId, unkInt1, unkInt2); skill.Stacks = 0; }
/// <summary> /// Handles initial bolts. /// </summary> private void Bolts(Creature attacker, Skill skill, int stacks, Creature mainTarget, List <Creature> targets, int thunderboltDelay) { // Combat action var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, mainTarget.EntityId); aAction.Set(AttackerOptions.Result); aAction.Stun = (short)thunderboltDelay; var cap = new CombatActionPack(attacker, skill.Info.Id, aAction); foreach (var target in targets) { target.StopMove(); target.Aggro(attacker); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result); tAction.Stun = (short)(thunderboltDelay + 1000); var damage = this.GetBoltDamage(attacker, skill, stacks); // Critical Hit var critChance = attacker.GetTotalCritChance(target.Protection, true); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Reduce damage Defense.Handle(aAction, tAction, ref damage); SkillHelper.HandleMagicDefenseProtection(target, ref damage); SkillHelper.HandleConditions(attacker, target, ref damage); ManaShield.Handle(target, ref damage, tAction); // Mana Deflector var mdResult = ManaDeflector.Handle(attacker, target, ref damage, tAction); var delayReduction = mdResult.DelayReduction; var pinged = mdResult.Pinged; // Deal damage if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); } // Reduce stun, based on ping if (pinged && delayReduction > 0) { tAction.Stun = (short)Math.Max(0, tAction.Stun - (tAction.Stun / 100 * delayReduction)); } // Death/Knockback if (target.IsDead) { tAction.Set(TargetOptions.FinishingKnockDown); } else { // If knocked down, instant recovery, // if repeat hit, knock down, // otherwise potential knock back. if (target.IsKnockedDown) { tAction.Stun = 0; } else if (target.Stability < MinStability) { tAction.Set(TargetOptions.KnockDown); } else { var stabilityReduction = StabilityReduction; if (delayReduction > 0) { stabilityReduction = (short)Math.Max(0, stabilityReduction - (stabilityReduction / 100 * delayReduction)); } target.Stability -= stabilityReduction; if (target.IsUnstable) { tAction.Set(TargetOptions.KnockBack); } } } cap.Add(tAction); this.Train(skill, tAction); } cap.Handle(); }
/// <summary> /// Uses the skill. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetEntityId"></param> /// <returns></returns> public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { // Get target var target = attacker.Region.GetCreature(targetEntityId); if (target == null) return CombatSkillResult.InvalidTarget; // "Cancels" the skill // 800 = old load time? == aAction.Stun? Varies? Doesn't seem to be a stun. Send.SkillUse(attacker, skill.Info.Id, AttackerStun, 1); var chance = attacker.AimMeter.GetAimChance(target); var rnd = RandomProvider.Get().NextDouble() * 100; var successfulHit = (rnd < chance); // Actions var cap = new CombatActionPack(attacker, skill.Info.Id); var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, targetEntityId); aAction.Set(AttackerOptions.Result); aAction.Stun = AttackerStun; cap.Add(aAction); // Target action if hit if (successfulHit) { target.StopMove(); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result); tAction.Stun = TargetStun; cap.Add(tAction); // Damage // Formula unofficial, but it kinda matches what you would // expect from the skill, and what players believed the damage // to be, back in G2. // bonus = (100 - (6 - stacks) * 5 + rank, +var2 on last shot // I'm using rank instead of Var1, which goes from 1-15 in // AR2, so AR1 gets a little bonus as well, as AR1's Var1 and // 2 are 0. // With this formula, the bonus range (1st shot rF vs 5th shot // r1) is 76~110% for AR1, and 76~140% for AR2. var bonus = 100f - (6 - skill.Stacks) * 5f + (byte)skill.Info.Rank; if (skill.Stacks == 1) bonus += skill.RankData.Var2; var damage = attacker.GetRndRangedDamage() * (bonus / 100f); // Elementals damage *= attacker.CalculateElementalDamageMultiplier(target); // More damage with fire arrow if (attacker.Temp.FireArrow) damage *= FireBonus; // Critical Hit var critChance = attacker.GetRightCritChance(target.Protection); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Defense Defense.Handle(aAction, tAction, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Natural Shield var delayReduction = NaturalShield.Handle(attacker, target, ref damage, tAction); // Deal with it! if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); SkillHelper.HandleInjury(attacker, target, damage); } // Aggro target.Aggro(attacker); // Knock down on deadly if (target.Conditions.Has(ConditionsA.Deadly)) tAction.Set(TargetOptions.KnockDown); // Death/Knockback if (target.IsDead) { tAction.Set(TargetOptions.FinishingKnockDown); } else { // Insta-recover in knock down if (target.IsKnockedDown) { tAction.Stun = 0; } // Knock down if hit repeatedly else if (target.Stability < 30) { tAction.Set(TargetOptions.KnockDown); } // Normal stability reduction else { var stabilityReduction = StabilityReduction; // Reduce reduction, based on ping // According to the Wiki, "the Knockdown Gauge // [does not] build up", but it's still possible // to knock back with repeated hits. The stability // reduction is probably reduced, just like stun. if (delayReduction > 0) stabilityReduction = (short)Math.Max(0, stabilityReduction - (stabilityReduction / 100 * delayReduction)); target.Stability -= stabilityReduction; if (target.IsUnstable) { tAction.Set(TargetOptions.KnockBack); } } } // Knock Back if (tAction.IsKnockBack) attacker.Shove(target, KnockBackDistance); // Reduce stun, based on ping if (delayReduction > 0) tAction.Stun = (short)Math.Max(0, tAction.Stun - (tAction.Stun / 100 * delayReduction)); } // Skill training if (skill.Info.Rank == SkillRank.RF) skill.Train(1); // Try attacking with Arrow Revolver. // Reduce arrows if (attacker.Magazine != null && !ChannelServer.Instance.Conf.World.InfiniteArrows && !attacker.Magazine.HasTag("/unlimited_arrow/")) attacker.Inventory.Decrement(attacker.Magazine); // Reduce stack skill.Stacks--; // Handle cap.Handle(); // Disable fire arrow effect if (attacker.Temp.FireArrow) Send.Effect(attacker, Effect.FireArrow, false); return CombatSkillResult.Okay; }
/// <summary> /// Uses WM, attacking targets. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="packet"></param> public void Use(Creature attacker, Skill skill, Packet packet) { var targetAreaId = packet.GetLong(); var unkInt1 = packet.GetInt(); var unkInt2 = packet.GetInt(); var range = this.GetRange(attacker, skill); var targets = attacker.GetTargetableCreaturesInRange(range); // Check targets if (targets.Count == 0) { Send.Notice(attacker, Localization.Get("There isn't a target nearby to use that on.")); Send.SkillUseSilentCancel(attacker); return; } // Create actions var cap = new CombatActionPack(attacker, skill.Info.Id); var aAction = new AttackerAction(CombatActionType.SpecialHit, attacker, skill.Info.Id, targetAreaId); aAction.Set(AttackerOptions.Result); cap.Add(aAction); var survived = new List <Creature>(); foreach (var target in targets) { target.StopMove(); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Delay = 300; // Usually 300, sometimes 350? // Calculate damage and crit var damage = attacker.GetRndTotalDamage(); var critChance = attacker.CriticalBase; damage *= skill.RankData.Var1 / 100f; // Handle skills and reductions CriticalHit.Handle(attacker, critChance, ref damage, tAction); SkillHelper.HandleDefenseProtection(target, ref damage); Defense.Handle(aAction, tAction, ref damage); ManaShield.Handle(target, ref damage, tAction); // Clean Hit if not defended nor critical if (!tAction.Is(CombatActionType.Defended) && !tAction.Has(TargetOptions.Critical)) { tAction.Set(TargetOptions.CleanHit); } // Take damage if any is left if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); } // Finish if dead, knock down if not defended if (target.IsDead) { tAction.Set(TargetOptions.KnockDownFinish); } else if (!tAction.Is(CombatActionType.Defended)) { tAction.Set(TargetOptions.KnockDown); } // Anger Management if (!target.IsDead) { survived.Add(target); } // Stun & knock back aAction.Stun = CombatMastery.GetAttackerStun(attacker.AverageKnockCount, attacker.AverageAttackSpeed, true); if (!tAction.Is(CombatActionType.Defended)) { tAction.Stun = CombatMastery.GetTargetStun(attacker.AverageKnockCount, attacker.AverageAttackSpeed, true); target.Stability = Creature.MinStability; attacker.Shove(target, KnockbackDistance); } // Add action cap.Add(tAction); } // Only select a random aggro if there is no aggro yet, // WM only aggroes one target at a time. if (survived.Count != 0 && attacker.Region.CountAggro(attacker) < 1) { var rnd = RandomProvider.Get(); var aggroTarget = survived.Random(); aggroTarget.Aggro(attacker); } // Spin it~ Send.UseMotion(attacker, 8, 4); cap.Handle(); Send.SkillUse(attacker, skill.Info.Id, targetAreaId, unkInt1, unkInt2); skill.Stacks = 0; }
/// <summary> /// Handles using the skill. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetAreaId"></param> /// <param name="unkInt1"></param> /// <param name="unkInt2"></param> public void Use(Creature attacker, Skill skill, long targetAreaId, int unkInt1, int unkInt2) { var range = this.GetRange(attacker, skill); var targets = attacker.GetTargetableCreaturesInRange(range, true); var rnd = RandomProvider.Get(); // Create actions var cap = new CombatActionPack(attacker, skill.Info.Id); var aAction = new AttackerAction(CombatActionType.Attacker, attacker, skill.Info.Id, targetAreaId); aAction.Set(AttackerOptions.Result); aAction.Stun = AttackerStun; cap.Add(aAction); foreach (var target in targets) { // Check if hit var hitChance = this.GetHitChance(attacker, target, skill); if (rnd.Next(0, 100) > hitChance) { continue; } target.StopMove(); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result); tAction.Delay = 300; // Calculate damage var damage = this.GetDamage(attacker, skill); // Handle skills and reductions CriticalHit.Handle(attacker, attacker.GetTotalCritChance(0), ref damage, tAction); SkillHelper.HandleDefenseProtection(target, ref damage); ManaShield.Handle(target, ref damage, tAction); // Clean Hit if not critical if (!tAction.Has(TargetOptions.Critical)) { tAction.Set(TargetOptions.CleanHit); } // Take damage if any is left if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); } // Finish if dead, knock down if not defended if (target.IsDead) { tAction.Set(TargetOptions.KnockDownFinish); } else { tAction.Set(TargetOptions.KnockDown); } // Anger Management if (!target.IsDead) { target.Aggro(attacker); } // Stun & knock down tAction.Stun = CombatMastery.GetTargetStun(attacker.AverageKnockCount, attacker.AverageAttackSpeed, true); target.Stability = Creature.MinStability; // Add action cap.Add(tAction); } Send.UseMotion(attacker, 10, 1); cap.Handle(); Send.SkillUse(attacker, skill.Info.Id, targetAreaId, unkInt1, unkInt2); }
/// <summary> /// Bolt specific use code. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="target"></param> protected override void UseSkillOnTarget(Creature attacker, Skill skill, Creature mainTarget) { // Create actions var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, mainTarget.EntityId); aAction.Set(AttackerOptions.Result); var cap = new CombatActionPack(attacker, skill.Info.Id, aAction); var targets = new List <Creature>(); targets.Add(mainTarget); targets.AddRange(mainTarget.Region.GetCreaturesInRange(mainTarget.GetPosition(), SplashRange).Where(a => a != mainTarget && attacker.CanTarget(a))); // Damage var damage = this.GetDamage(attacker, skill); var max = Math.Min(targets.Count, skill.Stacks); for (int i = 0; i < max; ++i) { var target = targets[i]; var targetDamage = damage; target.StopMove(); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result); tAction.Stun = TargetStun; // Full damage for the first target, -10% for every subsequent one. targetDamage -= (targetDamage * 0.1f) * i; // Reduce damage Defense.Handle(aAction, tAction, ref targetDamage); SkillHelper.HandleMagicDefenseProtection(target, ref targetDamage); ManaShield.Handle(target, ref targetDamage, tAction); // Deal damage if (targetDamage > 0) { target.TakeDamage(tAction.Damage = targetDamage, attacker); } if (target == mainTarget) { target.Aggro(attacker); } // Death/Knockback var overcharge = (skill.Stacks > targets.Count); this.HandleKnockBack(attacker, target, tAction, overcharge); cap.Add(tAction); } // Override stun set by defense aAction.Stun = AttackerStun; Send.Effect(attacker, Effect.UseMagic, EffectSkillName); Send.SkillUseStun(attacker, skill.Info.Id, aAction.Stun, 1); this.BeforeHandlingPack(attacker, skill); cap.Handle(); }
/// <summary> /// Bolt specific use code. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="target"></param> protected override void UseSkillOnTarget(Creature attacker, Skill skill, Creature target) { attacker.StopMove(); target.StopMove(); // Create actions var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, target.EntityId); aAction.Set(AttackerOptions.Result); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result); tAction.Stun = TargetStun; var cap = new CombatActionPack(attacker, skill.Info.Id, aAction, tAction); // Damage var damage = this.GetDamage(attacker, skill); // Elements damage *= this.GetElementalDamageMultiplier(attacker, target); // Critical Hit var critChance = attacker.GetTotalCritChance(target.Protection, true); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Reduce damage Defense.Handle(aAction, tAction, ref damage); SkillHelper.HandleMagicDefenseProtection(target, ref damage); ManaShield.Handle(target, ref damage, tAction); // Mana Deflector var delayReduction = ManaDeflector.Handle(attacker, target, ref damage, tAction); // Deal damage if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); } target.Aggro(attacker); // Knock down on deadly if (target.Conditions.Has(ConditionsA.Deadly)) { tAction.Set(TargetOptions.KnockDown); tAction.Stun = TargetStun; } // Reduce stun, based on ping if (delayReduction > 0) { tAction.Stun = (short)Math.Max(0, tAction.Stun - (tAction.Stun / 100 * delayReduction)); } // Death/Knockback if (target.IsDead) { tAction.Set(TargetOptions.FinishingKnockDown); } else { // If knocked down, instant recovery, // if repeat hit, knock down, // otherwise potential knock back. if (target.IsKnockedDown) { tAction.Stun = 0; } else if (target.Stability < MinStability) { tAction.Set(TargetOptions.KnockDown); } else { var stabilityReduction = StabilityReduction; // Reduce reduction, based on ping // While the Wiki says that "the Knockdown Gauge [does not] // build up", tests show that it does. However, it's // reduced, assumedly based on the MD rank. if (delayReduction > 0) { stabilityReduction = (short)Math.Max(0, stabilityReduction - (stabilityReduction / 100 * delayReduction)); } target.Stability -= stabilityReduction; if (target.IsUnstable) { tAction.Set(TargetOptions.KnockBack); } } } if (tAction.IsKnockBack) { attacker.Shove(target, KnockbackDistance); } // Override stun set by defense aAction.Stun = AttackerStun; Send.Effect(attacker, Effect.UseMagic, EffectSkillName); Send.SkillUseStun(attacker, skill.Info.Id, aAction.Stun, 1); skill.Stacks--; // Update current weapon SkillHelper.UpdateWeapon(attacker, target, ProficiencyGainType.Melee, attacker.RightHand); cap.Handle(); }
/// <summary> /// Handles attack. /// </summary> /// <param name="attacker">The creature attacking.</param> /// <param name="skill">The skill being used.</param> /// <param name="targetEntityId">The entity id of the target.</param> /// <returns></returns> public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { if (attacker.IsStunned) return CombatSkillResult.Okay; var target = attacker.Region.GetCreature(targetEntityId); if (target == null) return CombatSkillResult.Okay; if (!attacker.GetPosition().InRange(target.GetPosition(), attacker.AttackRangeFor(target))) return CombatSkillResult.OutOfRange; attacker.StopMove(); var targetPosition = target.StopMove(); // Counter if (Counterattack.Handle(target, attacker)) return CombatSkillResult.Okay; var rightWeapon = attacker.Inventory.RightHand; var leftWeapon = attacker.Inventory.LeftHand; var magazine = attacker.Inventory.Magazine; var dualWield = (rightWeapon != null && leftWeapon != null && leftWeapon.Data.WeaponType != 0); var maxHits = (byte)(dualWield ? 2 : 1); int prevId = 0; for (byte i = 1; i <= maxHits; ++i) { var weapon = (i == 1 ? rightWeapon : leftWeapon); var weaponIsKnuckle = (weapon != null && weapon.Data.HasTag("/knuckle/")); var aAction = new AttackerAction(CombatActionType.Attacker, attacker, skill.Info.Id, targetEntityId); aAction.Set(AttackerOptions.Result); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result); var cap = new CombatActionPack(attacker, skill.Info.Id, aAction, tAction); cap.Hit = i; cap.Type = (dualWield ? CombatActionPackType.TwinSwordAttack : CombatActionPackType.NormalAttack); cap.PrevId = prevId; prevId = cap.Id; // Default attacker options aAction.Set(AttackerOptions.Result); if (dualWield) { aAction.Set(AttackerOptions.DualWield); aAction.WeaponParameterType = (byte)(i == 1 ? 2 : 1); } // Base damage var damage = (i == 1 ? attacker.GetRndRightHandDamage() : attacker.GetRndLeftHandDamage()); // Critical Hit var critChance = (i == 1 ? attacker.GetRightCritChance(target.Protection) : attacker.GetLeftCritChance(target.Protection)); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Defense Defense.Handle(aAction, tAction, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Deal with it! if (damage > 0) target.TakeDamage(tAction.Damage = damage, attacker); // Aggro target.Aggro(attacker); // Evaluate caused damage if (!target.IsDead) { if (tAction.SkillId != SkillId.Defense) { target.Stability -= this.GetStabilityReduction(attacker, weapon) / maxHits; // React normal for CombatMastery, knock down if // FH and not dual wield, don't knock at all if dual. if (skill.Info.Id != SkillId.FinalHit) { // Originally we thought you knock enemies back, unless it's a critical // hit, but apparently you knock *down* under normal circumstances. // More research to be done. if (target.IsUnstable && target.Is(RaceStands.KnockBackable)) //tAction.Set(tAction.Has(TargetOptions.Critical) ? TargetOptions.KnockDown : TargetOptions.KnockBack); tAction.Set(TargetOptions.KnockDown); } else if (!dualWield && !weaponIsKnuckle) { target.Stability = Creature.MinStability; tAction.Set(TargetOptions.KnockDown); } } } else { tAction.Set(TargetOptions.FinishingKnockDown); } // React to knock back if (tAction.IsKnockBack) { attacker.Shove(target, KnockBackDistance); aAction.Set(AttackerOptions.KnockBackHit2); // Remove dual wield option if last hit doesn't come from // the second weapon. if (cap.Hit != 2) aAction.Options &= ~AttackerOptions.DualWield; } // Set stun time if (tAction.SkillId != SkillId.Defense) { aAction.Stun = GetAttackerStun(attacker, weapon, tAction.IsKnockBack && skill.Info.Id != SkillId.FinalHit); tAction.Stun = GetTargetStun(attacker, weapon, tAction.IsKnockBack); } // Second hit doubles stun time for normal hits if (cap.Hit == 2 && !tAction.IsKnockBack) aAction.Stun *= 2; // Update current weapon SkillHelper.UpdateWeapon(attacker, target, weapon); cap.Handle(); // No second hit if target was knocked back if (tAction.IsKnockBack) break; } return CombatSkillResult.Okay; }
/// <summary> /// Uses the skill. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetEntityId"></param> /// <returns></returns> public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { // Get target var mainTarget = attacker.Region.GetCreature(targetEntityId); if (mainTarget == null) return CombatSkillResult.InvalidTarget; // Actions var cap = new CombatActionPack(attacker, skill.Info.Id); var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, targetEntityId); aAction.Set(AttackerOptions.Result); aAction.Stun = AttackerStun; cap.Add(aAction); // Hit by chance var chance = attacker.AimMeter.GetAimChance(mainTarget); var rnd = RandomProvider.Get(); if (rnd.NextDouble() * 100 < chance) { aAction.Set(AttackerOptions.KnockBackHit2); // Get targets, incl. splash. // Splash happens from r5 onwards, but we'll base it on Var4, // which is the splash damage and first != 0 on r5. var targets = new HashSet<Creature>() { mainTarget }; if (skill.RankData.Var4 != 0) { var targetPosition = mainTarget.GetPosition(); var direction = attacker.GetPosition().GetDirection(targetPosition); targets.UnionWith(attacker.GetTargetableCreaturesInCone(targetPosition, direction, skill.RankData.Var5, skill.RankData.Var6)); } // Damage var mainDamage = this.GetDamage(attacker, skill); foreach (var target in targets) { var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result | TargetOptions.CleanHit); tAction.Stun = TargetStun; cap.Add(tAction); // Damage var damage = mainDamage; // Elementals damage *= attacker.CalculateElementalDamageMultiplier(target); // More damage with fire arrow // XXX: Does this affect the element? if (attacker.Temp.FireArrow) damage *= FireBonus; // Splash modifier if (target != mainTarget) damage *= (skill.RankData.Var4 / 100f); // Critical Hit var critChance = attacker.GetRightCritChance(target.Protection); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Conditions SkillHelper.HandleConditions(attacker, target, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Natural Shield // Ignore delay reduction, as knock downs shouldn't be shortened NaturalShield.Handle(attacker, target, ref damage, tAction); // Deal with it! if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); SkillHelper.HandleInjury(attacker, target, damage); } // Knock down // If target is using a shield and defense, don't KD. var targetLeftHand = target.LeftHand; if (tAction.SkillId != SkillId.Defense || targetLeftHand == null || !targetLeftHand.IsShield) { // TODO: We have to calculate knockback distance right attacker.Shove(target, KnockBackDistance); tAction.Set(TargetOptions.KnockDownFinish); } // Aggro if (target == mainTarget) target.Aggro(attacker); if (target.IsDead) { tAction.Set(TargetOptions.Finished); if (target == mainTarget) aAction.Set(AttackerOptions.KnockBackHit1); } } } else { aAction.Set(AttackerOptions.Missed); } // Update current weapon SkillHelper.UpdateWeapon(attacker, mainTarget, ProficiencyGainType.Ranged, attacker.RightHand); // Reduce arrows if (attacker.Magazine != null && !ChannelServer.Instance.Conf.World.InfiniteArrows && !attacker.Magazine.HasTag("/unlimited_arrow/")) attacker.Inventory.Decrement(attacker.Magazine); // Disable fire arrow effect if (attacker.Temp.FireArrow) Send.Effect(attacker, Effect.FireArrow, false); // "Cancels" the skill // 800 = old load time? == aAction.Stun? Varies? Doesn't seem to be a stun. Send.SkillUse(attacker, skill.Info.Id, 800, 1); cap.Handle(); return CombatSkillResult.Okay; }
/// <summary> /// Uses the skill /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="packet"></param> public void UseSkill(Creature attacker, Skill skill, long targetEntityId) { var target = attacker.Region.GetCreature(targetEntityId); var attackerPos = attacker.GetPosition(); var targetPos = target.GetPosition(); // Check target + collisions if (target == null || attacker.Region.Collisions.Any(attackerPos, targetPos)) { Send.SkillUseSilentCancel(attacker); return; } // Stop movement attacker.StopMove(); target.StopMove(); Send.SkillUseEntity(attacker, skill.Info.Id, targetEntityId); skill.State = SkillState.Used; // Counter if (Counterattack.Handle(target, attacker)) { return; } // Defense/Protection decrease on target var debuffChance = (int)skill.RankData.Var6; var defDecrease = (int)skill.RankData.Var3; var protDecrease = (int)skill.RankData.Var4; var extra = new MabiDictionary(); extra.SetShort("NEW_DEF", (short)defDecrease); extra.SetShort("NEW_PROT", (short)protDecrease); extra.SetLong("DDP_CHAR", attacker.EntityId); extra.SetShort("DDP_SKILL", (short)skill.Info.Id); var rnd = RandomProvider.Get(); if (rnd.NextDouble() * 100 < debuffChance) { Send.Effect(target, Effect.SpinningUppercutDebuff, (short)skill.Info.Id, 0, defDecrease, protDecrease); target.Conditions.Activate(ConditionsC.DefProtectDebuff, extra); attacker.Temp.SpinningUppercutDebuffApplied = true; } // Prepare Combat Actions var cap = new CombatActionPack(attacker, skill.Info.Id); var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, targetEntityId, skill.Info.Id); aAction.Set(AttackerOptions.Result); var tAction = new TargetAction(CombatActionType.TakeHit | CombatActionType.Attacker, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result | TargetOptions.FighterUnk); cap.Add(aAction, tAction); // Damage var damage = (attacker.GetRndFighterDamage() * (skill.RankData.Var1 / 100f)); // Chain Mastery Damage Bonus var chainMasterySkill = attacker.Skills.Get(SkillId.ChainMastery); var damageBonus = (chainMasterySkill == null ? 0 : chainMasterySkill.RankData.Var1); damage += damage * (damageBonus / 100f); // Master Title - Damage +20% if (attacker.Titles.SelectedTitle == skill.Data.MasterTitle) { damage += (damage * 0.2f); } // Critical Hit var critChance = attacker.GetRightCritChance(target.Protection); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Handle skills and reductions SkillHelper.HandleDefenseProtection(target, ref damage); HeavyStander.Handle(attacker, target, ref damage, tAction); SkillHelper.HandleConditions(attacker, target, ref damage); ManaShield.Handle(target, ref damage, tAction); // Apply Damage target.TakeDamage(tAction.Damage = damage, attacker); // Aggro target.Aggro(attacker); // Stun Times tAction.Stun = TargetStun; aAction.Stun = AttackerStun; // Death and Knockback if (target.IsDead) { if (target.Is(RaceStands.KnockDownable)) { tAction.Set(TargetOptions.FinishingKnockDown); attacker.Shove(target, KnockbackDistance); } else { tAction.Set(TargetOptions.Finished | TargetOptions.FinishingHit); } } else { if (!target.IsKnockedDown) { target.Stability -= StabilityReduction; } if (target.IsUnstable && target.Is(RaceStands.KnockDownable)) { tAction.Set(TargetOptions.KnockDown); attacker.Shove(target, KnockbackDistance); } } cap.Handle(); // Chain Progress to Stage 3 attacker.Temp.FighterChainStartTime = DateTime.Now; attacker.Temp.FighterChainLevel = 3; }
/// <summary> /// Handles skill usage. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetEntityId"></param> /// <returns></returns> public override CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { // Check target var target = attacker.Region.GetCreature(targetEntityId); if (target == null) return CombatSkillResult.InvalidTarget; // Check range var targetPosition = target.GetPosition(); if (!attacker.GetPosition().InRange(targetPosition, attacker.AttackRangeFor(target))) return CombatSkillResult.OutOfRange; // Stop movement attacker.StopMove(); target.StopMove(); // Counter if (Counterattack.Handle(target, attacker)) return CombatSkillResult.Okay; // Prepare combat actions var aAction = new AttackerAction(CombatActionType.HardHit, attacker, targetEntityId); aAction.Set(AttackerOptions.Result | AttackerOptions.KnockBackHit2); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result | TargetOptions.Smash); var cap = new CombatActionPack(attacker, skill.Info.Id, aAction, tAction); // Calculate damage var damage = this.GetDamage(attacker, skill); // Elementals damage *= attacker.CalculateElementalDamageMultiplier(target); // Critical Hit var critChance = this.GetCritChance(attacker, target, skill); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Heavy Stander HeavyStander.Handle(attacker, target, ref damage, tAction); // Apply damage if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); SkillHelper.HandleInjury(attacker, target, damage); } // Aggro target.Aggro(attacker); if (target.IsDead) tAction.Set(TargetOptions.FinishingHit | TargetOptions.Finished); // Set Stun/Knockback attacker.Stun = aAction.Stun = StunTime; target.Stun = tAction.Stun = StunTime; target.Stability = Creature.MinStability; // Set knockbacked position attacker.Shove(target, KnockbackDistance); // Response Send.SkillUseStun(attacker, skill.Info.Id, AfterUseStun, 1); // Update both weapons SkillHelper.UpdateWeapon(attacker, target, attacker.RightHand, attacker.LeftHand); // Action! cap.Handle(); return CombatSkillResult.Okay; }
/// <summary> /// Uses the skill. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetEntityId"></param> /// <returns></returns> public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { // Get target var target = attacker.Region.GetCreature(targetEntityId); if (target == null) { return(CombatSkillResult.InvalidTarget); } // "Cancels" the skill // 800 = old load time? == aAction.Stun? Varies? Doesn't seem to be a stun. Send.SkillUse(attacker, skill.Info.Id, AttackerStun, 1); var chance = attacker.AimMeter.GetAimChance(target); var rnd = RandomProvider.Get().NextDouble() * 100; var successfulHit = (rnd < chance); // Actions var cap = new CombatActionPack(attacker, skill.Info.Id); var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, targetEntityId); aAction.Set(AttackerOptions.Result); aAction.Stun = AttackerStun; cap.Add(aAction); // Target action if hit if (successfulHit) { target.StopMove(); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result); tAction.Stun = TargetStun; cap.Add(tAction); // Damage // Formula unofficial, but it kinda matches what you would // expect from the skill, and what players believed the damage // to be, back in G2. // bonus = (100 - (6 - stacks) * 5 + rank, +var2 on last shot // I'm using rank instead of Var1, which goes from 1-15 in // AR2, so AR1 gets a little bonus as well, as AR1's Var1 and // 2 are 0. // With this formula, the bonus range (1st shot rF vs 5th shot // r1) is 76~110% for AR1, and 76~140% for AR2. var bonus = 100f - (6 - skill.Stacks) * 5f + (byte)skill.Info.Rank; if (skill.Stacks == 1) { bonus += skill.RankData.Var2; } var damage = attacker.GetRndRangedDamage() * (bonus / 100f); // Elementals damage *= attacker.CalculateElementalDamageMultiplier(target); // More damage with fire arrow if (attacker.Temp.FireArrow) { damage *= FireBonus; } // Critical Hit var critChance = attacker.GetRightCritChance(target.Protection); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Defense Defense.Handle(aAction, tAction, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Natural Shield var delayReduction = NaturalShield.Handle(attacker, target, ref damage, tAction); // Deal with it! if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); SkillHelper.HandleInjury(attacker, target, damage); } // Aggro target.Aggro(attacker); // Knock down on deadly if (target.Conditions.Has(ConditionsA.Deadly)) { tAction.Set(TargetOptions.KnockDown); } // Death/Knockback if (target.IsDead) { tAction.Set(TargetOptions.FinishingKnockDown); } else { // Insta-recover in knock down if (target.IsKnockedDown) { tAction.Stun = 0; } // Knock down if hit repeatedly else if (target.Stability < 30) { tAction.Set(TargetOptions.KnockDown); } // Normal stability reduction else { var stabilityReduction = StabilityReduction; // Reduce reduction, based on ping // According to the Wiki, "the Knockdown Gauge // [does not] build up", but it's still possible // to knock back with repeated hits. The stability // reduction is probably reduced, just like stun. if (delayReduction > 0) { stabilityReduction = (short)Math.Max(0, stabilityReduction - (stabilityReduction / 100 * delayReduction)); } target.Stability -= stabilityReduction; if (target.IsUnstable) { tAction.Set(TargetOptions.KnockBack); } } } // Knock Back if (tAction.IsKnockBack) { attacker.Shove(target, KnockBackDistance); } // Reduce stun, based on ping if (delayReduction > 0) { tAction.Stun = (short)Math.Max(0, tAction.Stun - (tAction.Stun / 100 * delayReduction)); } } // Update current weapon SkillHelper.UpdateWeapon(attacker, target, ProficiencyGainType.Ranged, attacker.RightHand); // Skill training if (skill.Info.Rank == SkillRank.RF) { skill.Train(1); // Try attacking with Arrow Revolver. } // Reduce arrows if (attacker.Magazine != null && !ChannelServer.Instance.Conf.World.InfiniteArrows && !attacker.Magazine.HasTag("/unlimited_arrow/")) { attacker.Inventory.Decrement(attacker.Magazine); } // Reduce stack skill.Stacks--; // Handle cap.Handle(); // Disable fire arrow effect if (attacker.Temp.FireArrow) { Send.Effect(attacker, Effect.FireArrow, false); } return(CombatSkillResult.Okay); }
/// <summary> /// Handles attack. /// </summary> /// <param name="attacker">The creature attacking.</param> /// <param name="skill">The skill being used.</param> /// <param name="targetEntityId">The entity id of the target.</param> /// <returns></returns> public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { if (attacker.IsStunned) { return(CombatSkillResult.Okay); } var target = attacker.Region.GetCreature(targetEntityId); if (target == null) { return(CombatSkillResult.Okay); } if (!attacker.GetPosition().InRange(target.GetPosition(), attacker.AttackRangeFor(target))) { return(CombatSkillResult.OutOfRange); } attacker.StopMove(); var targetPosition = target.StopMove(); // Counter if (Counterattack.Handle(target, attacker)) { return(CombatSkillResult.Okay); } var rightWeapon = attacker.Inventory.RightHand; var leftWeapon = attacker.Inventory.LeftHand; var magazine = attacker.Inventory.Magazine; var dualWield = (rightWeapon != null && leftWeapon != null && leftWeapon.Data.WeaponType != 0); var maxHits = (byte)(dualWield ? 2 : 1); int prevId = 0; for (byte i = 1; i <= maxHits; ++i) { var weapon = (i == 1 ? rightWeapon : leftWeapon); var weaponIsKnuckle = (weapon != null && weapon.Data.HasTag("/knuckle/")); var aAction = new AttackerAction(CombatActionType.Hit, attacker, skill.Info.Id, targetEntityId); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); var cap = new CombatActionPack(attacker, skill.Info.Id, aAction, tAction); cap.Hit = i; cap.MaxHits = maxHits; cap.PrevId = prevId; prevId = cap.Id; // Default attacker options aAction.Set(AttackerOptions.Result); if (dualWield) { aAction.Set(AttackerOptions.DualWield); } // Base damage var damage = attacker.GetRndDamage(weapon); // Critical Hit CriticalHit.Handle(attacker, attacker.GetCritChanceFor(target), ref damage, tAction); // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Defense Defense.Handle(aAction, tAction, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Deal with it! if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); } // Aggro target.Aggro(attacker); // Evaluate caused damage if (!target.IsDead) { if (tAction.Type != CombatActionType.Defended) { target.Stability -= this.GetStabilityReduction(attacker, weapon) / maxHits; // React normal for CombatMastery, knock down if // FH and not dual wield, don't knock at all if dual. if (skill.Info.Id != SkillId.FinalHit) { // Originally we thought you knock enemies back, unless it's a critical // hit, but apparently you knock *down* under normal circumstances. // More research to be done. if (target.IsUnstable && target.Is(RaceStands.KnockBackable)) { //tAction.Set(tAction.Has(TargetOptions.Critical) ? TargetOptions.KnockDown : TargetOptions.KnockBack); tAction.Set(TargetOptions.KnockDown); } } else if (!dualWield && !weaponIsKnuckle) { target.Stability = Creature.MinStability; tAction.Set(TargetOptions.KnockDown); } } } else { tAction.Set(TargetOptions.FinishingKnockDown); } // React to knock back if (tAction.IsKnockBack) { attacker.Shove(target, KnockBackDistance); aAction.Set(AttackerOptions.KnockBackHit2); // Remove dual wield option if last hit doesn't come from // the second weapon. if (cap.MaxHits != cap.Hit) { aAction.Options &= ~AttackerOptions.DualWield; } } // Set stun time if (tAction.Type != CombatActionType.Defended) { aAction.Stun = GetAttackerStun(attacker, weapon, tAction.IsKnockBack && (skill.Info.Id != SkillId.FinalHit || !dualWield)); tAction.Stun = GetTargetStun(attacker, weapon, tAction.IsKnockBack); } // Second hit doubles stun time for normal hits if (cap.Hit == 2 && !tAction.IsKnockBack) { aAction.Stun *= 2; } // Update current weapon SkillHelper.UpdateWeapon(attacker, target, weapon); cap.Handle(); // No second hit if target was knocked back if (tAction.IsKnockBack) { break; } } return(CombatSkillResult.Okay); }
/// <summary> /// Handles skill usage. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetEntityId"></param> /// <returns></returns> public override CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { // Check target var mainTarget = attacker.Region.GetCreature(targetEntityId); if (mainTarget == null) return CombatSkillResult.InvalidTarget; // Check range var targetPosition = mainTarget.GetPosition(); if (!attacker.GetPosition().InRange(targetPosition, attacker.AttackRangeFor(mainTarget))) return CombatSkillResult.OutOfRange; // Stop movement attacker.StopMove(); // Get targets, incl. splash. // Splash happens from r5 onwards, but we'll base it on Var4, // which is the splash damage and first != 0 on r5. var targets = new HashSet<Creature>() { mainTarget }; if (skill.RankData.Var4 != 0) targets.UnionWith(attacker.GetTargetableCreaturesInCone(mainTarget.GetPosition(), attacker.GetTotalSplashRadius(), attacker.GetTotalSplashAngle())); // Counter if (Counterattack.Handle(targets, attacker)) return CombatSkillResult.Okay; // Prepare combat actions var aAction = new AttackerAction(CombatActionType.HardHit, attacker, targetEntityId); aAction.Set(AttackerOptions.Result | AttackerOptions.KnockBackHit2); aAction.Stun = StunTime; var cap = new CombatActionPack(attacker, skill.Info.Id, aAction); // Calculate damage var mainDamage = this.GetDamage(attacker, skill); foreach (var target in targets) { // Stop movement target.StopMove(); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result | TargetOptions.Smash); cap.Add(tAction); // Damage var damage = mainDamage; // Elementals damage *= attacker.CalculateElementalDamageMultiplier(target); // Splash modifier if (target != mainTarget) damage *= (skill.RankData.Var4 / 100f); // Critical Hit var critChance = this.GetCritChance(attacker, target, skill); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Heavy Stander HeavyStander.Handle(attacker, target, ref damage, tAction); // Apply damage if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); SkillHelper.HandleInjury(attacker, target, damage); } // Aggro if (target == mainTarget) target.Aggro(attacker); if (target.IsDead) tAction.Set(TargetOptions.FinishingHit | TargetOptions.Finished); // Set Stun/Knockback target.Stun = tAction.Stun = StunTime; target.Stability = Creature.MinStability; // Set knockbacked position attacker.Shove(target, KnockbackDistance); } // Response Send.SkillUseStun(attacker, skill.Info.Id, AfterUseStun, 1); // Update both weapons SkillHelper.UpdateWeapon(attacker, mainTarget, ProficiencyGainType.Melee, attacker.RightHand, attacker.LeftHand); // Action! cap.Handle(); return CombatSkillResult.Okay; }
/// <summary> /// Uses the skill. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetEntityId"></param> /// <returns></returns> public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { // Get target var target = attacker.Region.GetCreature(targetEntityId); if (target == null) return CombatSkillResult.InvalidTarget; var targetPos = target.GetPosition(); var attackerPos = attacker.GetPosition(); var actionType = (attacker.IsElf ? CombatActionPackType.ChainRangeAttack : CombatActionPackType.NormalAttack); var attackerStun = (short)(actionType == CombatActionPackType.ChainRangeAttack ? AttackerStunElf : AttackerStun); // "Cancels" the skill // 800 = old load time? == aAction.Stun? Varies? Doesn't seem to be a stun. Send.SkillUse(attacker, skill.Info.Id, attackerStun, 1); var chance = attacker.AimMeter.GetAimChance(target); var rnd = RandomProvider.Get().NextDouble() * 100; var successfulHit = (rnd < chance); var maxHits = (actionType == CombatActionPackType.ChainRangeAttack && successfulHit ? 2 : 1); var prevId = 0; for (byte i = 1; i <= maxHits; ++i) { target.StopMove(); // Actions var cap = new CombatActionPack(attacker, skill.Info.Id); cap.Hit = i; cap.Type = actionType; cap.PrevId = prevId; prevId = cap.Id; var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, skill.Info.Id, targetEntityId); aAction.Set(AttackerOptions.Result); aAction.Stun = attackerStun; cap.Add(aAction); // Target action if hit if (successfulHit) { var targetSkillId = target.Skills.ActiveSkill != null ? target.Skills.ActiveSkill.Info.Id : SkillId.CombatMastery; var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, targetSkillId); tAction.Set(TargetOptions.Result); tAction.AttackerSkillId = skill.Info.Id; tAction.Stun = (short)(actionType == CombatActionPackType.ChainRangeAttack ? TargetStunElf : TargetStun); if (actionType == CombatActionPackType.ChainRangeAttack) tAction.EffectFlags = 0x20; cap.Add(tAction); // Damage var damage = attacker.GetRndRangedDamage(); // More damage with fire arrow if (attacker.Temp.FireArrow) damage *= FireBonus; // Critical Hit var critChance = attacker.GetRightCritChance(target.Protection); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Defense Defense.Handle(aAction, tAction, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Deal with it! if (damage > 0) target.TakeDamage(tAction.Damage = damage, attacker); // Aggro target.Aggro(attacker); // Death/Knockback if (target.IsDead) { tAction.Set(TargetOptions.FinishingKnockDown); attacker.Shove(target, KnockBackDistance); maxHits = 1; } else { // Insta-recover in knock down // TODO: Tied to stability? if (target.IsKnockedDown) { tAction.Stun = 0; } // Knock down if hit repeatedly else if (target.Stability < 30) { tAction.Set(TargetOptions.KnockDown); } // Normal stability reduction else { target.Stability -= (actionType == CombatActionPackType.ChainRangeAttack ? StabilityReductionElf : StabilityReduction); if (target.IsUnstable) { tAction.Set(TargetOptions.KnockBack); attacker.Shove(target, KnockBackDistance); } } tAction.Creature.Stun = tAction.Stun; } } aAction.Creature.Stun = aAction.Stun; // Skill training if (skill.Info.Rank == SkillRank.Novice || skill.Info.Rank == SkillRank.RF) skill.Train(1); // Try ranged attack. // Reduce arrows if (attacker.Magazine != null && !ChannelServer.Instance.Conf.World.InfiniteArrows && attacker.Magazine.Info.Id != 67220) attacker.Inventory.Decrement(attacker.Magazine); cap.Handle(); } // Disable fire arrow effect if (attacker.Temp.FireArrow) Send.Effect(attacker, Effect.FireArrow, false); return CombatSkillResult.Okay; }
/// <summary> /// Uses WM, attacking targets. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetAreaId"></param> /// <param name="unkInt1"></param> /// <param name="unkInt2"></param> public void Use(Creature attacker, Skill skill, long targetAreaId, int unkInt1, int unkInt2) { var range = this.GetRange(attacker, skill); var targets = attacker.GetTargetableCreaturesInRange(range, TargetableOptions.AddAttackRange); // Check targets if (targets.Count == 0) { Send.Notice(attacker, Localization.Get("There isn't a target nearby to use that on.")); Send.SkillUseSilentCancel(attacker); return; } // Create actions var cap = new CombatActionPack(attacker, skill.Info.Id); var aAction = new AttackerAction(CombatActionType.SpecialHit, attacker, targetAreaId); aAction.Set(AttackerOptions.Result); aAction.Stun = CombatMastery.GetAttackerStun(attacker.AverageKnockCount, attacker.AverageAttackSpeed, true); cap.Add(aAction); var survived = new List<Creature>(); var rnd = RandomProvider.Get(); // Check crit var crit = false; if (attacker.Skills.Has(SkillId.CriticalHit, SkillRank.RF)) crit = (rnd.Next(100) < attacker.GetTotalCritChance(0)); // Handle all targets foreach (var target in targets) { target.StopMove(); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Delay = 300; // Usually 300, sometimes 350? // Calculate damage var damage = attacker.GetRndTotalDamage(); damage *= skill.RankData.Var1 / 100f; // Elementals damage *= attacker.CalculateElementalDamageMultiplier(target); // Crit bonus if (crit) CriticalHit.Handle(attacker, 100, ref damage, tAction); // Handle skills and reductions SkillHelper.HandleDefenseProtection(target, ref damage); SkillHelper.HandleConditions(attacker, target, ref damage); Defense.Handle(aAction, tAction, ref damage); ManaShield.Handle(target, ref damage, tAction); HeavyStander.Handle(attacker, target, ref damage, tAction); // Clean Hit if not defended nor critical if (tAction.SkillId != SkillId.Defense && !tAction.Has(TargetOptions.Critical)) tAction.Set(TargetOptions.CleanHit); // Take damage if any is left if (damage > 0) target.TakeDamage(tAction.Damage = damage, attacker); // Knock down on deadly if (target.Conditions.Has(ConditionsA.Deadly)) { tAction.Set(TargetOptions.KnockDown); tAction.Stun = CombatMastery.GetTargetStun(attacker.AverageKnockCount, attacker.AverageAttackSpeed, true); } // Finish if dead, knock down if not defended if (target.IsDead) tAction.Set(TargetOptions.KnockDownFinish); else if (tAction.SkillId != SkillId.Defense) tAction.Set(TargetOptions.KnockDown); // Anger Management if (!target.IsDead) survived.Add(target); // Stun and shove if not defended if (target.IsDead || tAction.SkillId != SkillId.Defense || target.Conditions.Has(ConditionsA.Deadly)) { tAction.Stun = CombatMastery.GetTargetStun(attacker.AverageKnockCount, attacker.AverageAttackSpeed, true); target.Stability = Creature.MinStability; attacker.Shove(target, KnockbackDistance); } // Add action cap.Add(tAction); } // Update current weapon SkillHelper.UpdateWeapon(attacker, targets.FirstOrDefault(), ProficiencyGainType.Melee, attacker.RightHand, attacker.LeftHand); // Only select a random aggro if there is no aggro yet, // WM only aggroes one target at a time. if (survived.Count != 0 && attacker.Region.CountAggro(attacker) < 1) { var aggroTarget = survived.Random(); aggroTarget.Aggro(attacker); } // Reduce life in old combat system if (!AuraData.FeaturesDb.IsEnabled("CombatSystemRenewal")) { // Default reduction is 10%, it's reduced to 2% if attacker // has less max life than the rate is set to. var lifeReducationRate = skill.RankData.Var2; if (attacker.LifeMax < lifeReducationRate) lifeReducationRate /= 5; var amount = attacker.LifeMax / 100f * lifeReducationRate; attacker.ModifyLife(-amount); // TODO: Invincibility } // Spin it~ Send.UseMotion(attacker, 8, 4); cap.Handle(); Send.SkillUse(attacker, skill.Info.Id, targetAreaId, unkInt1, unkInt2); skill.Stacks = 0; }
/// <summary> /// Uses the skill. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetEntityId"></param> /// <returns></returns> public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { // Get target var target = attacker.Region.GetCreature(targetEntityId); if (target == null) return CombatSkillResult.InvalidTarget; // "Cancels" the skill // 800 = old load time? == aAction.Stun? Varies? Doesn't seem to be a stun. Send.SkillUse(attacker, skill.Info.Id, AttackerStun, 1); var chance = attacker.AimMeter.GetAimChance(target); var rnd = RandomProvider.Get().NextDouble() * 100; var successfulHit = (rnd < chance); // Actions var cap = new CombatActionPack(attacker, skill.Info.Id); var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, targetEntityId); aAction.Set(AttackerOptions.Result); aAction.Stun = AttackerStun; cap.Add(aAction); // Target action if hit if (successfulHit) { target.StopMove(); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result); tAction.Stun = TargetStun; cap.Add(tAction); // Damage var damage = attacker.GetRndRangedDamage() * (skill.RankData.Var1 / 100f); // Elementals damage *= attacker.CalculateElementalDamageMultiplier(target); // More damage with fire arrow if (attacker.Temp.FireArrow) damage *= FireBonus; // Critical Hit var critChance = attacker.GetRightCritChance(target.Protection); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Defense Defense.Handle(aAction, tAction, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Natural Shield var delayReduction = NaturalShield.Handle(attacker, target, ref damage, tAction); // Deal with it! if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); SkillHelper.HandleInjury(attacker, target, damage); } // Aggro target.Aggro(attacker); // Knock down on deadly if (target.Conditions.Has(ConditionsA.Deadly)) tAction.Set(TargetOptions.KnockDown); // Death/Knockback if (target.IsDead) tAction.Set(TargetOptions.FinishingKnockDown); // Knock Back if (tAction.IsKnockBack) attacker.Shove(target, KnockBackDistance); // Reduce stun, based on ping if (delayReduction > 0) tAction.Stun = (short)Math.Max(0, tAction.Stun - (tAction.Stun / 100 * delayReduction)); // TODO: "Weakened" state (G12S2 gfSupportShotRenewal) } // Reduce arrows if (attacker.Magazine != null && !ChannelServer.Instance.Conf.World.InfiniteArrows && !attacker.Magazine.HasTag("/unlimited_arrow/")) attacker.Inventory.Decrement(attacker.Magazine); cap.Handle(); // Disable fire arrow effect if (attacker.Temp.FireArrow) Send.Effect(attacker, Effect.FireArrow, false); return CombatSkillResult.Okay; }
/// <summary> /// Handles skill usage. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetEntityId"></param> /// <returns></returns> public override CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { // Check target var target = attacker.Region.GetCreature(targetEntityId); if (target == null) { return(CombatSkillResult.InvalidTarget); } // Check range var targetPosition = target.GetPosition(); if (!attacker.GetPosition().InRange(targetPosition, attacker.AttackRangeFor(target))) { return(CombatSkillResult.OutOfRange); } // Stop movement attacker.StopMove(); target.StopMove(); // Counter if (Counterattack.Handle(target, attacker)) { return(CombatSkillResult.Okay); } // Prepare combat actions var aAction = new AttackerAction(CombatActionType.HardHit, attacker, skill.Info.Id, targetEntityId); aAction.Set(AttackerOptions.Result | AttackerOptions.KnockBackHit2); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result | TargetOptions.Smash); var cap = new CombatActionPack(attacker, skill.Info.Id, aAction, tAction); // Calculate damage var damage = this.GetDamage(attacker, skill); var critChance = this.GetCritChance(attacker, target, skill); // Critical Hit CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Apply damage target.TakeDamage(tAction.Damage = damage, attacker); // Aggro target.Aggro(attacker); if (target.IsDead) { tAction.Set(TargetOptions.FinishingHit | TargetOptions.Finished); } // Set Stun/Knockback attacker.Stun = aAction.Stun = StunTime; target.Stun = tAction.Stun = StunTime; target.Stability = Creature.MinStability; // Set knockbacked position attacker.Shove(target, KnockbackDistance); // Response Send.SkillUseStun(attacker, skill.Info.Id, AfterUseStun, 1); // Update both weapons SkillHelper.UpdateWeapon(attacker, target, attacker.RightHand, attacker.LeftHand); // Action! cap.Handle(); return(CombatSkillResult.Okay); }
/// <summary> /// Uses the skill. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetEntityId"></param> /// <returns></returns> public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { // Get target var target = attacker.Region.GetCreature(targetEntityId); if (target == null) return CombatSkillResult.InvalidTarget; if (target.IsNotReadyToBeHit) return CombatSkillResult.Okay; // Actions var cap = new CombatActionPack(attacker, skill.Info.Id); var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, skill.Info.Id, targetEntityId); aAction.Set(AttackerOptions.Result); aAction.Stun = AttackerStun; cap.Add(aAction); // Hit by chance var chance = attacker.AimMeter.GetAimChance(target); var rnd = RandomProvider.Get(); if (rnd.NextDouble() * 100 < chance) { aAction.Set(AttackerOptions.KnockBackHit2); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result | TargetOptions.CleanHit); tAction.Stun = TargetStun; cap.Add(tAction); // Damage var damage = this.GetDamage(attacker, skill); // More damage with fire arrow if (attacker.Temp.FireArrow) damage *= FireBonus; // Critical Hit var critShieldReduction = (target.LeftHand != null ? target.LeftHand.Data.DefenseBonusCrit : 0); var critChance = attacker.GetRightCritChance(target.Protection + critShieldReduction); CriticalHit.Handle(attacker, critChance, ref damage, tAction); var maxDamage = damage; //Damage without Defense and Protection // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Defense Defense.Handle(aAction, tAction, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction, maxDamage); // Deal with it! if (damage > 0) target.TakeDamage(tAction.Damage = damage, attacker); // TODO: We have to calculate knockback distance right // TODO: Target with Defense and shield shouldn't be knocked back attacker.Shove(target, KnockBackDistance); // Aggro target.Aggro(attacker); tAction.Set(TargetOptions.KnockDownFinish); if (target.IsDead) { aAction.Set(AttackerOptions.KnockBackHit1); tAction.Set(TargetOptions.Finished); } var weapon = attacker.RightHand; var critSkill = attacker.Skills.Get(SkillId.CriticalHit); if (skill.Info.Rank >= SkillRank.R5 && weapon != null && weapon.Data.SplashRadius != 0 && weapon.Data.SplashAngle != 0) { ICollection<Creature> targets = attacker.GetTargetableCreaturesInCone(weapon != null ? (int)weapon.Data.SplashRadius : 200, weapon != null ? (int)weapon.Data.SplashAngle : 20); foreach (var splashTarget in targets) { if (splashTarget != target) { if (splashTarget.IsNotReadyToBeHit) continue; TargetAction tSplashAction = new TargetAction(CombatActionType.TakeHit, splashTarget, attacker, skill.Info.Id); tSplashAction.Set(TargetOptions.Result | TargetOptions.CleanHit); splashTarget.Stun = TargetStun; // Base damage var damageSplash = this.GetDamage(attacker, skill); // Critical Hit if (critSkill != null && tAction.Has(TargetOptions.Critical)) { // Add crit bonus var bonus = critSkill.RankData.Var1 / 100f; damageSplash = damageSplash + (damageSplash * bonus); // Set splashTarget option tSplashAction.Set(TargetOptions.Critical); } var maxDamageSplash = damage; //Damage without Defense and Protection // Subtract splashTarget def/prot SkillHelper.HandleDefenseProtection(splashTarget, ref damageSplash); // Defense Defense.Handle(aAction, tSplashAction, ref damageSplash, true); // Mana Shield ManaShield.Handle(splashTarget, ref damageSplash, tSplashAction, maxDamageSplash); //Splash Damage Reduction damageSplash *= skill.Info.Rank < SkillRank.R1 ? 0.1f : 0.2f; // Deal with it! if (damageSplash > 0) splashTarget.TakeDamage(tSplashAction.Damage = damageSplash, attacker); attacker.Shove(splashTarget, KnockBackDistance); // Alert Network.Sending.Send.SetCombatTarget(splashTarget, attacker.EntityId, TargetMode.Alert); tAction.Set(TargetOptions.KnockDownFinish); if (splashTarget.IsDead) { aAction.Set(AttackerOptions.KnockBackHit1); tSplashAction.Set(TargetOptions.Finished); } cap.Add(tSplashAction); } } } } else { aAction.Set(AttackerOptions.Missed); } // Reduce arrows if (attacker.Magazine != null && !ChannelServer.Instance.Conf.World.InfiniteArrows) attacker.Inventory.Decrement(attacker.Magazine); // Disable fire arrow effect if (attacker.Temp.FireArrow) Send.Effect(attacker, Effect.FireArrow, false); // "Cancels" the skill // 800 = old load time? == aAction.Stun? Varies? Doesn't seem to be a stun. Send.SkillUse(attacker, skill.Info.Id, 800, 1); cap.Handle(); return CombatSkillResult.Okay; }
/// <summary> /// Uses the skill /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetEntityId"></param> /// <returns></returns> public void Use(Creature attacker, Skill skill, Packet packet) { // Check for full charge if (DateTime.Now < attacker.Temp.ExcaliburPrepareTime.AddMilliseconds(skill.RankData.Var1)) // Not enough time has passed during charging { Send.SkillUseSilentCancel(attacker); Send.Effect(attacker, Effect.Excalibur, ExcaliburEffect.Cancel); return; } // Skill Data var skillDamage = skill.RankData.Var2 / 100f; var skillLength = (int)skill.RankData.Var3; var skillWidth = (int)skill.RankData.Var4; // Get targets in rectangular area Position endPos; // Position on the skill area rectangle opposite of the attacker var targets = SkillHelper.GetTargetableCreaturesInSkillArea(attacker, skillLength, skillWidth, out endPos); // TargetProp var targetProp = new Prop(280, attacker.RegionId, endPos.X, endPos.Y, Mabi.MabiMath.ByteToRadian(attacker.Direction), 1f, 0f, "single"); attacker.Region.AddProp(targetProp); // Turn to target area attacker.TurnTo(endPos); // Prepare Combat Actions var cap = new CombatActionPack(attacker, skill.Info.Id); var targetAreaId = new Location(attacker.RegionId, endPos).ToLocationId(); var aAction = new AttackerAction(CombatActionType.SpecialHit, attacker, targetAreaId); aAction.Set(AttackerOptions.UseEffect); aAction.PropId = targetProp.EntityId; cap.Add(aAction); var rnd = RandomProvider.Get(); // Check crit var crit = false; var critSkill = attacker.Skills.Get(SkillId.CriticalHit); if (critSkill != null && critSkill.Info.Rank > SkillRank.Novice) { var critChance = attacker.GetTotalCritChance(0); if (rnd.NextDouble() * 100 < critChance) { crit = true; } } foreach (var target in targets) { var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, SkillId.CombatMastery); tAction.Set(TargetOptions.None); tAction.Delay = 1200; cap.Add(tAction); // Stop target movement target.StopMove(); var damage = (attacker.GetRndTotalDamage() * skillDamage); // Critical Hit if (crit) { CriticalHit.Handle(attacker, 100, ref damage, tAction); } // Handle skills and reductions SkillHelper.HandleDefenseProtection(target, ref damage); Defense.Handle(aAction, tAction, ref damage); HeavyStander.Handle(attacker, target, ref damage, tAction); SkillHelper.HandleConditions(attacker, target, ref damage); ManaShield.Handle(target, ref damage, tAction); // Apply Damage target.TakeDamage(tAction.Damage = damage, attacker); // Aggro target.Aggro(attacker); // Stun Time tAction.Stun = TargetStun; // Death or Knockback if (target.IsDead) { tAction.Set(TargetOptions.FinishingKnockDown); attacker.Shove(target, KnockbackDistance); } else { // Always knock down if (target.Is(RaceStands.KnockDownable)) { tAction.Set(TargetOptions.KnockDown); attacker.Shove(target, KnockbackDistance); } } } // Update current weapon SkillHelper.UpdateWeapon(attacker, targets.FirstOrDefault(), ProficiencyGainType.Melee, attacker.RightHand); cap.Handle(); Send.Effect(attacker, Effect.Excalibur, ExcaliburEffect.Attack, (float)endPos.X, (float)endPos.Y); Send.SkillUse(attacker, skill.Info.Id, targetAreaId, 0, 1); // Remove skill prop attacker.Region.RemoveProp(targetProp); }
/// <summary> /// Uses LightningRod /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="packet"></param> public void Use(Creature attacker, Skill skill, Packet packet) { // Set full charge variable attacker.Temp.LightningRodFullCharge = (DateTime.Now >= attacker.Temp.LightningRodPrepareTime.AddMilliseconds(skill.RankData.Var3)); // Get direction for target Area var direction = Mabi.MabiMath.ByteToRadian(attacker.Direction); var attackerPos = attacker.GetPosition(); // Calculate polygon points var r = MabiMath.ByteToRadian(attacker.Direction); var poe = attackerPos.GetRelative(r, 800); var pivot = new Point(poe.X, poe.Y); var p1 = new Point(pivot.X - SkillLength / 2, pivot.Y - SkillWidth / 2); var p2 = new Point(pivot.X - SkillLength / 2, pivot.Y + SkillWidth / 2); var p3 = new Point(pivot.X + SkillLength / 2, pivot.Y + SkillWidth / 2); var p4 = new Point(pivot.X + SkillLength / 2, pivot.Y - SkillWidth / 2); p1 = this.RotatePoint(p1, pivot, r); p2 = this.RotatePoint(p2, pivot, r); p3 = this.RotatePoint(p3, pivot, r); p4 = this.RotatePoint(p4, pivot, r); // TargetProp var lProp = new Prop(280, attacker.RegionId, poe.X, poe.Y, MabiMath.ByteToRadian(attacker.Direction), 1f, 0f, "single"); attacker.Region.AddProp(lProp); // Prepare Combat Actions var cap = new CombatActionPack(attacker, skill.Info.Id); var targetAreaId = new Location(attacker.RegionId, poe).ToLocationId(); var aAction = new AttackerAction(CombatActionType.SpecialHit, attacker, targetAreaId); aAction.Set(AttackerOptions.KnockBackHit1 | AttackerOptions.UseEffect); aAction.PropId = lProp.EntityId; cap.Add(aAction); // Get targets in Polygon - includes collission check var targets = attacker.Region.GetCreaturesInPolygon(p1, p2, p3, p4).Where(x => attacker.CanTarget(x) && !attacker.Region.Collisions.Any(attacker.GetPosition(), x.GetPosition())).ToList(); var rnd = RandomProvider.Get(); // Check crit var crit = false; var critSkill = attacker.Skills.Get(SkillId.CriticalHit); if (critSkill != null && critSkill.Info.Rank > SkillRank.Novice) { var critChance = Math2.Clamp(0, 30, attacker.GetTotalCritChance(0)); if (rnd.NextDouble() * 100 < critChance) { crit = true; } } foreach (var target in targets) { var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, SkillId.CombatMastery); tAction.Set(TargetOptions.None); tAction.AttackerSkillId = skill.Info.Id; cap.Add(tAction); var damage = attacker.GetRndMagicDamage(skill, skill.RankData.Var1, skill.RankData.Var2); // Add damage if the skill is fully charged var dmgMultiplier = skill.RankData.Var4 / 100f; if (attacker.Temp.LightningRodFullCharge) { damage += (damage * dmgMultiplier); } // Critical Hit if (crit) { var bonus = critSkill.RankData.Var1 / 100f; damage = damage + (damage * bonus); tAction.Set(TargetOptions.Critical); } // MDef and MProt SkillHelper.HandleMagicDefenseProtection(target, ref damage); // Mana Deflector var delayReduction = ManaDeflector.Handle(attacker, target, ref damage, tAction); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Apply Damage target.TakeDamage(tAction.Damage = damage, attacker); // Stun Time tAction.Stun = TargetStun; // Death or Knockback if (target.IsDead) { tAction.Set(TargetOptions.FinishingKnockDown); attacker.Shove(target, KnockbackDistance); } else { // Always knock down if (target.Is(RaceStands.KnockDownable)) { tAction.Set(TargetOptions.KnockDown); attacker.Shove(target, KnockbackDistance); } } } cap.Handle(); Send.Effect(attacker, Effect.LightningRod, (int)LightningRodEffect.Attack, poe.X, poe.Y); Send.SkillUse(attacker, skill.Info.Id, targetAreaId, 0, 1); skill.Train(1); // Use the Skill attacker.Region.RemoveProp(lProp); }
/// <summary> /// Handles skill usage. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetEntityId"></param> /// <returns></returns> public override CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { // Check target var mainTarget = attacker.Region.GetCreature(targetEntityId); if (mainTarget == null) { return(CombatSkillResult.InvalidTarget); } // Check range var targetPosition = mainTarget.GetPosition(); if (!attacker.GetPosition().InRange(targetPosition, attacker.AttackRangeFor(mainTarget))) { return(CombatSkillResult.OutOfRange); } // Stop movement attacker.StopMove(); // Get targets, incl. splash. // Splash happens from r5 onwards, but we'll base it on Var4, // which is the splash damage and first != 0 on r5. var targets = new HashSet <Creature>() { mainTarget }; if (skill.RankData.Var4 != 0) { targets.UnionWith(attacker.GetTargetableCreaturesInCone(mainTarget.GetPosition(), attacker.GetTotalSplashRadius(), attacker.GetTotalSplashAngle())); } // Counter if (Counterattack.Handle(targets, attacker)) { return(CombatSkillResult.Okay); } // Prepare combat actions var aAction = new AttackerAction(CombatActionType.HardHit, attacker, targetEntityId); aAction.Set(AttackerOptions.Result | AttackerOptions.KnockBackHit2); aAction.Stun = StunTime; var cap = new CombatActionPack(attacker, skill.Info.Id, aAction); // Calculate damage var mainDamage = this.GetDamage(attacker, skill); foreach (var target in targets) { // Stop movement target.StopMove(); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result | TargetOptions.Smash); cap.Add(tAction); // Damage var damage = mainDamage; // Elementals damage *= attacker.CalculateElementalDamageMultiplier(target); // Splash modifier if (target != mainTarget) { damage *= (skill.RankData.Var4 / 100f); } // Critical Hit var critChance = this.GetCritChance(attacker, target, skill); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Heavy Stander HeavyStander.Handle(attacker, target, ref damage, tAction); // Apply damage if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); SkillHelper.HandleInjury(attacker, target, damage); } // Aggro if (target == mainTarget) { target.Aggro(attacker); } if (target.IsDead) { tAction.Set(TargetOptions.FinishingHit | TargetOptions.Finished); } // Set Stun/Knockback target.Stun = tAction.Stun = StunTime; target.Stability = Creature.MinStability; // Set knockbacked position attacker.Shove(target, KnockbackDistance); } // Response Send.SkillUseStun(attacker, skill.Info.Id, AfterUseStun, 1); // Update both weapons SkillHelper.UpdateWeapon(attacker, mainTarget, ProficiencyGainType.Melee, attacker.RightHand, attacker.LeftHand); // Action! cap.Handle(); return(CombatSkillResult.Okay); }
/// <summary> /// Hits targets with thunderbolt. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="stacks"></param> /// <param name="damageProp"></param> /// <param name="targets"></param> private void Thunderbolt(Creature attacker, Skill skill, int stacks, Prop damageProp, List <Creature> targets) { // Skip if all targets are already dead if (targets.All(a => a.IsDead)) { return; } var locationId = damageProp.GetLocation().ToLocationId(); var aAction = new AttackerAction(CombatActionType.SpecialHit, attacker, locationId, skill.Info.Id); aAction.Set(AttackerOptions.KnockBackHit1 | AttackerOptions.UseEffect); aAction.PropId = damageProp.EntityId; var cap = new CombatActionPack(attacker, skill.Info.Id, aAction); foreach (var target in targets.Where(a => !a.IsDead)) { var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result | TargetOptions.KnockDown); tAction.EffectFlags = EffectFlags.SpecialRangeHit; tAction.Delay = 313; tAction.Stun = (short)ThunderboltTargetStun; var damage = this.GetThunderboltDamage(attacker, skill, stacks); // Critical Hit var critChance = attacker.GetTotalCritChance(target.Protection, true); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Reduce damage Defense.Handle(aAction, tAction, ref damage); SkillHelper.HandleMagicDefenseProtection(target, ref damage); SkillHelper.HandleConditions(attacker, target, ref damage); ManaShield.Handle(target, ref damage, tAction); // Mana Deflector var mdResult = ManaDeflector.Handle(attacker, target, ref damage, tAction); var delayReduction = mdResult.DelayReduction; var pinged = mdResult.Pinged; // Deal damage if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); } // Reduce stun, based on ping if (pinged && delayReduction > 0) { tAction.Stun = (short)Math.Max(0, tAction.Stun - (tAction.Stun / 100 * delayReduction)); } // Death/Knockback if (target.IsDead) { tAction.Set(TargetOptions.FinishingKnockDown); } cap.Add(tAction); } cap.Handle(); }
/// <summary> /// Handles attack. /// </summary> /// <param name="attacker">The creature attacking.</param> /// <param name="skill">The skill being used.</param> /// <param name="targetEntityId">The entity id of the target.</param> /// <returns></returns> public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { if (attacker.IsStunned) { return(CombatSkillResult.Okay); } var mainTarget = attacker.Region.GetCreature(targetEntityId); if (mainTarget == null) { return(CombatSkillResult.Okay); } if (!attacker.GetPosition().InRange(mainTarget.GetPosition(), attacker.AttackRangeFor(mainTarget))) { return(CombatSkillResult.OutOfRange); } attacker.StopMove(); // Get targets, incl. splash. var targets = new HashSet <Creature>() { mainTarget }; targets.UnionWith(attacker.GetTargetableCreaturesInCone(mainTarget.GetPosition(), attacker.GetTotalSplashRadius(), attacker.GetTotalSplashAngle())); // Counter if (Counterattack.Handle(targets, attacker)) { return(CombatSkillResult.Okay); } var rightWeapon = attacker.Inventory.RightHand; var leftWeapon = attacker.Inventory.LeftHand; var magazine = attacker.Inventory.Magazine; var maxHits = (byte)(attacker.IsDualWielding ? 2 : 1); int prevId = 0; for (byte i = 1; i <= maxHits; ++i) { var weapon = (i == 1 ? rightWeapon : leftWeapon); var weaponIsKnuckle = (weapon != null && weapon.Data.HasTag("/knuckle/")); var aAction = new AttackerAction(CombatActionType.Attacker, attacker, targetEntityId); aAction.Set(AttackerOptions.Result); if (attacker.IsDualWielding) { aAction.Set(AttackerOptions.DualWield); aAction.WeaponParameterType = (byte)(i == 1 ? 2 : 1); } var cap = new CombatActionPack(attacker, skill.Info.Id, aAction); cap.Hit = i; cap.Type = (attacker.IsDualWielding ? CombatActionPackType.TwinSwordAttack : CombatActionPackType.NormalAttack); cap.PrevId = prevId; prevId = cap.Id; var mainDamage = (i == 1 ? attacker.GetRndRightHandDamage() : attacker.GetRndLeftHandDamage()); foreach (var target in targets) { if (target.IsDead) { continue; } target.StopMove(); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result); cap.Add(tAction); // Base damage var damage = mainDamage; // Elementals damage *= attacker.CalculateElementalDamageMultiplier(target); // Splash modifier if (target != mainTarget) { damage *= attacker.GetSplashDamage(weapon); } // Critical Hit var critChance = (i == 1 ? attacker.GetRightCritChance(target.Protection) : attacker.GetLeftCritChance(target.Protection)); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Conditions SkillHelper.HandleConditions(attacker, target, ref damage); // Defense Defense.Handle(aAction, tAction, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Heavy Stander // Can only happen on the first hit var pinged = (cap.Hit == 1 && HeavyStander.Handle(attacker, target, ref damage, tAction)); // Deal with it! if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); SkillHelper.HandleInjury(attacker, target, damage); } // Knock down on deadly if (target.Conditions.Has(ConditionsA.Deadly)) { tAction.Set(TargetOptions.KnockDown); tAction.Stun = GetTargetStun(attacker, weapon, tAction.IsKnockBack); } // Aggro if (target == mainTarget) { target.Aggro(attacker); } // Evaluate caused damage if (!target.IsDead) { if (tAction.SkillId != SkillId.Defense) { target.Stability -= this.GetStabilityReduction(attacker, weapon) / maxHits; // React normal for CombatMastery, knock down if // FH and not dual wield, don't knock at all if dual. if (skill.Info.Id != SkillId.FinalHit) { // Originally we thought you knock enemies back, unless it's a critical // hit, but apparently you knock *down* under normal circumstances. // More research to be done. if (target.IsUnstable && target.Is(RaceStands.KnockBackable)) { //tAction.Set(tAction.Has(TargetOptions.Critical) ? TargetOptions.KnockDown : TargetOptions.KnockBack); tAction.Set(TargetOptions.KnockDown); } } else if (!attacker.IsDualWielding && !weaponIsKnuckle && target.Is(RaceStands.KnockBackable)) { target.Stability = Creature.MinStability; tAction.Set(TargetOptions.KnockDown); } } } else { tAction.Set(TargetOptions.FinishingKnockDown); } // React to knock back if (tAction.IsKnockBack) { attacker.Shove(target, KnockBackDistance); if (target == mainTarget) { aAction.Set(AttackerOptions.KnockBackHit2); } } // Set stun time if not defended, Defense handles the stun // in case the target used it. if (tAction.SkillId != SkillId.Defense) { if (target == mainTarget) { aAction.Stun = GetAttackerStun(attacker, weapon, tAction.IsKnockBack && skill.Info.Id != SkillId.FinalHit); } tAction.Stun = GetTargetStun(attacker, weapon, tAction.IsKnockBack); } if (target == mainTarget) { // Set increased stun if target pinged if (pinged) { aAction.Stun = GetAttackerStun(attacker, weapon, true); } // Second hit doubles stun time for normal hits if (cap.Hit == 2 && !tAction.IsKnockBack && !pinged) { aAction.Stun *= 2; } // Update current weapon SkillHelper.UpdateWeapon(attacker, target, ProficiencyGainType.Melee, weapon); // Consume stamina for weapon var staminaUsage = (weapon != null ? weapon.Data.StaminaUsage : Creature.BareHandStaminaUsage); if (attacker.Stamina < staminaUsage) { Send.Notice(attacker, Localization.Get("Your stamina is too low to fight properly!")); } attacker.Stamina -= staminaUsage; // No second hit if defended, pinged, or knocked back if (tAction.IsKnockBack || tAction.SkillId == SkillId.Defense || pinged) { // Set to 1 to prevent second run maxHits = 1; // Remove dual wield option if last hit doesn't come from // the second weapon. If this isn't done, the client shows // the second hit. if (cap.Hit != 2) { aAction.Options &= ~AttackerOptions.DualWield; } } // Reduce attacker's knockback stun in new combat, to allow // movement after sooner. // It's unknown when exactly this was added, but older EU logs // don't have this packet, so we'll assume it was part of the the // new combat, which's purpose was to be faster. // Sending the packet appears to reset the movement lock, and // officials seem to send this about 1s after the attack, for // an effective 1s movement lock after an attack. // If it's send for non-knockback hits, it can add a delay, // maybe increasing the time of the lock, like for dual-wielding. if (AuraData.FeaturesDb.IsEnabled("CombatSystemRenewal")) { if (tAction.IsKnockBack) { Task.Delay(1000).ContinueWith(_ => Send.CharacterLockUpdate(attacker, 18, 1500)); } } } } // Handle cap.Handle(); } return(CombatSkillResult.Okay); }
/// <summary> /// Bolt specific use code. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="target"></param> protected override void UseSkillOnTarget(Creature attacker, Skill skill, Creature target) { attacker.StopMove(); target.StopMove(); // Create actions var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, target.EntityId); aAction.Set(AttackerOptions.Result); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result); tAction.Stun = TargetStun; var cap = new CombatActionPack(attacker, skill.Info.Id, aAction, tAction); // Damage var damage = this.GetDamage(attacker, skill); // Elements damage *= this.GetElementalDamageMultiplier(attacker, target); // Critical Hit var critChance = attacker.GetTotalCritChance(target.Protection, true); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Reduce damage Defense.Handle(aAction, tAction, ref damage); SkillHelper.HandleMagicDefenseProtection(target, ref damage); ManaShield.Handle(target, ref damage, tAction); // Mana Deflector var delayReduction = ManaDeflector.Handle(attacker, target, ref damage, tAction); // Deal damage if (damage > 0) target.TakeDamage(tAction.Damage = damage, attacker); target.Aggro(attacker); // Knock down on deadly if (target.Conditions.Has(ConditionsA.Deadly)) { tAction.Set(TargetOptions.KnockDown); tAction.Stun = TargetStun; } // Reduce stun, based on ping if (delayReduction > 0) tAction.Stun = (short)Math.Max(0, tAction.Stun - (tAction.Stun / 100 * delayReduction)); // Death/Knockback if (target.IsDead) { tAction.Set(TargetOptions.FinishingKnockDown); } else { // If knocked down, instant recovery, // if repeat hit, knock down, // otherwise potential knock back. if (target.IsKnockedDown) { tAction.Stun = 0; } else if (target.Stability < MinStability) { tAction.Set(TargetOptions.KnockDown); } else { var stabilityReduction = StabilityReduction; // Reduce reduction, based on ping // While the Wiki says that "the Knockdown Gauge [does not] // build up", tests show that it does. However, it's // reduced, assumedly based on the MD rank. if (delayReduction > 0) stabilityReduction = (short)Math.Max(0, stabilityReduction - (stabilityReduction / 100 * delayReduction)); target.Stability -= stabilityReduction; if (target.IsUnstable) { tAction.Set(TargetOptions.KnockBack); } } } if (tAction.IsKnockBack) attacker.Shove(target, KnockbackDistance); // Override stun set by defense aAction.Stun = AttackerStun; Send.Effect(attacker, Effect.UseMagic, EffectSkillName); Send.SkillUseStun(attacker, skill.Info.Id, aAction.Stun, 1); skill.Stacks--; // Update current weapon SkillHelper.UpdateWeapon(attacker, target, ProficiencyGainType.Melee, attacker.RightHand); cap.Handle(); }
public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { if (attacker.IsStunned) { return(CombatSkillResult.Okay); } var target = attacker.Region.GetCreature(targetEntityId); if (target == null) { return(CombatSkillResult.Okay); } if (!attacker.GetPosition().InRange(target.GetPosition(), attacker.AttackRangeFor(target))) { return(CombatSkillResult.OutOfRange); } attacker.StopMove(); var targetPosition = target.StopMove(); // Counter if (SkillHelper.HandleCounter(target, attacker)) { return(CombatSkillResult.Okay); } var rightWeapon = attacker.Inventory.RightHand; var leftWeapon = attacker.Inventory.LeftHand; var magazine = attacker.Inventory.Magazine; var dualWield = (rightWeapon != null && leftWeapon != null && leftWeapon.Data.WeaponType != 0); var maxHits = (byte)(dualWield ? 2 : 1); int prevId = 0; for (byte i = 1; i <= maxHits; ++i) { var weapon = (i == 1 ? rightWeapon : leftWeapon); var aAction = new AttackerAction(CombatActionType.Hit, attacker, skill.Info.Id, targetEntityId); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); var cap = new CombatActionPack(attacker, skill.Info.Id, aAction, tAction); cap.Hit = i; cap.MaxHits = maxHits; cap.PrevId = prevId; prevId = cap.Id; // Default attacker options aAction.Set(AttackerOptions.Result); if (dualWield) { aAction.Set(AttackerOptions.DualWield); } // Base damage var damage = attacker.GetRndDamage(weapon); // Critical Hit SkillHelper.HandleCritical(attacker, attacker.GetCritChanceFor(target), ref damage, tAction); // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Defense SkillHelper.HandleDefense(aAction, tAction, ref damage); // Mana Shield SkillHelper.HandleManaShield(target, ref damage, tAction); // Deal with it! if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); } // Evaluate caused damage if (!target.IsDead) { if (tAction.Type != CombatActionType.Defended) { target.KnockBack += this.GetKnockBack(weapon) / maxHits; if (target.KnockBack >= 100 && target.Is(RaceStands.KnockBackable)) { tAction.Set(tAction.Has(TargetOptions.Critical) ? TargetOptions.KnockDown : TargetOptions.KnockBack); } } } else { tAction.Set(TargetOptions.FinishingKnockDown); } // React to knock back if (tAction.IsKnockBack) { var newPos = attacker.GetPosition().GetRelative(targetPosition, KnockBackDistance); Position intersection; if (target.Region.Collisions.Find(targetPosition, newPos, out intersection)) { newPos = targetPosition.GetRelative(intersection, -50); } target.SetPosition(newPos.X, newPos.Y); aAction.Set(AttackerOptions.KnockBackHit2); // Remove dual wield option if last hit doesn't come from // the second weapon. if (cap.MaxHits != cap.Hit) { aAction.Options &= ~AttackerOptions.DualWield; } } // Set stun time if (tAction.Type != CombatActionType.Defended) { aAction.Stun = this.GetAttackerStun(weapon, tAction.IsKnockBack); tAction.Stun = this.GetTargetStun(weapon, tAction.IsKnockBack); } // Second hit doubles stun time for normal hits if (cap.Hit == 2 && !tAction.IsKnockBack) { aAction.Stun *= 2; } // Update current weapon SkillHelper.UpdateWeapon(attacker, target, weapon); cap.Handle(); // No second hit if target was knocked back if (tAction.IsKnockBack) { break; } } return(CombatSkillResult.Okay); }
/// <summary> /// Uses the skill. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetEntityId"></param> /// <returns></returns> public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { // Get target var target = attacker.Region.GetCreature(targetEntityId); if (target == null) return CombatSkillResult.InvalidTarget; var targetPos = target.GetPosition(); var attackerPos = attacker.GetPosition(); var actionType = (attacker.IsElf ? CombatActionPackType.ChainRangeAttack : CombatActionPackType.NormalAttack); var attackerStun = (short)(actionType == CombatActionPackType.ChainRangeAttack ? AttackerStunElf : AttackerStun); // "Cancels" the skill // 800 = old load time? == aAction.Stun? Varies? Doesn't seem to be a stun. Send.SkillUse(attacker, skill.Info.Id, attackerStun, 1); var chance = attacker.AimMeter.GetAimChance(target); var rnd = RandomProvider.Get().NextDouble() * 100; var successfulHit = (rnd < chance); var maxHits = (actionType == CombatActionPackType.ChainRangeAttack && successfulHit ? 2 : 1); var prevId = 0; for (byte i = 1; i <= maxHits; ++i) { target.StopMove(); // Actions var cap = new CombatActionPack(attacker, skill.Info.Id); cap.Hit = i; cap.Type = actionType; cap.PrevId = prevId; prevId = cap.Id; var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, targetEntityId); aAction.Set(AttackerOptions.Result); aAction.Stun = attackerStun; cap.Add(aAction); // Target action if hit if (successfulHit) { var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result); tAction.Stun = (short)(actionType == CombatActionPackType.ChainRangeAttack ? TargetStunElf : TargetStun); if (actionType == CombatActionPackType.ChainRangeAttack) tAction.EffectFlags = EffectFlags.Unknown; cap.Add(tAction); // Damage var damage = attacker.GetRndRangedDamage(); // Elementals damage *= attacker.CalculateElementalDamageMultiplier(target); // More damage with fire arrow // XXX: Does this affect the element? if (attacker.Temp.FireArrow) damage *= FireBonus; // Critical Hit var critChance = attacker.GetRightCritChance(target.Protection); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Defense Defense.Handle(aAction, tAction, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Natural Shield var delayReduction = NaturalShield.Handle(attacker, target, ref damage, tAction); // Deal with it! if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); SkillHelper.HandleInjury(attacker, target, damage); } // Knock down on deadly if (target.Conditions.Has(ConditionsA.Deadly)) { tAction.Set(TargetOptions.KnockDown); tAction.Stun = (short)(actionType == CombatActionPackType.ChainRangeAttack ? TargetStunElf : TargetStun); } // Aggro target.Aggro(attacker); // Death/Knockback if (target.IsDead) { tAction.Set(TargetOptions.FinishingKnockDown); maxHits = 1; } else { // Insta-recover in knock down // TODO: Tied to stability? if (target.IsKnockedDown) { tAction.Stun = 0; } // Knock down if hit repeatedly else if (target.Stability < 30) { tAction.Set(TargetOptions.KnockDown); } // Normal stability reduction else { var stabilityReduction = (actionType == CombatActionPackType.ChainRangeAttack ? StabilityReductionElf : StabilityReduction); // Reduce reduction, based on ping // According to the Wiki, "the Knockdown Gauge // [does not] build up", but it's still possible // to knock back with repeated hits. The stability // reduction is probably reduced, just like stun. if (delayReduction > 0) stabilityReduction = (short)Math.Max(0, stabilityReduction - (stabilityReduction / 100 * delayReduction)); target.Stability -= stabilityReduction; if (target.IsUnstable) { tAction.Set(TargetOptions.KnockBack); } } } // Knock Back if (tAction.IsKnockBack) attacker.Shove(target, KnockBackDistance); // Reduce stun, based on ping if (delayReduction > 0) tAction.Stun = (short)Math.Max(0, tAction.Stun - (tAction.Stun / 100 * delayReduction)); } // Skill training if (skill.Info.Rank == SkillRank.Novice || skill.Info.Rank == SkillRank.RF) skill.Train(1); // Try ranged attack. // Reduce arrows if (attacker.Magazine != null && !ChannelServer.Instance.Conf.World.InfiniteArrows && !attacker.Magazine.HasTag("/unlimited_arrow/")) attacker.Inventory.Decrement(attacker.Magazine); cap.Handle(); } // Disable fire arrow effect if (attacker.Temp.FireArrow) Send.Effect(attacker, Effect.FireArrow, false); return CombatSkillResult.Okay; }
/// <summary> /// Handles skill usage. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetEntityId"></param> /// <returns></returns> public override CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { // Check target var target = attacker.Region.GetCreature(targetEntityId); if (target == null) return CombatSkillResult.InvalidTarget; if (target.IsNotReadyToBeHit) return CombatSkillResult.Okay; if ((attacker.IsStunned || attacker.IsOnAttackDelay) && attacker.InterceptingSkillId == SkillId.None) return CombatSkillResult.Okay; // Check range var attackerPosition = attacker.GetPosition(); var targetPosition = target.GetPosition(); if (!attacker.IgnoreAttackRange && (!attackerPosition.InRange(targetPosition, attacker.AttackRangeFor(target)))) { return CombatSkillResult.OutOfRange; } if (!attacker.IgnoreAttackRange && (attacker.Region.Collisions.Any(attackerPosition, targetPosition) // Check collisions between position || target.Conditions.Has(ConditionsA.Invisible))) // Check visiblility (GM) { return CombatSkillResult.Okay; } attacker.IgnoreAttackRange = false; // Against Normal Attack Skill combatMastery = target.Skills.Get(SkillId.CombatMastery); if (combatMastery != null && (target.Skills.ActiveSkill == null || target.Skills.ActiveSkill == combatMastery || target.Skills.IsReady(SkillId.FinalHit)) && target.IsInBattleStance && target.Target == attacker && target.AttemptingAttack && (!target.IsStunned || target.IsKnockedDown)) { target.InterceptingSkillId = SkillId.Smash; target.IgnoreAttackRange = true; var skillHandler = ChannelServer.Instance.SkillManager.GetHandler<ICombatSkill>(combatMastery.Info.Id); if (skillHandler == null) { Log.Error("Smash.Use: Target's skill handler not found for '{0}'.", combatMastery.Info.Id); return CombatSkillResult.Okay; } skillHandler.Use(target, combatMastery, attacker.EntityId); return CombatSkillResult.Okay; } // Against Windmill //TODO: Change this into the new NPC client system when it comes out if needed. Skill windmill = target.Skills.Get(SkillId.Windmill); if (windmill != null && target.Skills.IsReady(SkillId.Windmill) && !target.IsPlayer && target.CanAttack(attacker)) { target.InterceptingSkillId = SkillId.Smash; var skillHandler = ChannelServer.Instance.SkillManager.GetHandler<IUseable>(windmill.Info.Id) as Windmill; if (skillHandler == null) { Log.Error("Smash.Use: Target's skill handler not found for '{0}'.", windmill.Info.Id); return CombatSkillResult.Okay; } skillHandler.Use(target, windmill); return CombatSkillResult.Okay; } // Against Smash Skill smash = target.Skills.Get(SkillId.Smash); if (smash != null && target.Skills.IsReady(SkillId.Smash) && target.IsInBattleStance && target.Target == attacker && !target.IsStunned && attacker.CanAttack(target)) { var attackerStunTime = CombatMastery.GetAttackerStun(attacker, attacker.RightHand, false); var targetStunTime = CombatMastery.GetAttackerStun(target, target.Inventory.RightHand, false); if ((target.LastKnockedBackBy == attacker && target.KnockDownTime > attacker.KnockDownTime && target.KnockDownTime.AddMilliseconds(targetStunTime) < DateTime.Now //If last knocked down within the time it takes for you to finish attacking. || attackerStunTime > targetStunTime && !Math2.Probability(((2725 - attackerStunTime) / 2500) * 100) //Probability in percentage that you will not lose. 2725 is 2500 (Slowest stun) + 225 (Fastest stun divided by two so that the fastest stun isn't 100%) && !(attacker.LastKnockedBackBy == target && attacker.KnockDownTime > target.KnockDownTime && attacker.KnockDownTime.AddMilliseconds(attackerStunTime) < DateTime.Now))) { if (target.CanAttack(attacker)) { target.InterceptingSkillId = SkillId.Smash; target.IgnoreAttackRange = true; var skillHandler = ChannelServer.Instance.SkillManager.GetHandler<ICombatSkill>(smash.Info.Id); if (skillHandler == null) { Log.Error("Smash.Use: Target's skill handler not found for '{0}'.", smash.Info.Id); return CombatSkillResult.Okay; } skillHandler.Use(target, smash, attacker.EntityId); return CombatSkillResult.Okay; } } else { attacker.InterceptingSkillId = SkillId.Smash; } } // Stop movement attacker.StopMove(); target.StopMove(); target.IgnoreAttackRange = false; // Counter if (Counterattack.Handle(target, attacker)) return CombatSkillResult.Okay; var weapon = attacker.RightHand; ICollection<Creature> targets = null; if (skill.Info.Rank >= SkillRank.R5 && weapon != null && weapon.Data.SplashRadius != 0 && weapon.Data.SplashAngle != 0 || weapon == null) { targets = attacker.GetTargetableCreaturesInCone(weapon != null ? (int)weapon.Data.SplashRadius : 200, weapon != null ? (int)weapon.Data.SplashAngle : 20); foreach (var splashTarget in targets) { if (splashTarget != target) { // Counter if (Counterattack.Handle(target, attacker)) return CombatSkillResult.Okay; } } } // Prepare combat actions var aAction = new AttackerAction(CombatActionType.HardHit, attacker, skill.Info.Id, targetEntityId); aAction.Set(AttackerOptions.Result | AttackerOptions.KnockBackHit2); TargetAction tAction; if (attacker.InterceptingSkillId == SkillId.Smash) { aAction.Options |= AttackerOptions.Result; tAction = new TargetAction(CombatActionType.CounteredHit, target, attacker, SkillId.Smash); } else { tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); } tAction.Set(TargetOptions.Result | TargetOptions.Smash); attacker.InterceptingSkillId = SkillId.None; var cap = new CombatActionPack(attacker, skill.Info.Id, tAction, aAction); // Calculate damage var damage = this.GetDamage(attacker, skill); var critChance = this.GetCritChance(attacker, target, skill); // Critical Hit CriticalHit.Handle(attacker, critChance, ref damage, tAction); var maxDamage = damage; //Damage without Defense and Protection // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction, maxDamage); // Apply damage target.TakeDamage(tAction.Damage = damage, attacker); // Aggro target.Aggro(attacker); if (target.IsDead) tAction.Set(TargetOptions.FinishingHit | TargetOptions.Finished); // Set Stun/Knockback attacker.Stun = aAction.Stun = StunTime; target.Stun = tAction.Stun = StunTime; target.Stability = Creature.MinStability; // Set knockbacked position attacker.Shove(target, KnockbackDistance); // Response Send.SkillUseStun(attacker, skill.Info.Id, AfterUseStun, 1); // Update both weapons SkillHelper.UpdateWeapon(attacker, target, attacker.RightHand, attacker.LeftHand); var critSkill = attacker.Skills.Get(SkillId.CriticalHit); if (skill.Info.Rank >= SkillRank.R5 && weapon != null && weapon.Data.SplashRadius != 0 && weapon.Data.SplashAngle != 0) { foreach (var splashTarget in targets) { if (splashTarget != target) { if (splashTarget.IsNotReadyToBeHit) continue; TargetAction tSplashAction = new TargetAction(CombatActionType.TakeHit, splashTarget, attacker, skill.Info.Id); tSplashAction.Set(TargetOptions.Result | TargetOptions.Smash); // Base damage float damageSplash = this.GetDamage(attacker, skill); attacker.CalculateSplashDamage(splashTarget, ref damageSplash, skill, critSkill, aAction, tAction, tSplashAction); // Deal with it! if (damageSplash > 0) splashTarget.TakeDamage(tSplashAction.Damage = damageSplash, attacker); // Alert splashTarget.Aggro(attacker, true); if (splashTarget.IsDead) tSplashAction.Set(TargetOptions.FinishingHit | TargetOptions.Finished); splashTarget.Stun = tSplashAction.Stun = StunTime; splashTarget.Stability = Creature.MinStability; // Set knockbacked position attacker.Shove(splashTarget, KnockbackDistance); cap.Add(tSplashAction); } } } // Action! cap.Handle(); return CombatSkillResult.Okay; }
/// <summary> /// Handles attack. /// </summary> /// <param name="attacker">The creature attacking.</param> /// <param name="skill">The skill being used.</param> /// <param name="targetEntityId">The entity id of the target.</param> /// <returns></returns> public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { var target = attacker.Region.GetCreature(targetEntityId); if (target == null) return CombatSkillResult.Okay; if (target.IsNotReadyToBeHit) return CombatSkillResult.Okay; if ((attacker.IsStunned || attacker.IsOnAttackDelay) && attacker.InterceptingSkillId == SkillId.None) return CombatSkillResult.Okay; var attackerPosition = attacker.GetPosition(); var targetPosition = target.GetPosition(); if (!attacker.IgnoreAttackRange && (!attackerPosition.InRange(targetPosition, attacker.AttackRangeFor(target)))) { return CombatSkillResult.OutOfRange; } if (!attacker.IgnoreAttackRange && (attacker.Region.Collisions.Any(attackerPosition, targetPosition) // Check collisions between position || target.Conditions.Has(ConditionsA.Invisible))) // Check visiblility (GM) { return CombatSkillResult.Okay; } attacker.IgnoreAttackRange = false; //Against Smash Skill smash = target.Skills.Get(SkillId.Smash); if (smash != null && target.Skills.IsReady(SkillId.Smash) && attacker.CanAttack(target)) attacker.InterceptingSkillId = SkillId.Smash; var rightWeapon = attacker.RightHand; var leftWeapon = attacker.Inventory.LeftHand; var dualWield = (rightWeapon != null && leftWeapon != null && leftWeapon.Data.WeaponType != 0 && (leftWeapon.HasTag("/weapon/edged/") || leftWeapon.HasTag("/weapon/blunt/"))); var staminaUsage = (rightWeapon != null && rightWeapon.Data.StaminaUsage != 0 ? rightWeapon.Data.StaminaUsage : 0.7f) + (dualWield ? leftWeapon.Data.StaminaUsage : 0f); var lowStamina = false; if (attacker.Stamina < staminaUsage) { lowStamina = true; Send.Notice(attacker, Localization.Get("Your stamina is too low to attack properly!")); } attacker.Stamina -= staminaUsage; Send.StatUpdate(attacker, StatUpdateType.Private, Stat.Stamina); // Against Combat Mastery Skill combatMastery = target.Skills.Get(SkillId.CombatMastery); var simultaneousAttackStun = 0; if (attacker.InterceptingSkillId != SkillId.CombatMastery && target.InterceptingSkillId != SkillId.CombatMastery) { if (combatMastery != null && (target.Skills.ActiveSkill == null || target.Skills.ActiveSkill == combatMastery || target.Skills.IsReady(SkillId.FinalHit)) && target.IsInBattleStance && target.Target == attacker && target.AttemptingAttack && (!target.IsStunned || target.IsKnockedDown) && attacker.CanAttack(target)) { var attackerStunTime = CombatMastery.GetAttackerStun(attacker, attacker.RightHand, false); var targetStunTime = CombatMastery.GetAttackerStun(target, target.Inventory.RightHand, false); if ((target.LastKnockedBackBy == attacker && target.KnockDownTime > attacker.KnockDownTime && target.KnockDownTime.AddMilliseconds(targetStunTime) < DateTime.Now //If last knocked down within the time it takes for you to finish attacking. || attackerStunTime > targetStunTime && !Math2.Probability(((2725 - attackerStunTime) / 2500) * 100) //Probability in percentage that you will not lose. 2725 is 2500 (Slowest stun) + 225 (Fastest stun divided by two so that the fastest stun isn't 100%) && !(attacker.LastKnockedBackBy == target && attacker.KnockDownTime > target.KnockDownTime && attacker.KnockDownTime.AddMilliseconds(attackerStunTime) < DateTime.Now))) { if (target.CanAttack(attacker)) { target.InterceptingSkillId = SkillId.CombatMastery; target.IgnoreAttackRange = true; var skillHandler = ChannelServer.Instance.SkillManager.GetHandler<ICombatSkill>(combatMastery.Info.Id); if (skillHandler == null) { Log.Error("CombatMastery.Use: Target's skill handler not found for '{0}'.", combatMastery.Info.Id); return CombatSkillResult.Okay; } skillHandler.Use(target, combatMastery, attacker.EntityId); return CombatSkillResult.Okay; } } else { if (Math2.Probability(((2725 - attackerStunTime) / 2500) * 100)) //Probability in percentage that it will be an interception instead of a double hit. { attacker.InterceptingSkillId = SkillId.CombatMastery; } else { attacker.InterceptingSkillId = SkillId.CombatMastery; if (target.CanAttack(attacker)) { target.InterceptingSkillId = SkillId.CombatMastery; target.IgnoreAttackRange = true; var skillHandler = ChannelServer.Instance.SkillManager.GetHandler<ICombatSkill>(combatMastery.Info.Id); if (skillHandler == null) { Log.Error("CombatMastery.Use: Target's skill handler not found for '{0}'.", combatMastery.Info.Id); } else { skillHandler.Use(target, combatMastery, attacker.EntityId); simultaneousAttackStun = attacker.Stun; attacker.Stun = 0; } } } } } } attacker.StopMove(); targetPosition = target.StopMove(); // Counter if (Counterattack.Handle(target, attacker)) return CombatSkillResult.Okay; ICollection<Creature> targets = null; if (rightWeapon != null && rightWeapon.Data.SplashRadius != 0 && rightWeapon.Data.SplashAngle != 0 || rightWeapon == null) { targets = attacker.GetTargetableCreaturesInCone(rightWeapon != null ? (int)rightWeapon.Data.SplashRadius : 204, rightWeapon != null ? (int)rightWeapon.Data.SplashAngle : 60); foreach (var splashTarget in targets) { if (splashTarget != target) { // Counter if (Counterattack.Handle(target, attacker)) return CombatSkillResult.Okay; } } } var magazine = attacker.Inventory.Magazine; var maxHits = (byte)(dualWield ? 2 : 1); int prevId = 0; var defenseStun = 0; for (byte i = 1; i <= maxHits; ++i) { var weapon = (i == 1 ? rightWeapon : leftWeapon); var weaponIsKnuckle = (weapon != null && weapon.Data.HasTag("/knuckle/")); AttackerAction aAction; TargetAction tAction; if (attacker.InterceptingSkillId == SkillId.Smash) { aAction = new AttackerAction(CombatActionType.SimultaneousHit, attacker, SkillId.CombatMastery, target.EntityId); aAction.Options |= AttackerOptions.Result; tAction = new TargetAction(CombatActionType.CounteredHit, target, attacker, SkillId.Smash); tAction.Options |= TargetOptions.Result; } else if (attacker.InterceptingSkillId == SkillId.CombatMastery) { aAction = new AttackerAction(CombatActionType.SimultaneousHit, attacker, SkillId.CombatMastery, target.EntityId); aAction.Options |= AttackerOptions.Result; tAction = new TargetAction(CombatActionType.CounteredHit, target, attacker, target.Skills.IsReady(SkillId.FinalHit) ? SkillId.FinalHit : SkillId.CombatMastery); tAction.Options |= TargetOptions.Result; } else { aAction = new AttackerAction(CombatActionType.Hit, attacker, skill.Info.Id, targetEntityId); tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, target.Skills.IsReady(SkillId.FinalHit) ? SkillId.FinalHit : SkillId.CombatMastery); aAction.Options |= AttackerOptions.Result; tAction.Options |= TargetOptions.Result; } attacker.InterceptingSkillId = SkillId.None; var cap = new CombatActionPack(attacker, skill.Info.Id, tAction, aAction); cap.Hit = i; cap.MaxHits = maxHits; cap.PrevId = prevId; prevId = cap.Id; // Default attacker options aAction.Set(AttackerOptions.Result); if (dualWield) aAction.Set(AttackerOptions.DualWield); // Base damage var damage = (i == 1 ? attacker.GetRndRightHandDamage() : attacker.GetRndLeftHandDamage()); if (lowStamina) { damage = attacker.GetRndBareHandDamage(); } // Critical Hit var critShieldReduction = (target.LeftHand != null ? target.LeftHand.Data.DefenseBonusCrit : 0); var critChance = (i == 1 ? attacker.GetRightCritChance(target.Protection + critShieldReduction) : attacker.GetLeftCritChance(target.Protection + critShieldReduction)); CriticalHit.Handle(attacker, critChance, ref damage, tAction); var maxDamage = damage; //Damage without Defense and Protection // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Defense var tActionOldType = tAction.Type; Defense.Handle(aAction, tAction, ref damage); if (i == 1 && tAction.Type == CombatActionType.Defended) { defenseStun = tAction.Stun; } // Mana Shield ManaShield.Handle(target, ref damage, tAction, maxDamage); // Deal with it! if (damage > 0) target.TakeDamage(tAction.Damage = damage, attacker); if (tAction.Type == CombatActionType.Defended && target.Life <= 0) { tAction.Type = tActionOldType; } // Aggro target.Aggro(attacker); // Evaluate caused damage if (!target.IsDead) { if (tAction.Type != CombatActionType.Defended) { if (!target.Skills.IsReady(SkillId.FinalHit)) { target.Stability -= this.GetStabilityReduction(attacker, weapon) / maxHits; // React normal for CombatMastery, knock down if // FH and not dual wield, don't knock at all if dual. if (skill.Info.Id != SkillId.FinalHit) { // Originally we thought you knock enemies back, unless it's a critical // hit, but apparently you knock *down* under normal circumstances. // More research to be done. if (target.IsUnstable && target.Is(RaceStands.KnockBackable)) //tAction.Set(tAction.Has(TargetOptions.Critical) ? TargetOptions.KnockDown : TargetOptions.KnockBack); tAction.Set(TargetOptions.KnockDown); if (target.Life < 0) tAction.Set(TargetOptions.KnockDown); } else if (!dualWield && !weaponIsKnuckle) { target.Stability = Creature.MinStability; tAction.Set(TargetOptions.KnockDown); } } } } else { tAction.Set(TargetOptions.FinishingKnockDown); } // React to knock back if (tAction.IsKnockBack && tAction.Type != CombatActionType.Defended) { if (!target.Skills.IsReady(SkillId.FinalHit)) { attacker.Shove(target, KnockBackDistance); } aAction.Set(AttackerOptions.KnockBackHit2); // Remove dual wield option if last hit doesn't come from // the second weapon. if (cap.MaxHits != cap.Hit) aAction.Options &= ~AttackerOptions.DualWield; } else if (tAction.Type == CombatActionType.Defended) { // Remove dual wield option if last hit doesn't come from // the second weapon. if (cap.MaxHits != cap.Hit) aAction.Options &= ~AttackerOptions.DualWield; } // Set stun time if (tAction.Type != CombatActionType.Defended) { if (simultaneousAttackStun == 0) { aAction.Stun = GetAttackerStun(attacker, weapon, tAction.IsKnockBack && ((skill.Info.Id != SkillId.FinalHit) && !target.IsDead || AuraData.FeaturesDb.IsEnabled("CombatSystemRenewal"))); } else { aAction.Stun = (short)simultaneousAttackStun; } if (!target.Skills.IsReady(SkillId.FinalHit)) { tAction.Stun = GetTargetStun(attacker, weapon, tAction.IsKnockBack); } if (target.IsDead && skill.Info.Id != SkillId.FinalHit) { attacker.AttackDelayTime = DateTime.Now.AddMilliseconds(GetAttackerStun(attacker, weapon, true)); } } // Second hit doubles stun time for normal hits if (cap.Hit == 2 && !tAction.IsKnockBack) aAction.Stun *= 2; // Update current weapon SkillHelper.UpdateWeapon(attacker, target, weapon); var critSkill = attacker.Skills.Get(SkillId.CriticalHit); if (weapon != null && weapon.Data.SplashRadius != 0 && weapon.Data.SplashAngle != 0) { foreach (var splashTarget in targets) { if (splashTarget != target) { if (splashTarget.IsNotReadyToBeHit) continue; TargetAction tSplashAction = new TargetAction(CombatActionType.TakeHit, splashTarget, attacker, skill.Info.Id); // Base damage float damageSplash; if (lowStamina) { damageSplash = attacker.GetRndBareHandDamage(); } else { damageSplash = (i == 1 ? attacker.GetRndRightHandDamage() : attacker.GetRndLeftHandDamage()); } attacker.CalculateSplashDamage(splashTarget, ref damageSplash, skill, critSkill, aAction, tAction, tSplashAction, weapon); // Deal with it! if (damageSplash > 0) splashTarget.TakeDamage(tSplashAction.Damage = damageSplash, attacker); // Alert splashTarget.Aggro(attacker, true); // Evaluate caused damage if (!splashTarget.IsDead) { if (tSplashAction.Type != CombatActionType.Defended) { if (!splashTarget.Skills.IsReady(SkillId.FinalHit)) { splashTarget.Stability -= (this.GetStabilityReduction(attacker, weapon) / maxHits) / 2; //Less stability reduction for splash damage. // React normal for CombatMastery, knock down if // FH and not dual wield, don't knock at all if dual. if (skill.Info.Id != SkillId.FinalHit) { // Originally we thought you knock enemies back, unless it's a critical // hit, but apparently you knock *down* under normal circumstances. // More research to be done. if (splashTarget.IsUnstable && splashTarget.Is(RaceStands.KnockBackable)) //tSplashAction.Set(tSplashAction.Has(TargetOptions.Critical) ? TargetOptions.KnockDown : TargetOptions.KnockBack); tSplashAction.Set(TargetOptions.KnockDown); if (splashTarget.Life < 0) tSplashAction.Set(TargetOptions.KnockDown); } else if (!dualWield && !weaponIsKnuckle) { splashTarget.Stability = Creature.MinStability; tSplashAction.Set(TargetOptions.KnockDown); } } } } else { tSplashAction.Set(TargetOptions.FinishingKnockDown); } // React to knock back if (tSplashAction.IsKnockBack && tSplashAction.Type != CombatActionType.Defended) { if (!splashTarget.Skills.IsReady(SkillId.FinalHit)) { attacker.Shove(splashTarget, KnockBackDistance); } } // Set stun time if (tSplashAction.Type != CombatActionType.Defended) { if (!splashTarget.Skills.IsReady(SkillId.FinalHit)) { if (defenseStun != 0) tSplashAction.Stun = (short)defenseStun; else tSplashAction.Stun = GetTargetStun(attacker, weapon, tSplashAction.IsKnockBack); } } cap.Add(tSplashAction); } } } cap.Handle(); // No second hit if target was knocked back or defended. if (tAction.IsKnockBack || tAction.Type == CombatActionType.Defended) break; } attacker.AttemptingAttack = false; return CombatSkillResult.Okay; }
/// <summary> /// Uses the skill. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetEntityId"></param> /// <returns></returns> public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { // Get target var target = attacker.Region.GetCreature(targetEntityId); if (target == null) { return(CombatSkillResult.InvalidTarget); } // Actions var cap = new CombatActionPack(attacker, skill.Info.Id); var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, targetEntityId); aAction.Set(AttackerOptions.Result); aAction.Stun = AttackerStun; cap.Add(aAction); // Hit by chance var chance = attacker.AimMeter.GetAimChance(target); var rnd = RandomProvider.Get(); if (rnd.NextDouble() * 100 < chance) { target.StopMove(); aAction.Set(AttackerOptions.KnockBackHit2); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result | TargetOptions.CleanHit); tAction.Stun = TargetStun; cap.Add(tAction); // TODO: Splash damage // Damage var damage = this.GetDamage(attacker, skill); // Elementals damage *= attacker.CalculateElementalDamageMultiplier(target); // More damage with fire arrow // XXX: Does this affect the element? if (attacker.Temp.FireArrow) { damage *= FireBonus; } // Critical Hit var critChance = attacker.GetRightCritChance(target.Protection); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Natural Shield // Ignore delay reduction, as knock downs shouldn't be shortened NaturalShield.Handle(attacker, target, ref damage, tAction); // Deal with it! if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); SkillHelper.HandleInjury(attacker, target, damage); } // Knock down // If target is using a shield and defense, don't KD. var targetLeftHand = target.LeftHand; if (tAction.SkillId != SkillId.Defense || targetLeftHand == null || !targetLeftHand.IsShield) { // TODO: We have to calculate knockback distance right attacker.Shove(target, KnockBackDistance); tAction.Set(TargetOptions.KnockDownFinish); } // Aggro target.Aggro(attacker); if (target.IsDead) { aAction.Set(AttackerOptions.KnockBackHit1); tAction.Set(TargetOptions.Finished); } } else { aAction.Set(AttackerOptions.Missed); } // Update current weapon SkillHelper.UpdateWeapon(attacker, target, ProficiencyGainType.Ranged, attacker.RightHand); // Reduce arrows if (attacker.Magazine != null && !ChannelServer.Instance.Conf.World.InfiniteArrows && !attacker.Magazine.HasTag("/unlimited_arrow/")) { attacker.Inventory.Decrement(attacker.Magazine); } // Disable fire arrow effect if (attacker.Temp.FireArrow) { Send.Effect(attacker, Effect.FireArrow, false); } // "Cancels" the skill // 800 = old load time? == aAction.Stun? Varies? Doesn't seem to be a stun. Send.SkillUse(attacker, skill.Info.Id, 800, 1); cap.Handle(); return(CombatSkillResult.Okay); }
/// <summary> /// Uses the skill. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetEntityId"></param> /// <returns></returns> public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { // Get target var target = attacker.Region.GetCreature(targetEntityId); if (target == null) return CombatSkillResult.InvalidTarget; // Actions var cap = new CombatActionPack(attacker, skill.Info.Id); var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, targetEntityId); aAction.Set(AttackerOptions.Result); aAction.Stun = AttackerStun; cap.Add(aAction); // Hit by chance var chance = attacker.AimMeter.GetAimChance(target); var rnd = RandomProvider.Get(); if (rnd.NextDouble() * 100 < chance) { target.StopMove(); aAction.Set(AttackerOptions.KnockBackHit2); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result | TargetOptions.CleanHit); tAction.Stun = TargetStun; cap.Add(tAction); // TODO: Splash damage // Damage var damage = this.GetDamage(attacker, skill); // Elementals damage *= attacker.CalculateElementalDamageMultiplier(target); // More damage with fire arrow // XXX: Does this affect the element? if (attacker.Temp.FireArrow) damage *= FireBonus; // Critical Hit var critChance = attacker.GetRightCritChance(target.Protection); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Defense Defense.Handle(aAction, tAction, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Natural Shield // Ignore delay reduction, as knock downs shouldn't be shortened NaturalShield.Handle(attacker, target, ref damage, tAction); // Deal with it! if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); SkillHelper.HandleInjury(attacker, target, damage); } // Knock down // If target is using a shield and defense, don't KD. var targetLeftHand = target.LeftHand; if (tAction.SkillId != SkillId.Defense || targetLeftHand == null || !targetLeftHand.IsShield) { // TODO: We have to calculate knockback distance right attacker.Shove(target, KnockBackDistance); tAction.Set(TargetOptions.KnockDownFinish); } // Aggro target.Aggro(attacker); if (target.IsDead) { aAction.Set(AttackerOptions.KnockBackHit1); tAction.Set(TargetOptions.Finished); } } else { aAction.Set(AttackerOptions.Missed); } // Reduce arrows if (attacker.Magazine != null && !ChannelServer.Instance.Conf.World.InfiniteArrows && !attacker.Magazine.HasTag("/unlimited_arrow/")) attacker.Inventory.Decrement(attacker.Magazine); // Disable fire arrow effect if (attacker.Temp.FireArrow) Send.Effect(attacker, Effect.FireArrow, false); // "Cancels" the skill // 800 = old load time? == aAction.Stun? Varies? Doesn't seem to be a stun. Send.SkillUse(attacker, skill.Info.Id, 800, 1); cap.Handle(); return CombatSkillResult.Okay; }
/// <summary> /// Uses the skill. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetEntityId"></param> /// <returns></returns> public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { // Get target var target = attacker.Region.GetCreature(targetEntityId); if (target == null) { return(CombatSkillResult.InvalidTarget); } var targetPos = target.GetPosition(); var attackerPos = attacker.GetPosition(); var actionType = (attacker.IsElf ? CombatActionPackType.ChainRangeAttack : CombatActionPackType.NormalAttack); var attackerStun = (short)(actionType == CombatActionPackType.ChainRangeAttack ? AttackerStunElf : AttackerStun); // "Cancels" the skill // 800 = old load time? == aAction.Stun? Varies? Doesn't seem to be a stun. Send.SkillUse(attacker, skill.Info.Id, attackerStun, 1); var chance = attacker.AimMeter.GetAimChance(target); var rnd = RandomProvider.Get().NextDouble() * 100; var successfulHit = (rnd < chance); var maxHits = (actionType == CombatActionPackType.ChainRangeAttack && successfulHit ? 2 : 1); var prevId = 0; for (byte i = 1; i <= maxHits; ++i) { target.StopMove(); // Actions var cap = new CombatActionPack(attacker, skill.Info.Id); cap.Hit = i; cap.Type = actionType; cap.PrevId = prevId; prevId = cap.Id; var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, targetEntityId); aAction.Set(AttackerOptions.Result); aAction.Stun = attackerStun; cap.Add(aAction); // Target action if hit if (successfulHit) { var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result); tAction.Stun = (short)(actionType == CombatActionPackType.ChainRangeAttack ? TargetStunElf : TargetStun); if (actionType == CombatActionPackType.ChainRangeAttack) { tAction.EffectFlags = EffectFlags.SpecialRangeHit; } cap.Add(tAction); // Damage var damage = attacker.GetRndRangedDamage(); // Elementals damage *= attacker.CalculateElementalDamageMultiplier(target); // More damage with fire arrow // XXX: Does this affect the element? if (attacker.Temp.FireArrow) { damage *= FireBonus; } // Critical Hit var critChance = attacker.GetRightCritChance(target.Protection); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Conditions SkillHelper.HandleConditions(attacker, target, ref damage); // Defense Defense.Handle(aAction, tAction, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Natural Shield var nsResult = NaturalShield.Handle(attacker, target, ref damage, tAction); var delayReduction = nsResult.DelayReduction; var pinged = nsResult.Pinged; // Deal with it! if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); SkillHelper.HandleInjury(attacker, target, damage); } // Knock down on deadly if (target.Conditions.Has(ConditionsA.Deadly)) { tAction.Set(TargetOptions.KnockDown); tAction.Stun = (short)(actionType == CombatActionPackType.ChainRangeAttack ? TargetStunElf : TargetStun); } // Aggro target.Aggro(attacker); // Death/Knockback if (target.IsDead) { tAction.Set(TargetOptions.FinishingKnockDown); maxHits = 1; } else { // Insta-recover in knock down // TODO: Tied to stability? if (target.IsKnockedDown) { tAction.Stun = 0; } // Knock down if hit repeatedly else if (target.Stability < 30) { tAction.Set(TargetOptions.KnockDown); } // Normal stability reduction else { var stabilityReduction = (actionType == CombatActionPackType.ChainRangeAttack ? StabilityReductionElf : StabilityReduction); // Reduce reduction, based on ping // According to the Wiki, "the Knockdown Gauge // [does not] build up", but it's still possible // to knock back with repeated hits. The stability // reduction is probably reduced, just like stun. if (delayReduction > 0) { stabilityReduction = (short)Math.Max(0, stabilityReduction - (stabilityReduction / 100 * delayReduction)); } target.Stability -= stabilityReduction; if (target.IsUnstable) { tAction.Set(TargetOptions.KnockBack); } } } // Knock Back if (tAction.IsKnockBack) { attacker.Shove(target, KnockBackDistance); } // Reduce stun, based on ping if (pinged && delayReduction > 0) { tAction.Stun = (short)Math.Max(0, tAction.Stun - (tAction.Stun / 100 * delayReduction)); } // No second hit if defended if (tAction.SkillId == SkillId.Defense) { // Set to 1 to prevent second run maxHits = 1; // Set normal type if hit didn't come from the second // arrow. if (cap.Hit != 2) { cap.Type = CombatActionPackType.NormalAttack; } // Override stun set by Defense aAction.Stun = DefenseAttackerStun; } } else { // Dummy target action on miss, so the client knows what // the target would've been. Possibly affects arrow // animations. var tAction = new TargetAction(CombatActionType.None, target, attacker, SkillId.None); cap.Add(tAction); } // Update current weapon SkillHelper.UpdateWeapon(attacker, target, ProficiencyGainType.Ranged, attacker.RightHand); // Skill training if (skill.Info.Rank == SkillRank.Novice || skill.Info.Rank == SkillRank.RF) { skill.Train(1); // Try ranged attack. } // Reduce arrows if (attacker.Magazine != null && !ChannelServer.Instance.Conf.World.InfiniteArrows && !attacker.Magazine.HasTag("/unlimited_arrow/")) { attacker.Inventory.Decrement(attacker.Magazine); } cap.Handle(); } // Disable fire arrow effect if (attacker.Temp.FireArrow) { Send.Effect(attacker, Effect.FireArrow, false); } return(CombatSkillResult.Okay); }
/// <summary> /// Uses the skill. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetEntityId"></param> /// <returns></returns> public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { // Get target var target = attacker.Region.GetCreature(targetEntityId); if (target == null) return CombatSkillResult.InvalidTarget; if (target.IsNotReadyToBeHit) return CombatSkillResult.Okay; var targetPos = target.GetPosition(); var attackerPos = attacker.GetPosition(); // Check range //if (!attackerPos.InRange(targetPos, attacker.RightHand.OptionInfo.EffectiveRange + 100)) // return CombatSkillResult.OutOfRange; // Actions var cap = new CombatActionPack(attacker, skill.Info.Id); var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, skill.Info.Id, targetEntityId); aAction.Set(AttackerOptions.Result); aAction.Stun = AttackerStun; cap.Add(aAction); // Hit by chance var chance = attacker.AimMeter.GetAimChance(target); var rnd = RandomProvider.Get(); if (rnd.NextDouble() * 100 < chance) { var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result); tAction.Stun = TargetStun; cap.Add(tAction); // Damage var damage = attacker.GetRndRangedDamage(); // More damage with fire arrow if (attacker.Temp.FireArrow) damage *= FireBonus; // Critical Hit var critShieldReduction = (target.LeftHand != null ? target.LeftHand.Data.DefenseBonusCrit : 0); var critChance = attacker.GetRightCritChance(target.Protection + critShieldReduction); CriticalHit.Handle(attacker, critChance, ref damage, tAction); var maxDamage = damage; //Damage without Defense and Protection // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Defense Defense.Handle(aAction, tAction, ref damage, true); // Mana Shield ManaShield.Handle(target, ref damage, tAction, maxDamage); // Deal with it! if (damage > 0) target.TakeDamage(tAction.Damage = damage, attacker); // Aggro target.Aggro(attacker); // Death/Knockback if (target.IsDead) { tAction.Set(TargetOptions.FinishingKnockDown); attacker.Shove(target, KnockBackDistance); } else { // Insta-recover in knock down // TODO: Tied to stability? if (target.IsKnockedDown) { tAction.Stun = 0; } // Knock down if hit repeatedly else if (target.Stability < 30) { tAction.Set(TargetOptions.KnockDown); } // Normal stability reduction else { target.Stability -= StabilityReduction; if (target.IsUnstable) { tAction.Set(TargetOptions.KnockBack); attacker.Shove(target, KnockBackDistance); } } } } // Skill training if (skill.Info.Rank == SkillRank.Novice || skill.Info.Rank == SkillRank.RF) skill.Train(1); // Try ranged attack. // Reduce arrows if (attacker.Magazine != null && !ChannelServer.Instance.Conf.World.InfiniteArrows) attacker.Inventory.Decrement(attacker.Magazine); // Disable fire arrow effect if (attacker.Temp.FireArrow) Send.Effect(attacker, Effect.FireArrow, false); // "Cancels" the skill // 800 = old load time? == aAction.Stun? Varies? Doesn't seem to be a stun. Send.SkillUse(attacker, skill.Info.Id, 800, 1); cap.Handle(); return CombatSkillResult.Okay; }
/// <summary> /// Bolt specific use code. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="target"></param> protected override void UseSkillOnTarget(Creature attacker, Skill skill, Creature mainTarget) { // Create actions var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, mainTarget.EntityId); aAction.Set(AttackerOptions.Result); var cap = new CombatActionPack(attacker, skill.Info.Id, aAction); // Get targets // Add the main target as first target, so it gets the first hit, // and the full damage. var targets = new List <Creature>(); targets.Add(mainTarget); var inSplashRange = attacker.GetTargetableCreaturesAround(mainTarget.GetPosition(), SplashRange); targets.AddRange(inSplashRange.Where(a => a != mainTarget)); // Damage var damage = this.GetDamage(attacker, skill); var max = Math.Min(targets.Count, skill.Stacks); for (int i = 0; i < max; ++i) { var target = targets[i]; var targetDamage = damage; target.StopMove(); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result); tAction.Stun = TargetStun; // Full damage for the first target, -10% for every subsequent one. targetDamage -= (targetDamage * 0.1f) * i; // Critical Hit var critChance = attacker.GetTotalCritChance(target.Protection, true); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Reduce damage Defense.Handle(aAction, tAction, ref targetDamage); SkillHelper.HandleMagicDefenseProtection(target, ref targetDamage); SkillHelper.HandleConditions(attacker, target, ref damage); ManaShield.Handle(target, ref targetDamage, tAction); // Mana Deflector var delayReduction = ManaDeflector.Handle(attacker, target, ref targetDamage, tAction); // Deal damage if (targetDamage > 0) { target.TakeDamage(tAction.Damage = targetDamage, attacker); } if (target == mainTarget) { target.Aggro(attacker); } // Reduce stun, based on ping if (delayReduction > 0) { tAction.Stun = (short)Math.Max(0, tAction.Stun - (tAction.Stun / 100 * delayReduction)); } // Death/Knockback if (target.IsDead) { tAction.Set(TargetOptions.FinishingKnockDown); } else { // If knocked down, instant recovery, // if repeat hit, knock down, // otherwise potential knock back. if (target.IsKnockedDown) { tAction.Stun = 0; } else if (target.Stability < MinStability) { tAction.Set(TargetOptions.KnockDown); } else { // If number of stacks is greater than the number of // targets hit, the targets are knocked back, which is // done by reducing the stability to min here. // Targets with high enough Mana Deflector might // negate this knock back, by reducing the stability // reduction to 0. var stabilityReduction = (skill.Stacks > targets.Count ? OverchargeStabilityReduction : StabilityReduction); // Reduce reduction, based on ping // While the Wiki says that "the Knockdown Gauge [does not] // build up", tests show that it does. However, it's // reduced, assumedly based on the MD rank. if (delayReduction > 0) { stabilityReduction = (short)Math.Max(0, stabilityReduction - (stabilityReduction / 100 * delayReduction)); } target.Stability -= stabilityReduction; if (target.IsUnstable) { tAction.Set(TargetOptions.KnockBack); } } } if (tAction.IsKnockBack) { attacker.Shove(target, KnockbackDistance); } cap.Add(tAction); } // Override stun set by defense aAction.Stun = AttackerStun; Send.Effect(attacker, Effect.UseMagic, EffectSkillName); Send.SkillUseStun(attacker, skill.Info.Id, aAction.Stun, 1); skill.Stacks = 0; // Update current weapon SkillHelper.UpdateWeapon(attacker, targets.FirstOrDefault(), ProficiencyGainType.Melee, attacker.RightHand); cap.Handle(); }
/// <summary> /// Uses the skill. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetEntityId"></param> /// <returns></returns> public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { // Get target var target = attacker.Region.GetCreature(targetEntityId); if (target == null) { return(CombatSkillResult.InvalidTarget); } // "Cancels" the skill // 800 = old load time? == aAction.Stun? Varies? Doesn't seem to be a stun. Send.SkillUse(attacker, skill.Info.Id, AttackerStun, 1); var chance = attacker.AimMeter.GetAimChance(target); var rnd = RandomProvider.Get().NextDouble() * 100; var successfulHit = (rnd < chance); // Actions var cap = new CombatActionPack(attacker, skill.Info.Id); var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, targetEntityId); aAction.Set(AttackerOptions.Result); aAction.Stun = AttackerStun; cap.Add(aAction); // Target action if hit if (successfulHit) { target.StopMove(); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result); tAction.Stun = TargetStun; cap.Add(tAction); // Damage var damage = attacker.GetRndRangedDamage() * (skill.RankData.Var1 / 100f); // Elementals damage *= attacker.CalculateElementalDamageMultiplier(target); // More damage with fire arrow if (attacker.Temp.FireArrow) { damage *= FireBonus; } // Critical Hit var critChance = attacker.GetRightCritChance(target.Protection); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Defense Defense.Handle(aAction, tAction, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Natural Shield var delayReduction = NaturalShield.Handle(attacker, target, ref damage, tAction); // Deal with it! if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); SkillHelper.HandleInjury(attacker, target, damage); } // Aggro target.Aggro(attacker); // Knock down on deadly if (target.Conditions.Has(ConditionsA.Deadly)) { tAction.Set(TargetOptions.KnockDown); } // Death/Knockback if (target.IsDead) { tAction.Set(TargetOptions.FinishingKnockDown); } // Knock Back if (tAction.IsKnockBack) { attacker.Shove(target, KnockBackDistance); } // Reduce stun, based on ping if (delayReduction > 0) { tAction.Stun = (short)Math.Max(0, tAction.Stun - (tAction.Stun / 100 * delayReduction)); } // TODO: "Weakened" state (G12S2 gfSupportShotRenewal) } // Update current weapon SkillHelper.UpdateWeapon(attacker, target, ProficiencyGainType.Ranged, attacker.RightHand); // Reduce arrows if (attacker.Magazine != null && !ChannelServer.Instance.Conf.World.InfiniteArrows && !attacker.Magazine.HasTag("/unlimited_arrow/")) { attacker.Inventory.Decrement(attacker.Magazine); } cap.Handle(); // Disable fire arrow effect if (attacker.Temp.FireArrow) { Send.Effect(attacker, Effect.FireArrow, false); } return(CombatSkillResult.Okay); }
/// <summary> /// Uses LightningRod /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="packet"></param> public void Use(Creature attacker, Skill skill, Packet packet) { // Set full charge variable attacker.Temp.LightningRodFullCharge = (DateTime.Now >= attacker.Temp.LightningRodPrepareTime.AddMilliseconds(skill.RankData.Var3)); // Get direction for target Area var direction = Mabi.MabiMath.ByteToRadian(attacker.Direction); var attackerPos = attacker.GetPosition(); // Calculate polygon points var r = MabiMath.ByteToRadian(attacker.Direction); var poe = attackerPos.GetRelative(r, 800); var pivot = new Point(poe.X, poe.Y); var p1 = new Point(pivot.X - SkillLength / 2, pivot.Y - SkillWidth / 2); var p2 = new Point(pivot.X - SkillLength / 2, pivot.Y + SkillWidth / 2); var p3 = new Point(pivot.X + SkillLength / 2, pivot.Y + SkillWidth / 2); var p4 = new Point(pivot.X + SkillLength / 2, pivot.Y - SkillWidth / 2); p1 = this.RotatePoint(p1, pivot, r); p2 = this.RotatePoint(p2, pivot, r); p3 = this.RotatePoint(p3, pivot, r); p4 = this.RotatePoint(p4, pivot, r); // TargetProp var lProp = new Prop(280, attacker.RegionId, poe.X, poe.Y, MabiMath.ByteToRadian(attacker.Direction), 1f, 0f, "single"); attacker.Region.AddProp(lProp); // Prepare Combat Actions var cap = new CombatActionPack(attacker, skill.Info.Id); var targetAreaId = new Location(attacker.RegionId, poe).ToLocationId(); var aAction = new AttackerAction(CombatActionType.SpecialHit, attacker, targetAreaId); aAction.Set(AttackerOptions.KnockBackHit1 | AttackerOptions.UseEffect); aAction.PropId = lProp.EntityId; cap.Add(aAction); // Get targets in Polygon - includes collission check var targets = attacker.Region.GetCreaturesInPolygon(p1, p2, p3, p4).Where(x => attacker.CanTarget(x) && !attacker.Region.Collisions.Any(attacker.GetPosition(), x.GetPosition())).ToList(); var rnd = RandomProvider.Get(); // Check crit var crit = false; var critSkill = attacker.Skills.Get(SkillId.CriticalHit); if (critSkill != null && critSkill.Info.Rank > SkillRank.Novice) { var critChance = Math2.Clamp(0, 30, attacker.GetTotalCritChance(0)); if (rnd.NextDouble() * 100 < critChance) crit = true; } foreach (var target in targets) { var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, SkillId.CombatMastery); tAction.Set(TargetOptions.None); tAction.AttackerSkillId = skill.Info.Id; cap.Add(tAction); var damage = attacker.GetRndMagicDamage(skill, skill.RankData.Var1, skill.RankData.Var2); // Add damage if the skill is fully charged var dmgMultiplier = skill.RankData.Var4 / 100f; if (attacker.Temp.LightningRodFullCharge) { damage += (damage * dmgMultiplier); } // Critical Hit if (crit) { var bonus = critSkill.RankData.Var1 / 100f; damage = damage + (damage * bonus); tAction.Set(TargetOptions.Critical); } // MDef and MProt SkillHelper.HandleMagicDefenseProtection(target, ref damage); // Conditions SkillHelper.HandleConditions(attacker, target, ref damage); // Mana Deflector var delayReduction = ManaDeflector.Handle(attacker, target, ref damage, tAction); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Apply Damage target.TakeDamage(tAction.Damage = damage, attacker); // Stun Time tAction.Stun = TargetStun; // Death or Knockback if (target.IsDead) { tAction.Set(TargetOptions.FinishingKnockDown); attacker.Shove(target, KnockbackDistance); } else { // Always knock down if (target.Is(RaceStands.KnockDownable)) { tAction.Set(TargetOptions.KnockDown); attacker.Shove(target, KnockbackDistance); } } } // Update current weapon SkillHelper.UpdateWeapon(attacker, targets.FirstOrDefault(), ProficiencyGainType.Melee, attacker.RightHand); cap.Handle(); Send.Effect(attacker, Effect.LightningRod, (int)LightningRodEffect.Attack, poe.X, poe.Y); Send.SkillUse(attacker, skill.Info.Id, targetAreaId, 0, 1); skill.Train(1); // Use the Skill attacker.Region.RemoveProp(lProp); }
/// <summary> /// Uses the skill /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetEntityId"></param> /// <returns></returns> public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { // Get Target var initTarget = attacker.Region.GetCreature(targetEntityId); // Check Target if (initTarget == null) { return(CombatSkillResult.InvalidTarget); } var attackerPos = attacker.StopMove(); var initTargetPos = initTarget.GetPosition(); // Check Range var range = (int)skill.RankData.Var2; if (!attacker.GetPosition().InRange(initTargetPos, range)) { return(CombatSkillResult.OutOfRange); } // Check for Collisions if (attacker.Region.Collisions.Any(attackerPos, initTargetPos)) { return(CombatSkillResult.InvalidTarget); } initTarget.StopMove(); // Effects Send.Effect(attacker, Effect.TheFakeSpiralSword, TheFakeSpiralSwordEffect.Attack, (DateTime.Now.Ticks / 10000), (byte)1); // Skill Use Send.SkillUseStun(attacker, skill.Info.Id, AttackerStun, 1); skill.Stacks = 0; // Prepare Combat Actions var cap = new CombatActionPack(attacker, skill.Info.Id); var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, targetEntityId); aAction.Set(AttackerOptions.KnockBackHit1 | AttackerOptions.KnockBackHit2 | AttackerOptions.Result); cap.Add(aAction); aAction.Stun = AttackerStun; // Get Explosion Radius of Attack var explosionRadius = (int)skill.RankData.Var3 / 2; // Get Explosion Targets var targets = attacker.GetTargetableCreaturesAround(initTargetPos, explosionRadius); var rnd = RandomProvider.Get(); // Get Critical Hit var crit = false; if (attacker.Skills.Has(SkillId.CriticalHit, SkillRank.RF)) { var critChance = attacker.GetRightCritChance(0); crit = (rnd.Next(100) < critChance); } foreach (var target in targets) { var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result); tAction.Delay = attackerPos.GetDistance(initTargetPos) / 2; cap.Add(tAction); // Damage var damage = (attacker.GetRndTotalDamage() * (skill.RankData.Var1 / 100f)); // Critical Hit if (crit) { CriticalHit.Handle(attacker, 100, ref damage, tAction); } // Defense and Prot SkillHelper.HandleDefenseProtection(target, ref damage); // Defense Defense.Handle(aAction, tAction, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Heavy Stander HeavyStander.Handle(attacker, target, ref damage, tAction); // Apply Damage target.TakeDamage(tAction.Damage = damage, attacker); // Aggro target.Aggro(attacker); // Stun Time tAction.Stun = TargetStun; // Death and Knockback if (target.Is(RaceStands.KnockDownable)) { if (target.IsDead) { tAction.Set(TargetOptions.FinishingKnockDown); } else { tAction.Set(TargetOptions.KnockDown); } // Shove if (target == initTarget) { attacker.Shove(target, KnockbackDistance); } else { initTarget.Shove(target, KnockbackDistance); } } } aAction.Creature.Stun = aAction.Stun; cap.Handle(); // User can attack multiple times if attack isn't locked, which will cause them to freeze. // This is automatically unlocked by the skill after Use is finished. attacker.Lock(Locks.Attack); return(CombatSkillResult.Okay); }
/// <summary> /// Handles using the skill. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetAreaId"></param> /// <param name="unkInt1"></param> /// <param name="unkInt2"></param> public void Use(Creature attacker, Skill skill, long targetAreaId, int unkInt1, int unkInt2) { var range = this.GetRange(attacker, skill); var targets = attacker.GetTargetableCreaturesInRange(range, TargetableOptions.AddAttackRange); var rnd = RandomProvider.Get(); // Create actions var cap = new CombatActionPack(attacker, skill.Info.Id); var aAction = new AttackerAction(CombatActionType.Attacker, attacker, targetAreaId); aAction.Set(AttackerOptions.Result); aAction.Stun = AttackerStun; cap.Add(aAction); foreach (var target in targets) { // Check if hit var hitChance = this.GetHitChance(attacker, target, skill); if (rnd.Next(0, 100) > hitChance) continue; target.StopMove(); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result); tAction.Delay = 300; // Calculate damage var damage = this.GetDamage(attacker, skill); // Elementals damage *= attacker.CalculateElementalDamageMultiplier(target); // Handle skills and reductions CriticalHit.Handle(attacker, attacker.GetTotalCritChance(0), ref damage, tAction); SkillHelper.HandleDefenseProtection(target, ref damage); ManaShield.Handle(target, ref damage, tAction); HeavyStander.Handle(attacker, target, ref damage, tAction); // Clean Hit if not critical if (!tAction.Has(TargetOptions.Critical)) tAction.Set(TargetOptions.CleanHit); // Take damage if any is left if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); SkillHelper.HandleInjury(attacker, target, damage); } // Finish if dead, knock down if not defended if (target.IsDead) tAction.Set(TargetOptions.KnockDownFinish); else tAction.Set(TargetOptions.KnockDown); // Anger Management if (!target.IsDead) target.Aggro(attacker); // Stun & knock down tAction.Stun = CombatMastery.GetTargetStun(attacker.AverageKnockCount, attacker.AverageAttackSpeed, true); target.Stability = Creature.MinStability; // Add action cap.Add(tAction); } Send.UseMotion(attacker, 10, 1); cap.Handle(); Send.SkillUse(attacker, skill.Info.Id, targetAreaId, unkInt1, unkInt2); }
/// <summary> /// Uses Charging Strike /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="packet"></param> public void Use(Creature attacker, Skill skill, Packet packet) { // Get Target var targetEntityId = packet.GetLong(); var target = attacker.Region.GetCreature(targetEntityId); var attackerPos = attacker.GetPosition(); var targetPos = target.GetPosition(); // Check target + collisions if (target == null || attacker.Region.Collisions.Any(attackerPos, targetPos)) { Send.SkillUseSilentCancel(attacker); attacker.Unlock(Locks.All); return; } // Stop movement attacker.Lock(Locks.Walk | Locks.Run); attacker.StopMove(); target.StopMove(); // Effects Send.EffectDelayed(attacker, attackerPos.GetDistance(targetPos), Effect.ChargingStrike, (byte)0, targetEntityId); // Conditions var extra = new MabiDictionary(); extra.SetBool("CONDITION_FAST_MOVE_NO_LOCK", false); attacker.Conditions.Activate(ConditionsC.FastMove, extra); Send.ForceRunTo(attacker, targetPos); attacker.SetPosition(targetPos.X, targetPos.Y); Send.SkillUseEntity(attacker, skill.Info.Id, targetEntityId); Send.EffectDelayed(attacker, attackerPos.GetDistance(targetPos), Effect.ChargingStrike, (byte)1, targetEntityId); // Counter if (Counterattack.Handle(target, attacker)) { attacker.Conditions.Deactivate(ConditionsC.FastMove); return; } // Prepare Combat Actions var cap = new CombatActionPack(attacker, skill.Info.Id); var aAction = new AttackerAction(CombatActionType.SpecialHit, attacker, targetEntityId, skill.Info.Id); aAction.Set(AttackerOptions.UseEffect); aAction.PropId = targetEntityId; var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result); tAction.Delay = attackerPos.GetDistance(targetPos); cap.Add(aAction, tAction); // Damage var damage = (attacker.GetRndFighterDamage() * (skill.RankData.Var1 / 100f)); // Chain Mastery Damage Bonus var chainMasterySkill = attacker.Skills.Get(SkillId.ChainMastery); var damageBonus = (chainMasterySkill == null ? 0 : chainMasterySkill.RankData.Var1); damage += damage * (damageBonus / 100f); // Master Title - Damage +30% if (attacker.Titles.SelectedTitle == skill.Data.MasterTitle) { damage += (damage * 0.3f); } // Critical Hit var critChance = attacker.GetRightCritChance(target.Protection); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Handle skills and reductions SkillHelper.HandleDefenseProtection(target, ref damage); HeavyStander.Handle(attacker, target, ref damage, tAction); SkillHelper.HandleConditions(attacker, target, ref damage); ManaShield.Handle(target, ref damage, tAction); // Apply Damage target.TakeDamage(tAction.Damage = damage, attacker); // Aggro target.Aggro(attacker); // Stun Times tAction.Stun = TargetStun; aAction.Stun = AttackerStun; // Death and Knockback if (target.IsDead) { if (target.Is(RaceStands.KnockDownable)) { tAction.Set(TargetOptions.FinishingKnockDown); attacker.Shove(target, KnockbackDistance); } else { tAction.Set(TargetOptions.Finished | TargetOptions.FinishingHit); } } else // This skill never knocks back normally { if (!target.IsKnockedDown) { target.Stability -= StabilityReduction; } } cap.Handle(); attacker.Conditions.Deactivate(ConditionsC.FastMove); Send.SkillComplete(attacker, skill.Info.Id); // Chain Progress to Stage 2 attacker.Temp.FighterChainStartTime = DateTime.Now; attacker.Temp.FighterChainLevel = 2; attacker.Skills.ActiveSkill = null; // Charging strike locks EVERYTHING for some reason... attacker.Unlock(Locks.All); }
/// <summary> /// Bolt specific use code. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="target"></param> protected virtual void UseSkillOnTarget(Creature attacker, Skill skill, Creature target) { target.StopMove(); // Create actions var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, skill.Info.Id, target.EntityId); aAction.Set(AttackerOptions.Result); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result); tAction.Stun = TargetStun; var cap = new CombatActionPack(attacker, skill.Info.Id, aAction, tAction); // Damage var damage = this.GetDamage(attacker, skill); // Reduce damage if (this.Defendable) Defense.Handle(aAction, tAction, ref damage); SkillHelper.HandleMagicDefenseProtection(target, ref damage); ManaShield.Handle(target, ref damage, tAction); // Deal damage if (damage > 0) target.TakeDamage(tAction.Damage = damage, attacker); target.Aggro(attacker); // Death/Knockback this.HandleKnockBack(attacker, target, tAction); // Override stun set by defense aAction.Stun = AttackerStun; Send.Effect(attacker, Effect.UseMagic, EffectSkillName); Send.SkillUseStun(attacker, skill.Info.Id, aAction.Stun, 1); this.BeforeHandlingPack(attacker, skill); cap.Handle(); }
/// <summary> /// Handles attack. /// </summary> /// <param name="attacker">The creature attacking.</param> /// <param name="skill">The skill being used.</param> /// <param name="targetEntityId">The entity id of the target.</param> /// <returns></returns> public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { if (attacker.IsStunned) return CombatSkillResult.Okay; var mainTarget = attacker.Region.GetCreature(targetEntityId); if (mainTarget == null) return CombatSkillResult.Okay; if (!attacker.GetPosition().InRange(mainTarget.GetPosition(), attacker.AttackRangeFor(mainTarget))) return CombatSkillResult.OutOfRange; attacker.StopMove(); // Get targets, incl. splash. var targets = new HashSet<Creature>() { mainTarget }; targets.UnionWith(attacker.GetTargetableCreaturesInCone(mainTarget.GetPosition(), attacker.GetTotalSplashRadius(), attacker.GetTotalSplashAngle())); // Counter if (Counterattack.Handle(targets, attacker)) return CombatSkillResult.Okay; var rightWeapon = attacker.Inventory.RightHand; var leftWeapon = attacker.Inventory.LeftHand; var magazine = attacker.Inventory.Magazine; var maxHits = (byte)(attacker.IsDualWielding ? 2 : 1); int prevId = 0; for (byte i = 1; i <= maxHits; ++i) { var weapon = (i == 1 ? rightWeapon : leftWeapon); var weaponIsKnuckle = (weapon != null && weapon.Data.HasTag("/knuckle/")); var aAction = new AttackerAction(CombatActionType.Attacker, attacker, targetEntityId); aAction.Set(AttackerOptions.Result); if (attacker.IsDualWielding) { aAction.Set(AttackerOptions.DualWield); aAction.WeaponParameterType = (byte)(i == 1 ? 2 : 1); } var cap = new CombatActionPack(attacker, skill.Info.Id, aAction); cap.Hit = i; cap.Type = (attacker.IsDualWielding ? CombatActionPackType.TwinSwordAttack : CombatActionPackType.NormalAttack); cap.PrevId = prevId; prevId = cap.Id; var mainDamage = (i == 1 ? attacker.GetRndRightHandDamage() : attacker.GetRndLeftHandDamage()); foreach (var target in targets) { if (target.IsDead) continue; target.StopMove(); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result); cap.Add(tAction); // Base damage var damage = mainDamage; // Elementals damage *= attacker.CalculateElementalDamageMultiplier(target); // Splash modifier if (target != mainTarget) damage *= attacker.GetSplashDamage(weapon); // Critical Hit var critChance = (i == 1 ? attacker.GetRightCritChance(target.Protection) : attacker.GetLeftCritChance(target.Protection)); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Defense Defense.Handle(aAction, tAction, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Heavy Stander // Can only happen on the first hit var pinged = (cap.Hit == 1 && HeavyStander.Handle(attacker, target, ref damage, tAction)); // Deal with it! if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); SkillHelper.HandleInjury(attacker, target, damage); } // Knock down on deadly if (target.Conditions.Has(ConditionsA.Deadly)) { tAction.Set(TargetOptions.KnockDown); tAction.Stun = GetTargetStun(attacker, weapon, tAction.IsKnockBack); } // Aggro if (target == mainTarget) target.Aggro(attacker); // Evaluate caused damage if (!target.IsDead) { if (tAction.SkillId != SkillId.Defense) { target.Stability -= this.GetStabilityReduction(attacker, weapon) / maxHits; // React normal for CombatMastery, knock down if // FH and not dual wield, don't knock at all if dual. if (skill.Info.Id != SkillId.FinalHit) { // Originally we thought you knock enemies back, unless it's a critical // hit, but apparently you knock *down* under normal circumstances. // More research to be done. if (target.IsUnstable && target.Is(RaceStands.KnockBackable)) //tAction.Set(tAction.Has(TargetOptions.Critical) ? TargetOptions.KnockDown : TargetOptions.KnockBack); tAction.Set(TargetOptions.KnockDown); } else if (!attacker.IsDualWielding && !weaponIsKnuckle && target.Is(RaceStands.KnockBackable)) { target.Stability = Creature.MinStability; tAction.Set(TargetOptions.KnockDown); } } } else { tAction.Set(TargetOptions.FinishingKnockDown); } // React to knock back if (tAction.IsKnockBack) { attacker.Shove(target, KnockBackDistance); if (target == mainTarget) aAction.Set(AttackerOptions.KnockBackHit2); } // Set stun time if not defended, Defense handles the stun // in case the target used it. if (tAction.SkillId != SkillId.Defense) { if (target == mainTarget) aAction.Stun = GetAttackerStun(attacker, weapon, tAction.IsKnockBack && skill.Info.Id != SkillId.FinalHit); tAction.Stun = GetTargetStun(attacker, weapon, tAction.IsKnockBack); } if (target == mainTarget) { // Set increased stun if target pinged if (pinged) aAction.Stun = GetAttackerStun(attacker, weapon, true); // Second hit doubles stun time for normal hits if (cap.Hit == 2 && !tAction.IsKnockBack && !pinged) aAction.Stun *= 2; // Update current weapon SkillHelper.UpdateWeapon(attacker, target, ProficiencyGainType.Melee, weapon); // Consume stamina for weapon var staminaUsage = (weapon != null ? weapon.Data.StaminaUsage : Creature.BareHandStaminaUsage); if (attacker.Stamina < staminaUsage) Send.Notice(attacker, Localization.Get("Your stamina is too low to fight properly!")); attacker.Stamina -= staminaUsage; // No second hit if defended, pinged, or knocked back if (tAction.IsKnockBack || tAction.SkillId == SkillId.Defense || pinged) { // Set to 1 to prevent second run maxHits = 1; // Remove dual wield option if last hit doesn't come from // the second weapon. If this isn't done, the client shows // the second hit. if (cap.Hit != 2) aAction.Options &= ~AttackerOptions.DualWield; } } } // Handle cap.Handle(); } return CombatSkillResult.Okay; }
/// <summary> /// Uses WM, attacking targets. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetAreaId"></param> /// <param name="unkInt1"></param> /// <param name="unkInt2"></param> public void Use(Creature attacker, Skill skill, long targetAreaId, int unkInt1, int unkInt2) { var range = this.GetRange(attacker, skill); var targets = attacker.GetTargetableCreaturesInRange(range, TargetableOptions.AddAttackRange); // Check targets if (targets.Count == 0) { Send.Notice(attacker, Localization.Get("There isn't a target nearby to use that on.")); Send.SkillUseSilentCancel(attacker); return; } // Create actions var cap = new CombatActionPack(attacker, skill.Info.Id); var aAction = new AttackerAction(CombatActionType.SpecialHit, attacker, targetAreaId); aAction.Set(AttackerOptions.Result); aAction.Stun = CombatMastery.GetAttackerStun(attacker.AverageKnockCount, attacker.AverageAttackSpeed, true); cap.Add(aAction); var survived = new List <Creature>(); var rnd = RandomProvider.Get(); // Check crit var crit = false; if (attacker.Skills.Has(SkillId.CriticalHit, SkillRank.RF)) { crit = (rnd.Next(100) < attacker.GetTotalCritChance(0)); } // Handle all targets foreach (var target in targets) { target.StopMove(); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Delay = 300; // Usually 300, sometimes 350? // Calculate damage var damage = attacker.GetRndTotalDamage(); damage *= skill.RankData.Var1 / 100f; // Elementals damage *= attacker.CalculateElementalDamageMultiplier(target); // Crit bonus if (crit) { CriticalHit.Handle(attacker, 100, ref damage, tAction); } // Handle skills and reductions SkillHelper.HandleDefenseProtection(target, ref damage); SkillHelper.HandleConditions(attacker, target, ref damage); Defense.Handle(aAction, tAction, ref damage); ManaShield.Handle(target, ref damage, tAction); HeavyStander.Handle(attacker, target, ref damage, tAction); // Clean Hit if not defended nor critical if (tAction.SkillId != SkillId.Defense && !tAction.Has(TargetOptions.Critical)) { tAction.Set(TargetOptions.CleanHit); } // Take damage if any is left if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); } // Knock down on deadly if (target.Conditions.Has(ConditionsA.Deadly)) { tAction.Set(TargetOptions.KnockDown); tAction.Stun = CombatMastery.GetTargetStun(attacker.AverageKnockCount, attacker.AverageAttackSpeed, true); } // Finish if dead, knock down if not defended if (target.IsDead) { tAction.Set(TargetOptions.KnockDownFinish); } else if (tAction.SkillId != SkillId.Defense) { tAction.Set(TargetOptions.KnockDown); } // Anger Management if (!target.IsDead) { survived.Add(target); } // Stun and shove if not defended if (target.IsDead || tAction.SkillId != SkillId.Defense || target.Conditions.Has(ConditionsA.Deadly)) { tAction.Stun = CombatMastery.GetTargetStun(attacker.AverageKnockCount, attacker.AverageAttackSpeed, true); target.Stability = Creature.MinStability; attacker.Shove(target, KnockbackDistance); } // Add action cap.Add(tAction); } // Update current weapon SkillHelper.UpdateWeapon(attacker, targets.FirstOrDefault(), ProficiencyGainType.Melee, attacker.RightHand, attacker.LeftHand); // Only select a random aggro if there is no aggro yet, // WM only aggroes one target at a time. if (survived.Count != 0 && attacker.Region.CountAggro(attacker) < 1) { var aggroTarget = survived.Random(); aggroTarget.Aggro(attacker); } // Reduce life in old combat system if (!AuraData.FeaturesDb.IsEnabled("CombatSystemRenewal")) { var amount = (attacker.LifeMax < 10 ? 2 : attacker.LifeMax / 10); attacker.ModifyLife(-amount); // TODO: Invincibility } // Spin it~ Send.UseMotion(attacker, 8, 4); cap.Handle(); Send.SkillUse(attacker, skill.Info.Id, targetAreaId, unkInt1, unkInt2); skill.Stacks = 0; }
/// <summary> /// Uses the skill. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetEntityId"></param> /// <returns></returns> public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { // Get target var target = attacker.Region.GetCreature(targetEntityId); if (target == null) { return(CombatSkillResult.InvalidTarget); } var targetPos = target.GetPosition(); var attackerPos = attacker.GetPosition(); var actionType = (attacker.IsElf ? CombatActionPackType.ChainRangeAttack : CombatActionPackType.NormalAttack); var attackerStun = (short)(actionType == CombatActionPackType.ChainRangeAttack ? AttackerStunElf : AttackerStun); // "Cancels" the skill // 800 = old load time? == aAction.Stun? Varies? Doesn't seem to be a stun. Send.SkillUse(attacker, skill.Info.Id, attackerStun, 1); var chance = attacker.AimMeter.GetAimChance(target); var rnd = RandomProvider.Get().NextDouble() * 100; var successfulHit = (rnd < chance); var maxHits = (actionType == CombatActionPackType.ChainRangeAttack && successfulHit ? 2 : 1); var prevId = 0; for (byte i = 1; i <= maxHits; ++i) { target.StopMove(); // Actions var cap = new CombatActionPack(attacker, skill.Info.Id); cap.Hit = i; cap.Type = actionType; cap.PrevId = prevId; prevId = cap.Id; var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, skill.Info.Id, targetEntityId); aAction.Set(AttackerOptions.Result); aAction.Stun = attackerStun; cap.Add(aAction); // Target action if hit if (successfulHit) { var targetSkillId = target.Skills.ActiveSkill != null ? target.Skills.ActiveSkill.Info.Id : SkillId.CombatMastery; var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, targetSkillId); tAction.Set(TargetOptions.Result); tAction.AttackerSkillId = skill.Info.Id; tAction.Stun = (short)(actionType == CombatActionPackType.ChainRangeAttack ? TargetStunElf : TargetStun); if (actionType == CombatActionPackType.ChainRangeAttack) { tAction.EffectFlags = 0x20; } cap.Add(tAction); // Damage var damage = attacker.GetRndRangedDamage(); // More damage with fire arrow if (attacker.Temp.FireArrow) { damage *= FireBonus; } // Critical Hit var critChance = attacker.GetRightCritChance(target.Protection); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Defense Defense.Handle(aAction, tAction, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Deal with it! if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); } // Aggro target.Aggro(attacker); // Death/Knockback if (target.IsDead) { tAction.Set(TargetOptions.FinishingKnockDown); attacker.Shove(target, KnockBackDistance); maxHits = 1; } else { // Insta-recover in knock down // TODO: Tied to stability? if (target.IsKnockedDown) { tAction.Stun = 0; } // Knock down if hit repeatedly else if (target.Stability < 30) { tAction.Set(TargetOptions.KnockDown); } // Normal stability reduction else { target.Stability -= (actionType == CombatActionPackType.ChainRangeAttack ? StabilityReductionElf : StabilityReduction); if (target.IsUnstable) { tAction.Set(TargetOptions.KnockBack); attacker.Shove(target, KnockBackDistance); } } tAction.Creature.Stun = tAction.Stun; } } aAction.Creature.Stun = aAction.Stun; // Skill training if (skill.Info.Rank == SkillRank.Novice || skill.Info.Rank == SkillRank.RF) { skill.Train(1); // Try ranged attack. } // Reduce arrows if (attacker.Magazine != null && !ChannelServer.Instance.Conf.World.InfiniteArrows && !attacker.Magazine.HasTag("/unlimited_arrow/")) { attacker.Inventory.Decrement(attacker.Magazine); } cap.Handle(); } // Disable fire arrow effect if (attacker.Temp.FireArrow) { Send.Effect(attacker, Effect.FireArrow, false); } return(CombatSkillResult.Okay); }
/// <summary> /// Handles attack. /// </summary> /// <param name="attacker">The creature attacking.</param> /// <param name="skill">The skill being used.</param> /// <param name="targetEntityId">The entity id of the target.</param> /// <returns></returns> public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { if (attacker.IsStunned) { return(CombatSkillResult.Okay); } var target = attacker.Region.GetCreature(targetEntityId); if (target == null) { return(CombatSkillResult.Okay); } if (!attacker.GetPosition().InRange(target.GetPosition(), attacker.AttackRangeFor(target))) { return(CombatSkillResult.OutOfRange); } attacker.StopMove(); var targetPosition = target.StopMove(); // Counter if (Counterattack.Handle(target, attacker)) { return(CombatSkillResult.Okay); } var rightWeapon = attacker.Inventory.RightHand; var leftWeapon = attacker.Inventory.LeftHand; var magazine = attacker.Inventory.Magazine; var maxHits = (byte)(attacker.IsDualWielding ? 2 : 1); int prevId = 0; for (byte i = 1; i <= maxHits; ++i) { var weapon = (i == 1 ? rightWeapon : leftWeapon); var weaponIsKnuckle = (weapon != null && weapon.Data.HasTag("/knuckle/")); var aAction = new AttackerAction(CombatActionType.Attacker, attacker, targetEntityId); aAction.Set(AttackerOptions.Result); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result); var cap = new CombatActionPack(attacker, skill.Info.Id, aAction, tAction); cap.Hit = i; cap.Type = (attacker.IsDualWielding ? CombatActionPackType.TwinSwordAttack : CombatActionPackType.NormalAttack); cap.PrevId = prevId; prevId = cap.Id; // Default attacker options aAction.Set(AttackerOptions.Result); if (attacker.IsDualWielding) { aAction.Set(AttackerOptions.DualWield); aAction.WeaponParameterType = (byte)(i == 1 ? 2 : 1); } // Base damage var damage = (i == 1 ? attacker.GetRndRightHandDamage() : attacker.GetRndLeftHandDamage()); // Elementals damage *= attacker.CalculateElementalDamageMultiplier(target); // Critical Hit var critChance = (i == 1 ? attacker.GetRightCritChance(target.Protection) : attacker.GetLeftCritChance(target.Protection)); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Defense Defense.Handle(aAction, tAction, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Heavy Stander // Can only happen on the first hit var pinged = (cap.Hit == 1 && HeavyStander.Handle(attacker, target, ref damage, tAction)); // Deal with it! if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); SkillHelper.HandleInjury(attacker, target, damage); } // Knock down on deadly if (target.Conditions.Has(ConditionsA.Deadly)) { tAction.Set(TargetOptions.KnockDown); tAction.Stun = GetTargetStun(attacker, weapon, tAction.IsKnockBack); } // Aggro target.Aggro(attacker); // Evaluate caused damage if (!target.IsDead) { if (tAction.SkillId != SkillId.Defense) { target.Stability -= this.GetStabilityReduction(attacker, weapon) / maxHits; // React normal for CombatMastery, knock down if // FH and not dual wield, don't knock at all if dual. if (skill.Info.Id != SkillId.FinalHit) { // Originally we thought you knock enemies back, unless it's a critical // hit, but apparently you knock *down* under normal circumstances. // More research to be done. if (target.IsUnstable && target.Is(RaceStands.KnockBackable)) { //tAction.Set(tAction.Has(TargetOptions.Critical) ? TargetOptions.KnockDown : TargetOptions.KnockBack); tAction.Set(TargetOptions.KnockDown); } } else if (!attacker.IsDualWielding && !weaponIsKnuckle) { target.Stability = Creature.MinStability; tAction.Set(TargetOptions.KnockDown); } } } else { tAction.Set(TargetOptions.FinishingKnockDown); } // React to knock back if (tAction.IsKnockBack) { attacker.Shove(target, KnockBackDistance); aAction.Set(AttackerOptions.KnockBackHit2); } // Set stun time if not defended, Defense handles the stun // in case the target used it. if (tAction.SkillId != SkillId.Defense) { aAction.Stun = GetAttackerStun(attacker, weapon, tAction.IsKnockBack && skill.Info.Id != SkillId.FinalHit); tAction.Stun = GetTargetStun(attacker, weapon, tAction.IsKnockBack); } // Set increased stun if target pinged if (pinged) { aAction.Stun = GetAttackerStun(attacker, weapon, true); } // Second hit doubles stun time for normal hits if (cap.Hit == 2 && !tAction.IsKnockBack && !pinged) { aAction.Stun *= 2; } // Update current weapon SkillHelper.UpdateWeapon(attacker, target, weapon); // Consume stamina for weapon var staminaUsage = (weapon != null ? weapon.Data.StaminaUsage : Creature.BareHandStaminaUsage); if (attacker.Stamina < staminaUsage) { Send.Notice(attacker, Localization.Get("Your stamina is too low to fight properly!")); } attacker.Stamina -= staminaUsage; // No second hit if defended, pinged, or knocked back if (tAction.IsKnockBack || tAction.SkillId == SkillId.Defense || pinged) { // Set to 1 to prevent second run maxHits = 1; // Remove dual wield option if last hit doesn't come from // the second weapon. If this isn't done, the client shows // the second hit. if (cap.Hit != 2) { aAction.Options &= ~AttackerOptions.DualWield; } } // Handle cap.Handle(); } return(CombatSkillResult.Okay); }
/// <summary> /// Bolt specific use code. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="target"></param> protected override void UseSkillOnTarget(Creature attacker, Skill skill, Creature target) { attacker.StopMove(); target.StopMove(); // Create actions var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, target.EntityId); aAction.Set(AttackerOptions.Result); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result | TargetOptions.KnockDown); tAction.Stun = TargetStun; var cap = new CombatActionPack(attacker, skill.Info.Id, aAction, tAction); // Damage var damage = this.GetDamage(attacker, skill); // Elements damage *= this.GetElementalDamageMultiplier(attacker, target); // Critical Hit var critChance = attacker.GetTotalCritChance(target.Protection, true); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Reduce damage SkillHelper.HandleMagicDefenseProtection(target, ref damage); SkillHelper.HandleConditions(attacker, target, ref damage); ManaShield.Handle(target, ref damage, tAction); ManaDeflector.Handle(attacker, target, ref damage, tAction); // Deal damage if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); } target.Aggro(attacker); // Knockback target.Stability = Creature.MinStability; attacker.Shove(target, KnockbackDistance); if (target.IsDead) { tAction.Set(TargetOptions.FinishingKnockDown); } // Override stun set by defense aAction.Stun = AttackerStun; Send.Effect(attacker, Effect.UseMagic, EffectSkillName); Send.SkillUseStun(attacker, skill.Info.Id, aAction.Stun, 1); skill.Stacks = 0; // Update current weapon SkillHelper.UpdateWeapon(attacker, target, ProficiencyGainType.Melee, attacker.RightHand); cap.Handle(); }
/// <summary> /// Bolt specific use code. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="target"></param> protected override void UseSkillOnTarget(Creature attacker, Skill skill, Creature mainTarget) { // Create actions var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, mainTarget.EntityId); aAction.Set(AttackerOptions.Result); var cap = new CombatActionPack(attacker, skill.Info.Id, aAction); // Get targets // Add the main target as first target, so it gets the first hit, // and the full damage. var targets = new List<Creature>(); targets.Add(mainTarget); var inSplashRange = attacker.GetTargetableCreaturesAround(mainTarget.GetPosition(), SplashRange); targets.AddRange(inSplashRange.Where(a => a != mainTarget)); // Damage var damage = this.GetDamage(attacker, skill); var max = Math.Min(targets.Count, skill.Stacks); for (int i = 0; i < max; ++i) { var target = targets[i]; var targetDamage = damage; target.StopMove(); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result); tAction.Stun = TargetStun; // Full damage for the first target, -10% for every subsequent one. targetDamage -= (targetDamage * 0.1f) * i; // Critical Hit var critChance = attacker.GetTotalCritChance(target.Protection, true); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Reduce damage Defense.Handle(aAction, tAction, ref targetDamage); SkillHelper.HandleMagicDefenseProtection(target, ref targetDamage); SkillHelper.HandleConditions(attacker, target, ref damage); ManaShield.Handle(target, ref targetDamage, tAction); // Mana Deflector var mdResult = ManaDeflector.Handle(attacker, target, ref targetDamage, tAction); var delayReduction = mdResult.DelayReduction; var pinged = mdResult.Pinged; // Deal damage if (targetDamage > 0) target.TakeDamage(tAction.Damage = targetDamage, attacker); if (target == mainTarget) target.Aggro(attacker); // Reduce stun, based on ping if (pinged && delayReduction > 0) tAction.Stun = (short)Math.Max(0, tAction.Stun - (tAction.Stun / 100 * delayReduction)); // Death/Knockback if (target.IsDead) { tAction.Set(TargetOptions.FinishingKnockDown); } else { // If knocked down, instant recovery, // if repeat hit, knock down, // otherwise potential knock back. if (target.IsKnockedDown) { tAction.Stun = 0; } else if (target.Stability < MinStability) { tAction.Set(TargetOptions.KnockDown); } else { // If number of stacks is greater than the number of // targets hit, the targets are knocked back, which is // done by reducing the stability to min here. // Targets with high enough Mana Deflector might // negate this knock back, by reducing the stability // reduction to 0. var stabilityReduction = (skill.Stacks > targets.Count ? OverchargeStabilityReduction : StabilityReduction); // Reduce reduction, based on ping // While the Wiki says that "the Knockdown Gauge [does not] // build up", tests show that it does. However, it's // reduced, assumedly based on the MD rank. if (delayReduction > 0) stabilityReduction = (short)Math.Max(0, stabilityReduction - (stabilityReduction / 100 * delayReduction)); target.Stability -= stabilityReduction; if (target.IsUnstable) { tAction.Set(TargetOptions.KnockBack); } } } if (tAction.IsKnockBack) attacker.Shove(target, KnockbackDistance); cap.Add(tAction); } // Override stun set by defense aAction.Stun = AttackerStun; Send.Effect(attacker, Effect.UseMagic, EffectSkillName); Send.SkillUseStun(attacker, skill.Info.Id, aAction.Stun, 1); skill.Stacks = 0; // Update current weapon SkillHelper.UpdateWeapon(attacker, targets.FirstOrDefault(), ProficiencyGainType.Melee, attacker.RightHand); cap.Handle(); }
public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { if (attacker.IsStunned) return CombatSkillResult.Okay; var target = attacker.Region.GetCreature(targetEntityId); if (target == null) return CombatSkillResult.Okay; if (!attacker.GetPosition().InRange(target.GetPosition(), attacker.AttackRangeFor(target))) return CombatSkillResult.OutOfRange; attacker.StopMove(); var targetPosition = target.StopMove(); var rightWeapon = attacker.Inventory.RightHand; var leftWeapon = attacker.Inventory.LeftHand; var magazine = attacker.Inventory.Magazine; var dualWield = (rightWeapon != null && leftWeapon != null); var maxHits = (byte)(dualWield ? 2 : 1); int prevId = 0; for (byte i = 1; i <= maxHits; ++i) { var weapon = (i == 1 ? rightWeapon : leftWeapon); var cap = new CombatActionPack(attacker, skill.Info.Id); var aAction = new AttackerAction(CombatActionType.Hit, attacker, skill.Info.Id, targetEntityId); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); cap.Add(aAction, tAction); cap.Hit = i; cap.MaxHits = maxHits; cap.PrevId = prevId; prevId = cap.Id; aAction.Set(AttackerOptions.Result); if (dualWield) aAction.Set(AttackerOptions.DualWield); var damage = attacker.GetRndDamage(weapon); tAction.Damage = damage; target.TakeDamage(tAction.Damage, attacker); if (!target.IsDead) { target.KnockBack += this.GetKnockBack(weapon) / maxHits; if (target.KnockBack >= 100 && target.Is(RaceStands.KnockBackable)) tAction.Set(tAction.Has(TargetOptions.Critical) ? TargetOptions.KnockDown : TargetOptions.KnockBack); } else { tAction.Set(TargetOptions.FinishingKnockDown); } if (tAction.IsKnockBack) { var newPos = attacker.GetPosition().GetRelative(targetPosition, KnockBackDistance); Position intersection; if (target.Region.Collissions.Find(targetPosition, newPos, out intersection)) newPos = targetPosition.GetRelative(intersection, -50); target.SetPosition(newPos.X, newPos.Y); aAction.Set(AttackerOptions.KnockBackHit2); cap.MaxHits = cap.Hit; } aAction.Stun = this.GetAttackerStun(weapon, tAction.IsKnockBack); tAction.Stun = this.GetTargetStun(weapon, tAction.IsKnockBack); cap.Handle(); if (tAction.IsKnockBack) break; } return CombatSkillResult.Okay; }