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