// Computes offensive attacks. public List <ListGroup> Offense() { List <ListGroup> groups = new List <ListGroup>(); foreach (Item item in Items) { if (item.Gems == null) { continue; } foreach (Item gem in item.Gems) { if (AttackSkill.IsAttackSkill(gem)) { // Skip gems linked to totems and Cast on gems for now. if (item.GetLinkedGems(gem).Find(g => g.Name.Contains("Totem")) != null) { continue; } AttackSkill attack = AttackSkill.Create(gem, this); attack.Link(item.GetLinkedGems(gem), item); attack.Apply(item); groups.Add(attack.ToListGroup()); } } } return(groups); }
public AttackSource(string name, AttackSkill skill, Weapon weapon, Computation compute) { Name = name; Compute = compute; if (weapon == null) // Spells get damage from gem local attributes. { Nature = new DamageNature(skill.Nature); foreach (var attr in skill.Local) { Damage damage = Damage.Create(skill.Nature, attr.Key, attr.Value); if (damage != null) { Deals.Add(damage); } } if (skill.Gem.Properties.TryGetValue("Cast Time: # sec", 0, out CastTime)) { APS = 1 / CastTime; } else { APS = CastTime = 1; // Spell without Cast Time has cast time of 1 second. } if (!skill.Gem.Properties.TryGetValue("Critical Strike Chance: #%", 0, out CriticalChance)) { CriticalChance = 0; // Spell without Critical Strike Chance has none. } Local = new AttributeSet(); // No local weapon attributes. } else { if ((skill.Nature.WeaponType & weapon.Nature.WeaponType) == 0) // Skill can't be used. // Override weapon type and form of skill with actual weapon (client shows damage of unuseable skills as well). { Nature = new DamageNature(skill.Nature) { Form = weapon.Nature.Form, WeaponHand = weapon.Hand, WeaponType = weapon.Nature.WeaponType } } ; else // Narrow down weapon type and form of skill gem to actual weapon (e.g. Frenzy). { Nature = new DamageNature(skill.Nature) { Form = skill.Nature.ChooseWeaponForm(weapon.Nature), // XXX: Choose between melee or projectile form according to weapon. WeaponHand = weapon.Hand, WeaponType = skill.Nature.WeaponType & weapon.Nature.WeaponType } }; // XXX: If source has no form, but skill has form defined, then force form of skill. // This happens in form transition from melee to projectile with skills like Spectral Throw. if (Nature.Form == DamageForm.Any && skill.Nature.Form != DamageForm.Any) { Nature.Form = skill.Nature.Form; } foreach (Damage damage in weapon.Deals) { Deals.Add(new Damage(damage) { Form = Nature.Form, Source = Nature.Source, WeaponHand = Nature.WeaponHand, WeaponType = Nature.WeaponType }); } foreach (Damage.Added added in weapon.Added) { if (weapon.Is(added.Hand)) // Added damage may require specific hand. { added.Apply(this, 100); } } APS = weapon.Attributes["Attacks per Second: #"][0]; if (weapon.Attributes.ContainsKey("Critical Strike Chance: #%")) { CriticalChance = weapon.Attributes["Critical Strike Chance: #%"][0]; } else { CriticalChance = 0; // Weapon without Critical Strike Chance has none. } Local = weapon.Attributes; } }
// Computes attacks or casts per second. public void AttackSpeed(AttackSkill skill, AttributeSet attrs) { if (Nature.Is(DamageSource.Attack)) { // If gem has own Attacks per Second, use it instead of weapon one. if (skill.Local.ContainsKey("Attacks per Second: #")) { APS = skill.Local["Attacks per Second: #"][0]; // Apply local increased attack speed of weapon. if (Local.ContainsKey("#% increased Attack Speed")) { APS = IncreaseValueByPercentage(APS, Local["#% increased Attack Speed"][0]); } } float incAS = 0; if (attrs.ContainsKey("#% increased Attack Speed")) { incAS += attrs["#% increased Attack Speed"][0]; } if (attrs.ContainsKey("#% reduced Attack Speed")) { incAS -= attrs["#% reduced Attack Speed"][0]; } if (attrs.ContainsKey("#% increased Attack and Cast Speed")) { incAS += attrs["#% increased Attack and Cast Speed"][0]; } if (attrs.ContainsKey("#% reduced Attack and Cast Speed")) { incAS -= attrs["#% reduced Attack and Cast Speed"][0]; } foreach (var attr in attrs.MatchesAny(new Regex[] { ReIncreasedAttackSpeedWithWeaponHandOrType, ReIncreasedAttackSpeedType })) { Match m = ReIncreasedAttackSpeedWithWeaponHandOrType.Match(attr.Key); if (m.Success) { if (WithWeaponHand.ContainsKey(m.Groups[2].Value) && Nature.Is(WithWeaponHand[m.Groups[2].Value])) { incAS += m.Groups[1].Value == "increased" ? attr.Value[0] : -attr.Value[0]; } else if (WithWeaponType.ContainsKey(m.Groups[2].Value) && Nature.Is(WithWeaponType[m.Groups[2].Value])) { incAS += m.Groups[1].Value == "increased" ? attr.Value[0] : -attr.Value[0]; } } else { m = ReIncreasedAttackSpeedType.Match(attr.Key); if (m.Success) { // XXX: Not sure there are any mods with WeaponType here (Melee string in mod is DamageForm now, maybe Unarmed should be form as well). if (Weapon.Types.ContainsKey(m.Groups[2].Value) && Nature.Is(Weapon.Types[m.Groups[2].Value])) { incAS += m.Groups[1].Value == "increased" ? attr.Value[0] : -attr.Value[0]; } else if (DamageNature.Forms.ContainsKey(m.Groups[2].Value) && Nature.Is(DamageNature.Forms[m.Groups[2].Value])) { incAS += m.Groups[1].Value == "increased" ? attr.Value[0] : -attr.Value[0]; } } } } if (Compute.IsDualWielding && attrs.ContainsKey("#% increased Attack Speed while Dual Wielding")) { incAS += attrs["#% increased Attack Speed while Dual Wielding"][0]; } if (Compute.OffHand.IsShield() && attrs.ContainsKey("#% increased Attack Speed while holding a Shield")) { incAS += attrs["#% increased Attack Speed while holding a Shield"][0]; } if (incAS != 0) { APS = IncreaseValueByPercentage(APS, incAS); } float moreAS = 1; if (attrs.ContainsKey("#% more Attack Speed")) { moreAS *= 1 + attrs["#% more Attack Speed"][0] / 100; } if (attrs.ContainsKey("#% less Attack Speed")) { moreAS *= 1 - attrs["#% less Attack Speed"][0] / 100; } foreach (var attr in attrs.Matches(ReMoreAttackSpeedType)) { Match m = ReMoreAttackSpeedType.Match(attr.Key); if (m.Success) { // XXX: Not sure there are any mods with WeaponType here (Melee string in mod is DamageForm now, maybe Unarmed should be form as well). if (Weapon.Types.ContainsKey(m.Groups[2].Value) && Nature.Is(Weapon.Types[m.Groups[2].Value]) || DamageNature.Forms.ContainsKey(m.Groups[2].Value) && Nature.Is(DamageNature.Forms[m.Groups[2].Value])) { moreAS *= m.Groups[1].Value == "more" ? 1 + attr.Value[0] / 100 : 1 - attr.Value[0] / 100; } } } APS = APS * moreAS; APS = RoundHalfDownEvenValue(APS, 2); } else // Spell (use Cast Time directly). { float incCS = 0; if (attrs.ContainsKey("#% increased Cast Speed")) { incCS += attrs["#% increased Cast Speed"][0]; } if (attrs.ContainsKey("#% reduced Cast Speed")) { incCS -= attrs["#% reduced Cast Speed"][0]; } if (attrs.ContainsKey("#% increased Attack and Cast Speed")) { incCS += attrs["#% increased Attack and Cast Speed"][0]; } if (attrs.ContainsKey("#% reduced Attack and Cast Speed")) { incCS -= attrs["#% reduced Attack and Cast Speed"][0]; } if (Compute.IsDualWielding && attrs.ContainsKey("#% increased Cast Speed while Dual Wielding")) { incCS += attrs["#% increased Cast Speed while Dual Wielding"][0]; } if (incCS != 0) { CastTime = RoundValue(CastTime / ((100 + incCS) / 100), 3); } float moreCS = 0; if (attrs.ContainsKey("#% more Cast Speed")) { moreCS += attrs["#% more Cast Speed"][0]; } if (attrs.ContainsKey("#% less Cast Speed")) { moreCS -= attrs["#% less Cast Speed"][0]; } if (moreCS != 0) { CastTime = FloorValue(CastTime / ((100 + moreCS) / 100), 3); } APS = RoundValue(1 / CastTime, 2); } }