/// <summary> /// Returns elemental damage multiplier for this skill. /// </summary> /// <param name="attacker"></param> /// <param name="target"></param> /// <returns></returns> protected override float GetElementalDamageMultiplier(Creature attacker, Creature target) { return attacker.CalculateElementalDamageMultiplier(Creature.MaxElementalAffinity, 0, 0, target); }
/// <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) } // 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> /// Handles using the skill. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetAreaId"></param> /// <param name="unkInt1"></param> /// <param name="unkInt2"></param> public void Use(Creature attacker, Skill skill, long targetAreaId, int unkInt1, int unkInt2) { var range = this.GetRange(attacker, skill); var targets = attacker.GetTargetableCreaturesInRange(range, TargetableOptions.AddAttackRange); var rnd = RandomProvider.Get(); // Create actions var cap = new CombatActionPack(attacker, skill.Info.Id); var aAction = new AttackerAction(CombatActionType.Attacker, attacker, targetAreaId); aAction.Set(AttackerOptions.Result); aAction.Stun = AttackerStun; cap.Add(aAction); foreach (var target in targets) { // Check if hit var hitChance = this.GetHitChance(attacker, target, skill); if (rnd.Next(0, 100) > hitChance) continue; target.StopMove(); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result); tAction.Delay = 300; // Calculate damage var damage = this.GetDamage(attacker, skill); // Elementals damage *= attacker.CalculateElementalDamageMultiplier(target); // Handle skills and reductions CriticalHit.Handle(attacker, attacker.GetTotalCritChance(0), ref damage, tAction); SkillHelper.HandleDefenseProtection(target, ref damage); ManaShield.Handle(target, ref damage, tAction); HeavyStander.Handle(attacker, target, ref damage, tAction); // Clean Hit if not critical if (!tAction.Has(TargetOptions.Critical)) tAction.Set(TargetOptions.CleanHit); // Take damage if any is left if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); SkillHelper.HandleInjury(attacker, target, damage); } // Finish if dead, knock down if not defended if (target.IsDead) tAction.Set(TargetOptions.KnockDownFinish); else tAction.Set(TargetOptions.KnockDown); // Anger Management if (!target.IsDead) target.Aggro(attacker); // Stun & knock down tAction.Stun = CombatMastery.GetTargetStun(attacker.AverageKnockCount, attacker.AverageAttackSpeed, true); target.Stability = Creature.MinStability; // Add action cap.Add(tAction); } Send.UseMotion(attacker, 10, 1); cap.Handle(); Send.SkillUse(attacker, skill.Info.Id, targetAreaId, unkInt1, unkInt2); }
/// <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)); } // 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.Unknown; 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); // 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); } // 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 (delayReduction > 0) tAction.Stun = (short)Math.Max(0, tAction.Stun - (tAction.Stun / 100 * delayReduction)); } // 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 WM, attacking targets. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetAreaId"></param> /// <param name="unkInt1"></param> /// <param name="unkInt2"></param> public void Use(Creature attacker, Skill skill, long targetAreaId, int unkInt1, int unkInt2) { var range = this.GetRange(attacker, skill); var targets = attacker.GetTargetableCreaturesInRange(range, TargetableOptions.AddAttackRange); // Check targets if (targets.Count == 0) { Send.Notice(attacker, Localization.Get("There isn't a target nearby to use that on.")); Send.SkillUseSilentCancel(attacker); return; } // Create actions var cap = new CombatActionPack(attacker, skill.Info.Id); var aAction = new AttackerAction(CombatActionType.SpecialHit, attacker, targetAreaId); aAction.Set(AttackerOptions.Result); aAction.Stun = CombatMastery.GetAttackerStun(attacker.AverageKnockCount, attacker.AverageAttackSpeed, true); cap.Add(aAction); var survived = new List<Creature>(); var rnd = RandomProvider.Get(); // Check crit var crit = false; if (attacker.Skills.Has(SkillId.CriticalHit, SkillRank.RF)) crit = (rnd.Next(100) < attacker.GetTotalCritChance(0)); // Handle all targets foreach (var target in targets) { target.StopMove(); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Delay = 300; // Usually 300, sometimes 350? // Calculate damage var damage = attacker.GetRndTotalDamage(); damage *= skill.RankData.Var1 / 100f; // Elementals damage *= attacker.CalculateElementalDamageMultiplier(target); // Crit bonus if (crit) CriticalHit.Handle(attacker, 100, ref damage, tAction); // Handle skills and reductions SkillHelper.HandleDefenseProtection(target, ref damage); SkillHelper.HandleConditions(attacker, target, ref damage); Defense.Handle(aAction, tAction, ref damage); ManaShield.Handle(target, ref damage, tAction); HeavyStander.Handle(attacker, target, ref damage, tAction); // Clean Hit if not defended nor critical if (tAction.SkillId != SkillId.Defense && !tAction.Has(TargetOptions.Critical)) tAction.Set(TargetOptions.CleanHit); // Take damage if any is left if (damage > 0) target.TakeDamage(tAction.Damage = damage, attacker); // Knock down on deadly if (target.Conditions.Has(ConditionsA.Deadly)) { tAction.Set(TargetOptions.KnockDown); tAction.Stun = CombatMastery.GetTargetStun(attacker.AverageKnockCount, attacker.AverageAttackSpeed, true); } // Finish if dead, knock down if not defended if (target.IsDead) tAction.Set(TargetOptions.KnockDownFinish); else if (tAction.SkillId != SkillId.Defense) tAction.Set(TargetOptions.KnockDown); // Anger Management if (!target.IsDead) survived.Add(target); // Stun and shove if not defended if (target.IsDead || tAction.SkillId != SkillId.Defense || target.Conditions.Has(ConditionsA.Deadly)) { tAction.Stun = CombatMastery.GetTargetStun(attacker.AverageKnockCount, attacker.AverageAttackSpeed, true); target.Stability = Creature.MinStability; attacker.Shove(target, KnockbackDistance); } // Add action cap.Add(tAction); } // Update current weapon SkillHelper.UpdateWeapon(attacker, targets.FirstOrDefault(), ProficiencyGainType.Melee, attacker.RightHand, attacker.LeftHand); // Only select a random aggro if there is no aggro yet, // WM only aggroes one target at a time. if (survived.Count != 0 && attacker.Region.CountAggro(attacker) < 1) { var aggroTarget = survived.Random(); aggroTarget.Aggro(attacker); } // Reduce life in old combat system if (!AuraData.FeaturesDb.IsEnabled("CombatSystemRenewal")) { // Default reduction is 10%, it's reduced to 2% if attacker // has less max life than the rate is set to. var lifeReducationRate = skill.RankData.Var2; if (attacker.LifeMax < lifeReducationRate) lifeReducationRate /= 5; var amount = attacker.LifeMax / 100f * lifeReducationRate; attacker.ModifyLife(-amount); // TODO: Invincibility } // Spin it~ Send.UseMotion(attacker, 8, 4); cap.Handle(); Send.SkillUse(attacker, skill.Info.Id, targetAreaId, unkInt1, unkInt2); skill.Stacks = 0; }
/// <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; }
/// <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; }
/// <summary> /// Uses the skill. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetEntityId"></param> /// <returns></returns> public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { // Get target var mainTarget = attacker.Region.GetCreature(targetEntityId); if (mainTarget == null) return CombatSkillResult.InvalidTarget; // Actions var cap = new CombatActionPack(attacker, skill.Info.Id); var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, targetEntityId); aAction.Set(AttackerOptions.Result); aAction.Stun = AttackerStun; cap.Add(aAction); // Hit by chance var chance = attacker.AimMeter.GetAimChance(mainTarget); var rnd = RandomProvider.Get(); if (rnd.NextDouble() * 100 < chance) { aAction.Set(AttackerOptions.KnockBackHit2); // Get targets, incl. splash. // Splash happens from r5 onwards, but we'll base it on Var4, // which is the splash damage and first != 0 on r5. var targets = new HashSet<Creature>() { mainTarget }; if (skill.RankData.Var4 != 0) { var targetPosition = mainTarget.GetPosition(); var direction = attacker.GetPosition().GetDirection(targetPosition); targets.UnionWith(attacker.GetTargetableCreaturesInCone(targetPosition, direction, skill.RankData.Var5, skill.RankData.Var6)); } // Damage var mainDamage = this.GetDamage(attacker, skill); foreach (var target in targets) { var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result | TargetOptions.CleanHit); tAction.Stun = TargetStun; cap.Add(tAction); // Damage var damage = mainDamage; // Elementals damage *= attacker.CalculateElementalDamageMultiplier(target); // More damage with fire arrow // XXX: Does this affect the element? if (attacker.Temp.FireArrow) damage *= FireBonus; // Splash modifier if (target != mainTarget) damage *= (skill.RankData.Var4 / 100f); // Critical Hit var critChance = attacker.GetRightCritChance(target.Protection); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Conditions SkillHelper.HandleConditions(attacker, target, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Natural Shield // Ignore delay reduction, as knock downs shouldn't be shortened NaturalShield.Handle(attacker, target, ref damage, tAction); // Deal with it! if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); SkillHelper.HandleInjury(attacker, target, damage); } // Knock down // If target is using a shield and defense, don't KD. var targetLeftHand = target.LeftHand; if (tAction.SkillId != SkillId.Defense || targetLeftHand == null || !targetLeftHand.IsShield) { // TODO: We have to calculate knockback distance right attacker.Shove(target, KnockBackDistance); tAction.Set(TargetOptions.KnockDownFinish); } // Aggro if (target == mainTarget) target.Aggro(attacker); if (target.IsDead) { tAction.Set(TargetOptions.Finished); if (target == mainTarget) aAction.Set(AttackerOptions.KnockBackHit1); } } } else { aAction.Set(AttackerOptions.Missed); } // Update current weapon SkillHelper.UpdateWeapon(attacker, mainTarget, ProficiencyGainType.Ranged, attacker.RightHand); // Reduce arrows if (attacker.Magazine != null && !ChannelServer.Instance.Conf.World.InfiniteArrows && !attacker.Magazine.HasTag("/unlimited_arrow/")) attacker.Inventory.Decrement(attacker.Magazine); // Disable fire arrow effect if (attacker.Temp.FireArrow) Send.Effect(attacker, Effect.FireArrow, false); // "Cancels" the skill // 800 = old load time? == aAction.Stun? Varies? Doesn't seem to be a stun. Send.SkillUse(attacker, skill.Info.Id, 800, 1); cap.Handle(); return CombatSkillResult.Okay; }
/// <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; }
/// <summary> /// Handles usage of the skill. /// </summary> /// <param name="attacker"></param> /// <param name="target"></param> public void Use(Creature attacker, Creature target) { // Updating unlock because of the updating lock for pre-renovation // Has to be done here because we can't have an updating unlock // after the combat action, it resets the stun. if (!AuraData.FeaturesDb.IsEnabled("TalentRenovationCloseCombat")) attacker.Unlock(Locks.Move, true); var skill = attacker.Skills.Get(SkillId.Counterattack); var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, target.EntityId); aAction.Options |= AttackerOptions.Result | AttackerOptions.KnockBackHit2; var tAction = new TargetAction(CombatActionType.CounteredHit2, target, attacker, skill.Info.Id); tAction.Options |= TargetOptions.Result | TargetOptions.Smash; var cap = new CombatActionPack(attacker, skill.Info.Id); cap.Add(aAction, tAction); var damage = (attacker.GetRndTotalDamage() * (skill.RankData.Var2 / 100f)) + (target.GetRndTotalDamage() * (skill.RankData.Var1 / 100f)); // Elementals damage *= attacker.CalculateElementalDamageMultiplier(target); // Critical Hit var critChance = attacker.GetTotalCritChance(target.Protection) + skill.RankData.Var3; CriticalHit.Handle(attacker, critChance, ref damage, tAction, true); // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage, true, true); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Heavy Stander HeavyStander.Handle(attacker, target, ref damage, tAction); // Deal with it! if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); SkillHelper.HandleInjury(attacker, target, damage); } target.Aggro(attacker); if (target.IsDead) tAction.Options |= TargetOptions.FinishingKnockDown; aAction.Stun = StunTime; tAction.Stun = StunTime; target.Stability = Creature.MinStability; attacker.Shove(target, KnockbackDistance); // Update both weapons SkillHelper.UpdateWeapon(attacker, target, ProficiencyGainType.Melee, attacker.RightHand, attacker.LeftHand); Send.SkillUseStun(attacker, skill.Info.Id, StunTime, 1); this.Training(aAction, tAction); cap.Handle(); }
/// <summary> /// Uses the skill. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="targetEntityId"></param> /// <returns></returns> public CombatSkillResult Use(Creature attacker, Skill skill, long targetEntityId) { // Get target var target = attacker.Region.GetCreature(targetEntityId); if (target == null) return CombatSkillResult.InvalidTarget; // Actions var cap = new CombatActionPack(attacker, skill.Info.Id); var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, targetEntityId); aAction.Set(AttackerOptions.Result); aAction.Stun = AttackerStun; cap.Add(aAction); // Hit by chance var chance = attacker.AimMeter.GetAimChance(target); var rnd = RandomProvider.Get(); if (rnd.NextDouble() * 100 < chance) { target.StopMove(); aAction.Set(AttackerOptions.KnockBackHit2); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result | TargetOptions.CleanHit); tAction.Stun = TargetStun; cap.Add(tAction); // TODO: Splash damage // Damage var damage = this.GetDamage(attacker, skill); // Elementals damage *= attacker.CalculateElementalDamageMultiplier(target); // More damage with fire arrow // XXX: Does this affect the element? if (attacker.Temp.FireArrow) damage *= FireBonus; // Critical Hit var critChance = attacker.GetRightCritChance(target.Protection); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Subtract target def/prot SkillHelper.HandleDefenseProtection(target, ref damage); // Defense Defense.Handle(aAction, tAction, ref damage); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Natural Shield // Ignore delay reduction, as knock downs shouldn't be shortened NaturalShield.Handle(attacker, target, ref damage, tAction); // Deal with it! if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); SkillHelper.HandleInjury(attacker, target, damage); } // Knock down // If target is using a shield and defense, don't KD. var targetLeftHand = target.LeftHand; if (tAction.SkillId != SkillId.Defense || targetLeftHand == null || !targetLeftHand.IsShield) { // TODO: We have to calculate knockback distance right attacker.Shove(target, KnockBackDistance); tAction.Set(TargetOptions.KnockDownFinish); } // Aggro target.Aggro(attacker); if (target.IsDead) { aAction.Set(AttackerOptions.KnockBackHit1); tAction.Set(TargetOptions.Finished); } } else { aAction.Set(AttackerOptions.Missed); } // Reduce arrows if (attacker.Magazine != null && !ChannelServer.Instance.Conf.World.InfiniteArrows && !attacker.Magazine.HasTag("/unlimited_arrow/")) attacker.Inventory.Decrement(attacker.Magazine); // Disable fire arrow effect if (attacker.Temp.FireArrow) Send.Effect(attacker, Effect.FireArrow, false); // "Cancels" the skill // 800 = old load time? == aAction.Stun? Varies? Doesn't seem to be a stun. Send.SkillUse(attacker, skill.Info.Id, 800, 1); cap.Handle(); return CombatSkillResult.Okay; }