/// <summary> /// Uses the skill. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetEntityId"></param> /// <returns></returns> public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { // Get target var target = attacker.Region.GetCreature(targetEntityId); if (target == null) { return(CombatSkillResult.InvalidTarget); } // "Cancels" the skill // 800 = old load time? == aAction.Stun? Varies? Doesn't seem to be a stun. Send.SkillUse(attacker, skill.Info.Id, AttackerStun, 1); var chance = attacker.AimMeter.GetAimChance(target); var rnd = RandomProvider.Get().NextDouble() * 100; var successfulHit = (rnd < chance); // Actions var cap = new CombatActionPack(attacker, skill.Info.Id); var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, targetEntityId); aAction.Set(AttackerOptions.Result); aAction.Stun = AttackerStun; cap.Add(aAction); // Target action if hit if (successfulHit) { target.StopMove(); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result); tAction.Stun = TargetStun; cap.Add(tAction); // Damage var damage = attacker.GetRndRangedDamage() * (skill.RankData.Var1 / 100f); // Elementals damage *= attacker.CalculateElementalDamageMultiplier(target); // More damage with fire arrow if (attacker.Temp.FireArrow) { damage *= FireBonus; } // Critical Hit var critChance = attacker.GetRightCritChance(target.Protection); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Defense Defense.Handle(aAction, tAction, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Natural Shield var delayReduction = NaturalShield.Handle(attacker, target, ref damage, tAction); // Deal with it! if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); SkillHelper.HandleInjury(attacker, target, damage); } // Aggro target.Aggro(attacker); // Knock down on deadly if (target.Conditions.Has(ConditionsA.Deadly)) { tAction.Set(TargetOptions.KnockDown); } // Death/Knockback if (target.IsDead) { tAction.Set(TargetOptions.FinishingKnockDown); } // Knock Back if (tAction.IsKnockBack) { attacker.Shove(target, KnockBackDistance); } // Reduce stun, based on ping if (delayReduction > 0) { tAction.Stun = (short)Math.Max(0, tAction.Stun - (tAction.Stun / 100 * delayReduction)); } // TODO: "Weakened" state (G12S2 gfSupportShotRenewal) } // Update current weapon SkillHelper.UpdateWeapon(attacker, target, ProficiencyGainType.Ranged, attacker.RightHand); // Reduce arrows if (attacker.Magazine != null && !ChannelServer.Instance.Conf.World.InfiniteArrows && !attacker.Magazine.HasTag("/unlimited_arrow/")) { attacker.Inventory.Decrement(attacker.Magazine); } cap.Handle(); // Disable fire arrow effect if (attacker.Temp.FireArrow) { Send.Effect(attacker, Effect.FireArrow, false); } return(CombatSkillResult.Okay); }
/// <summary> /// Uses the skill. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetEntityId"></param> /// <returns></returns> public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { // Get target var target = attacker.Region.GetCreature(targetEntityId); if (target == null) { return(CombatSkillResult.InvalidTarget); } // Actions var cap = new CombatActionPack(attacker, skill.Info.Id); var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, targetEntityId); aAction.Set(AttackerOptions.Result); aAction.Stun = AttackerStun; cap.Add(aAction); // Hit by chance var chance = attacker.AimMeter.GetAimChance(target); var rnd = RandomProvider.Get(); if (rnd.NextDouble() * 100 < chance) { target.StopMove(); aAction.Set(AttackerOptions.KnockBackHit2); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result | TargetOptions.CleanHit); tAction.Stun = TargetStun; cap.Add(tAction); // TODO: Splash damage // Damage var damage = this.GetDamage(attacker, skill); // Elementals damage *= attacker.CalculateElementalDamageMultiplier(target); // More damage with fire arrow // XXX: Does this affect the element? if (attacker.Temp.FireArrow) { damage *= FireBonus; } // Critical Hit var critChance = attacker.GetRightCritChance(target.Protection); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Natural Shield // Ignore delay reduction, as knock downs shouldn't be shortened NaturalShield.Handle(attacker, target, ref damage, tAction); // Deal with it! if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); SkillHelper.HandleInjury(attacker, target, damage); } // Knock down // If target is using a shield and defense, don't KD. var targetLeftHand = target.LeftHand; if (tAction.SkillId != SkillId.Defense || targetLeftHand == null || !targetLeftHand.IsShield) { // TODO: We have to calculate knockback distance right attacker.Shove(target, KnockBackDistance); tAction.Set(TargetOptions.KnockDownFinish); } // Aggro target.Aggro(attacker); if (target.IsDead) { aAction.Set(AttackerOptions.KnockBackHit1); tAction.Set(TargetOptions.Finished); } } else { aAction.Set(AttackerOptions.Missed); } // Update current weapon SkillHelper.UpdateWeapon(attacker, target, ProficiencyGainType.Ranged, attacker.RightHand); // Reduce arrows if (attacker.Magazine != null && !ChannelServer.Instance.Conf.World.InfiniteArrows && !attacker.Magazine.HasTag("/unlimited_arrow/")) { attacker.Inventory.Decrement(attacker.Magazine); } // Disable fire arrow effect if (attacker.Temp.FireArrow) { Send.Effect(attacker, Effect.FireArrow, false); } // "Cancels" the skill // 800 = old load time? == aAction.Stun? Varies? Doesn't seem to be a stun. Send.SkillUse(attacker, skill.Info.Id, 800, 1); cap.Handle(); return(CombatSkillResult.Okay); }
/// <summary> /// Uses the skill. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetEntityId"></param> /// <returns></returns> public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { // Get target var target = attacker.Region.GetCreature(targetEntityId); if (target == null) { return(CombatSkillResult.InvalidTarget); } // "Cancels" the skill // 800 = old load time? == aAction.Stun? Varies? Doesn't seem to be a stun. Send.SkillUse(attacker, skill.Info.Id, AttackerStun, 1); var chance = attacker.AimMeter.GetAimChance(target); var rnd = RandomProvider.Get().NextDouble() * 100; var successfulHit = (rnd < chance); // Actions var cap = new CombatActionPack(attacker, skill.Info.Id); var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, targetEntityId); aAction.Set(AttackerOptions.Result); aAction.Stun = AttackerStun; cap.Add(aAction); // Target action if hit if (successfulHit) { target.StopMove(); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result); tAction.Stun = TargetStun; cap.Add(tAction); // Damage // Formula unofficial, but it kinda matches what you would // expect from the skill, and what players believed the damage // to be, back in G2. // bonus = (100 - (6 - stacks) * 5 + rank, +var2 on last shot // I'm using rank instead of Var1, which goes from 1-15 in // AR2, so AR1 gets a little bonus as well, as AR1's Var1 and // 2 are 0. // With this formula, the bonus range (1st shot rF vs 5th shot // r1) is 76~110% for AR1, and 76~140% for AR2. var bonus = 100f - (6 - skill.Stacks) * 5f + (byte)skill.Info.Rank; if (skill.Stacks == 1) { bonus += skill.RankData.Var2; } var damage = attacker.GetRndRangedDamage() * (bonus / 100f); // Elementals damage *= attacker.CalculateElementalDamageMultiplier(target); // More damage with fire arrow if (attacker.Temp.FireArrow) { damage *= FireBonus; } // Critical Hit var critChance = attacker.GetRightCritChance(target.Protection); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Defense Defense.Handle(aAction, tAction, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Natural Shield var delayReduction = NaturalShield.Handle(attacker, target, ref damage, tAction); // Deal with it! if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); SkillHelper.HandleInjury(attacker, target, damage); } // Aggro target.Aggro(attacker); // Knock down on deadly if (target.Conditions.Has(ConditionsA.Deadly)) { tAction.Set(TargetOptions.KnockDown); } // Death/Knockback if (target.IsDead) { tAction.Set(TargetOptions.FinishingKnockDown); } else { // Insta-recover in knock down if (target.IsKnockedDown) { tAction.Stun = 0; } // Knock down if hit repeatedly else if (target.Stability < 30) { tAction.Set(TargetOptions.KnockDown); } // Normal stability reduction else { var stabilityReduction = StabilityReduction; // Reduce reduction, based on ping // According to the Wiki, "the Knockdown Gauge // [does not] build up", but it's still possible // to knock back with repeated hits. The stability // reduction is probably reduced, just like stun. if (delayReduction > 0) { stabilityReduction = (short)Math.Max(0, stabilityReduction - (stabilityReduction / 100 * delayReduction)); } target.Stability -= stabilityReduction; if (target.IsUnstable) { tAction.Set(TargetOptions.KnockBack); } } } // Knock Back if (tAction.IsKnockBack) { attacker.Shove(target, KnockBackDistance); } // Reduce stun, based on ping if (delayReduction > 0) { tAction.Stun = (short)Math.Max(0, tAction.Stun - (tAction.Stun / 100 * delayReduction)); } } // Update current weapon SkillHelper.UpdateWeapon(attacker, target, ProficiencyGainType.Ranged, attacker.RightHand); // Skill training if (skill.Info.Rank == SkillRank.RF) { skill.Train(1); // Try attacking with Arrow Revolver. } // Reduce arrows if (attacker.Magazine != null && !ChannelServer.Instance.Conf.World.InfiniteArrows && !attacker.Magazine.HasTag("/unlimited_arrow/")) { attacker.Inventory.Decrement(attacker.Magazine); } // Reduce stack skill.Stacks--; // Handle cap.Handle(); // Disable fire arrow effect if (attacker.Temp.FireArrow) { Send.Effect(attacker, Effect.FireArrow, false); } return(CombatSkillResult.Okay); }
/// <summary> /// Uses the skill. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetEntityId"></param> /// <returns></returns> public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { // Get target var target = attacker.Region.GetCreature(targetEntityId); if (target == null) { return(CombatSkillResult.InvalidTarget); } var targetPos = target.GetPosition(); var attackerPos = attacker.GetPosition(); var actionType = (attacker.IsElf ? CombatActionPackType.ChainRangeAttack : CombatActionPackType.NormalAttack); var attackerStun = (short)(actionType == CombatActionPackType.ChainRangeAttack ? AttackerStunElf : AttackerStun); // "Cancels" the skill // 800 = old load time? == aAction.Stun? Varies? Doesn't seem to be a stun. Send.SkillUse(attacker, skill.Info.Id, attackerStun, 1); var chance = attacker.AimMeter.GetAimChance(target); var rnd = RandomProvider.Get().NextDouble() * 100; var successfulHit = (rnd < chance); var maxHits = (actionType == CombatActionPackType.ChainRangeAttack && successfulHit ? 2 : 1); var prevId = 0; for (byte i = 1; i <= maxHits; ++i) { target.StopMove(); // Actions var cap = new CombatActionPack(attacker, skill.Info.Id); cap.Hit = i; cap.Type = actionType; cap.PrevId = prevId; prevId = cap.Id; var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, targetEntityId); aAction.Set(AttackerOptions.Result); aAction.Stun = attackerStun; cap.Add(aAction); // Target action if hit if (successfulHit) { var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result); tAction.Stun = (short)(actionType == CombatActionPackType.ChainRangeAttack ? TargetStunElf : TargetStun); if (actionType == CombatActionPackType.ChainRangeAttack) { tAction.EffectFlags = EffectFlags.SpecialRangeHit; } cap.Add(tAction); // Damage var damage = attacker.GetRndRangedDamage(); // Elementals damage *= attacker.CalculateElementalDamageMultiplier(target); // More damage with fire arrow // XXX: Does this affect the element? if (attacker.Temp.FireArrow) { damage *= FireBonus; } // Critical Hit var critChance = attacker.GetRightCritChance(target.Protection); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Conditions SkillHelper.HandleConditions(attacker, target, ref damage); // Defense Defense.Handle(aAction, tAction, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Natural Shield var nsResult = NaturalShield.Handle(attacker, target, ref damage, tAction); var delayReduction = nsResult.DelayReduction; var pinged = nsResult.Pinged; // Deal with it! if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); SkillHelper.HandleInjury(attacker, target, damage); } // Knock down on deadly if (target.Conditions.Has(ConditionsA.Deadly)) { tAction.Set(TargetOptions.KnockDown); tAction.Stun = (short)(actionType == CombatActionPackType.ChainRangeAttack ? TargetStunElf : TargetStun); } // Aggro target.Aggro(attacker); // Death/Knockback if (target.IsDead) { tAction.Set(TargetOptions.FinishingKnockDown); maxHits = 1; } else { // Insta-recover in knock down // TODO: Tied to stability? if (target.IsKnockedDown) { tAction.Stun = 0; } // Knock down if hit repeatedly else if (target.Stability < 30) { tAction.Set(TargetOptions.KnockDown); } // Normal stability reduction else { var stabilityReduction = (actionType == CombatActionPackType.ChainRangeAttack ? StabilityReductionElf : StabilityReduction); // Reduce reduction, based on ping // According to the Wiki, "the Knockdown Gauge // [does not] build up", but it's still possible // to knock back with repeated hits. The stability // reduction is probably reduced, just like stun. if (delayReduction > 0) { stabilityReduction = (short)Math.Max(0, stabilityReduction - (stabilityReduction / 100 * delayReduction)); } target.Stability -= stabilityReduction; if (target.IsUnstable) { tAction.Set(TargetOptions.KnockBack); } } } // Knock Back if (tAction.IsKnockBack) { attacker.Shove(target, KnockBackDistance); } // Reduce stun, based on ping if (pinged && delayReduction > 0) { tAction.Stun = (short)Math.Max(0, tAction.Stun - (tAction.Stun / 100 * delayReduction)); } // No second hit if defended if (tAction.SkillId == SkillId.Defense) { // Set to 1 to prevent second run maxHits = 1; // Set normal type if hit didn't come from the second // arrow. if (cap.Hit != 2) { cap.Type = CombatActionPackType.NormalAttack; } // Override stun set by Defense aAction.Stun = DefenseAttackerStun; } } else { // Dummy target action on miss, so the client knows what // the target would've been. Possibly affects arrow // animations. var tAction = new TargetAction(CombatActionType.None, target, attacker, SkillId.None); cap.Add(tAction); } // Update current weapon SkillHelper.UpdateWeapon(attacker, target, ProficiencyGainType.Ranged, attacker.RightHand); // Skill training if (skill.Info.Rank == SkillRank.Novice || skill.Info.Rank == SkillRank.RF) { skill.Train(1); // Try ranged attack. } // Reduce arrows if (attacker.Magazine != null && !ChannelServer.Instance.Conf.World.InfiniteArrows && !attacker.Magazine.HasTag("/unlimited_arrow/")) { attacker.Inventory.Decrement(attacker.Magazine); } cap.Handle(); } // Disable fire arrow effect if (attacker.Temp.FireArrow) { Send.Effect(attacker, Effect.FireArrow, false); } return(CombatSkillResult.Okay); }
/// <summary> /// Uses the skill. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetEntityId"></param> /// <returns></returns> public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { // Get target var mainTarget = attacker.Region.GetCreature(targetEntityId); if (mainTarget == null) { return(CombatSkillResult.InvalidTarget); } // Actions var cap = new CombatActionPack(attacker, skill.Info.Id); var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, targetEntityId); aAction.Set(AttackerOptions.Result); aAction.Stun = AttackerStun; cap.Add(aAction); // Hit by chance var chance = attacker.AimMeter.GetAimChance(mainTarget); var rnd = RandomProvider.Get(); if (rnd.NextDouble() * 100 < chance) { aAction.Set(AttackerOptions.KnockBackHit2); // Get targets, incl. splash. // Splash happens from r5 onwards, but we'll base it on Var4, // which is the splash damage and first != 0 on r5. var targets = new HashSet <Creature>() { mainTarget }; if (skill.RankData.Var4 != 0) { var targetPosition = mainTarget.GetPosition(); var direction = attacker.GetPosition().GetDirection(targetPosition); targets.UnionWith(attacker.GetTargetableCreaturesInCone(targetPosition, direction, skill.RankData.Var5, skill.RankData.Var6)); } // Damage var mainDamage = this.GetDamage(attacker, skill); foreach (var target in targets) { var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result | TargetOptions.CleanHit); tAction.Stun = TargetStun; cap.Add(tAction); // Damage var damage = mainDamage; // Elementals damage *= attacker.CalculateElementalDamageMultiplier(target); // More damage with fire arrow // XXX: Does this affect the element? if (attacker.Temp.FireArrow) { damage *= FireBonus; } // Splash modifier if (target != mainTarget) { damage *= (skill.RankData.Var4 / 100f); } // Critical Hit var critChance = attacker.GetRightCritChance(target.Protection); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Conditions SkillHelper.HandleConditions(attacker, target, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Natural Shield // Ignore delay reduction, as knock downs shouldn't be shortened NaturalShield.Handle(attacker, target, ref damage, tAction); // Deal with it! if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); SkillHelper.HandleInjury(attacker, target, damage); } // Knock down // If target is using a shield and defense, don't KD. var targetLeftHand = target.LeftHand; if (tAction.SkillId != SkillId.Defense || targetLeftHand == null || !targetLeftHand.IsShield) { target.Stability = Creature.MinStability; attacker.Shove(target, KnockBackDistance); tAction.Set(TargetOptions.KnockDownFinish); } // Aggro if (target == mainTarget) { target.Aggro(attacker); } if (target.IsDead) { tAction.Set(TargetOptions.Finished); if (target == mainTarget) { aAction.Set(AttackerOptions.KnockBackHit1); } } } } else { aAction.Set(AttackerOptions.Missed); } // Update current weapon SkillHelper.UpdateWeapon(attacker, mainTarget, ProficiencyGainType.Ranged, attacker.RightHand); // Reduce arrows if (attacker.Magazine != null && !ChannelServer.Instance.Conf.World.InfiniteArrows && !attacker.Magazine.HasTag("/unlimited_arrow/")) { attacker.Inventory.Decrement(attacker.Magazine); } // Disable fire arrow effect if (attacker.Temp.FireArrow) { Send.Effect(attacker, Effect.FireArrow, false); } // "Cancels" the skill // 800 = old load time? == aAction.Stun? Varies? Doesn't seem to be a stun. Send.SkillUse(attacker, skill.Info.Id, 800, 1); cap.Handle(); return(CombatSkillResult.Okay); }