Inheritance: Aura.Channel.Skills.Base.StandardPrepareHandler
Ejemplo n.º 1
0
        /// <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);
        }
Ejemplo n.º 2
0
        /// <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);
        }
Ejemplo n.º 3
0
        /// <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);
        }
Ejemplo n.º 4
0
        /// <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);
        }
Ejemplo n.º 5
0
        /// <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);
        }
Ejemplo n.º 6
0
        public CombatSkillResult UseWithoutRangeCheck(Creature attacker, Skill skill, long targetEntityId, Creature mainTarget, SkillId interceptingSkillId = SkillId.None)
        {
            //Against Smash
            Skill smash = mainTarget.Skills.Get(SkillId.Smash);

            if (interceptingSkillId == SkillId.None && smash != null && mainTarget.Skills.IsReady(SkillId.Smash))
            {
                interceptingSkillId = SkillId.Smash;
            }

            var rightWeapon = attacker.Inventory.RightHand;
            var leftWeapon  = attacker.Inventory.LeftHand;
            var dualWield   = (rightWeapon != null && leftWeapon != null && leftWeapon.Data.WeaponType != 0 && (leftWeapon.HasTag("/weapon/edged/") || leftWeapon.HasTag("/weapon/blunt/")));

            // Against Combat Mastery
            Skill combatMastery          = mainTarget.Skills.Get(SkillId.CombatMastery);
            var   simultaneousAttackStun = 0;

            if (interceptingSkillId == SkillId.None)
            {
                if (combatMastery != null && (mainTarget.Skills.ActiveSkill == null || mainTarget.Skills.ActiveSkill == combatMastery || mainTarget.Skills.IsReady(SkillId.FinalHit)) && mainTarget.IsInBattleStance && mainTarget.Target == attacker && mainTarget.AttemptingAttack && (!mainTarget.IsStunned || mainTarget.IsKnockedDown))
                {
                    var    attackerStunTime   = CombatMastery.GetAttackerStun(attacker, attacker.RightHand, false);
                    var    mainTargetStunTime = CombatMastery.GetAttackerStun(mainTarget, mainTarget.Inventory.RightHand, false);
                    var    slowestStun        = CombatMastery.GetAttackerStun(1, AttackSpeed.VerySlow, false);
                    var    additionalStun     = slowestStun + (CombatMastery.GetAttackerStun(5, AttackSpeed.VeryFast, false) / 2);                                                                                            //Fastest stun divided by two so that the fastest stun doesn't always beat out the slowest stun.  The addition is so that the subtration (Ex. additionalStun - attackerStunTime) ends in the desired range.
                    var    formulaMultiplier  = 320;                                                                                                                                                                          //Multiplier to keep the result reasonable, found through trial and error?
                    var    formulaEqualizer   = 50;                                                                                                                                                                           //Balances the subtraction to keep the result in a reasonable range and balanced out no matter the order.
                    double chances            = ((((additionalStun - attackerStunTime) / slowestStun) * formulaMultiplier) - (((additionalStun - mainTargetStunTime) / slowestStun) * formulaMultiplier)) + formulaEqualizer; //Probability in percentage that you will not lose.
                    chances = Math2.Clamp(0.0, 99.0, chances);                                                                                                                                                                //Cap the stun, just in case.

                    if (((mainTarget.LastKnockedBackBy == attacker && mainTarget.KnockDownTime > attacker.KnockDownTime && mainTarget.KnockDownTime.AddMilliseconds(mainTargetStunTime) > DateTime.Now ||
                          /*attackerStunTime > initialTargetStunTime && */
                          !Math2.Probability(chances) && !(attacker.LastKnockedBackBy == mainTarget && attacker.KnockDownTime > mainTarget.KnockDownTime && attacker.KnockDownTime.AddMilliseconds(attackerStunTime) > DateTime.Now))))
                    {
                        if (!Math2.Probability(chances))                                        //Probability in percentage that it will be an interception instead of a double hit.  Always in favor of the faster attacker.
                        {
                            if (mainTarget.CanTarget(attacker) && mainTarget.Can(Locks.Attack)) //TODO: Add Hit lock when available.
                            {
                                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);
                                }
                                ((CombatMastery)skillHandler).UseWithoutRangeCheck(mainTarget, combatMastery, attacker.EntityId, attacker, SkillId.CombatMastery);
                                return(CombatSkillResult.Okay);
                            }
                        }
                        else
                        {
                            interceptingSkillId = SkillId.CombatMastery;
                            if (mainTarget.CanTarget(attacker) && mainTarget.Can(Locks.Attack))                             //TODO: Add Hit lock when available.
                            {
                                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
                                {
                                    ((CombatMastery)skillHandler).UseWithoutRangeCheck(mainTarget, combatMastery, attacker.EntityId, attacker, SkillId.CombatMastery);
                                    simultaneousAttackStun = attacker.Stun;
                                    attacker.Stun          = 0;
                                }
                            }
                        }
                    }
                    else
                    {
                        if (Math2.Probability(chances))                         //Probability in percentage that it will be an interception instead of a double hit.  Always in favor of the faster attacker.
                        {
                            interceptingSkillId = SkillId.CombatMastery;
                        }
                        else
                        {
                            interceptingSkillId = SkillId.CombatMastery;
                            if (mainTarget.CanTarget(attacker) && mainTarget.Can(Locks.Attack))                             //TODO: Add Hit lock when available.
                            {
                                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
                                {
                                    ((CombatMastery)skillHandler).UseWithoutRangeCheck(mainTarget, combatMastery, attacker.EntityId, attacker, SkillId.CombatMastery);
                                    simultaneousAttackStun = attacker.Stun;
                                    attacker.Stun          = 0;
                                }
                            }
                        }
                    }
                }
            }

            attacker.StopMove();
            mainTarget.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 magazine = attacker.Inventory.Magazine;
            var maxHits  = (byte)(attacker.IsDualWielding ? 2 : 1);
            int prevId   = 0;

            var knockedBackTargets = new HashSet <long>();

            for (byte i = 1; i <= maxHits; ++i)
            {
                var weapon          = (i == 1 ? rightWeapon : leftWeapon);
                var weaponIsKnuckle = (weapon != null && weapon.Data.HasTag("/knuckle/"));

                AttackerAction aAction;

                if (interceptingSkillId == SkillId.Smash)
                {
                    aAction = new AttackerAction(CombatActionType.SimultaneousHit, attacker, targetEntityId);
                }
                else if (interceptingSkillId == SkillId.CombatMastery)
                {
                    aAction = new AttackerAction(CombatActionType.SimultaneousHit, attacker, targetEntityId);
                }
                else
                {
                    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);
                if (interceptingSkillId != SkillId.Smash)
                {
                    cap.Add(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)
                {
                    // Skip targets that were knocked back, as they aren't in
                    // range anymore.
                    if (knockedBackTargets.Contains(target.EntityId))
                    {
                        continue;
                    }

                    target.StopMove();

                    TargetAction tAction;

                    if (target == mainTarget)
                    {
                        if (interceptingSkillId == SkillId.Smash)
                        {
                            tAction = new TargetAction(CombatActionType.CounteredHit, target, attacker, SkillId.Smash);
                        }
                        else if (interceptingSkillId == SkillId.CombatMastery)
                        {
                            tAction = new TargetAction(CombatActionType.CounteredHit, target, attacker, target.Skills.IsReady(SkillId.FinalHit) ? SkillId.FinalHit : SkillId.CombatMastery);
                        }
                        else
                        {
                            tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, target.Skills.IsReady(SkillId.FinalHit) ? SkillId.FinalHit : SkillId.CombatMastery);
                        }
                    }
                    else
                    {
                        tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, target.Skills.IsReady(SkillId.FinalHit) ? SkillId.FinalHit : SkillId.CombatMastery);
                    }

                    tAction.Set(TargetOptions.Result);

                    cap.Add(tAction);
                    if (target == mainTarget && interceptingSkillId == SkillId.Smash)
                    {
                        cap.Add(aAction);
                    }


                    // 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);
                        }

                        knockedBackTargets.Add(target.EntityId);
                    }

                    // 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 = (rightWeapon != null && rightWeapon.Data.StaminaUsage != 0 ? rightWeapon.Data.StaminaUsage : 0.7f) + (dualWield ? leftWeapon.Data.StaminaUsage : 0f);
                        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();
            }

            attacker.AttemptingAttack = false;
            return(CombatSkillResult.Okay);
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Handles skill usage while ignoring range.
        /// </summary>
        /// <param name="attacker"></param>
        /// <param name="skill"></param>
        /// <param name="targetEntityId"></param>
        /// <returns></returns>
        public CombatSkillResult UseWithoutRangeCheck(Creature attacker, Skill skill, long targetEntityId, Creature mainTarget, SkillId interceptingSkillId = SkillId.None)
        {
            // Against Normal Attack
            Skill combatMastery = mainTarget.Skills.Get(SkillId.CombatMastery);

            if (interceptingSkillId == SkillId.None && combatMastery != null && (mainTarget.Skills.ActiveSkill == null || mainTarget.Skills.ActiveSkill == combatMastery || mainTarget.Skills.IsReady(SkillId.FinalHit)) && mainTarget.IsInBattleStance && mainTarget.Target == attacker && mainTarget.AttemptingAttack && (!mainTarget.IsStunned || mainTarget.IsKnockedDown))
            {
                if (mainTarget.CanTarget(attacker) && mainTarget.Can(Locks.Attack))                 //TODO: Add Hit lock when available.
                {
                    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);
                    }
                    ((CombatMastery)skillHandler).UseWithoutRangeCheck(mainTarget, combatMastery, attacker.EntityId, attacker, SkillId.Smash);
                    return(CombatSkillResult.Okay);
                }
            }

            // Against Smash
            Skill smash = mainTarget.Skills.Get(SkillId.Smash);

            if (interceptingSkillId == SkillId.None && smash != null && mainTarget.Skills.IsReady(SkillId.Smash) && mainTarget.IsInBattleStance && mainTarget.Target == attacker && !mainTarget.IsStunned)
            {
                var    attackerStunTime   = CombatMastery.GetAttackerStun(attacker, attacker.RightHand, false);
                var    mainTargetStunTime = CombatMastery.GetAttackerStun(mainTarget, mainTarget.Inventory.RightHand, false);
                var    slowestStun        = CombatMastery.GetAttackerStun(1, AttackSpeed.VerySlow, false);
                var    additionalStun     = slowestStun + (CombatMastery.GetAttackerStun(5, AttackSpeed.VeryFast, false) / 2);                                                                                            //Fastest stun divided by two so that the fastest stun doesn't always beat out the slowest stun.  The addition is so that the subtration (Ex. additionalStun - attackerStunTime) ends in the desired range.
                var    formulaMultiplier  = 320;                                                                                                                                                                          //Multiplier to keep the result reasonable, found through trial and error?
                var    formulaEqualizer   = 50;                                                                                                                                                                           //Balances the subtraction to keep the result in a reasonable range and balanced out no matter the order.
                double chances            = ((((additionalStun - attackerStunTime) / slowestStun) * formulaMultiplier) - (((additionalStun - mainTargetStunTime) / slowestStun) * formulaMultiplier)) + formulaEqualizer; //Probability in percentage that you will not lose.
                chances = Math2.Clamp(0.0, 99.0, chances);                                                                                                                                                                //Cap the stun, just in case.

                if (((mainTarget.LastKnockedBackBy == attacker && mainTarget.KnockDownTime > attacker.KnockDownTime && mainTarget.KnockDownTime.AddMilliseconds(mainTargetStunTime) > DateTime.Now ||
                      /*attackerStunTime > initialTargetStunTime && */
                      !(attacker.LastKnockedBackBy == mainTarget && attacker.KnockDownTime > mainTarget.KnockDownTime && attacker.KnockDownTime.AddMilliseconds(attackerStunTime) > DateTime.Now))))
                {
                    if (mainTarget.CanTarget(attacker) && mainTarget.Can(Locks.Attack))                     //TODO: Add Hit lock when available.
                    {
                        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);
                        }
                        ((Smash)skillHandler).UseWithoutRangeCheck(mainTarget, smash, attacker.EntityId, attacker, SkillId.Smash);
                        return(CombatSkillResult.Okay);
                    }
                }
                else
                {
                    interceptingSkillId = SkillId.Smash;
                }
            }

            // Stop movement
            attacker.StopMove();
            mainTarget.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);

            // Calculate damage
            var mainDamage = this.GetDamage(attacker, skill);

            foreach (var target in targets)
            {
                // Stop movement
                target.StopMove();

                TargetAction tAction;
                if (target == mainTarget)
                {
                    if (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);
                    }
                }
                else
                {
                    tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id);
                }

                tAction.Set(TargetOptions.Result | TargetOptions.Smash);

                cap.Add(tAction);
                if (target == mainTarget)
                {
                    cap.Add(aAction);
                }

                // 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);

                // Conditions
                SkillHelper.HandleConditions(attacker, 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);
        }