GetTargetableCreaturesInCone() public method

Returns targetable creatures in cone, based on the given parameters, with direction of cone being based on the given positions.
public GetTargetableCreaturesInCone ( Position position, Position targetPosition, float radius, float angle, TargetableOptions options = TargetableOptions.None ) : ICollection
position Position Pointy end of the cone.
targetPosition Position Target's position, used as reference for the direction of the cone.
radius float Cone's radius.
angle float Cone's angle in degree.
options TargetableOptions Options to change the result.
return ICollection
Beispiel #1
0
        /// <summary>
        /// Handles attack.
        /// </summary>
        /// <param name="attacker">The creature attacking.</param>
        /// <param name="skill">The skill being used.</param>
        /// <param name="targetEntityId">The entity id of the target.</param>
        /// <returns></returns>
        public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId)
        {
            if (attacker.IsStunned)
                return CombatSkillResult.Okay;

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

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

                // Handle
                cap.Handle();
            }

            return CombatSkillResult.Okay;
        }
Beispiel #2
0
        /// <summary>
        /// Handles skill usage.
        /// </summary>
        /// <param name="attacker"></param>
        /// <param name="skill"></param>
        /// <param name="targetEntityId"></param>
        /// <returns></returns>
        public override CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId)
        {
            // Check target
            var mainTarget = attacker.Region.GetCreature(targetEntityId);
            if (mainTarget == null)
                return CombatSkillResult.InvalidTarget;

            // Check range
            var targetPosition = mainTarget.GetPosition();
            if (!attacker.GetPosition().InRange(targetPosition, attacker.AttackRangeFor(mainTarget)))
                return CombatSkillResult.OutOfRange;

            // Stop movement
            attacker.StopMove();

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

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

            // Prepare combat actions
            var aAction = new AttackerAction(CombatActionType.HardHit, attacker, targetEntityId);
            aAction.Set(AttackerOptions.Result | AttackerOptions.KnockBackHit2);
            aAction.Stun = StunTime;

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

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

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

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

                cap.Add(tAction);

                // Damage
                var damage = mainDamage;

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

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

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

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

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

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

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

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

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

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

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

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

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

            // Action!
            cap.Handle();

            return CombatSkillResult.Okay;
        }
Beispiel #3
0
		/// <summary>
		/// Uses the skill.
		/// </summary>
		/// <param name="attacker"></param>
		/// <param name="skill"></param>
		/// <param name="targetEntityId"></param>
		/// <returns></returns>
		public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId)
		{
			// Get target
			var mainTarget = attacker.Region.GetCreature(targetEntityId);
			if (mainTarget == null)
				return CombatSkillResult.InvalidTarget;

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

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

			// Hit by chance
			var chance = attacker.AimMeter.GetAimChance(mainTarget);
			var rnd = RandomProvider.Get();
			if (rnd.NextDouble() * 100 < chance)
			{
				aAction.Set(AttackerOptions.KnockBackHit2);

				// Get targets, incl. splash.
				// Splash happens from r5 onwards, but we'll base it on Var4,
				// which is the splash damage and first != 0 on r5.
				var targets = new HashSet<Creature>() { mainTarget };
				if (skill.RankData.Var4 != 0)
				{
					var targetPosition = mainTarget.GetPosition();
					var direction = attacker.GetPosition().GetDirection(targetPosition);
					targets.UnionWith(attacker.GetTargetableCreaturesInCone(targetPosition, direction, skill.RankData.Var5, skill.RankData.Var6));
				}

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

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

					// Damage
					var damage = mainDamage;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

			cap.Handle();

			return CombatSkillResult.Okay;
		}
Beispiel #4
0
		/// <summary>
		/// Handles skill usage.
		/// </summary>
		/// <param name="attacker"></param>
		/// <param name="skill"></param>
		/// <param name="targetEntityId"></param>
		/// <returns></returns>
		public override CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId)
		{
			// Check target
			var target = attacker.Region.GetCreature(targetEntityId);
			if (target == null)
				return CombatSkillResult.InvalidTarget;

			if (target.IsNotReadyToBeHit)
				return CombatSkillResult.Okay;

			if ((attacker.IsStunned || attacker.IsOnAttackDelay) && attacker.InterceptingSkillId == SkillId.None)
				return CombatSkillResult.Okay;

			// Check range
			var attackerPosition = attacker.GetPosition();
			var targetPosition = target.GetPosition();
			if (!attacker.IgnoreAttackRange &&
				(!attackerPosition.InRange(targetPosition, attacker.AttackRangeFor(target))))
			{ return CombatSkillResult.OutOfRange; }
			if (!attacker.IgnoreAttackRange &&
				(attacker.Region.Collisions.Any(attackerPosition, targetPosition) // Check collisions between position
				|| target.Conditions.Has(ConditionsA.Invisible))) // Check visiblility (GM)
			{ return CombatSkillResult.Okay; }

			attacker.IgnoreAttackRange = false;
			// Against Normal Attack
			Skill combatMastery = target.Skills.Get(SkillId.CombatMastery);
			if (combatMastery != null && (target.Skills.ActiveSkill == null || target.Skills.ActiveSkill == combatMastery || target.Skills.IsReady(SkillId.FinalHit)) && target.IsInBattleStance && target.Target == attacker && target.AttemptingAttack && (!target.IsStunned || target.IsKnockedDown))
			{
				target.InterceptingSkillId = SkillId.Smash;
				target.IgnoreAttackRange = true;
				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;
				}
				skillHandler.Use(target, combatMastery, attacker.EntityId);
				return CombatSkillResult.Okay;
			}

			// Against Windmill
			//TODO: Change this into the new NPC client system when it comes out if needed.
			Skill windmill = target.Skills.Get(SkillId.Windmill);
			if (windmill != null && target.Skills.IsReady(SkillId.Windmill) && !target.IsPlayer && target.CanAttack(attacker))
			{
				target.InterceptingSkillId = SkillId.Smash;
				var skillHandler = ChannelServer.Instance.SkillManager.GetHandler<IUseable>(windmill.Info.Id) as Windmill;
				if (skillHandler == null)
				{
					Log.Error("Smash.Use: Target's skill handler not found for '{0}'.", windmill.Info.Id);
					return CombatSkillResult.Okay;
				}
				skillHandler.Use(target, windmill);
				return CombatSkillResult.Okay;
			}

			// Against Smash
			Skill smash = target.Skills.Get(SkillId.Smash);
			if (smash != null && target.Skills.IsReady(SkillId.Smash) && target.IsInBattleStance && target.Target == attacker && !target.IsStunned && attacker.CanAttack(target))
			{
				var attackerStunTime = CombatMastery.GetAttackerStun(attacker, attacker.RightHand, false);
				var targetStunTime = CombatMastery.GetAttackerStun(target, target.Inventory.RightHand, false);
				if ((target.LastKnockedBackBy == attacker && target.KnockDownTime > attacker.KnockDownTime &&
						target.KnockDownTime.AddMilliseconds(targetStunTime) < DateTime.Now //If last knocked down within the time it takes for you to finish attacking.
						|| attackerStunTime > targetStunTime &&
						!Math2.Probability(((2725 - attackerStunTime) / 2500) * 100) //Probability in percentage that you will not lose.  2725 is 2500 (Slowest stun) + 225 (Fastest stun divided by two so that the fastest stun isn't 100%)
						&& !(attacker.LastKnockedBackBy == target && attacker.KnockDownTime > target.KnockDownTime && attacker.KnockDownTime.AddMilliseconds(attackerStunTime) < DateTime.Now)))
				{
					if (target.CanAttack(attacker))
					{
						target.InterceptingSkillId = SkillId.Smash;
						target.IgnoreAttackRange = true;
						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;
						}
						skillHandler.Use(target, smash, attacker.EntityId);
						return CombatSkillResult.Okay;
					}
				}
				else
				{
					attacker.InterceptingSkillId = SkillId.Smash;
				}
			}

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

			target.IgnoreAttackRange = false;

			// Counter
			if (Counterattack.Handle(target, attacker))
				return CombatSkillResult.Okay;

			var weapon = attacker.RightHand;
			ICollection<Creature> targets = null;
			if (skill.Info.Rank >= SkillRank.R5 && weapon != null && weapon.Data.SplashRadius != 0 && weapon.Data.SplashAngle != 0 || weapon == null)
			{
				targets = attacker.GetTargetableCreaturesInCone(weapon != null ? (int)weapon.Data.SplashRadius : 200, weapon != null ? (int)weapon.Data.SplashAngle : 20);

				foreach (var splashTarget in targets)
				{
					if (splashTarget != target)
					{
						// Counter
						if (Counterattack.Handle(target, attacker))
							return CombatSkillResult.Okay;
					}

				}
			}

			// Prepare combat actions
			var aAction = new AttackerAction(CombatActionType.HardHit, attacker, skill.Info.Id, targetEntityId);
			aAction.Set(AttackerOptions.Result | AttackerOptions.KnockBackHit2);

			TargetAction tAction;
			if (attacker.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);
			}
			tAction.Set(TargetOptions.Result | TargetOptions.Smash);

			attacker.InterceptingSkillId = SkillId.None;

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

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

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

			var maxDamage = damage; //Damage without Defense and Protection
			// Subtract target def/prot
			SkillHelper.HandleDefenseProtection(target, ref damage);

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

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

			// Aggro
			target.Aggro(attacker);

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

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

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

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

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

			var critSkill = attacker.Skills.Get(SkillId.CriticalHit);
			if (skill.Info.Rank >= SkillRank.R5 && weapon != null && weapon.Data.SplashRadius != 0 && weapon.Data.SplashAngle != 0)
			{
				foreach (var splashTarget in targets)
				{
					if (splashTarget != target)
					{
						if (splashTarget.IsNotReadyToBeHit)
							continue;
						TargetAction tSplashAction = new TargetAction(CombatActionType.TakeHit, splashTarget, attacker, skill.Info.Id);
						tSplashAction.Set(TargetOptions.Result | TargetOptions.Smash);

						// Base damage
						float damageSplash = this.GetDamage(attacker, skill);
						attacker.CalculateSplashDamage(splashTarget, ref damageSplash, skill, critSkill, aAction, tAction, tSplashAction);

						// Deal with it!
						if (damageSplash > 0)
							splashTarget.TakeDamage(tSplashAction.Damage = damageSplash, attacker);

						// Alert
						splashTarget.Aggro(attacker, true);

						if (splashTarget.IsDead)
							tSplashAction.Set(TargetOptions.FinishingHit | TargetOptions.Finished);

						splashTarget.Stun = tSplashAction.Stun = StunTime;
						splashTarget.Stability = Creature.MinStability;

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

						cap.Add(tSplashAction);
					}

				}
			}

			// Action!
			cap.Handle();

			return CombatSkillResult.Okay;
		}
Beispiel #5
0
		/// <summary>
		/// Handles attack.
		/// </summary>
		/// <param name="attacker">The creature attacking.</param>
		/// <param name="skill">The skill being used.</param>
		/// <param name="targetEntityId">The entity id of the target.</param>
		/// <returns></returns>
		public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId)
		{
			var target = attacker.Region.GetCreature(targetEntityId);
			if (target == null)
				return CombatSkillResult.Okay;

			if (target.IsNotReadyToBeHit)
				return CombatSkillResult.Okay;

			if ((attacker.IsStunned || attacker.IsOnAttackDelay) && attacker.InterceptingSkillId == SkillId.None)
				return CombatSkillResult.Okay;

			var attackerPosition = attacker.GetPosition();
			var targetPosition = target.GetPosition();
			if (!attacker.IgnoreAttackRange &&
				(!attackerPosition.InRange(targetPosition, attacker.AttackRangeFor(target))))
			{ return CombatSkillResult.OutOfRange; }
			if (!attacker.IgnoreAttackRange &&
				(attacker.Region.Collisions.Any(attackerPosition, targetPosition) // Check collisions between position
				|| target.Conditions.Has(ConditionsA.Invisible))) // Check visiblility (GM)
			{ return CombatSkillResult.Okay; }

			attacker.IgnoreAttackRange = false;

			//Against Smash
			Skill smash = target.Skills.Get(SkillId.Smash);
			if (smash != null && target.Skills.IsReady(SkillId.Smash) && attacker.CanAttack(target))
				attacker.InterceptingSkillId = SkillId.Smash;

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


			var staminaUsage = (rightWeapon != null && rightWeapon.Data.StaminaUsage != 0 ? rightWeapon.Data.StaminaUsage : 0.7f) + (dualWield ? leftWeapon.Data.StaminaUsage : 0f);
			var lowStamina = false;
			if (attacker.Stamina < staminaUsage)
			{
				lowStamina = true;
				Send.Notice(attacker, Localization.Get("Your stamina is too low to attack properly!"));
			}
			attacker.Stamina -= staminaUsage;
			Send.StatUpdate(attacker, StatUpdateType.Private, Stat.Stamina);

			// Against Combat Mastery
			Skill combatMastery = target.Skills.Get(SkillId.CombatMastery);
			var simultaneousAttackStun = 0;
			if (attacker.InterceptingSkillId != SkillId.CombatMastery && target.InterceptingSkillId != SkillId.CombatMastery)
			{
				if (combatMastery != null && (target.Skills.ActiveSkill == null || target.Skills.ActiveSkill == combatMastery || target.Skills.IsReady(SkillId.FinalHit)) && target.IsInBattleStance && target.Target == attacker && target.AttemptingAttack && (!target.IsStunned || target.IsKnockedDown) && attacker.CanAttack(target))
				{
					var attackerStunTime = CombatMastery.GetAttackerStun(attacker, attacker.RightHand, false);
					var targetStunTime = CombatMastery.GetAttackerStun(target, target.Inventory.RightHand, false);
					if ((target.LastKnockedBackBy == attacker && target.KnockDownTime > attacker.KnockDownTime &&
						target.KnockDownTime.AddMilliseconds(targetStunTime) < DateTime.Now //If last knocked down within the time it takes for you to finish attacking.
						|| attackerStunTime > targetStunTime &&
						!Math2.Probability(((2725 - attackerStunTime) / 2500) * 100) //Probability in percentage that you will not lose.  2725 is 2500 (Slowest stun) + 225 (Fastest stun divided by two so that the fastest stun isn't 100%)
						&& !(attacker.LastKnockedBackBy == target && attacker.KnockDownTime > target.KnockDownTime && attacker.KnockDownTime.AddMilliseconds(attackerStunTime) < DateTime.Now)))
					{
						if (target.CanAttack(attacker))
						{
							target.InterceptingSkillId = SkillId.CombatMastery;
							target.IgnoreAttackRange = true;
							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;
							}
							skillHandler.Use(target, combatMastery, attacker.EntityId);
							return CombatSkillResult.Okay;
						}
					}
					else
					{
						if (Math2.Probability(((2725 - attackerStunTime) / 2500) * 100)) //Probability in percentage that it will be an interception instead of a double hit.
						{
							attacker.InterceptingSkillId = SkillId.CombatMastery;
						}
						else
						{
							attacker.InterceptingSkillId = SkillId.CombatMastery;
							if (target.CanAttack(attacker))
							{
								target.InterceptingSkillId = SkillId.CombatMastery;
								target.IgnoreAttackRange = true;
								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
								{
									skillHandler.Use(target, combatMastery, attacker.EntityId);
									simultaneousAttackStun = attacker.Stun;
									attacker.Stun = 0;
								}
							}
						}
					}
				}
			}

			attacker.StopMove();
			targetPosition = target.StopMove();

			// Counter
			if (Counterattack.Handle(target, attacker))
				return CombatSkillResult.Okay;

			ICollection<Creature> targets = null;
			if (rightWeapon != null && rightWeapon.Data.SplashRadius != 0 && rightWeapon.Data.SplashAngle != 0 || rightWeapon == null)
			{
				targets = attacker.GetTargetableCreaturesInCone(rightWeapon != null ? (int)rightWeapon.Data.SplashRadius : 204, rightWeapon != null ? (int)rightWeapon.Data.SplashAngle : 60);

				foreach (var splashTarget in targets)
				{
					if (splashTarget != target)
					{
						// Counter
						if (Counterattack.Handle(target, attacker))
							return CombatSkillResult.Okay;
					}

				}
			}

			var magazine = attacker.Inventory.Magazine;
			var maxHits = (byte)(dualWield ? 2 : 1);
			int prevId = 0;

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

				AttackerAction aAction;
				TargetAction tAction;
				if (attacker.InterceptingSkillId == SkillId.Smash)
				{
					aAction = new AttackerAction(CombatActionType.SimultaneousHit, attacker, SkillId.CombatMastery, target.EntityId);
					aAction.Options |= AttackerOptions.Result;
					tAction = new TargetAction(CombatActionType.CounteredHit, target, attacker, SkillId.Smash);
					tAction.Options |= TargetOptions.Result;

				}
				else if (attacker.InterceptingSkillId == SkillId.CombatMastery)
				{
					aAction = new AttackerAction(CombatActionType.SimultaneousHit, attacker, SkillId.CombatMastery, target.EntityId);
					aAction.Options |= AttackerOptions.Result;
					tAction = new TargetAction(CombatActionType.CounteredHit, target, attacker, target.Skills.IsReady(SkillId.FinalHit) ? SkillId.FinalHit : SkillId.CombatMastery);
					tAction.Options |= TargetOptions.Result;

				}
				else
				{
					aAction = new AttackerAction(CombatActionType.Hit, attacker, skill.Info.Id, targetEntityId);
					tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, target.Skills.IsReady(SkillId.FinalHit) ? SkillId.FinalHit : SkillId.CombatMastery);
					aAction.Options |= AttackerOptions.Result;
					tAction.Options |= TargetOptions.Result;
				}

				attacker.InterceptingSkillId = SkillId.None;

				var cap = new CombatActionPack(attacker, skill.Info.Id, tAction, aAction);
				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 = (i == 1 ? attacker.GetRndRightHandDamage() : attacker.GetRndLeftHandDamage());
				if (lowStamina)
				{
					damage = attacker.GetRndBareHandDamage();
				}

				// Critical Hit
				var critShieldReduction = (target.LeftHand != null ? target.LeftHand.Data.DefenseBonusCrit : 0);
				var critChance = (i == 1 ? attacker.GetRightCritChance(target.Protection + critShieldReduction) : attacker.GetLeftCritChance(target.Protection + critShieldReduction));
				CriticalHit.Handle(attacker, critChance, ref damage, tAction);

				var maxDamage = damage; //Damage without Defense and Protection
										// Subtract target def/prot
				SkillHelper.HandleDefenseProtection(target, ref damage);

				// Defense
				var tActionOldType = tAction.Type;
				Defense.Handle(aAction, tAction, ref damage);
				if (i == 1 && tAction.Type == CombatActionType.Defended)
				{
					defenseStun = tAction.Stun;
				}


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

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

				if (tAction.Type == CombatActionType.Defended && target.Life <= 0)
				{
					tAction.Type = tActionOldType;
				}

				// Aggro
				target.Aggro(attacker);

				// Evaluate caused damage
				if (!target.IsDead)
				{
					if (tAction.Type != CombatActionType.Defended)
					{
						if (!target.Skills.IsReady(SkillId.FinalHit))
						{
							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);
								if (target.Life < 0)
									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 && tAction.Type != CombatActionType.Defended)
				{
					if (!target.Skills.IsReady(SkillId.FinalHit))
					{
						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;


				}
				else if (tAction.Type == CombatActionType.Defended)
				{
					// 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)
				{
					if (simultaneousAttackStun == 0)
					{
						aAction.Stun = GetAttackerStun(attacker, weapon, tAction.IsKnockBack && ((skill.Info.Id != SkillId.FinalHit) && !target.IsDead || AuraData.FeaturesDb.IsEnabled("CombatSystemRenewal")));
					}
					else
					{
						aAction.Stun = (short)simultaneousAttackStun;
					}
					if (!target.Skills.IsReady(SkillId.FinalHit))
					{
						tAction.Stun = GetTargetStun(attacker, weapon, tAction.IsKnockBack);
					}

					if (target.IsDead && skill.Info.Id != SkillId.FinalHit)
					{
						attacker.AttackDelayTime = DateTime.Now.AddMilliseconds(GetAttackerStun(attacker, weapon, true));
					}
				}

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

				var critSkill = attacker.Skills.Get(SkillId.CriticalHit);
				if (weapon != null && weapon.Data.SplashRadius != 0 && weapon.Data.SplashAngle != 0)
				{
					foreach (var splashTarget in targets)
					{
						if (splashTarget != target)
						{
							if (splashTarget.IsNotReadyToBeHit)
								continue;
							TargetAction tSplashAction = new TargetAction(CombatActionType.TakeHit, splashTarget, attacker, skill.Info.Id);

							// Base damage
							float damageSplash;
							if (lowStamina)
							{
								damageSplash = attacker.GetRndBareHandDamage();
							}
							else
							{
								damageSplash = (i == 1 ? attacker.GetRndRightHandDamage() : attacker.GetRndLeftHandDamage());
                            }
							attacker.CalculateSplashDamage(splashTarget, ref damageSplash, skill, critSkill, aAction, tAction, tSplashAction, weapon);

							// Deal with it!
							if (damageSplash > 0)
								splashTarget.TakeDamage(tSplashAction.Damage = damageSplash, attacker);

							// Alert
							splashTarget.Aggro(attacker, true);

							// Evaluate caused damage
							if (!splashTarget.IsDead)
							{
								if (tSplashAction.Type != CombatActionType.Defended)
								{
									if (!splashTarget.Skills.IsReady(SkillId.FinalHit))
									{

										splashTarget.Stability -= (this.GetStabilityReduction(attacker, weapon) / maxHits) / 2;  //Less stability reduction for splash damage.

										// 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 (splashTarget.IsUnstable && splashTarget.Is(RaceStands.KnockBackable))
												//tSplashAction.Set(tSplashAction.Has(TargetOptions.Critical) ? TargetOptions.KnockDown : TargetOptions.KnockBack);
												tSplashAction.Set(TargetOptions.KnockDown);
											if (splashTarget.Life < 0)
												tSplashAction.Set(TargetOptions.KnockDown);
										}
										else if (!dualWield && !weaponIsKnuckle)
										{
											splashTarget.Stability = Creature.MinStability;
											tSplashAction.Set(TargetOptions.KnockDown);
										}
									}
								}
							}
							else
							{
								tSplashAction.Set(TargetOptions.FinishingKnockDown);
							}

							// React to knock back
							if (tSplashAction.IsKnockBack && tSplashAction.Type != CombatActionType.Defended)
							{

								if (!splashTarget.Skills.IsReady(SkillId.FinalHit))
								{
									attacker.Shove(splashTarget, KnockBackDistance);
								}
							}


							// Set stun time
							if (tSplashAction.Type != CombatActionType.Defended)
							{
								if (!splashTarget.Skills.IsReady(SkillId.FinalHit))
								{
									if (defenseStun != 0)
										tSplashAction.Stun = (short)defenseStun;
									else
										tSplashAction.Stun = GetTargetStun(attacker, weapon, tSplashAction.IsKnockBack);
								}
							}

							cap.Add(tSplashAction);
						}

					}
				}

				cap.Handle();

				// No second hit if target was knocked back or defended.
				if (tAction.IsKnockBack || tAction.Type == CombatActionType.Defended)
					break;
			}
			attacker.AttemptingAttack = false;
			return CombatSkillResult.Okay;
		}
Beispiel #6
0
		/// <summary>
		/// Uses the skill.
		/// </summary>
		/// <param name="attacker"></param>
		/// <param name="skill"></param>
		/// <param name="targetEntityId"></param>
		/// <returns></returns>
		public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId)
		{
			// Get target
			var target = attacker.Region.GetCreature(targetEntityId);
			if (target == null)
				return CombatSkillResult.InvalidTarget;

			if (target.IsNotReadyToBeHit)
				return CombatSkillResult.Okay;

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

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

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

				// Critical Hit
				var critShieldReduction = (target.LeftHand != null ? target.LeftHand.Data.DefenseBonusCrit : 0);
				var critChance = attacker.GetRightCritChance(target.Protection + critShieldReduction);
				CriticalHit.Handle(attacker, critChance, ref damage, tAction);

				var maxDamage = damage; //Damage without Defense and Protection
				// Subtract target def/prot
				SkillHelper.HandleDefenseProtection(target, ref damage);

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

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

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

				var weapon = attacker.RightHand;
				var critSkill = attacker.Skills.Get(SkillId.CriticalHit);
				if (skill.Info.Rank >= SkillRank.R5 && weapon != null && weapon.Data.SplashRadius != 0 && weapon.Data.SplashAngle != 0)
				{
					ICollection<Creature> targets = attacker.GetTargetableCreaturesInCone(weapon != null ? (int)weapon.Data.SplashRadius : 200, weapon != null ? (int)weapon.Data.SplashAngle : 20);
					foreach (var splashTarget in targets)
					{
						if (splashTarget != target)
						{
							if (splashTarget.IsNotReadyToBeHit)
								continue;
							TargetAction tSplashAction = new TargetAction(CombatActionType.TakeHit, splashTarget, attacker, skill.Info.Id);
							tSplashAction.Set(TargetOptions.Result | TargetOptions.CleanHit);
							splashTarget.Stun = TargetStun;

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

							// Critical Hit

							if (critSkill != null && tAction.Has(TargetOptions.Critical))
							{
								// Add crit bonus
								var bonus = critSkill.RankData.Var1 / 100f;
								damageSplash = damageSplash + (damageSplash * bonus);

								// Set splashTarget option
								tSplashAction.Set(TargetOptions.Critical);
							}

							var maxDamageSplash = damage; //Damage without Defense and Protection
							// Subtract splashTarget def/prot
							SkillHelper.HandleDefenseProtection(splashTarget, ref damageSplash);

							// Defense
							Defense.Handle(aAction, tSplashAction, ref damageSplash, true);

							// Mana Shield
							ManaShield.Handle(splashTarget, ref damageSplash, tSplashAction, maxDamageSplash);

							//Splash Damage Reduction
							damageSplash *= skill.Info.Rank < SkillRank.R1 ? 0.1f : 0.2f;

							// Deal with it!
							if (damageSplash > 0)
								splashTarget.TakeDamage(tSplashAction.Damage = damageSplash, attacker);

							attacker.Shove(splashTarget, KnockBackDistance);

							// Alert
							Network.Sending.Send.SetCombatTarget(splashTarget, attacker.EntityId, TargetMode.Alert);

							tAction.Set(TargetOptions.KnockDownFinish);

							if (splashTarget.IsDead)
							{
								aAction.Set(AttackerOptions.KnockBackHit1);
								tSplashAction.Set(TargetOptions.Finished);
							}

							cap.Add(tSplashAction);
						}

					}

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