Наследование: ICombatSkill, IInitiableSkillHandler
Пример #1
0
        /// <summary>
        /// Handles skill usage.
        /// </summary>
        /// <param name="creature"></param>
        /// <param name="skill"></param>
        /// <param name="packet"></param>
        public void Use(Creature creature, Skill skill, Packet packet)
        {
            var targetEntityId = packet.GetLong();

            // Similar to WM there is a case where these aren't sent.
            // Apparently this can happen if you activate the skill while
            // targetting an enemy.
            var unk1 = (packet.Peek() != PacketElementType.None ? packet.GetInt() : 0);
            var unk2 = (packet.Peek() != PacketElementType.None ? packet.GetInt() : 0);

            if (_cm == null)
            {
                _cm = ChannelServer.Instance.SkillManager.GetHandler <CombatMastery>(SkillId.CombatMastery);
            }

            // TODO: Check duration

            var attackResult = false;

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

            if (target != null && !creature.IsStunned && creature.CanTarget(target))
            {
                var pos       = creature.GetPosition();
                var targetPos = target.GetPosition();
                var inRange   = pos.InRange(targetPos, creature.AttackRangeFor(target));

                if (!inRange)
                {
                    var telePos = pos.GetRelative(targetPos, -creature.AttackRangeFor(target) + 100);

                    // Check teleport distance
                    if (pos.GetDistance(telePos) > skill.RankData.Var3 + 100)
                    {
                        Send.Notice(creature, "Out of range");
                    }
                    else
                    {
                        Send.Effect(creature, Effect.SilentMoveTeleport, targetEntityId, (byte)0);

                        creature.SetPosition(telePos.X, telePos.Y);
                        Send.SkillTeleport(creature, telePos.X, telePos.Y);

                        inRange = true;
                    }
                }

                if (inRange)
                {
                    attackResult = (_cm.Use(creature, skill, targetEntityId) == CombatSkillResult.Okay);
                }
            }

            Send.CombatAttackR(creature, attackResult);

            Send.SkillUse(creature, skill.Info.Id, targetEntityId, unk1, unk2);
        }
Пример #2
0
        /// <summary>
        /// Handles skill usage.
        /// </summary>
        /// <param name="creature"></param>
        /// <param name="skill"></param>
        /// <param name="packet"></param>
        public void Use(Creature creature, Skill skill, Packet packet)
        {
            var targetEntityId = packet.GetLong();
            var unk1           = packet.GetInt();
            var unk2           = packet.GetInt();

            if (_cm == null)
            {
                _cm = ChannelServer.Instance.SkillManager.GetHandler <CombatMastery>(SkillId.CombatMastery);
            }

            // TODO: Check duration

            var attackResult = false;

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

            if (target != null && !creature.IsStunned && creature.CanTarget(target))
            {
                var pos       = creature.GetPosition();
                var targetPos = target.GetPosition();
                var inRange   = pos.InRange(targetPos, creature.AttackRangeFor(target));

                if (!inRange)
                {
                    var telePos = pos.GetRelative(targetPos, -creature.AttackRangeFor(target) + 100);

                    // Check teleport distance
                    if (pos.GetDistance(telePos) > skill.RankData.Var3 + 100)
                    {
                        Send.Notice(creature, "Out of range");
                    }
                    else
                    {
                        Send.Effect(creature, Effect.SilentMoveTeleport, targetEntityId, (byte)0);

                        creature.SetPosition(telePos.X, telePos.Y);
                        Send.SkillTeleport(creature, telePos.X, telePos.Y);

                        inRange = true;
                    }
                }

                if (inRange)
                {
                    attackResult = (_cm.Use(creature, skill, targetEntityId) == CombatSkillResult.Okay);
                }
            }

            Send.CombatAttackR(creature, attackResult);

            Send.SkillUse(creature, skill.Info.Id, targetEntityId, unk1, unk2);
        }
Пример #3
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;
        }
Пример #4
0
        /// <summary>
        /// Handles using the skill.
        /// </summary>
        /// <param name="attacker"></param>
        /// <param name="skill"></param>
        /// <param name="targetAreaId"></param>
        /// <param name="unkInt1"></param>
        /// <param name="unkInt2"></param>
        public void Use(Creature attacker, Skill skill, long targetAreaId, int unkInt1, int unkInt2)
        {
            var range   = this.GetRange(attacker, skill);
            var targets = attacker.GetTargetableCreaturesInRange(range, true);
            var rnd     = RandomProvider.Get();

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

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

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

            cap.Add(aAction);

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

                target.StopMove();

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

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

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

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

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

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

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

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

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

            Send.UseMotion(attacker, 10, 1);

            cap.Handle();

            Send.SkillUse(attacker, skill.Info.Id, targetAreaId, unkInt1, unkInt2);
        }
Пример #5
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;
        }
Пример #6
0
        /// <summary>
        /// Handles skill usage.
        /// </summary>
        /// <param name="creature"></param>
        /// <param name="skill"></param>
        /// <param name="packet"></param>
        public void Use(Creature creature, Skill skill, Packet packet)
        {
            var targetEntityId = packet.GetLong();

            // Similar to WM there is a case where these aren't sent.
            // Apparently this can happen if you activate the skill while
            // targetting an enemy.
            var unk1 = (packet.Peek() != PacketElementType.None ? packet.GetInt() : 0);
            var unk2 = (packet.Peek() != PacketElementType.None ? packet.GetInt() : 0);

            if (_cm == null)
                _cm = ChannelServer.Instance.SkillManager.GetHandler<CombatMastery>(SkillId.CombatMastery);

            // TODO: Check duration

            var attackResult = false;

            var target = creature.Region.GetCreature(targetEntityId);
            if (target != null && !creature.IsStunned && creature.CanTarget(target))
            {
                var pos = creature.GetPosition();
                var targetPos = target.GetPosition();
                var inRange = pos.InRange(targetPos, creature.AttackRangeFor(target));

                if (!inRange)
                {
                    var telePos = pos.GetRelative(targetPos, -creature.AttackRangeFor(target) + 100);

                    // Check teleport distance
                    if (pos.GetDistance(telePos) > skill.RankData.Var3 + 100)
                    {
                        Send.Notice(creature, "Out of range");
                    }
                    else
                    {
                        Send.Effect(creature, Effect.SilentMoveTeleport, targetEntityId, (byte)0);

                        creature.SetPosition(telePos.X, telePos.Y);
                        Send.SkillTeleport(creature, telePos.X, telePos.Y);

                        inRange = true;
                    }
                }

                if (inRange)
                    attackResult = (_cm.Use(creature, skill, targetEntityId) == CombatSkillResult.Okay);
            }

            Send.CombatAttackR(creature, attackResult);

            Send.SkillUse(creature, skill.Info.Id, targetEntityId, unk1, unk2);
        }
Пример #7
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);
        }
Пример #8
0
		public void Use(Creature creature, Skill skill, long targetEntityId, int unk1, int unk2)
		{

			if (_cm == null)
				_cm = ChannelServer.Instance.SkillManager.GetHandler<CombatMastery>(SkillId.CombatMastery);

			var attackResult = false;

			var target = creature.Region.GetCreature(targetEntityId);
			if (target != null && !creature.IsStunned && !creature.IsOnAttackDelay && creature.CanTarget(target) && creature.CanAttack(target))
			{
				var pos = creature.GetPosition();
				var targetPos = target.GetPosition();
				var inRange = (pos.InRange(targetPos, creature.AttackRangeFor(target))
				&& !creature.Region.Collisions.Any(pos, targetPos) // Check collisions between position
				&& !target.Conditions.Has(ConditionsA.Invisible)); // Check visiblility (GM)

				if (!inRange && !target.IsNotReadyToBeHit)
				{
					var telePos = pos.GetRelative(targetPos, -creature.AttackRangeFor(target) + 100);

					// Check teleport distance
					if (pos.GetDistance(telePos) > skill.RankData.Var3 + 100)
					{
						Send.Notice(creature, "Out of range");
					}
					else
					{
						Send.Effect(creature, Effect.SilentMoveTeleport, targetEntityId, (byte)0);

						creature.SetPosition(telePos.X, telePos.Y);
						Send.SkillTeleport(creature, telePos.X, telePos.Y);

						inRange = true;
					}
				}

				if (inRange)
					attackResult = (_cm.Use(creature, skill, targetEntityId) == CombatSkillResult.Okay);
			}

			Send.CombatAttackR(creature, attackResult);

			Send.SkillUse(creature, skill.Info.Id, targetEntityId, unk1, unk2);
		}
Пример #9
0
        /// <summary>
        /// Handles skill usage while ignoring range.
        /// </summary>
        /// <param name="attacker"></param>
        /// <param name="skill"></param>
        /// <param name="targetEntityId"></param>
        /// <returns></returns>
        public CombatSkillResult UseWithoutRangeCheck(Creature attacker, Skill skill, long targetEntityId, Creature mainTarget, SkillId interceptingSkillId = SkillId.None)
        {
            // Against Normal Attack
            Skill combatMastery = mainTarget.Skills.Get(SkillId.CombatMastery);

            if (interceptingSkillId == SkillId.None && combatMastery != null && (mainTarget.Skills.ActiveSkill == null || mainTarget.Skills.ActiveSkill == combatMastery || mainTarget.Skills.IsReady(SkillId.FinalHit)) && mainTarget.IsInBattleStance && mainTarget.Target == attacker && mainTarget.AttemptingAttack && (!mainTarget.IsStunned || mainTarget.IsKnockedDown))
            {
                if (mainTarget.CanTarget(attacker) && mainTarget.Can(Locks.Attack))                 //TODO: Add Hit lock when available.
                {
                    var skillHandler = ChannelServer.Instance.SkillManager.GetHandler <ICombatSkill>(combatMastery.Info.Id);
                    if (skillHandler == null)
                    {
                        Log.Error("Smash.Use: Target's skill handler not found for '{0}'.", combatMastery.Info.Id);
                        return(CombatSkillResult.Okay);
                    }
                    ((CombatMastery)skillHandler).UseWithoutRangeCheck(mainTarget, combatMastery, attacker.EntityId, attacker, SkillId.Smash);
                    return(CombatSkillResult.Okay);
                }
            }

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

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

                if (((mainTarget.LastKnockedBackBy == attacker && mainTarget.KnockDownTime > attacker.KnockDownTime && mainTarget.KnockDownTime.AddMilliseconds(mainTargetStunTime) > DateTime.Now ||
                      /*attackerStunTime > initialTargetStunTime && */
                      !(attacker.LastKnockedBackBy == mainTarget && attacker.KnockDownTime > mainTarget.KnockDownTime && attacker.KnockDownTime.AddMilliseconds(attackerStunTime) > DateTime.Now))))
                {
                    if (mainTarget.CanTarget(attacker) && mainTarget.Can(Locks.Attack))                     //TODO: Add Hit lock when available.
                    {
                        var skillHandler = ChannelServer.Instance.SkillManager.GetHandler <ICombatSkill>(smash.Info.Id);
                        if (skillHandler == null)
                        {
                            Log.Error("Smash.Use: Target's skill handler not found for '{0}'.", smash.Info.Id);
                            return(CombatSkillResult.Okay);
                        }
                        ((Smash)skillHandler).UseWithoutRangeCheck(mainTarget, smash, attacker.EntityId, attacker, SkillId.Smash);
                        return(CombatSkillResult.Okay);
                    }
                }
                else
                {
                    interceptingSkillId = SkillId.Smash;
                }
            }

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

            // Get targets, incl. splash.
            // Splash happens from r5 onwards, but we'll base it on Var4,
            // which is the splash damage and first != 0 on r5.
            var targets = new HashSet <Creature>()
            {
                mainTarget
            };

            if (skill.RankData.Var4 != 0)
            {
                targets.UnionWith(attacker.GetTargetableCreaturesInCone(mainTarget.GetPosition(), attacker.GetTotalSplashRadius(), attacker.GetTotalSplashAngle()));
            }

            // Counter
            if (Counterattack.Handle(targets, attacker))
            {
                return(CombatSkillResult.Okay);
            }

            // Prepare combat actions
            var aAction = new AttackerAction(CombatActionType.HardHit, attacker, targetEntityId);

            aAction.Set(AttackerOptions.Result | AttackerOptions.KnockBackHit2);
            aAction.Stun = StunTime;

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

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

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

                TargetAction tAction;
                if (target == mainTarget)
                {
                    if (interceptingSkillId == SkillId.Smash)
                    {
                        aAction.Options |= AttackerOptions.Result;
                        tAction          = new TargetAction(CombatActionType.CounteredHit, target, attacker, SkillId.Smash);
                    }
                    else
                    {
                        tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id);
                    }
                }
                else
                {
                    tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id);
                }

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

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

                // Damage
                var damage = mainDamage;

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

                // Splash modifier
                if (target != mainTarget)
                {
                    damage *= (skill.RankData.Var4 / 100f);
                }

                // Critical Hit
                var critChance = this.GetCritChance(attacker, target, skill);
                CriticalHit.Handle(attacker, critChance, ref damage, tAction);

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

                // Conditions
                SkillHelper.HandleConditions(attacker, target, ref damage);

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

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

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

                // Aggro
                if (target == mainTarget)
                {
                    target.Aggro(attacker);
                }

                if (target.IsDead)
                {
                    tAction.Set(TargetOptions.FinishingHit | TargetOptions.Finished);
                }

                // Set Stun/Knockback
                target.Stun      = tAction.Stun = StunTime;
                target.Stability = Creature.MinStability;

                // Set knockbacked position
                attacker.Shove(target, KnockbackDistance);
            }

            // Response
            Send.SkillUseStun(attacker, skill.Info.Id, AfterUseStun, 1);

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

            // Action!
            cap.Handle();

            return(CombatSkillResult.Okay);
        }