/// <summary>
        /// Handles skill usage.
        /// </summary>
        /// <param name="attacker"></param>
        /// <param name="skill"></param>
        /// <param name="targetAreaEntityId"></param>
        public void Use(Creature attacker, Skill skill, long targetAreaEntityId)
        {
            Send.Effect(attacker, 5, (byte)1, targetAreaEntityId);

            var cap = new CombatActionPack(attacker, skill.Info.Id);

            var aAction = new AttackerAction(CombatActionType.Attacker, attacker, targetAreaEntityId);

            aAction.Options |= AttackerOptions.Result;
            aAction.Stun     = UseStun;
            cap.Add(aAction);

            // Get targets in skill area
            var targets = SkillHelper.GetTargetableCreaturesInSkillArea(attacker, LaserRectHeight, LaserRectWidth);

            // Attack targets
            foreach (var target in targets)
            {
                var targetPosition = target.GetPosition();

                var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id);
                tAction.Options = TargetOptions.Result | TargetOptions.KnockDown;
                tAction.Stun    = TargetStun;
                tAction.Delay   = 1200;
                cap.Add(tAction);

                // Var2: 300/1000, based on rank. Could be damage?
                var damage = skill.RankData.Var2;

                // Modify damage
                CriticalHit.Handle(attacker, attacker.GetTotalCritChance(target.Protection), ref damage, tAction);
                SkillHelper.HandleDefenseProtection(target, ref damage);
                SkillHelper.HandleConditions(attacker, target, ref damage);
                ManaShield.Handle(target, ref damage, tAction);

                // Apply damage
                if (damage > 0)
                {
                    target.TakeDamage(tAction.Damage = 300, attacker);
                    SkillHelper.HandleInjury(attacker, target, damage);
                }
                target.Stability = Creature.MinStability;

                // Aggro
                target.Aggro(attacker);

                // Check death
                if (target.IsDead)
                {
                    tAction.Options |= TargetOptions.FinishingKnockDown;
                }

                // Knock back
                attacker.Shove(target, KnockbackDistance);
            }

            cap.Handle();

            Send.SkillUse(attacker, skill.Info.Id, 0);
        }
Beispiel #2
0
        /// <summary>
        /// Bolt specific use code.
        /// </summary>
        /// <param name="attacker"></param>
        /// <param name="skill"></param>
        /// <param name="target"></param>
        protected virtual void UseSkillOnTarget(Creature attacker, Skill skill, Creature target)
        {
            target.StopMove();

            // Create actions
            var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, skill.Info.Id, target.EntityId);

            aAction.Set(AttackerOptions.Result);

            var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id);

            tAction.Set(TargetOptions.Result);
            tAction.Stun = TargetStun;

            var cap = new CombatActionPack(attacker, skill.Info.Id, aAction, tAction);

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

            // Reduce damage
            if (this.Defendable)
            {
                Defense.Handle(aAction, tAction, ref damage);
            }
            SkillHelper.HandleMagicDefenseProtection(target, ref damage);
            ManaShield.Handle(target, ref damage, tAction);

            // Deal damage
            if (damage > 0)
            {
                target.TakeDamage(tAction.Damage = damage, attacker);
            }
            target.Aggro(attacker);

            // Death/Knockback
            this.HandleKnockBack(attacker, target, tAction);

            // Override stun set by defense
            aAction.Stun = AttackerStun;

            Send.Effect(attacker, Effect.UseMagic, EffectSkillName);
            Send.SkillUseStun(attacker, skill.Info.Id, aAction.Stun, 1);

            this.BeforeHandlingPack(attacker, skill);

            cap.Handle();
        }
Beispiel #3
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);
        }
Beispiel #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 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);
        }
Beispiel #5
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);
        }
Beispiel #6
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);
        }
Beispiel #7
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);
        }
Beispiel #8
0
        /// <summary>
        /// Handles using the skill.
        /// </summary>
        /// <param name="attacker"></param>
        /// <param name="skill"></param>
        /// <param name="targetAreaId"></param>
        /// <param name="unkInt1"></param>
        /// <param name="unkInt2"></param>
        public void Use(Creature attacker, Skill skill, long targetAreaId, int unkInt1, int unkInt2)
        {
            var range   = this.GetRange(attacker, skill);
            var targets = attacker.GetTargetableCreaturesInRange(range, true);
            var rnd     = RandomProvider.Get();

            // Create actions
            var cap = new CombatActionPack(attacker, skill.Info.Id);

            var aAction = new AttackerAction(CombatActionType.Attacker, attacker, skill.Info.Id, targetAreaId);

            aAction.Set(AttackerOptions.Result);
            aAction.Stun = AttackerStun;

            cap.Add(aAction);

            foreach (var target in targets)
            {
                // Check if hit
                var hitChance = this.GetHitChance(attacker, target, skill);
                if (rnd.Next(0, 100) > hitChance)
                {
                    continue;
                }

                target.StopMove();

                var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id);
                tAction.Set(TargetOptions.Result);
                tAction.Delay = 300;

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

                // Handle skills and reductions
                CriticalHit.Handle(attacker, attacker.GetTotalCritChance(0), ref damage, tAction);
                SkillHelper.HandleDefenseProtection(target, ref damage);
                ManaShield.Handle(target, ref damage, tAction);

                // Clean Hit if not critical
                if (!tAction.Has(TargetOptions.Critical))
                {
                    tAction.Set(TargetOptions.CleanHit);
                }

                // Take damage if any is left
                if (damage > 0)
                {
                    target.TakeDamage(tAction.Damage = damage, attacker);
                }

                // Finish if dead, knock down if not defended
                if (target.IsDead)
                {
                    tAction.Set(TargetOptions.KnockDownFinish);
                }
                else
                {
                    tAction.Set(TargetOptions.KnockDown);
                }

                // Anger Management
                if (!target.IsDead)
                {
                    target.Aggro(attacker);
                }

                // Stun & knock down
                tAction.Stun     = CombatMastery.GetTargetStun(attacker.AverageKnockCount, attacker.AverageAttackSpeed, true);
                target.Stability = Creature.MinStability;

                // Add action
                cap.Add(tAction);
            }

            Send.UseMotion(attacker, 10, 1);

            cap.Handle();

            Send.SkillUse(attacker, skill.Info.Id, targetAreaId, unkInt1, unkInt2);
        }
Beispiel #9
0
        /// <summary>
        /// Uses Charging Strike
        /// </summary>
        /// <param name="attacker"></param>
        /// <param name="skill"></param>
        /// <param name="packet"></param>
        public void Use(Creature attacker, Skill skill, Packet packet)
        {
            // Get Target
            var targetEntityId = packet.GetLong();

            var target = attacker.Region.GetCreature(targetEntityId);

            var attackerPos = attacker.GetPosition();
            var targetPos   = target.GetPosition();

            // Check target + collisions
            if (target == null || attacker.Region.Collisions.Any(attackerPos, targetPos))
            {
                Send.SkillUseSilentCancel(attacker);
                attacker.Unlock(Locks.All);
                return;
            }

            // Stop movement
            attacker.Lock(Locks.Walk | Locks.Run);
            attacker.StopMove();
            target.StopMove();

            // Effects
            Send.EffectDelayed(attacker, attackerPos.GetDistance(targetPos), Effect.ChargingStrike, (byte)0, targetEntityId);

            // Conditions
            var extra = new MabiDictionary();

            extra.SetBool("CONDITION_FAST_MOVE_NO_LOCK", false);
            attacker.Conditions.Activate(ConditionsC.FastMove, extra);

            Send.ForceRunTo(attacker, targetPos);
            attacker.SetPosition(targetPos.X, targetPos.Y);

            Send.SkillUseEntity(attacker, skill.Info.Id, targetEntityId);

            Send.EffectDelayed(attacker, attackerPos.GetDistance(targetPos), Effect.ChargingStrike, (byte)1, targetEntityId);

            // Counter
            if (Counterattack.Handle(target, attacker))
            {
                attacker.Conditions.Deactivate(ConditionsC.FastMove);
                return;
            }

            // Prepare Combat Actions
            var cap = new CombatActionPack(attacker, skill.Info.Id);

            var aAction = new AttackerAction(CombatActionType.SpecialHit, attacker, targetEntityId, skill.Info.Id);

            aAction.Set(AttackerOptions.UseEffect);
            aAction.PropId = targetEntityId;

            var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id);

            tAction.Set(TargetOptions.Result);
            tAction.Delay = attackerPos.GetDistance(targetPos);

            cap.Add(aAction, tAction);

            // Damage
            var damage = (attacker.GetRndFighterDamage() * (skill.RankData.Var1 / 100f));

            // Chain Mastery Damage Bonus
            var chainMasterySkill = attacker.Skills.Get(SkillId.ChainMastery);
            var damageBonus       = (chainMasterySkill == null ? 0 : chainMasterySkill.RankData.Var1);

            damage += damage * (damageBonus / 100f);

            // Master Title - Damage +30%
            if (attacker.Titles.SelectedTitle == skill.Data.MasterTitle)
            {
                damage += (damage * 0.3f);
            }

            // Critical Hit
            var critChance = attacker.GetRightCritChance(target.Protection);

            CriticalHit.Handle(attacker, critChance, ref damage, tAction);

            // Handle skills and reductions
            SkillHelper.HandleDefenseProtection(target, ref damage);
            HeavyStander.Handle(attacker, target, ref damage, tAction);
            SkillHelper.HandleConditions(attacker, target, ref damage);
            ManaShield.Handle(target, ref damage, tAction);

            // Apply Damage
            target.TakeDamage(tAction.Damage = damage, attacker);

            // Aggro
            target.Aggro(attacker);

            // Stun Times
            tAction.Stun = TargetStun;
            aAction.Stun = AttackerStun;

            // Death and Knockback
            if (target.IsDead)
            {
                if (target.Is(RaceStands.KnockDownable))
                {
                    tAction.Set(TargetOptions.FinishingKnockDown);
                    attacker.Shove(target, KnockbackDistance);
                }
                else
                {
                    tAction.Set(TargetOptions.Finished | TargetOptions.FinishingHit);
                }
            }
            else             // This skill never knocks back normally
            {
                if (!target.IsKnockedDown)
                {
                    target.Stability -= StabilityReduction;
                }
            }
            cap.Handle();

            attacker.Conditions.Deactivate(ConditionsC.FastMove);
            Send.SkillComplete(attacker, skill.Info.Id);

            // Chain Progress to Stage 2
            attacker.Temp.FighterChainStartTime = DateTime.Now;
            attacker.Temp.FighterChainLevel     = 2;

            attacker.Skills.ActiveSkill = null;

            // Charging strike locks EVERYTHING for some reason...
            attacker.Unlock(Locks.All);
        }
Beispiel #10
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 initTarget = attacker.Region.GetCreature(targetEntityId);

            // Check Target
            if (initTarget == null)
            {
                return(CombatSkillResult.InvalidTarget);
            }

            var attackerPos   = attacker.StopMove();
            var initTargetPos = initTarget.GetPosition();

            // Check Range
            var range = (int)skill.RankData.Var2;

            if (!attacker.GetPosition().InRange(initTargetPos, range))
            {
                return(CombatSkillResult.OutOfRange);
            }

            // Check for Collisions
            if (attacker.Region.Collisions.Any(attackerPos, initTargetPos))
            {
                return(CombatSkillResult.InvalidTarget);
            }

            initTarget.StopMove();

            // Effects
            Send.Effect(attacker, Effect.TheFakeSpiralSword, TheFakeSpiralSwordEffect.Attack, (DateTime.Now.Ticks / 10000), (byte)1);

            // Skill Use
            Send.SkillUseStun(attacker, skill.Info.Id, AttackerStun, 1);
            skill.Stacks = 0;

            // Prepare Combat Actions
            var cap = new CombatActionPack(attacker, skill.Info.Id);

            var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, targetEntityId);

            aAction.Set(AttackerOptions.KnockBackHit1 | AttackerOptions.KnockBackHit2 | AttackerOptions.Result);
            cap.Add(aAction);

            aAction.Stun = AttackerStun;

            // Get Explosion Radius of Attack
            var explosionRadius = (int)skill.RankData.Var3 / 2;

            // Get Explosion Targets
            var targets = attacker.GetTargetableCreaturesAround(initTargetPos, explosionRadius);

            var rnd = RandomProvider.Get();

            // Get Critical Hit
            var crit = false;

            if (attacker.Skills.Has(SkillId.CriticalHit, SkillRank.RF))
            {
                var critChance = attacker.GetRightCritChance(0);
                crit = (rnd.Next(100) < critChance);
            }

            foreach (var target in targets)
            {
                var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id);
                tAction.Set(TargetOptions.Result);
                tAction.Delay = attackerPos.GetDistance(initTargetPos) / 2;
                cap.Add(tAction);

                // Damage
                var damage = (attacker.GetRndTotalDamage() * (skill.RankData.Var1 / 100f));

                // Critical Hit
                if (crit)
                {
                    CriticalHit.Handle(attacker, 100, ref damage, tAction);
                }

                // Defense and Prot
                SkillHelper.HandleDefenseProtection(target, ref damage);

                // Defense
                Defense.Handle(aAction, tAction, ref damage);

                // Mana Shield
                ManaShield.Handle(target, ref damage, tAction);

                // Heavy Stander
                HeavyStander.Handle(attacker, target, ref damage, tAction);

                // Apply Damage
                target.TakeDamage(tAction.Damage = damage, attacker);

                // Aggro
                target.Aggro(attacker);

                // Stun Time
                tAction.Stun = TargetStun;

                // Death and Knockback
                if (target.Is(RaceStands.KnockDownable))
                {
                    if (target.IsDead)
                    {
                        tAction.Set(TargetOptions.FinishingKnockDown);
                    }
                    else
                    {
                        tAction.Set(TargetOptions.KnockDown);
                    }

                    // Shove
                    if (target == initTarget)
                    {
                        attacker.Shove(target, KnockbackDistance);
                    }
                    else
                    {
                        initTarget.Shove(target, KnockbackDistance);
                    }
                }
            }

            aAction.Creature.Stun = aAction.Stun;
            cap.Handle();

            // User can attack multiple times if attack isn't locked, which will cause them to freeze.
            // This is automatically unlocked by the skill after Use is finished.
            attacker.Lock(Locks.Attack);

            return(CombatSkillResult.Okay);
        }
Beispiel #11
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);
        }
Beispiel #12
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, skill.Info.Id, targetEntityId);

            aAction.Set(AttackerOptions.Result);
            aAction.Stun = AttackerStun;
            cap.Add(aAction);

            // Hit by chance
            var chance = attacker.AimMeter.GetAimChance(target);
            var rnd    = RandomProvider.Get();

            if (rnd.NextDouble() * 100 < chance)
            {
                target.StopMove();

                aAction.Set(AttackerOptions.KnockBackHit2);

                var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id);
                tAction.Set(TargetOptions.Result | TargetOptions.CleanHit);
                tAction.Stun = TargetStun;
                cap.Add(tAction);

                // TODO: Splash damage

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

                // More damage with fire arrow
                if (attacker.Temp.FireArrow)
                {
                    damage *= FireBonus;
                }

                // Critical Hit
                var critChance = attacker.GetRightCritChance(target.Protection);
                CriticalHit.Handle(attacker, critChance, ref damage, tAction);

                // Subtract target def/prot
                SkillHelper.HandleDefenseProtection(target, ref damage);

                // Defense
                Defense.Handle(aAction, tAction, ref damage);

                // Mana Shield
                ManaShield.Handle(target, ref damage, tAction);

                // Deal with it!
                if (damage > 0)
                {
                    target.TakeDamage(tAction.Damage = damage, attacker);
                }

                // TODO: We have to calculate knockback distance right
                // TODO: Target with Defense and shield shouldn't be knocked back
                attacker.Shove(target, KnockBackDistance);

                // Aggro
                target.Aggro(attacker);

                tAction.Set(TargetOptions.KnockDownFinish);

                if (target.IsDead)
                {
                    aAction.Set(AttackerOptions.KnockBackHit1);
                    tAction.Set(TargetOptions.Finished);
                }
            }
            else
            {
                aAction.Set(AttackerOptions.Missed);
            }

            // Reduce arrows
            if (attacker.Magazine != null && !ChannelServer.Instance.Conf.World.InfiniteArrows)
            {
                attacker.Inventory.Decrement(attacker.Magazine);
            }

            // Disable fire arrow effect
            if (attacker.Temp.FireArrow)
            {
                Send.Effect(attacker, Effect.FireArrow, false);
            }

            // "Cancels" the skill
            // 800 = old load time? == aAction.Stun? Varies? Doesn't seem to be a stun.
            Send.SkillUse(attacker, skill.Info.Id, 800, 1);

            cap.Handle();

            return(CombatSkillResult.Okay);
        }
Beispiel #13
0
        /// <summary>
        /// Handles skill usage.
        /// </summary>
        /// <param name="attacker"></param>
        /// <param name="skill"></param>
        /// <param name="packet"></param>
        public void Use(Creature attacker, Skill skill, Packet packet)
        {
            var targetAreaEntityId = packet.GetLong();

            Send.Effect(attacker, 5, (byte)1, targetAreaEntityId);

            var cap = new CombatActionPack(attacker, skill.Info.Id);

            var aAction = new AttackerAction(CombatActionType.Attacker, attacker, skill.Info.Id, targetAreaEntityId);

            aAction.Options |= AttackerOptions.Result;
            aAction.Stun     = UseStun;
            cap.Add(aAction);

            var attackerPosition = attacker.GetPosition();

            // Calculate rectangular target area
            var targetAreaPos = new Position(targetAreaEntityId);
            var poe           = targetAreaPos.GetRelative(attackerPosition, -800);
            var r             = (Math.PI / 2) + Math.Atan2(attackerPosition.Y - targetAreaPos.Y, attackerPosition.X - targetAreaPos.X);
            var pivot         = new Point(poe.X, poe.Y);
            var p1            = new Point(pivot.X - LaserRectWidth / 2, pivot.Y - LaserRectHeight / 2);
            var p2            = new Point(pivot.X - LaserRectWidth / 2, pivot.Y + LaserRectHeight / 2);
            var p3            = new Point(pivot.X + LaserRectWidth / 2, pivot.Y + LaserRectHeight / 2);
            var p4            = new Point(pivot.X + LaserRectWidth / 2, pivot.Y - LaserRectHeight / 2);

            p1 = this.RotatePoint(p1, pivot, r);
            p2 = this.RotatePoint(p2, pivot, r);
            p3 = this.RotatePoint(p3, pivot, r);
            p4 = this.RotatePoint(p4, pivot, r);

            // Attack targets
            var targets = attacker.Region.GetCreaturesInPolygon(p1, p2, p3, p4);

            foreach (var target in targets.Where(cr => !cr.IsDead && !cr.Has(CreatureStates.NamedNpc)))
            {
                var targetPosition = target.GetPosition();

                var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id);
                tAction.Options = TargetOptions.Result | TargetOptions.KnockDown;
                tAction.Stun    = TargetStun;
                tAction.Delay   = 1200;
                cap.Add(tAction);

                // Var2: 300/1000, based on rank. Could be damage?
                var damage = skill.RankData.Var2;

                // Increase damage
                CriticalHit.Handle(attacker, attacker.GetTotalCritChance(target.Protection), ref damage, tAction);

                // Reduce damage
                SkillHelper.HandleDefenseProtection(target, ref damage);
                ManaShield.Handle(target, ref damage, tAction);

                // Apply damage
                target.TakeDamage(tAction.Damage = 300, attacker);
                target.Stability = Creature.MinStability;

                // Aggro
                target.Aggro(attacker);

                // Check death
                if (target.IsDead)
                {
                    tAction.Options |= TargetOptions.FinishingKnockDown;
                }

                // Knock back
                attacker.Shove(target, KnockbackDistance);
            }

            cap.Handle();

            Send.SkillUse(attacker, skill.Info.Id, 0);
        }
Beispiel #14
0
        /// <summary>
        /// Handles drop kick's splash attack
        /// </summary>
        /// <param name="attacker"></param>
        /// <param name="skill"></param>
        /// <param name="initTarget"></param>
        /// <param name="skillLength"></param>
        /// <param name="damageBonus"></param>
        public void HandleSplash(Creature attacker, Skill skill, Creature initTarget, float skillLength, float damageBonus, float damageReduction)
        {
            // Variables
            var skillWidth  = skill.RankData.Var3;
            var attackerPos = attacker.GetPosition();

            // Set training variable
            attacker.Temp.DropKickSplashTargetCount = 0;

            // Get splash targets
            var targets = SkillHelper.GetTargetableCreaturesInSkillArea(attacker, (int)skillLength, (int)skillWidth);

            targets.Remove(initTarget);

            // Target Area?
            var targetAreaLoc = new Location(attacker.RegionId, attackerPos);
            var targetAreaId  = targetAreaLoc.ToLocationId();

            // Prepare Splash Combat Actions
            var cap = new CombatActionPack(attacker, skill.Info.Id);

            var aAction = new AttackerAction(CombatActionType.SpecialHit, attacker, targetAreaId, skill.Info.Id);

            aAction.Set(AttackerOptions.Result);

            cap.Add(aAction);

            // Get critical hit (also for splash attacks)
            var rnd       = RandomProvider.Get();
            var crit      = false;
            var critSkill = attacker.Skills.Get(SkillId.CriticalHit);

            if (critSkill != null && critSkill.Info.Rank > SkillRank.Novice)
            {
                var critChance = Math2.Clamp(0, 30, attacker.GetTotalCritChance(0));
                if (rnd.NextDouble() * 100 < critChance)
                {
                    crit = true;
                }
            }

            // Target Actions
            foreach (var target in targets)
            {
                attacker.Temp.DropKickSplashTargetCount += 1;

                var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id);
                tAction.Delay = target.GetPosition().GetDistance(attackerPos);
                cap.Add(tAction);

                // Splash Damage
                var damage = (attacker.GetRndFighterDamage() * (skill.RankData.Var5 / 100f));

                // Chain Mastery Damage Bonus
                damage += damage * (damageBonus / 100f);

                // Heavy Armor Damage Reduction
                if (attacker.HasEquipped("/heavyarmor/"))
                {
                    damage -= damage * (damageReduction / 100f);
                }

                // Master Title - Damage +10%
                if (attacker.Titles.SelectedTitle == skill.Data.MasterTitle)
                {
                    damage += (damage * 0.1f);
                }

                // Critical Hit
                if (crit)
                {
                    CriticalHit.Handle(attacker, 100, ref damage, tAction);
                }

                // Handle skills and reductions
                SkillHelper.HandleDefenseProtection(target, ref damage);
                HeavyStander.Handle(attacker, target, ref damage, tAction);
                SkillHelper.HandleConditions(attacker, target, ref damage);
                ManaShield.Handle(target, ref damage, tAction);

                // Apply Damage
                target.TakeDamage(tAction.Damage = damage, attacker);

                // Aggro
                target.Aggro(attacker);

                // Stun
                tAction.Stun = TargetStun;

                // Death and Knockback
                if (target.IsDead)
                {
                    if (target.Is(RaceStands.KnockDownable))
                    {
                        tAction.Set(TargetOptions.FinishingKnockDown);
                        attacker.Shove(target, (int)skillLength);
                    }
                    else
                    {
                        tAction.Set(TargetOptions.Finished | TargetOptions.FinishingHit);
                    }
                }
                else
                {
                    if (!target.IsKnockedDown)
                    {
                        target.Stability -= StabilityReduction;
                    }

                    if (target.Is(RaceStands.KnockDownable))                     // Always knock down
                    {
                        tAction.Set(TargetOptions.KnockDown);
                        attacker.Shove(target, (int)skillLength);
                    }
                }
            }
            cap.Handle();
        }
Beispiel #15
0
        /// <summary>
        /// Uses the skill
        /// </summary>
        /// <param name="attacker"></param>
        /// <param name="skill"></param>
        /// <param name="packet"></param>
        public void UseSkill(Creature attacker, Skill skill, long targetEntityId)
        {
            var target = attacker.Region.GetCreature(targetEntityId);

            var attackerPos = attacker.GetPosition();
            var targetPos   = target.GetPosition();

            // Check target + collisions
            if (target == null || attacker.Region.Collisions.Any(attackerPos, targetPos))
            {
                Send.SkillUseSilentCancel(attacker);
                return;
            }

            // Stop movement
            target.StopMove();

            Send.SkillUseEntity(attacker, skill.Info.Id, targetEntityId);
            skill.State = SkillState.Used;

            // Variables
            var skillLength = skill.RankData.Var4;

            // Effects
            Send.Effect(attacker, Effect.DropKick, (byte)1, targetEntityId);
            Send.EffectDelayed(attacker, 300, Effect.DropKick, (byte)2, targetEntityId, (short)400);

            // Prepare Singular Target Combat Actions
            var cap = new CombatActionPack(attacker, skill.Info.Id);

            var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, targetEntityId, skill.Info.Id);

            aAction.Set(AttackerOptions.Result);

            var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id);

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

            cap.Add(aAction, tAction);

            // Damage
            var damage = (attacker.GetRndFighterDamage() * (skill.RankData.Var1 / 100f));

            // Chain Mastery Damage Bonus
            var chainMasterySkill = attacker.Skills.Get(SkillId.ChainMastery);
            var damageBonus       = (chainMasterySkill == null ? 0 : chainMasterySkill.RankData.Var1);

            damage += damage * (damageBonus / 100f);

            // Heavy Armor Damage Reduction
            var damageReduction = (chainMasterySkill == null ? 0 : (100 - chainMasterySkill.RankData.Var3));

            if (attacker.HasEquipped("/heavyarmor/"))
            {
                damage -= damage * (damageReduction / 100f);
            }

            // Master Title - Damage +10%
            if (attacker.Titles.SelectedTitle == skill.Data.MasterTitle)
            {
                damage += (damage * 0.1f);
            }

            // Critical Hit
            var critChance = attacker.GetRightCritChance(target.Protection);

            CriticalHit.Handle(attacker, critChance, ref damage, tAction);

            // Handle skills and reductions
            SkillHelper.HandleDefenseProtection(target, ref damage);
            HeavyStander.Handle(attacker, target, ref damage, tAction);
            SkillHelper.HandleConditions(attacker, target, ref damage);
            ManaShield.Handle(target, ref damage, tAction);

            // Apply Damage
            target.TakeDamage(tAction.Damage = damage, attacker);

            // Aggro
            target.Aggro(attacker);

            // Stun Times
            tAction.Stun = TargetStun;
            aAction.Stun = AttackerStun;

            // Death and Knockback
            if (target.IsDead)
            {
                if (target.Is(RaceStands.KnockDownable))
                {
                    tAction.Set(TargetOptions.FinishingKnockDown);
                    attacker.Shove(target, (int)skillLength);
                }
                else
                {
                    tAction.Set(TargetOptions.Finished | TargetOptions.FinishingHit);
                }
            }
            else
            {
                if (!target.IsKnockedDown)
                {
                    target.Stability -= StabilityReduction;
                }

                if (target.Is(RaceStands.KnockDownable))                 // Always knock down
                {
                    tAction.Set(TargetOptions.KnockDown);
                    attacker.Shove(target, (int)skillLength);
                }
            }
            cap.Handle();

            // Splash Damage
            this.HandleSplash(attacker, skill, target, skillLength, damageBonus, damageReduction);
        }
Beispiel #16
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);
        }
Beispiel #17
0
        /// <summary>
        /// Uses WM, attacking targets.
        /// </summary>
        /// <param name="attacker"></param>
        /// <param name="skill"></param>
        /// <param name="packet"></param>
        public void Use(Creature attacker, Skill skill, Packet packet)
        {
            var targetAreaId = packet.GetLong();
            var unkInt1      = packet.GetInt();
            var unkInt2      = packet.GetInt();

            var range   = this.GetRange(attacker, skill);
            var targets = attacker.GetTargetableCreaturesInRange(range);

            // Check targets
            if (targets.Count == 0)
            {
                Send.Notice(attacker, Localization.Get("There isn't a target nearby to use that on."));
                Send.SkillUseSilentCancel(attacker);
                return;
            }

            // Create actions
            var cap = new CombatActionPack(attacker, skill.Info.Id);

            var aAction = new AttackerAction(CombatActionType.SpecialHit, attacker, skill.Info.Id, targetAreaId);

            aAction.Set(AttackerOptions.Result);

            cap.Add(aAction);

            var survived = new List <Creature>();

            foreach (var target in targets)
            {
                target.StopMove();

                var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id);
                tAction.Delay = 300;                 // Usually 300, sometimes 350?

                // Calculate damage and crit
                var damage     = attacker.GetRndTotalDamage();
                var critChance = attacker.CriticalBase;

                damage *= skill.RankData.Var1 / 100f;

                // Handle skills and reductions
                CriticalHit.Handle(attacker, critChance, ref damage, tAction);
                SkillHelper.HandleDefenseProtection(target, ref damage);
                Defense.Handle(aAction, tAction, ref damage);
                ManaShield.Handle(target, ref damage, tAction);

                // Clean Hit if not defended nor critical
                if (!tAction.Is(CombatActionType.Defended) && !tAction.Has(TargetOptions.Critical))
                {
                    tAction.Set(TargetOptions.CleanHit);
                }

                // Take damage if any is left
                if (damage > 0)
                {
                    target.TakeDamage(tAction.Damage = damage, attacker);
                }

                // Finish if dead, knock down if not defended
                if (target.IsDead)
                {
                    tAction.Set(TargetOptions.KnockDownFinish);
                }
                else if (!tAction.Is(CombatActionType.Defended))
                {
                    tAction.Set(TargetOptions.KnockDown);
                }

                // Anger Management
                if (!target.IsDead)
                {
                    survived.Add(target);
                }

                // Stun & knock back
                aAction.Stun = CombatMastery.GetAttackerStun(attacker.AverageKnockCount, attacker.AverageAttackSpeed, true);

                if (!tAction.Is(CombatActionType.Defended))
                {
                    tAction.Stun     = CombatMastery.GetTargetStun(attacker.AverageKnockCount, attacker.AverageAttackSpeed, true);
                    target.Stability = Creature.MinStability;
                    attacker.Shove(target, KnockbackDistance);
                }

                // Add action
                cap.Add(tAction);
            }

            // Only select a random aggro if there is no aggro yet,
            // WM only aggroes one target at a time.
            if (survived.Count != 0 && attacker.Region.CountAggro(attacker) < 1)
            {
                var rnd         = RandomProvider.Get();
                var aggroTarget = survived.Random();
                aggroTarget.Aggro(attacker);
            }

            // Spin it~
            Send.UseMotion(attacker, 8, 4);

            cap.Handle();

            Send.SkillUse(attacker, skill.Info.Id, targetAreaId, unkInt1, unkInt2);

            skill.Stacks = 0;
        }
Beispiel #18
0
        /// <summary>
        /// Uses the skill.
        /// </summary>
        /// <param name="attacker"></param>
        /// <param name="skill"></param>
        /// <param name="targetEntityId"></param>
        /// <returns></returns>
        public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId)
        {
            // Get target
            var target = attacker.Region.GetCreature(targetEntityId);

            if (target == null)
            {
                return(CombatSkillResult.InvalidTarget);
            }

            var targetPos   = target.GetPosition();
            var attackerPos = attacker.GetPosition();

            var actionType   = (attacker.IsElf ? CombatActionPackType.ChainRangeAttack : CombatActionPackType.NormalAttack);
            var attackerStun = (short)(actionType == CombatActionPackType.ChainRangeAttack ? AttackerStunElf : AttackerStun);

            // "Cancels" the skill
            // 800 = old load time? == aAction.Stun? Varies? Doesn't seem to be a stun.
            Send.SkillUse(attacker, skill.Info.Id, attackerStun, 1);

            var chance        = attacker.AimMeter.GetAimChance(target);
            var rnd           = RandomProvider.Get().NextDouble() * 100;
            var successfulHit = (rnd < chance);

            var maxHits = (actionType == CombatActionPackType.ChainRangeAttack && successfulHit ? 2 : 1);
            var prevId  = 0;

            for (byte i = 1; i <= maxHits; ++i)
            {
                target.StopMove();

                // Actions
                var cap = new CombatActionPack(attacker, skill.Info.Id);
                cap.Hit    = i;
                cap.Type   = actionType;
                cap.PrevId = prevId;
                prevId     = cap.Id;

                var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, skill.Info.Id, targetEntityId);
                aAction.Set(AttackerOptions.Result);
                aAction.Stun = attackerStun;
                cap.Add(aAction);

                // Target action if hit
                if (successfulHit)
                {
                    var targetSkillId = target.Skills.ActiveSkill != null ? target.Skills.ActiveSkill.Info.Id : SkillId.CombatMastery;

                    var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, targetSkillId);
                    tAction.Set(TargetOptions.Result);
                    tAction.AttackerSkillId = skill.Info.Id;
                    tAction.Stun            = (short)(actionType == CombatActionPackType.ChainRangeAttack ? TargetStunElf : TargetStun);
                    if (actionType == CombatActionPackType.ChainRangeAttack)
                    {
                        tAction.EffectFlags = 0x20;
                    }
                    cap.Add(tAction);

                    // Damage
                    var damage = attacker.GetRndRangedDamage();

                    // More damage with fire arrow
                    if (attacker.Temp.FireArrow)
                    {
                        damage *= FireBonus;
                    }

                    // Critical Hit
                    var critChance = attacker.GetRightCritChance(target.Protection);
                    CriticalHit.Handle(attacker, critChance, ref damage, tAction);

                    // Subtract target def/prot
                    SkillHelper.HandleDefenseProtection(target, ref damage);

                    // Defense
                    Defense.Handle(aAction, tAction, ref damage);

                    // Mana Shield
                    ManaShield.Handle(target, ref damage, tAction);

                    // Deal with it!
                    if (damage > 0)
                    {
                        target.TakeDamage(tAction.Damage = damage, attacker);
                    }

                    // Aggro
                    target.Aggro(attacker);

                    // Death/Knockback
                    if (target.IsDead)
                    {
                        tAction.Set(TargetOptions.FinishingKnockDown);
                        attacker.Shove(target, KnockBackDistance);
                        maxHits = 1;
                    }
                    else
                    {
                        // Insta-recover in knock down
                        // TODO: Tied to stability?
                        if (target.IsKnockedDown)
                        {
                            tAction.Stun = 0;
                        }
                        // Knock down if hit repeatedly
                        else if (target.Stability < 30)
                        {
                            tAction.Set(TargetOptions.KnockDown);
                        }
                        // Normal stability reduction
                        else
                        {
                            target.Stability -= (actionType == CombatActionPackType.ChainRangeAttack ? StabilityReductionElf : StabilityReduction);
                            if (target.IsUnstable)
                            {
                                tAction.Set(TargetOptions.KnockBack);
                                attacker.Shove(target, KnockBackDistance);
                            }
                        }
                        tAction.Creature.Stun = tAction.Stun;
                    }
                }

                aAction.Creature.Stun = aAction.Stun;

                // Skill training
                if (skill.Info.Rank == SkillRank.Novice || skill.Info.Rank == SkillRank.RF)
                {
                    skill.Train(1);                     // Try ranged attack.
                }
                // Reduce arrows
                if (attacker.Magazine != null && !ChannelServer.Instance.Conf.World.InfiniteArrows && !attacker.Magazine.HasTag("/unlimited_arrow/"))
                {
                    attacker.Inventory.Decrement(attacker.Magazine);
                }

                cap.Handle();
            }

            // Disable fire arrow effect
            if (attacker.Temp.FireArrow)
            {
                Send.Effect(attacker, Effect.FireArrow, false);
            }

            return(CombatSkillResult.Okay);
        }
Beispiel #19
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);
        }
Beispiel #20
0
        /// <summary>
        /// Handles usage of the skill.
        /// </summary>
        /// <param name="attacker"></param>
        /// <param name="target"></param>
        public void Use(Creature attacker, Creature target)
        {
            // Updating unlock because of the updating lock for pre-renovation
            // Has to be done here because we can't have an updating unlock
            // after the combat action, it resets the stun.
            if (!AuraData.FeaturesDb.IsEnabled("TalentRenovationCloseCombat"))
            {
                attacker.Unlock(Locks.Move, true);
            }

            var skill = attacker.Skills.Get(SkillId.Counterattack);

            var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, target.EntityId);

            aAction.Options |= AttackerOptions.Result | AttackerOptions.KnockBackHit2;

            var tAction = new TargetAction(CombatActionType.CounteredHit2, target, attacker, skill.Info.Id);

            tAction.Options |= TargetOptions.Result | TargetOptions.Smash;

            var cap = new CombatActionPack(attacker, skill.Info.Id);

            cap.Add(aAction, tAction);

            var damage =
                (attacker.GetRndTotalDamage() * (skill.RankData.Var2 / 100f)) +
                (target.GetRndTotalDamage() * (skill.RankData.Var1 / 100f));

            // Elementals
            damage *= attacker.CalculateElementalDamageMultiplier(target);

            // Critical Hit
            var critChance = attacker.GetTotalCritChance(target.Protection) + skill.RankData.Var3;

            CriticalHit.Handle(attacker, critChance, ref damage, tAction, true);

            // Subtract target def/prot
            SkillHelper.HandleDefenseProtection(target, ref damage, true, true);

            // Mana Shield
            ManaShield.Handle(target, ref damage, tAction);

            // Heavy Stander
            HeavyStander.Handle(attacker, target, ref damage, tAction);

            // Deal with it!
            if (damage > 0)
            {
                target.TakeDamage(tAction.Damage = damage, attacker);
                SkillHelper.HandleInjury(attacker, target, damage);
            }

            target.Aggro(attacker);

            if (target.IsDead)
            {
                tAction.Options |= TargetOptions.FinishingKnockDown;
            }

            aAction.Stun = StunTime;
            tAction.Stun = StunTime;

            target.Stability = Creature.MinStability;
            attacker.Shove(target, KnockbackDistance);

            // Update both weapons
            SkillHelper.UpdateWeapon(attacker, target, ProficiencyGainType.Melee, attacker.RightHand, attacker.LeftHand);

            Send.SkillUseStun(attacker, skill.Info.Id, StunTime, 1);

            this.Training(aAction, tAction);

            cap.Handle();
        }
Beispiel #21
0
        /// <summary>
        /// Uses the skill
        /// </summary>
        /// <param name="attacker"></param>
        /// <param name="skill"></param>
        /// <param name="packet"></param>
        public void UseSkill(Creature attacker, Skill skill, long targetEntityId)
        {
            var target = attacker.Region.GetCreature(targetEntityId);

            var attackerPos = attacker.GetPosition();
            var targetPos   = target.GetPosition();

            // Check target + collisions
            if (target == null || attacker.Region.Collisions.Any(attackerPos, targetPos))
            {
                Send.SkillUseSilentCancel(attacker);
                return;
            }

            // Stop movement
            attacker.StopMove();
            target.StopMove();

            Send.SkillUseEntity(attacker, skill.Info.Id, targetEntityId);
            skill.State = SkillState.Used;

            // Counter
            if (Counterattack.Handle(target, attacker))
            {
                return;
            }

            // Defense/Protection decrease on target
            var debuffChance = (int)skill.RankData.Var6;
            var defDecrease  = (int)skill.RankData.Var3;
            var protDecrease = (int)skill.RankData.Var4;

            var extra = new MabiDictionary();

            extra.SetShort("NEW_DEF", (short)defDecrease);
            extra.SetShort("NEW_PROT", (short)protDecrease);
            extra.SetLong("DDP_CHAR", attacker.EntityId);
            extra.SetShort("DDP_SKILL", (short)skill.Info.Id);

            var rnd = RandomProvider.Get();

            if (rnd.NextDouble() * 100 < debuffChance)
            {
                Send.Effect(target, Effect.SpinningUppercutDebuff, (short)skill.Info.Id, 0, defDecrease, protDecrease);
                target.Conditions.Activate(ConditionsC.DefProtectDebuff, extra);
                attacker.Temp.SpinningUppercutDebuffApplied = true;
            }

            // Prepare Combat Actions
            var cap = new CombatActionPack(attacker, skill.Info.Id);

            var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, targetEntityId, skill.Info.Id);

            aAction.Set(AttackerOptions.Result);

            var tAction = new TargetAction(CombatActionType.TakeHit | CombatActionType.Attacker, target, attacker, skill.Info.Id);

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

            cap.Add(aAction, tAction);

            // Damage
            var damage = (attacker.GetRndFighterDamage() * (skill.RankData.Var1 / 100f));

            // Chain Mastery Damage Bonus
            var chainMasterySkill = attacker.Skills.Get(SkillId.ChainMastery);
            var damageBonus       = (chainMasterySkill == null ? 0 : chainMasterySkill.RankData.Var1);

            damage += damage * (damageBonus / 100f);

            // Master Title - Damage +20%
            if (attacker.Titles.SelectedTitle == skill.Data.MasterTitle)
            {
                damage += (damage * 0.2f);
            }

            // Critical Hit
            var critChance = attacker.GetRightCritChance(target.Protection);

            CriticalHit.Handle(attacker, critChance, ref damage, tAction);

            // Handle skills and reductions
            SkillHelper.HandleDefenseProtection(target, ref damage);
            HeavyStander.Handle(attacker, target, ref damage, tAction);
            SkillHelper.HandleConditions(attacker, target, ref damage);
            ManaShield.Handle(target, ref damage, tAction);

            // Apply Damage
            target.TakeDamage(tAction.Damage = damage, attacker);

            // Aggro
            target.Aggro(attacker);

            // Stun Times
            tAction.Stun = TargetStun;
            aAction.Stun = AttackerStun;

            // Death and Knockback
            if (target.IsDead)
            {
                if (target.Is(RaceStands.KnockDownable))
                {
                    tAction.Set(TargetOptions.FinishingKnockDown);
                    attacker.Shove(target, KnockbackDistance);
                }
                else
                {
                    tAction.Set(TargetOptions.Finished | TargetOptions.FinishingHit);
                }
            }
            else
            {
                if (!target.IsKnockedDown)
                {
                    target.Stability -= StabilityReduction;
                }

                if (target.IsUnstable && target.Is(RaceStands.KnockDownable))
                {
                    tAction.Set(TargetOptions.KnockDown);
                    attacker.Shove(target, KnockbackDistance);
                }
            }
            cap.Handle();

            // Chain Progress to Stage 3
            attacker.Temp.FighterChainStartTime = DateTime.Now;
            attacker.Temp.FighterChainLevel     = 3;
        }
Beispiel #22
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);
        }
Beispiel #23
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);
        }
Beispiel #24
0
        /// <summary>
        /// Uses WM, attacking targets.
        /// </summary>
        /// <param name="attacker"></param>
        /// <param name="skill"></param>
        /// <param name="targetAreaId"></param>
        /// <param name="unkInt1"></param>
        /// <param name="unkInt2"></param>
        public void Use(Creature attacker, Skill skill, long targetAreaId, int unkInt1, int unkInt2)
        {
            var range   = this.GetRange(attacker, skill);
            var targets = attacker.GetTargetableCreaturesInRange(range, TargetableOptions.AddAttackRange);

            // Check targets
            if (targets.Count == 0)
            {
                Send.Notice(attacker, Localization.Get("There isn't a target nearby to use that on."));
                Send.SkillUseSilentCancel(attacker);
                return;
            }

            // Create actions
            var cap = new CombatActionPack(attacker, skill.Info.Id);

            var aAction = new AttackerAction(CombatActionType.SpecialHit, attacker, targetAreaId);

            aAction.Set(AttackerOptions.Result);
            aAction.Stun = CombatMastery.GetAttackerStun(attacker.AverageKnockCount, attacker.AverageAttackSpeed, true);

            cap.Add(aAction);

            var survived = new List <Creature>();
            var rnd      = RandomProvider.Get();

            // Check crit
            var crit = false;

            if (attacker.Skills.Has(SkillId.CriticalHit, SkillRank.RF))
            {
                crit = (rnd.Next(100) < attacker.GetTotalCritChance(0));
            }

            // Handle all targets
            foreach (var target in targets)
            {
                target.StopMove();

                var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id);
                tAction.Delay = 300;                 // Usually 300, sometimes 350?

                // Calculate damage
                var damage = attacker.GetRndTotalDamage();
                damage *= skill.RankData.Var1 / 100f;

                // Elementals
                damage *= attacker.CalculateElementalDamageMultiplier(target);

                // Crit bonus
                if (crit)
                {
                    CriticalHit.Handle(attacker, 100, ref damage, tAction);
                }

                // Handle skills and reductions
                SkillHelper.HandleDefenseProtection(target, ref damage);
                SkillHelper.HandleConditions(attacker, target, ref damage);
                Defense.Handle(aAction, tAction, ref damage);
                ManaShield.Handle(target, ref damage, tAction);
                HeavyStander.Handle(attacker, target, ref damage, tAction);

                // Clean Hit if not defended nor critical
                if (tAction.SkillId != SkillId.Defense && !tAction.Has(TargetOptions.Critical))
                {
                    tAction.Set(TargetOptions.CleanHit);
                }

                // Take damage if any is left
                if (damage > 0)
                {
                    target.TakeDamage(tAction.Damage = damage, attacker);
                }

                // Knock down on deadly
                if (target.Conditions.Has(ConditionsA.Deadly))
                {
                    tAction.Set(TargetOptions.KnockDown);
                    tAction.Stun = CombatMastery.GetTargetStun(attacker.AverageKnockCount, attacker.AverageAttackSpeed, true);
                }

                // Finish if dead, knock down if not defended
                if (target.IsDead)
                {
                    tAction.Set(TargetOptions.KnockDownFinish);
                }
                else if (tAction.SkillId != SkillId.Defense)
                {
                    tAction.Set(TargetOptions.KnockDown);
                }

                // Anger Management
                if (!target.IsDead)
                {
                    survived.Add(target);
                }

                // Stun and shove if not defended
                if (target.IsDead || tAction.SkillId != SkillId.Defense || target.Conditions.Has(ConditionsA.Deadly))
                {
                    tAction.Stun     = CombatMastery.GetTargetStun(attacker.AverageKnockCount, attacker.AverageAttackSpeed, true);
                    target.Stability = Creature.MinStability;
                    attacker.Shove(target, KnockbackDistance);
                }

                // Add action
                cap.Add(tAction);
            }

            // Update current weapon
            SkillHelper.UpdateWeapon(attacker, targets.FirstOrDefault(), ProficiencyGainType.Melee, attacker.RightHand, attacker.LeftHand);

            // Only select a random aggro if there is no aggro yet,
            // WM only aggroes one target at a time.
            if (survived.Count != 0 && attacker.Region.CountAggro(attacker) < 1)
            {
                var aggroTarget = survived.Random();
                aggroTarget.Aggro(attacker);
            }

            // Reduce life in old combat system
            if (!AuraData.FeaturesDb.IsEnabled("CombatSystemRenewal"))
            {
                var amount = (attacker.LifeMax < 10 ? 2 : attacker.LifeMax / 10);
                attacker.ModifyLife(-amount);

                // TODO: Invincibility
            }

            // Spin it~
            Send.UseMotion(attacker, 8, 4);

            cap.Handle();

            Send.SkillUse(attacker, skill.Info.Id, targetAreaId, unkInt1, unkInt2);

            skill.Stacks = 0;
        }
Beispiel #25
0
        /// <summary>
        /// Uses the skill
        /// </summary>
        /// <param name="attacker"></param>
        /// <param name="skill"></param>
        /// <param name="targetEntityId"></param>
        /// <returns></returns>
        public void Use(Creature attacker, Skill skill, Packet packet)
        {
            // Check for full charge
            if (DateTime.Now < attacker.Temp.ExcaliburPrepareTime.AddMilliseconds(skill.RankData.Var1))             // Not enough time has passed during charging
            {
                Send.SkillUseSilentCancel(attacker);
                Send.Effect(attacker, Effect.Excalibur, ExcaliburEffect.Cancel);
                return;
            }

            // Skill Data
            var skillDamage = skill.RankData.Var2 / 100f;
            var skillLength = (int)skill.RankData.Var3;
            var skillWidth  = (int)skill.RankData.Var4;

            // Get targets in rectangular area
            Position endPos;             // Position on the skill area rectangle opposite of the attacker
            var      targets = SkillHelper.GetTargetableCreaturesInSkillArea(attacker, skillLength, skillWidth, out endPos);

            // TargetProp
            var targetProp = new Prop(280, attacker.RegionId, endPos.X, endPos.Y, Mabi.MabiMath.ByteToRadian(attacker.Direction), 1f, 0f, "single");

            attacker.Region.AddProp(targetProp);

            // Turn to target area
            attacker.TurnTo(endPos);

            // Prepare Combat Actions
            var cap = new CombatActionPack(attacker, skill.Info.Id);

            var targetAreaId = new Location(attacker.RegionId, endPos).ToLocationId();

            var aAction = new AttackerAction(CombatActionType.SpecialHit, attacker, targetAreaId);

            aAction.Set(AttackerOptions.UseEffect);
            aAction.PropId = targetProp.EntityId;
            cap.Add(aAction);

            var rnd = RandomProvider.Get();

            // Check crit
            var crit      = false;
            var critSkill = attacker.Skills.Get(SkillId.CriticalHit);

            if (critSkill != null && critSkill.Info.Rank > SkillRank.Novice)
            {
                var critChance = attacker.GetTotalCritChance(0);
                if (rnd.NextDouble() * 100 < critChance)
                {
                    crit = true;
                }
            }

            foreach (var target in targets)
            {
                var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, SkillId.CombatMastery);
                tAction.Set(TargetOptions.None);
                tAction.Delay = 1200;
                cap.Add(tAction);

                // Stop target movement
                target.StopMove();

                var damage = (attacker.GetRndTotalDamage() * skillDamage);

                // Critical Hit
                if (crit)
                {
                    CriticalHit.Handle(attacker, 100, ref damage, tAction);
                }

                // Handle skills and reductions
                SkillHelper.HandleDefenseProtection(target, ref damage);
                Defense.Handle(aAction, tAction, ref damage);
                HeavyStander.Handle(attacker, target, ref damage, tAction);
                SkillHelper.HandleConditions(attacker, target, ref damage);
                ManaShield.Handle(target, ref damage, tAction);

                // Apply Damage
                target.TakeDamage(tAction.Damage = damage, attacker);

                // Aggro
                target.Aggro(attacker);

                // Stun Time
                tAction.Stun = TargetStun;

                // Death or Knockback
                if (target.IsDead)
                {
                    tAction.Set(TargetOptions.FinishingKnockDown);
                    attacker.Shove(target, KnockbackDistance);
                }
                else
                {
                    // Always knock down
                    if (target.Is(RaceStands.KnockDownable))
                    {
                        tAction.Set(TargetOptions.KnockDown);
                        attacker.Shove(target, KnockbackDistance);
                    }
                }
            }

            // Update current weapon
            SkillHelper.UpdateWeapon(attacker, targets.FirstOrDefault(), ProficiencyGainType.Melee, attacker.RightHand);

            cap.Handle();

            Send.Effect(attacker, Effect.Excalibur, ExcaliburEffect.Attack, (float)endPos.X, (float)endPos.Y);
            Send.SkillUse(attacker, skill.Info.Id, targetAreaId, 0, 1);

            // Remove skill prop
            attacker.Region.RemoveProp(targetProp);
        }
Beispiel #26
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);
        }
Beispiel #27
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);
        }