StopMove() public method

Stops movement, returning new current position. Sends Force(Walk|Run)To.
public StopMove ( ) : Position
return Position
Esempio n. 1
0
		/// <summary>
		/// Prepares skill, skips right to used.
		/// </summary>
		/// <remarks>
		/// Doesn't check anything, like what you can gather with what,
		/// because at this point there's no chance for abuse.
		/// </remarks>
		/// <param name="creature"></param>
		/// <param name="skill"></param>
		/// <param name="packet"></param>
		/// <returns></returns>
		public bool Prepare(Creature creature, Skill skill, Packet packet)
		{
			var entityId = packet.GetLong();
			var collectId = packet.GetInt();

			// You shall stop
			creature.StopMove();
			var creaturePosition = creature.GetPosition();

			// Get target (either prop or creature)
			var targetEntity = this.GetTargetEntity(creature.Region, entityId);
			if (targetEntity != null)
				creature.Temp.GatheringTargetPosition = targetEntity.GetPosition();

			// Check distance
			if (!creaturePosition.InRange(creature.Temp.GatheringTargetPosition, MaxDistance))
			{
				Send.Notice(creature, Localization.Get("Your arms are too short to reach that from here."));
				return false;
			}

			// ? (sets creatures position on the client side)
			Send.CollectAnimation(creature, entityId, collectId, creaturePosition);

			// Use
			Send.SkillUse(creature, skill.Info.Id, entityId, collectId);
			skill.State = SkillState.Used;

			return true;
		}
Esempio n. 2
0
		/// <summary>
		/// Prepares skill, showing a casting motion.
		/// </summary>
		/// <param name="creature"></param>
		/// <param name="skill"></param>
		/// <param name="packet"></param>
		/// <returns></returns>
		public virtual bool Prepare(Creature creature, Skill skill, Packet packet)
		{
			creature.StopMove();

			Send.Effect(creature, Effect.Casting, (short)skill.Info.Id, (byte)0, (byte)1, (short)0);
			Send.SkillPrepare(creature, skill.Info.Id, skill.GetCastTime());

			return true;
		}
Esempio n. 3
0
		/// <summary>
		/// Preapres the skill.
		/// </summary>
		/// <param name="creature"></param>
		/// <param name="skill"></param>
		/// <param name="packet"></param>
		/// <returns></returns>
		public override bool Prepare(Creature creature, Skill skill, Packet packet)
		{
			creature.StopMove();

			Send.SkillInitEffect(creature, "");
			Send.UseMotion(creature, 10, 2);
			Send.SkillPrepare(creature, skill.Info.Id, skill.GetCastTime());

			return true;
		}
Esempio n. 4
0
		/// <summary>
		/// Prepares WM, empty skill init for only the loading sound.
		/// </summary>
		/// <param name="creature"></param>
		/// <param name="skill"></param>
		/// <param name="packet"></param>
		/// <returns></returns>
		public bool Prepare(Creature creature, Skill skill, Packet packet)
		{
			creature.StopMove();
			creature.Lock(Locks.Move);

			Send.SkillInitEffect(creature, null);
			Send.SkillPrepare(creature, skill.Info.Id, skill.GetCastTime());

			return true;
		}
Esempio n. 5
0
		/// <summary>
		/// Bolt specific use code.
		/// </summary>
		/// <param name="attacker"></param>
		/// <param name="skill"></param>
		/// <param name="target"></param>
		protected override void UseSkillOnTarget(Creature attacker, Skill skill, Creature target)
		{
			attacker.StopMove();
			target.StopMove();

			// Create actions
			var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, target.EntityId);
			aAction.Set(AttackerOptions.Result);

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

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

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

			// Elements
			damage *= this.GetElementalDamageMultiplier(attacker, target);

			// Reduce damage
			SkillHelper.HandleMagicDefenseProtection(target, ref damage);
			ManaShield.Handle(target, ref damage, tAction);
			ManaDeflector.Handle(attacker, target, ref damage, tAction);

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

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

			// Death/Knockback
			attacker.Shove(target, KnockbackDistance);
			if (target.IsDead)
				tAction.Set(TargetOptions.FinishingKnockDown);
			else
				tAction.Set(TargetOptions.KnockDown);

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

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

			skill.Stacks = 0;

			cap.Handle();
		}
Esempio n. 6
0
		/// <summary>
		/// Handles skill preparation.
		/// </summary>
		/// <param name="creature"></param>
		/// <param name="skill"></param>
		/// <param name="packet"></param>
		public override bool Prepare(Creature creature, Skill skill, Packet packet)
		{
			Send.SkillFlashEffect(creature);
			Send.SkillPrepare(creature, skill.Info.Id, skill.GetCastTime());

			// Default lock is Walk|Run,  since renovation you are stopped when
			// loading counter, previously you kept running till you were at your
			// destination.
			if (AuraData.FeaturesDb.IsEnabled("TalentRenovationCloseCombat"))
				creature.StopMove();

			return true;
		}
Esempio n. 7
0
		/// <summary>
		/// Prepares skill, fails if no Dice is found.
		/// </summary>
		/// <param name="creature"></param>
		/// <param name="skill"></param>
		/// <param name="packet"></param>
		/// <returns></returns>
		public bool Prepare(Creature creature, Skill skill, Packet packet)
		{
			// Check if dice are equipped (checked on client as well)
			if (creature.RightHand == null || !creature.RightHand.HasTag("/dice/"))
			{
				Send.Notice(creature, Localization.Get("You must equip one Six Sided Dice to use this Action."));
				return false;
			}

			creature.StopMove();

			Send.UseMotion(creature, 27, 0, false, false);
			Send.Effect(creature, Effect.Dice, 0, "prepare"); // [200200, NA233 (2016-08-12)] New 0 int after effect id
			Send.SkillPrepare(creature, skill.Info.Id, skill.GetCastTime());

			return true;
		}
Esempio n. 8
0
        public override StartStopResult Start(Creature creature, Skill skill, MabiDictionary dict)
        {
            creature.StopMove();

            var chairItemEntityId = dict.GetLong("ITEMID");

            if (chairItemEntityId != 0)
                this.SetUpChair(creature, chairItemEntityId);

            creature.Activate(CreatureStates.SitDown);
            Send.SitDown(creature);

            creature.Regens.Add("Rest", Stat.Life, (0.12f * ((skill.RankData.Var1 - 100) / 100)), creature.LifeMax);
            creature.Regens.Add("Rest", Stat.Stamina, (0.4f * ((skill.RankData.Var2 - 100) / 100)), creature.StaminaMax);
            creature.Regens.Add("Rest", Stat.LifeInjured, skill.RankData.Var3, creature.LifeMax);

            if (skill.Info.Rank == SkillRank.Novice) skill.Train(1); // Use Rest.

            return StartStopResult.Okay;
        }
Esempio n. 9
0
File: Rest.cs Progetto: xKamuna/aura
		/// <summary>
		/// Starts rest skill.
		/// </summary>
		/// <param name="creature"></param>
		/// <param name="skill"></param>
		/// <param name="dict"></param>
		/// <returns></returns>
		public override StartStopResult Start(Creature creature, Skill skill, MabiDictionary dict)
		{
			creature.StopMove();

			creature.IsInBattleStance = false;
			creature.AttemptingAttack = false;

			var chairItemEntityId = dict.GetLong("ITEMID");

			if (chairItemEntityId != 0)
				this.SetUpChair(creature, chairItemEntityId);

			creature.Activate(CreatureStates.SitDown);
			if (skill.Info.Rank >= SkillRank.R9)
				creature.Activate(CreatureStatesEx.RestR9);

			Send.SitDown(creature);

			// Get bonuses if meditation isn't active.
			if (!creature.Conditions.Has(ConditionsE.Meditation))
			{
				ApplyRestBonus(creature, skill, chairItemEntityId);
			}
			else
			{
				RestCampfireBonus(creature, skill, chairItemEntityId);
			}

			// Add bonus from campfire
			// TODO: Check for disappearing of campfire? (OnDisappears+Recheck)
			var campfires = creature.Region.GetProps(a => a.Info.Id == 203 && a.GetPosition().InRange(creature.GetPosition(), 500));
			if (campfires.Count > 0)
			{
				Send.Notice(creature, Localization.Get("The fire feels very warm."));
			}

			if (skill.Info.Rank == SkillRank.Novice) skill.Train(1); // Use Rest.

			return StartStopResult.Okay;
		}
Esempio n. 10
0
		/// <summary>
		/// Prepares the skill, called to start casting.
		/// </summary>
		/// <param name="creature"></param>
		/// <param name="skill"></param>
		/// <param name="packet"></param>
		public override bool Prepare(Creature creature, Skill skill, Packet packet)
		{
			Send.SkillFlashEffect(creature);
			Send.SkillPrepare(creature, skill.Info.Id, skill.GetCastTime());

			// Default lock is Walk|Run, since renovation you're not able to
			// move while loading anymore.
			if (AuraData.FeaturesDb.IsEnabled("TalentRenovationCloseCombat"))
			{
				creature.StopMove();
			}
			// Since the client locks Walk|Run by default we have to tell it
			// to enable walk but disable run (under any circumstances) if
			// renovation is disabled.
			else
			{
				creature.Lock(Locks.Run, true);
				creature.Unlock(Locks.Walk, true);
			}

			return true;
		}
Esempio n. 11
0
		public void Use(Creature creature, Skill skill, Packet packet)
		{
			var targetPos = new Position(packet.GetLong());

			// Check distance to target position
			if (!creature.GetPosition().InRange(targetPos, (int)skill.RankData.Var2 + DistanceBuffer))
			{
				Send.Notice(creature, Localization.Get("Out of range."));
				Send.SkillUse(creature, skill.Info.Id, 0);
				return;
			}

			// Stop creature's movement.
			creature.StopMove();

			// Teleport effect (does not actually teleport)
			Send.Effect(creature, Effect.SilentMoveTeleport, (byte)2, targetPos.X, targetPos.Y);

			// Teleport player to target position
			creature.SetPosition(targetPos.X, targetPos.Y);
			Send.SkillTeleport(creature, targetPos.X, targetPos.Y);

			Send.SkillUse(creature, skill.Info.Id, 1);
		}
Esempio n. 12
0
		/// <summary>
		/// Starts rest skill.
		/// </summary>
		/// <param name="creature"></param>
		/// <param name="skill"></param>
		/// <param name="dict"></param>
		/// <returns></returns>
		public override StartStopResult Start(Creature creature, Skill skill, MabiDictionary dict)
		{
			creature.StopMove();

			var chairItemEntityId = dict.GetLong("ITEMID");

			if (chairItemEntityId != 0)
				this.SetUpChair(creature, chairItemEntityId);

			creature.Activate(CreatureStates.SitDown);
			if (skill.Info.Rank >= SkillRank.R9)
				creature.Activate(CreatureStatesEx.RestR9);

			Send.SitDown(creature);

			// Get base bonuses
			var bonusLife = ((skill.RankData.Var1 - 100) / 100);
			var bonusStamina = ((skill.RankData.Var2 - 100) / 100);
			var bonusInjury = skill.RankData.Var3;

			// Add bonus from campfire
			// TODO: Check for disappearing of campfire? (OnDisappears+Recheck)
			var campfires = creature.Region.GetProps(a => a.Info.Id == 203 && a.GetPosition().InRange(creature.GetPosition(), 500));
			if (campfires.Count > 0)
			{
				// Add bonus if no chair?
				if (chairItemEntityId == 0)
				{
					// TODO: Select nearest? Random?
					var campfire = campfires[0];

					var multi = (campfire.Temp.CampfireSkillRank != null ? campfire.Temp.CampfireSkillRank.Var1 / 100f : 1);

					// Add bonus for better wood.
					// Amounts unofficial.
					if (campfire.Temp.CampfireFirewood != null)
					{
						if (campfire.Temp.CampfireFirewood.HasTag("/firewood01/"))
							multi += 0.1f;
						else if (campfire.Temp.CampfireFirewood.HasTag("/firewood02/"))
							multi += 0.2f;
						else if (campfire.Temp.CampfireFirewood.HasTag("/firewood03/"))
							multi += 0.3f;
					}

					// Apply multiplicator
					bonusLife *= multi;
					bonusStamina *= multi;
					bonusInjury *= multi;
				}

				Send.Notice(creature, Localization.Get("The fire feels very warm"));
			}

			creature.Regens.Add("Rest", Stat.Life, (0.12f * bonusLife), creature.LifeMax);
			creature.Regens.Add("Rest", Stat.Stamina, (0.4f * bonusStamina), creature.StaminaMax);
			creature.Regens.Add("Rest", Stat.LifeInjured, bonusInjury, creature.LifeMax); // TODO: Test if LifeInjured = Injuries

			if (skill.Info.Rank == SkillRank.Novice) skill.Train(1); // Use Rest.

			return StartStopResult.Okay;
		}
Esempio n. 13
0
        /// <summary>
        /// Bolt specific use code.
        /// </summary>
        /// <param name="attacker"></param>
        /// <param name="skill"></param>
        /// <param name="target"></param>
        protected override void UseSkillOnTarget(Creature attacker, Skill skill, Creature target)
        {
            attacker.StopMove();
            target.StopMove();

            // Create actions
            var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, target.EntityId);
            aAction.Set(AttackerOptions.Result);

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

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

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

            // Elements
            damage *= this.GetElementalDamageMultiplier(attacker, target);

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

            // Reduce damage
            Defense.Handle(aAction, tAction, ref damage);
            SkillHelper.HandleMagicDefenseProtection(target, ref damage);
            ManaShield.Handle(target, ref damage, tAction);

            // Mana Deflector
            var delayReduction = ManaDeflector.Handle(attacker, target, ref damage, tAction);

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

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

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

            // Death/Knockback
            if (target.IsDead)
            {
                tAction.Set(TargetOptions.FinishingKnockDown);
            }
            else
            {
                // If knocked down, instant recovery,
                // if repeat hit, knock down,
                // otherwise potential knock back.
                if (target.IsKnockedDown)
                {
                    tAction.Stun = 0;
                }
                else if (target.Stability < MinStability)
                {
                    tAction.Set(TargetOptions.KnockDown);
                }
                else
                {
                    var stabilityReduction = StabilityReduction;

                    // Reduce reduction, based on ping
                    // While the Wiki says that "the Knockdown Gauge [does not]
                    // build up", tests show that it does. However, it's
                    // reduced, assumedly based on the MD rank.
                    if (delayReduction > 0)
                        stabilityReduction = (short)Math.Max(0, stabilityReduction - (stabilityReduction / 100 * delayReduction));

                    target.Stability -= stabilityReduction;

                    if (target.IsUnstable)
                    {
                        tAction.Set(TargetOptions.KnockBack);
                    }
                }
            }

            if (tAction.IsKnockBack)
                attacker.Shove(target, KnockbackDistance);

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

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

            skill.Stacks--;

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

            cap.Handle();
        }
Esempio n. 14
0
		/// <summary>
		/// Uses skill, the actual usage is in Complete.
		/// </summary>
		/// <param name="creature"></param>
		/// <param name="skill"></param>
		/// <param name="packet"></param>
		public void Use(Creature creature, Skill skill, Packet packet)
		{
			var entityId = packet.GetLong();
			var unkInt1 = packet.GetInt();
			var unkInt2 = packet.GetInt();

			creature.StopMove();

			// Do checks in Complete.

			Send.SkillUse(creature, skill.Info.Id, entityId, unkInt1, unkInt2);
		}
Esempio n. 15
0
		/// <summary>
		/// Bolt specific use code.
		/// </summary>
		/// <param name="attacker"></param>
		/// <param name="skill"></param>
		/// <param name="target"></param>
		protected virtual void UseSkillOnTarget(Creature attacker, Skill skill, Creature target)
		{
			target.StopMove();

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

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

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

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

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

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

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

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

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

			this.BeforeHandlingPack(attacker, skill);

			cap.Handle();
		}
Esempio n. 16
0
		/// <summary>
		/// Starts rest skill.
		/// </summary>
		/// <param name="creature"></param>
		/// <param name="skill"></param>
		/// <param name="dict"></param>
		/// <returns></returns>
		public override StartStopResult Start(Creature creature, Skill skill, MabiDictionary dict)
		{
			creature.StopMove();

			var chairItemEntityId = dict.GetLong("ITEMID");

			if (chairItemEntityId != 0)
				this.SetUpChair(creature, chairItemEntityId);
			else
			{
				// Find all nearby sittable props and sit on closest one
				var crpos = creature.GetPosition();
				var props = creature.Region.GetProps(prop => prop.HasTag("/sittable/")
					&& (!prop.HasXml || prop.Xml.Attribute("SITCHAR") == null || prop.Xml.Attribute("SITCHAR").Value == "0")
					&& prop.GetPosition().GetDistance(crpos) < 125);
				if (props.Count > 0)
				{
					int nearest = 0;
					int minDist = 125;
					for (int i = 0; i < props.Count; i++)
					{
						var dist = crpos.GetDistance(props[i].GetPosition());
						if(dist < minDist)
						{
							nearest = i;
							minDist = dist;
						}
					}
					this.SitOnProp(creature, props[nearest]);
				}
			}

			creature.Activate(CreatureStates.SitDown);
			if (skill.Info.Rank >= SkillRank.R9)
				creature.Activate(CreatureStatesEx.RestR9);

			Send.SitDown(creature);

			// Get base bonuses
			var bonusLife = ((skill.RankData.Var1 - 100) / 100);
			var bonusStamina = ((skill.RankData.Var2 - 100) / 100);
			var bonusInjury = skill.RankData.Var3;

			// Add bonus from campfire
			// TODO: Check for disappearing of campfire? (OnDisappears+Recheck)
			var campfires = creature.Region.GetProps(a => a.Info.Id == 203 && a.GetPosition().InRange(creature.GetPosition(), 500));
			if (campfires.Count > 0)
			{
				// Add bonus if no chair?
				if (chairItemEntityId == 0)
				{
					// TODO: Select nearest? Random?
					var campfire = campfires[0];

					var multi = (campfire.Temp.CampfireSkillRank != null ? campfire.Temp.CampfireSkillRank.Var1 / 100f : 1);

					// Add bonus for better wood.
					// Amounts unofficial.
					if (campfire.Temp.CampfireFirewood != null)
					{
						if (campfire.Temp.CampfireFirewood.HasTag("/firewood01/"))
							multi += 0.1f;
						else if (campfire.Temp.CampfireFirewood.HasTag("/firewood02/"))
							multi += 0.2f;
						else if (campfire.Temp.CampfireFirewood.HasTag("/firewood03/"))
							multi += 0.3f;
					}

					// Apply multiplicator
					bonusLife *= multi;
					bonusStamina *= multi;
					bonusInjury *= multi;
				}

				Send.Notice(creature, Localization.Get("The fire feels very warm"));
			}

			creature.Regens.Add("Rest", Stat.Life, (0.12f * bonusLife), creature.LifeMax);
			creature.Regens.Add("Rest", Stat.Stamina, (0.4f * bonusStamina), creature.StaminaMax);
			creature.Regens.Add("Rest", Stat.LifeInjured, bonusInjury, creature.LifeMax); // TODO: Test if LifeInjured = Injuries

			if (skill.Info.Rank == SkillRank.Novice) skill.Train(1); // Use Rest.

			return StartStopResult.Okay;
		}
Esempio n. 17
0
		/// <summary>
		/// Prepares the skill
		/// </summary>
		/// <param name="creature"></param>
		/// <param name="skill"></param>
		/// <param name="packet"></param>
		/// <returns></returns>
		public bool Prepare(Creature creature, Skill skill, Packet packet)
		{
			if (creature.RightHand == null || !creature.RightHand.HasTag("/staff/"))
			{
				Send.SkillPrepareSilentCancel(creature, skill.Info.Id);
				return false;
			}

			creature.StopMove();

			skill.State = SkillState.Prepared;

			Send.MotionCancel2(creature, 0);
			Send.Effect(creature, Effect.LightningRod, (int)LightningRodEffect.Prepare, 0);

			Send.SkillReady(creature, skill.Info.Id);
			skill.State = SkillState.Ready;

			creature.Temp.LightningRodPrepareTime = DateTime.Now;

			return true;
		}
Esempio n. 18
0
		/// <summary>
		/// Prepares skill, goes straight to Use and starts playing.
		/// </summary>
		/// <param name="creature"></param>
		/// <param name="skill"></param>
		/// <param name="packet"></param>
		public bool Prepare(Creature creature, Skill skill, Packet packet)
		{
			var rnd = RandomProvider.Get();

			// Check for instrument
			if (creature.RightHand == null || creature.RightHand.Data.Type != ItemType.Instrument)
				return false;

			creature.StopMove();

			// Get instrument type
			var instrumentType = this.GetInstrumentType(creature);

			// TODO: Make db for instruments with installable props.

			// Get mml from equipped score scroll if available.
			var mml = this.GetScore(creature);

			// Random score if no usable scroll was found.
			var rndScore = (mml == null ? this.GetRandomScore(rnd) : 0);

			// Quality seems to go from 0 (worst) to 3 (best).
			// TODO: Base quality on skills and score ranks.
			// The quality was apparently changed to a value from 0 to 100
			// in the MusicQ update. We'll use the quality "rating" as fall-
			// back for the success messages and the training for now.
			var quality = rnd.Next(0, 100 + 1);

			// Sunday: Increase in success rate for instrument playing.
			// Another attempt if quality was bad, unofficial.
			if (quality < 50 && ErinnTime.Now.Month == ErinnMonth.Imbolic)
				quality = rnd.Next(0, 100 + 1);

			// Up quality by chance, based on Musical Knowledge
			var musicalKnowledgeSkill = creature.Skills.Get(SkillId.MusicalKnowledge);
			if (musicalKnowledgeSkill != null && rnd.Next(100) < musicalKnowledgeSkill.RankData.Var2)
				quality += 25;

			if (quality > 100)
				quality = 100;

			// Get quality for the effect, perfect play makes every sound perfect.
			var effectQuality = quality;
			if (ChannelServer.Instance.Conf.World.PerfectPlay)
			{
				effectQuality = 100;
				Send.ServerMessage(creature, Localization.Get("Perfect play is enabled, your performance will sound perfect."));
			}

			// Reduce scroll's durability.
			if (mml != null)
				creature.Inventory.ReduceDurability(creature.Magazine, DurabilityUse);

			// Music effect and Use
			this.StartPlay(creature, skill, instrumentType, effectQuality, mml, rndScore);
			this.OnPlay(creature, skill, quality);
			Send.SkillUsePlayingInstrument(creature, skill.Info.Id, instrumentType, mml, rndScore);
			skill.State = SkillState.Used;

			// Special motion on highest quality.
			if (quality >= 100)
				Send.UseMotion(creature, 88, 2, true);

			// Give proficiency
			if (creature.RightHand.Durability != 0)
			{
				var amount = Item.GetProficiencyGain(creature.Age, ProficiencyGainType.Music);
				creature.Inventory.AddProficiency(creature.RightHand, amount);
			}

			// Called from Complete, once the song is finished.
			creature.Skills.Callback(skill.Info.Id, () =>
			{
				Send.Notice(creature, this.GetRandomQualityMessage(quality));
				this.AfterPlay(creature, skill, quality);
			});

			return true;
		}
Esempio n. 19
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;
		}
Esempio n. 20
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;
		}
Esempio n. 21
0
		/// <summary>
		/// Prepares skill, goes straight to Use and starts playing.
		/// </summary>
		/// <param name="creature"></param>
		/// <param name="skill"></param>
		/// <param name="packet"></param>
		public bool Prepare(Creature creature, Skill skill, Packet packet)
		{
			var rnd = RandomProvider.Get();

			// Check for instrument
			if (creature.RightHand == null || creature.RightHand.Data.Type != ItemType.Instrument)
				return false;

			creature.StopMove();

			// Get instrument type
			var instrumentType = this.GetInstrumentType(creature);

			// TODO: Make db for instruments with installable props.

			// Get mml from equipped score scroll if available.
			var mml = this.GetScore(creature);

			// Random score if no usable scroll was found.
			var rndScore = (mml == null ? this.GetRandomScore(rnd) : 0);

			// Quality seems to go from 0 (worst) to 3 (best).
			// TODO: Base quality on skills and score ranks.
			var quality = (PlayingQuality)rnd.Next((int)PlayingQuality.VeryBad, (int)PlayingQuality.VeryGood + 1);

			// Up quality by chance, based on Musical Knowledge
			var musicalKnowledgeSkill = creature.Skills.Get(SkillId.MusicalKnowledge);
			if (musicalKnowledgeSkill != null && rnd.Next(100) < musicalKnowledgeSkill.RankData.Var2)
				quality++;

			if (quality > PlayingQuality.VeryGood)
				quality = PlayingQuality.VeryGood;

			// Get quality for the effect, perfect play makes every sound perfect.
			var effectQuality = quality;
			if (ChannelServer.Instance.Conf.World.PerfectPlay)
			{
				effectQuality = PlayingQuality.VeryGood;
				Send.ServerMessage(creature, Localization.Get("Perfect play is enabled, your performance will sound perfect."));
			}

			// Reduce scroll's durability.
			if (mml != null)
				creature.Inventory.ReduceDurability(creature.Magazine, DurabilityUse);

			// Music effect and Use
			Send.PlayEffect(creature, instrumentType, effectQuality, mml, rndScore);
			this.OnPlay(creature, skill, quality);
			Send.SkillUsePlayingInstrument(creature, skill.Info.Id, instrumentType, mml, rndScore);
			skill.State = SkillState.Used;

			creature.Skills.Callback(skill.Info.Id, () =>
			{
				Send.Notice(creature, this.GetRandomQualityMessage(quality));
				this.AfterPlay(creature, skill, quality);
			});

			creature.Regens.Add("PlayingInstrument", Stat.Stamina, skill.RankData.StaminaActive, creature.StaminaMax);

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

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

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

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

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

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

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

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

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

			// 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
			target.Aggro(attacker);

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

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

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

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

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

			// Action!
			cap.Handle();

			return CombatSkillResult.Okay;
		}
Esempio n. 23
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;
        }
Esempio n. 24
0
        public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId)
        {
            if (attacker.IsStunned)
                return CombatSkillResult.Okay;

            var target = attacker.Region.GetCreature(targetEntityId);
            if (target == null)
                return CombatSkillResult.Okay;

            if (!attacker.GetPosition().InRange(target.GetPosition(), attacker.AttackRangeFor(target)))
                return CombatSkillResult.OutOfRange;

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

            var rightWeapon = attacker.Inventory.RightHand;
            var leftWeapon = attacker.Inventory.LeftHand;
            var magazine = attacker.Inventory.Magazine;
            var dualWield = (rightWeapon != null && leftWeapon != null);
            var maxHits = (byte)(dualWield ? 2 : 1);
            int prevId = 0;

            for (byte i = 1; i <= maxHits; ++i)
            {
                var weapon = (i == 1 ? rightWeapon : leftWeapon);

                var cap = new CombatActionPack(attacker, skill.Info.Id);
                var aAction = new AttackerAction(CombatActionType.Hit, attacker, skill.Info.Id, targetEntityId);
                var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id);
                cap.Add(aAction, tAction);

                cap.Hit = i;
                cap.MaxHits = maxHits;
                cap.PrevId = prevId;
                prevId = cap.Id;

                aAction.Set(AttackerOptions.Result);
                if (dualWield)
                    aAction.Set(AttackerOptions.DualWield);

                var damage = attacker.GetRndDamage(weapon);
                tAction.Damage = damage;

                target.TakeDamage(tAction.Damage, attacker);

                if (!target.IsDead)
                {
                    target.KnockBack += this.GetKnockBack(weapon) / maxHits;
                    if (target.KnockBack >= 100 && target.Is(RaceStands.KnockBackable))
                        tAction.Set(tAction.Has(TargetOptions.Critical) ? TargetOptions.KnockDown : TargetOptions.KnockBack);
                }
                else
                {
                    tAction.Set(TargetOptions.FinishingKnockDown);
                }

                if (tAction.IsKnockBack)
                {
                    var newPos = attacker.GetPosition().GetRelative(targetPosition, KnockBackDistance);

                    Position intersection;
                    if (target.Region.Collissions.Find(targetPosition, newPos, out intersection))
                        newPos = targetPosition.GetRelative(intersection, -50);

                    target.SetPosition(newPos.X, newPos.Y);

                    aAction.Set(AttackerOptions.KnockBackHit2);

                    cap.MaxHits = cap.Hit;
                }

                aAction.Stun = this.GetAttackerStun(weapon, tAction.IsKnockBack);
                tAction.Stun = this.GetTargetStun(weapon, tAction.IsKnockBack);

                cap.Handle();

                if (tAction.IsKnockBack)
                    break;
            }

            return CombatSkillResult.Okay;
        }
Esempio n. 25
0
		/// <summary>
		/// Handles attack.
		/// </summary>
		/// <param name="attacker">The creature attacking.</param>
		/// <param name="skill">The skill being used.</param>
		/// <param name="targetEntityId">The entity id of the target.</param>
		/// <returns></returns>
		public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId)
		{
			if (attacker.IsStunned)
				return CombatSkillResult.Okay;

			var target = attacker.Region.GetCreature(targetEntityId);
			if (target == null)
				return CombatSkillResult.Okay;

			if (!attacker.GetPosition().InRange(target.GetPosition(), attacker.AttackRangeFor(target)))
				return CombatSkillResult.OutOfRange;

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

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

			var rightWeapon = attacker.Inventory.RightHand;
			var leftWeapon = attacker.Inventory.LeftHand;
			var magazine = attacker.Inventory.Magazine;
			var dualWield = (rightWeapon != null && leftWeapon != null && leftWeapon.Data.WeaponType != 0);
			var maxHits = (byte)(dualWield ? 2 : 1);
			int prevId = 0;

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

				var aAction = new AttackerAction(CombatActionType.Attacker, attacker, skill.Info.Id, targetEntityId);
				aAction.Set(AttackerOptions.Result);

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

				var cap = new CombatActionPack(attacker, skill.Info.Id, aAction, tAction);
				cap.Hit = i;
				cap.Type = (dualWield ? CombatActionPackType.TwinSwordAttack : CombatActionPackType.NormalAttack);
				cap.PrevId = prevId;
				prevId = cap.Id;

				// Default attacker options
				aAction.Set(AttackerOptions.Result);
				if (dualWield)
				{
					aAction.Set(AttackerOptions.DualWield);
					aAction.WeaponParameterType = (byte)(i == 1 ? 2 : 1);
				}

				// Base damage
				var damage = (i == 1 ? attacker.GetRndRightHandDamage() : attacker.GetRndLeftHandDamage());

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

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

				// Aggro
				target.Aggro(attacker);

				// Evaluate caused damage
				if (!target.IsDead)
				{
					if (tAction.SkillId != SkillId.Defense)
					{
						target.Stability -= this.GetStabilityReduction(attacker, weapon) / maxHits;

						// React normal for CombatMastery, knock down if 
						// FH and not dual wield, don't knock at all if dual.
						if (skill.Info.Id != SkillId.FinalHit)
						{
							// Originally we thought you knock enemies back, unless it's a critical
							// hit, but apparently you knock *down* under normal circumstances.
							// More research to be done.
							if (target.IsUnstable && target.Is(RaceStands.KnockBackable))
								//tAction.Set(tAction.Has(TargetOptions.Critical) ? TargetOptions.KnockDown : TargetOptions.KnockBack);
								tAction.Set(TargetOptions.KnockDown);
						}
						else if (!dualWield && !weaponIsKnuckle)
						{
							target.Stability = Creature.MinStability;
							tAction.Set(TargetOptions.KnockDown);
						}
					}
				}
				else
				{
					tAction.Set(TargetOptions.FinishingKnockDown);
				}

				// React to knock back
				if (tAction.IsKnockBack)
				{
					attacker.Shove(target, KnockBackDistance);

					aAction.Set(AttackerOptions.KnockBackHit2);

					// Remove dual wield option if last hit doesn't come from
					// the second weapon.
					if (cap.Hit != 2)
						aAction.Options &= ~AttackerOptions.DualWield;
				}

				// Set stun time
				if (tAction.SkillId != SkillId.Defense)
				{
					aAction.Stun = GetAttackerStun(attacker, weapon, tAction.IsKnockBack && skill.Info.Id != SkillId.FinalHit);
					tAction.Stun = GetTargetStun(attacker, weapon, tAction.IsKnockBack);
				}

				// Second hit doubles stun time for normal hits
				if (cap.Hit == 2 && !tAction.IsKnockBack)
					aAction.Stun *= 2;

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

				cap.Handle();

				// No second hit if target was knocked back
				if (tAction.IsKnockBack)
					break;
			}

			return CombatSkillResult.Okay;
		}
Esempio n. 26
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;
        }