Esempio n. 1
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)
            }

            // 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);
        }
Esempio n. 2
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);
            }

            // 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);
        }
Esempio n. 3
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
                // 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);
        }
Esempio n. 4
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, 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);
        }
Esempio n. 5
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 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)
                    {
                        target.Stability = Creature.MinStability;
                        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);
        }