Inheritance: Aura.Channel.Skills.Base.StandardPrepareHandler, IInitiableSkillHandler
Example #1
0
        /// <summary>
        /// Uses the skill.
        /// </summary>
        /// <param name="attacker"></param>
        /// <param name="skill"></param>
        /// <param name="targetEntityId"></param>
        /// <returns></returns>
        public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId)
        {
            // Get target
            var target = attacker.Region.GetCreature(targetEntityId);

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

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

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

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

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

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

            // Target action if hit
            if (successfulHit)
            {
                target.StopMove();

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

                cap.Add(tAction);

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

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

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

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

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

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

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

                // Natural Shield
                var delayReduction = NaturalShield.Handle(attacker, target, ref damage, tAction);

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

                // Aggro
                target.Aggro(attacker);

                // Knock down on deadly
                if (target.Conditions.Has(ConditionsA.Deadly))
                {
                    tAction.Set(TargetOptions.KnockDown);
                }

                // Death/Knockback
                if (target.IsDead)
                {
                    tAction.Set(TargetOptions.FinishingKnockDown);
                }

                // Knock Back
                if (tAction.IsKnockBack)
                {
                    attacker.Shove(target, KnockBackDistance);
                }

                // Reduce stun, based on ping
                if (delayReduction > 0)
                {
                    tAction.Stun = (short)Math.Max(0, tAction.Stun - (tAction.Stun / 100 * delayReduction));
                }

                // TODO: "Weakened" state (G12S2 gfSupportShotRenewal)
            }

            // Update current weapon
            SkillHelper.UpdateWeapon(attacker, target, ProficiencyGainType.Ranged, attacker.RightHand);

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

            cap.Handle();

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

            return(CombatSkillResult.Okay);
        }
Example #2
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;
        }
Example #3
0
        /// <summary>
        /// Handles attack.
        /// </summary>
        /// <param name="attacker">The creature attacking.</param>
        /// <param name="skill">The skill being used.</param>
        /// <param name="targetEntityId">The entity id of the target.</param>
        /// <returns></returns>
        public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId)
        {
            if (attacker.IsStunned)
            {
                return(CombatSkillResult.Okay);
            }

            var 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);
        }
Example #4
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);
        }
Example #5
0
        /// <summary>
        /// Uses the skill.
        /// </summary>
        /// <param name="attacker"></param>
        /// <param name="skill"></param>
        /// <param name="targetEntityId"></param>
        /// <returns></returns>
        public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId)
        {
            // Get target
            var 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);
        }
Example #6
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);
        }
Example #7
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);
        }
Example #8
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);
        }
Example #9
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);
        }
Example #10
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;
        }
Example #11
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);
        }
Example #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, targetEntityId);

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

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

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

                aAction.Set(AttackerOptions.KnockBackHit2);

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

                // TODO: Splash damage

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

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

                // More damage with fire arrow
                // XXX: Does this affect the element?
                if (attacker.Temp.FireArrow)
                {
                    damage *= FireBonus;
                }

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

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

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

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

                // Natural Shield
                // Ignore delay reduction, as knock downs shouldn't be shortened
                NaturalShield.Handle(attacker, target, ref damage, tAction);

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

                // Knock down
                // If target is using a shield and defense, don't KD.
                var targetLeftHand = target.LeftHand;
                if (tAction.SkillId != SkillId.Defense || targetLeftHand == null || !targetLeftHand.IsShield)
                {
                    // TODO: We have to calculate knockback distance right
                    attacker.Shove(target, KnockBackDistance);
                    tAction.Set(TargetOptions.KnockDownFinish);
                }

                // Aggro
                target.Aggro(attacker);

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

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

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

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

            cap.Handle();

            return(CombatSkillResult.Okay);
        }
Example #13
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);
        }
Example #14
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);
        }