public void UseBushidoStance() { Spell spell = null; if (this.m_Mobile.Debug) { this.m_Mobile.Say(2117, "Using a samurai stance"); } BaseWeapon weapon = this.m_Mobile.Weapon as BaseWeapon; if (weapon == null) { return; } int whichone = Utility.RandomMinMax(1, 3); if (whichone == 3 && this.m_Mobile.Skills[SkillName.Bushido].Value >= 60.0) { spell = new Evasion(this.m_Mobile, null); } else if (whichone >= 2 && this.m_Mobile.Skills[SkillName.Bushido].Value >= 40.0) { spell = new CounterAttack(this.m_Mobile, null); } else if (whichone >= 1 && this.m_Mobile.Skills[SkillName.Bushido].Value >= 25.0) { spell = new Confidence(this.m_Mobile, null); } if (spell != null) { spell.Cast(); } }
public virtual double GetDamageScalar(Mobile target) { double scalar = 1.0; if (target == null) { return(scalar); } if (!Core.AOS) //EvalInt stuff for AoS is handled elsewhere { double casterEI = m_Caster.Skills[DamageSkill].Value; double targetRS = target.Skills[SkillName.MagicResist].Value; /* * if( Core.AOS ) * targetRS = 0; */ //m_Caster.CheckSkill( DamageSkill, 0.0, 120.0 ); if (casterEI > targetRS) { scalar = (1.0 + ((casterEI - targetRS) / 500.0)); } else { scalar = (1.0 + ((casterEI - targetRS) / 200.0)); } // magery damage bonus, -25% at 0 skill, +0% at 100 skill, +5% at 120 skill scalar += (m_Caster.Skills[CastSkill].Value - 100.0) / 400.0; if (!target.Player && !target.Body.IsHuman /*&& !Core.AOS*/) { scalar *= 2.0; // Double magery damage to monsters/animals if not AOS } } if (target is BaseCreature) { ((BaseCreature)target).AlterDamageScalarFrom(m_Caster, ref scalar); } if (m_Caster is BaseCreature) { ((BaseCreature)m_Caster).AlterDamageScalarTo(target, ref scalar); } if (Core.SE) { scalar *= GetSlayerDamageScalar(target); } target.Region.SpellDamageScalar(m_Caster, target, ref scalar); if (Evasion.CheckSpellEvasion(target)) //Only single target spells an be evaded { scalar = 0; } return(scalar); }
public void BushidoPower() { if (0.5 > Utility.RandomDouble() && !(Confidence.IsConfident(m_Mobile) || CounterAttack.IsCountering(m_Mobile) || Evasion.IsEvading(m_Mobile))) { UseBushidoStance(); } else { UseBushidoMove(); } }
string BuildEvasionCounts(Evasion evadedCounts) { return(string.Join(Environment.NewLine, evadedCounts.Counts.Select(pair => pair.Key + ": " + pair.Value))); }
protected override IEnumerable <MatcherData> CreateCollection() => new FormAndStatMatcherCollection(_modifierBuilder, ValueFactory) { // attributes // offense // - damage { @"adds # to # ({DamageTypeMatchers}) damage", BaseAdd, ValueFactory.FromMinAndMax(Values[0], Values[1]), Reference.AsDamageType.Damage.WithHits }, { @"adds # to # ({DamageTypeMatchers}) damage to unarmed attacks", BaseAdd, ValueFactory.FromMinAndMax(Values[0], Values[1]), Reference.AsDamageType.Damage.WithSkills(DamageSource.Attack), And(Not(MainHand.HasItem), With(Keyword.Melee)) }, { @"adds # to # ({DamageTypeMatchers}) damage to spells", BaseAdd, ValueFactory.FromMinAndMax(Values[0], Values[1]), Reference.AsDamageType.Damage.WithSkills(DamageSource.Spell) }, { @"# to # additional ({DamageTypeMatchers}) damage", BaseAdd, ValueFactory.FromMinAndMax(Values[0], Values[1]), Reference.AsDamageType.Damage.WithHits }, { @"adds # maximum ({DamageTypeMatchers}) damage", BaseAdd, Value.MaximumOnly, Reference.AsDamageType.Damage.WithHits }, { "deal no ({DamageTypeMatchers}) damage", TotalOverride, 0, Reference.AsDamageType.Damage }, // - conversion and gain { "(gain )?#% of ({DamageTypeMatchers}) damage (gained |added )?as (extra )?({DamageTypeMatchers}) damage", BaseAdd, Value, References[0].AsDamageType.Damage.WithHitsAndAilments .GainAs(References[1].AsDamageType.Damage.WithHitsAndAilments) }, { "gain #% of ({DamageTypeMatchers}) damage as extra damage of a random element", BaseAdd, Value, Reference.AsDamageType.Damage.WithHitsAndAilments .GainAs(RandomElement.Damage.WithHitsAndAilments) }, { "gain #% of wand ({DamageTypeMatchers}) damage as extra ({DamageTypeMatchers}) damage", BaseAdd, Value, References[0].AsDamageType.Damage.With(AttackDamageHand.MainHand) .GainAs(References[1].AsDamageType.Damage.With(AttackDamageHand.MainHand)) .WithCondition(MainHand.Has(Tags.Wand)), References[0].AsDamageType.Damage.With(AttackDamageHand.OffHand) .GainAs(References[1].AsDamageType.Damage.With(AttackDamageHand.OffHand)) .WithCondition(OffHand.Has(Tags.Wand)) }, { "#% of ({DamageTypeMatchers}) damage converted to ({DamageTypeMatchers}) damage", BaseAdd, Value, References[0].AsDamageType.Damage.WithHitsAndAilments .ConvertTo(References[1].AsDamageType.Damage.WithHitsAndAilments) }, // - penetration { "damage penetrates #% (of enemy )?({DamageTypeMatchers}) resistances?", BaseAdd, Value, Reference.AsDamageType.Penetration }, { "damage (?<inner>with .*|dealt by .*) penetrates #% ({DamageTypeMatchers}) resistances?", BaseAdd, Value, Reference.AsDamageType.Penetration, "${inner}" }, { "penetrate #% ({DamageTypeMatchers}) resistances?", BaseAdd, Value, Reference.AsDamageType.Penetration }, // - crit { @"\+#% critical strike chance", BaseAdd, Value, CriticalStrike.Chance }, { "no critical strike multiplier, no damage multiplier for ailments from critical strikes", TotalOverride, 0, CriticalStrike.Multiplier }, { "never deal critical strikes", TotalOverride, 0, CriticalStrike.Chance }, // - speed // - projectiles { "skills fire an additional projectile", BaseAdd, 1, Projectile.Count }, { "pierces # additional targets", BaseAdd, Value, Projectile.PierceCount }, { "projectiles pierce an additional target", BaseAdd, 1, Projectile.PierceCount }, { "projectiles pierce # (additional )?targets", BaseAdd, Value, Projectile.PierceCount }, { "projectiles pierce all nearby targets", TotalOverride, double.PositiveInfinity, Projectile.PierceCount, Enemy.IsNearby }, { @"skills chain \+# times", BaseAdd, Value, Projectile.ChainCount }, // - other { "your hits can't be evaded", TotalOverride, 100, Stat.ChanceToHit }, // defense // - life, mana, defences { "maximum life becomes #", TotalOverride, Value, Life }, { "removes all mana", TotalOverride, 0, Mana }, { "converts all evasion rating to armour", TotalOverride, 100, Evasion.ConvertTo(Armour) }, { "cannot evade enemy attacks", TotalOverride, 0, Evasion.Chance }, { @"\+# evasion rating", BaseAdd, Value, Evasion }, // - resistances { "immune to ({DamageTypeMatchers}) damage", TotalOverride, 100, Reference.AsDamageType.Resistance }, { @"\+#% elemental resistances", BaseAdd, Value, Elemental.Resistance }, { @"\+?#% physical damage reduction", BaseAdd, Value, Physical.Resistance }, // - leech { "life leech is applied to energy shield instead", TotalOverride, (int)Pool.EnergyShield, Life.Leech.TargetPool }, { "gain life from leech instantly", TotalOverride, 1, Life.InstantLeech }, { "leech #% of damage as life", BaseAdd, Value, Life.Leech.Of(Damage) }, // - block { "#% of block chance applied to spells", BaseAdd, Value.PercentOf(Block.AttackChance), Block.SpellChance }, // - other { "chaos damage does not bypass energy shield", TotalOverride, 100, Chaos.DamageTakenFrom(EnergyShield).Before(Life) }, { "#% of chaos damage does not bypass energy shield", BaseAdd, Value, Chaos.DamageTakenFrom(EnergyShield).Before(Life) }, { "#% of physical damage bypasses energy shield", BaseSubtract, Value, Physical.DamageTakenFrom(EnergyShield).Before(Life) }, { "you take #% reduced extra damage from critical strikes", PercentReduce, Value, CriticalStrike.ExtraDamageTaken }, { "you take no extra damage from critical strikes", PercentLess, 100, CriticalStrike.ExtraDamageTaken }, // regen and recharge // (need to be FormAndStatMatcher because they also exist with flat values) { "#%( of)? ({PoolStatMatchers}) regenerated per second", BaseAdd, Value, Reference.AsPoolStat.Regen.Percent }, { "#% of ({PoolStatMatchers}) and ({PoolStatMatchers}) regenerated per second", BaseAdd, Value, References[0].AsPoolStat.Regen.Percent, References[1].AsPoolStat.Regen.Percent }, { "regenerate #%( of)?( their| your)? ({PoolStatMatchers}) per second", BaseAdd, Value, Reference.AsPoolStat.Regen.Percent }, { "# ({PoolStatMatchers}) regenerated per second", BaseAdd, Value, Reference.AsPoolStat.Regen }, { "#% faster start of energy shield recharge", PercentIncrease, Value, EnergyShield.Recharge.Start }, { "life regeneration has no effect", PercentLess, 100, Life.Regen }, { "life regeneration is applied to energy shield instead", TotalOverride, (int)Pool.EnergyShield, Life.Regen.TargetPool }, // gain (need to be FormAndStatMatcher because they also exist with flat values) { "#% of ({PoolStatMatchers}) gained", BaseAdd, Value.PercentOf(Reference.AsStat), Reference.AsPoolStat.Gain }, { "recover #% of( their)? ({PoolStatMatchers})", BaseAdd, Value.PercentOf(Reference.AsStat), Reference.AsPoolStat.Gain }, { "removes #% of ({PoolStatMatchers})", BaseSubtract, Value.PercentOf(Reference.AsStat), Reference.AsPoolStat.Gain }, { @"\+# ({PoolStatMatchers}) gained", BaseAdd, Value, Reference.AsPoolStat.Gain }, { @"gain \+# ({PoolStatMatchers})", BaseAdd, Value, Reference.AsPoolStat.Gain }, // charges { "#% chance to gain a power, frenzy or endurance charge", BaseAdd, Value / 3, Charge.Power.ChanceToGain, Charge.Frenzy.ChanceToGain, Charge.Endurance.ChanceToGain }, { "(?<!chance to |when you )gain an? ({ChargeTypeMatchers})", BaseAdd, 100, Reference.AsChargeType.ChanceToGain }, // skills // traps, mines, totems { "detonating mines is instant", TotalOverride, double.PositiveInfinity, Stat.CastRate, With(Skills.DetonateMines) }, // minions // buffs { "(?<!while |chance to )you have ({BuffMatchers})", TotalOverride, 1, Reference.AsBuff.NotAsBuffOn(Self) }, { "(?<!while |chance to )gain ({BuffMatchers})", TotalOverride, 1, Reference.AsBuff.On(Self) }, { "you can have one additional curse", BaseAdd, 1, Buff.CurseLimit }, { "enemies can have # additional curse", BaseAdd, Value, Buff.CurseLimit.For(Enemy) }, { "unaffected by curses", PercentLess, 100, Buffs(targets: Self).With(Keyword.Curse).Effect }, { "grants fortify", TotalOverride, 1, Buff.Fortify.On(Self) }, { "gain elemental conflux", TotalOverride, 1, Buff.Conflux.Elemental.On(Self) }, // flags // ailments { "causes bleeding", TotalOverride, 100, Ailment.Bleed.Chance }, { "always poison", TotalOverride, 100, Ailment.Poison.Chance }, { "(you )?can afflict an additional ignite on an enemy", BaseAdd, 1, Ailment.Ignite.InstancesOn(Enemy).Maximum }, { "(you are )?immune to ({AilmentMatchers})", TotalOverride, 100, Reference.AsAilment.Avoidance }, { "cannot be ({AilmentMatchers})", TotalOverride, 100, Reference.AsAilment.Avoidance }, { "cannot be ({AilmentMatchers}) or ({AilmentMatchers})", TotalOverride, 100, References[0].AsAilment.Avoidance, References[1].AsAilment.Avoidance }, { "(immune to|cannot be affected by) elemental ailments", TotalOverride, 100, Ailment.Elemental.Select(a => a.Avoidance) }, { "poison you inflict with critical strikes deals #% more damage", PercentMore, Value, CriticalStrike.Multiplier.With(Ailment.Poison) }, // stun { "(you )?cannot be stunned", TotalOverride, 100, Effect.Stun.Avoidance }, // item quantity/quality // range and area of effect // other { "knocks back enemies", TotalOverride, 100, Effect.Knockback.Chance }, };
public static void Damage(Spell spell, TimeSpan delay, Mobile target, Mobile from, double damage, int phys, int fire, int cold, int pois, int nrgy, DFAlgorithm dfa) { int iDamage = (int)damage; #region Taran: Spell damage is based on armor difference /* * double newDamage = damage; * * if (target is PlayerMobile && from is PlayerMobile) * { * World.Broadcast(25, true, "Old damage is: " + iDamage); * * double defar = ((PlayerMobile) target).BaseArmorRatingSpells; * double attar = ((PlayerMobile) from).BaseArmorRatingSpells; * * double diff = defar - attar; * * World.Broadcast(50, true, "Difference in armor rating is: " + diff); * * if (defar - attar > 0) * newDamage = iDamage*(1.00 - ((diff * 1.6) / 100)); * * if (newDamage / iDamage < 0.6) //Don't deal less than 60% damage * newDamage = iDamage*0.6; * * iDamage = (int) newDamage; * * World.Broadcast(25, true, "New damage is: " + iDamage); * } */ #endregion //Maka); if (target == from && from.MagicDamageAbsorb > 0) { from.FixedEffect(0x37B9, 10, 5); from.MagicDamageAbsorb = 0; iDamage = 0; DefensiveSpell.Nullify(from); } if (Evasion.CheckSpellEvasion(target)) { iDamage = 0; } if (delay == TimeSpan.Zero) { if (from is BaseCreature) { ((BaseCreature)from).AlterSpellDamageTo(target, ref iDamage); } if (target is BaseCreature) { ((BaseCreature)target).AlterSpellDamageFrom(from, ref iDamage); } //WeightOverloading.DFA = dfa; int damageGiven = AOS.Damage(target, from, iDamage, phys, fire, cold, pois, nrgy); DoLeech(damageGiven, from, target); //WeightOverloading.DFA = DFAlgorithm.Standard; } else { new SpellDamageTimerAOS(spell, target, from, iDamage, phys, fire, cold, pois, nrgy, delay, dfa).Start(); } if (target is BaseCreature && from != null && delay == TimeSpan.Zero) { BaseCreature c = (BaseCreature)target; c.OnHarmfulSpell(from); c.OnDamagedBySpell(from); } }
private DataDrivenMechanicCollection CreateCollection() => new DataDrivenMechanicCollection(_modifierBuilder, BuilderFactories) { // skill hit damage // - DPS { TotalOverride, _stat.SkillDpsWithHits, _stat.AverageHitDamage.Value *_stat.CastRate.Value }, // - average damage { TotalOverride, _stat.AverageHitDamage, CombineSource(_stat.AverageDamage.WithHits, CombineHandsByAverage) }, // - average damage per source { TotalOverride, _stat.AverageDamage.WithHits.With(AttackDamageHand.MainHand), _stat.AverageDamagePerHit.With(AttackDamageHand.MainHand).Value * Stat.ChanceToHit.With(AttackDamageHand.MainHand).Value.AsPercentage }, { TotalOverride, _stat.AverageDamage.WithHits.With(AttackDamageHand.OffHand), _stat.AverageDamagePerHit.With(AttackDamageHand.OffHand).Value * Stat.ChanceToHit.With(AttackDamageHand.OffHand).Value.AsPercentage }, { TotalOverride, _stat.AverageDamage.WithHits.With(DamageSource.Spell), _stat.AverageDamagePerHit.With(DamageSource.Spell).Value }, { TotalOverride, _stat.AverageDamage.WithHits.With(DamageSource.Secondary), _stat.AverageDamagePerHit.With(DamageSource.Secondary).Value }, // - average damage of a successful hit per source { TotalOverride, _stat.AverageDamagePerHit, _stat.DamageWithNonCrits().WithHits, _stat.DamageWithCrits().WithHits, _stat.EffectiveCritChance, (nonCritDamage, critDamage, critChance) => nonCritDamage.Value.Average * (1 - critChance.Value) + critDamage.Value.Average * critChance.Value }, // - crit/non-crit damage per source and type { TotalOverride, dt => _stat.DamageWithNonCrits(dt).WithHits, dt => _stat.Damage(dt).WithHits, dt => _stat.EffectiveDamageMultiplierWithNonCrits(dt).WithHits, (_, damage, mult) => damage.Value * mult.Value }, { TotalOverride, dt => _stat.DamageWithCrits(dt).WithHits, dt => _stat.Damage(dt).WithHits, dt => _stat.EffectiveDamageMultiplierWithCrits(dt).WithHits, (_, damage, mult) => damage.Value * mult.Value }, // - effective crit/non-crit damage multiplier per source and type { TotalOverride, dt => _stat.EffectiveDamageMultiplierWithNonCrits(dt).WithHits, dt => _stat.EnemyResistanceAgainstNonCrits(dt), dt => DamageTaken(dt).WithHits.For(Enemy), (_, resistance, damageTaken) => DamageTakenMultiplier(resistance, damageTaken) }, { TotalOverride, dt => _stat.EffectiveDamageMultiplierWithCrits(dt).WithHits, dt => _stat.EnemyResistanceAgainstCrits(dt), dt => DamageTaken(dt).WithHits.For(Enemy), _ => CriticalStrike.Multiplier.WithHits, (_, resistance, damageTaken, mult) => DamageTakenMultiplier(resistance, damageTaken) * mult.Value.AsPercentage }, // - enemy resistance against crit/non-crit hits per source and type { TotalOverride, dt => _stat.EnemyResistanceAgainstNonCrits(dt), dt => DamageTypeBuilders.From(dt).IgnoreResistanceWithNonCrits, dt => DamageTypeBuilders.From(dt).PenetrationWithNonCrits, (dt, ignoreResistance, penetration) => ValueFactory.If(ignoreResistance.IsSet).Then(0) .Else(DamageTypeBuilders.From(dt).Resistance.For(Enemy).Value - penetration.Value) }, { TotalOverride, dt => _stat.EnemyResistanceAgainstCrits(dt), dt => DamageTypeBuilders.From(dt).IgnoreResistanceWithCrits, dt => DamageTypeBuilders.From(dt).PenetrationWithCrits, (dt, ignoreResistance, penetration) => ValueFactory.If(ignoreResistance.Value.Eq(1)).Then(0) .Else(DamageTypeBuilders.From(dt).Resistance.For(Enemy).Value - penetration.Value) }, // skill damage over time // - DPS = average damage = non-crit damage { TotalOverride, _stat.SkillDpsWithDoTs, _stat.AverageDamage.WithSkills(DamageSource.OverTime).Value }, { TotalOverride, _stat.AverageDamage.WithSkills(DamageSource.OverTime), _stat.DamageWithNonCrits().WithSkills(DamageSource.OverTime).Value }, // - damage per type { TotalOverride, dt => _stat.DamageWithNonCrits(dt).WithSkills(DamageSource.OverTime), dt => _stat.Damage(dt).WithSkills(DamageSource.OverTime).Value * _stat.EffectiveDamageMultiplierWithNonCrits(dt).WithSkills(DamageSource.OverTime).Value }, // - effective damage multiplier per type { TotalOverride, dt => _stat.EffectiveDamageMultiplierWithNonCrits(dt).WithSkills(DamageSource.OverTime), dt => EnemyDamageTakenMultiplier(DamageTypeBuilders.From(dt), DamageTaken(dt).WithSkills(DamageSource.OverTime)) }, // ignite damage // - average damage { TotalOverride, _stat.AverageAilmentDamage(Common.Builders.Effects.Ailment.Ignite), CombineSource(_stat.AverageDamage.With(Ailment.Ignite), CombineHandsForAverageAilmentDamage(Common.Builders.Effects.Ailment.Ignite)) }, // - effective crit/non-crit damage multiplier per source and type { TotalOverride, dt => _stat.EffectiveDamageMultiplierWithNonCrits(dt).With(Ailment.Ignite), _ => Fire.Damage.Taken.With(Ailment.Ignite), (_, damageTaken) => EnemyDamageTakenMultiplier(Fire, damageTaken) }, { TotalOverride, dt => _stat.EffectiveDamageMultiplierWithCrits(dt).With(Ailment.Ignite), _ => Fire.Damage.Taken.With(Ailment.Ignite), _ => CriticalStrike.Multiplier.With(Ailment.Ignite), (_, damageTaken, mult) => EnemyDamageTakenMultiplier(Fire, damageTaken) * mult.Value.AsPercentage }, // bleed damage // - average damage { TotalOverride, _stat.AverageAilmentDamage(Common.Builders.Effects.Ailment.Bleed), CombineHandsForAverageAilmentDamage(Common.Builders.Effects.Ailment.Bleed) (_stat.AverageDamage.With(Ailment.Bleed)) }, // - effective crit/non-crit damage multiplier per source and type { TotalOverride, dt => _stat.EffectiveDamageMultiplierWithNonCrits(dt).With(Ailment.Bleed), _ => Physical.Damage.Taken.With(Ailment.Bleed), (_, damageTaken) => EnemyDamageTakenMultiplier(Physical, damageTaken) }, { TotalOverride, dt => _stat.EffectiveDamageMultiplierWithCrits(dt).With(Ailment.Bleed), _ => Physical.Damage.Taken.With(Ailment.Bleed), _ => CriticalStrike.Multiplier.With(Ailment.Bleed), (_, damageTaken, mult) => EnemyDamageTakenMultiplier(Physical, damageTaken) * mult.Value.AsPercentage }, // poison damage // - average damage { TotalOverride, _stat.AverageAilmentDamage(Common.Builders.Effects.Ailment.Poison), CombineSource(_stat.AverageDamage.With(Ailment.Poison), CombineHandsForAverageAilmentDamage(Common.Builders.Effects.Ailment.Poison)) }, // - effective crit/non-crit damage multiplier per source and type { TotalOverride, dt => _stat.EffectiveDamageMultiplierWithNonCrits(dt).With(Ailment.Poison), _ => Chaos.Damage.Taken.With(Ailment.Poison), (_, damageTaken) => EnemyDamageTakenMultiplier(Chaos, damageTaken) }, { TotalOverride, dt => _stat.EffectiveDamageMultiplierWithCrits(dt).With(Ailment.Poison), _ => Chaos.Damage.Taken.With(Ailment.Poison), _ => CriticalStrike.Multiplier.With(Ailment.Poison), (_, damageTaken, mult) => EnemyDamageTakenMultiplier(Chaos, damageTaken) * mult.Value.AsPercentage }, // shared ailment damage // - DPS { TotalOverride, _stat.AilmentDps, ailment => _stat.AverageAilmentDamage(ailment).Value * _stat.AilmentEffectiveInstances(ailment).Value }, // - lifetime damage of one instance { TotalOverride, _stat.AilmentInstanceLifetimeDamage, ailment => _stat.AverageAilmentDamage(ailment).Value *Ailment.From(ailment).Duration.Value }, // - average damage per source { TotalOverride, ailment => _stat.AverageDamage.With(Ailment.From(ailment)), ailment => _stat.DamageWithNonCrits().With(Ailment.From(ailment)), ailment => _stat.DamageWithCrits().With(Ailment.From(ailment)), _ => _stat.EffectiveCritChance, ailment => Ailment.From(ailment).Chance, ailment => _stat.AilmentChanceWithCrits(ailment), AverageAilmentDamageFromCritAndNonCrit }, // - crit/non-crit damage per source and type { TotalOverride, (a, dt) => _stat.DamageWithNonCrits(dt).With(Ailment.From(a)), (a, dt) => _stat.Damage(dt).With(Ailment.From(a)), (a, dt) => _stat.EffectiveDamageMultiplierWithNonCrits(dt).With(Ailment.From(a)), (damage, mult) => damage.Value * mult.Value }, { TotalOverride, (a, dt) => _stat.DamageWithCrits(dt).With(Ailment.From(a)), (a, dt) => _stat.Damage(dt).With(Ailment.From(a)), (a, dt) => _stat.EffectiveDamageMultiplierWithCrits(dt).With(Ailment.From(a)), (damage, mult) => damage.Value * mult.Value }, // speed { TotalOverride, _stat.CastRate, CombineSource(Stat.CastRate, CombineHandsByAverage) }, { TotalOverride, _stat.CastTime, _stat.CastRate.Value.Invert }, { PercentMore, Stat.MovementSpeed, ActionSpeedValueForPercentMore }, { PercentMore, Stat.CastRate, ActionSpeedValueForPercentMore, Not(Or(With(Keyword.Totem), With(Keyword.Trap), With(Keyword.Mine))) }, { PercentMore, Stat.Totem.Speed, ActionSpeedValueForPercentMore }, { PercentMore, Stat.Trap.Speed, ActionSpeedValueForPercentMore }, { PercentMore, Stat.Mine.Speed, ActionSpeedValueForPercentMore }, // resistances/damage reduction { BaseSet, _stat.ResistanceAgainstHits(DamageType.Physical), Physical.Resistance.Value }, { BaseAdd, _stat.ResistanceAgainstHits(DamageType.Physical), 100 * Armour.Value / (Armour.Value + 10 * Physical.Damage.WithSkills.With(AttackDamageHand.MainHand).For(Enemy).Value) }, { BaseSet, _stat.ResistanceAgainstHits(DamageType.Physical).Maximum, 90 }, { TotalOverride, _stat.ResistanceAgainstHits(DamageType.Lightning), Lightning.Resistance.Value }, { TotalOverride, _stat.ResistanceAgainstHits(DamageType.Cold), Cold.Resistance.Value }, { TotalOverride, _stat.ResistanceAgainstHits(DamageType.Fire), Fire.Resistance.Value }, { TotalOverride, _stat.ResistanceAgainstHits(DamageType.Chaos), Chaos.Resistance.Value }, // damage mitigation (1 - (1 - resistance / 100) * damage taken) { TotalOverride, _stat.MitigationAgainstHits, dt => 1 - DamageTakenMultiplier(_stat.ResistanceAgainstHits(dt), DamageTaken(dt).WithSkills(DamageSource.Secondary)) }, { TotalOverride, _stat.MitigationAgainstDoTs, dt => 1 - DamageTakenMultiplier(DamageTypeBuilders.From(dt).Resistance, DamageTaken(dt).WithSkills(DamageSource.OverTime)) }, // chance to hit/evade { BaseSet, Evasion.Chance, 100 - ChanceToHitValue(Stat.Accuracy.With(AttackDamageHand.MainHand).For(Enemy), Evasion, Buff.Blind.IsOn(Enemy)) }, { BaseSet, Stat.ChanceToHit.With(AttackDamageHand.MainHand), ChanceToHitValue(Stat.Accuracy.With(AttackDamageHand.MainHand), Evasion.For(Enemy), Buff.Blind.IsOn(Self)) }, { BaseSet, Stat.ChanceToHit.With(AttackDamageHand.OffHand), ChanceToHitValue(Stat.Accuracy.With(AttackDamageHand.OffHand), Evasion.For(Enemy), Buff.Blind.IsOn(Self)) }, // chance to avoid { TotalOverride, _stat.ChanceToAvoidMeleeAttacks, 100 - 100 * (FailureProbability(Evasion.ChanceAgainstMeleeAttacks) * FailureProbability(Stat.Dodge.AttackChance) * FailureProbability(Block.AttackChance)) }, { TotalOverride, _stat.ChanceToAvoidProjectileAttacks, 100 - 100 * (FailureProbability(Evasion.ChanceAgainstProjectileAttacks) * FailureProbability(Stat.Dodge.AttackChance) * FailureProbability(Block.AttackChance)) }, { TotalOverride, _stat.ChanceToAvoidSpells, 100 - 100 * (FailureProbability(Stat.Dodge.SpellChance) * FailureProbability(Block.SpellChance)) }, // crit { TotalOverride, _stat.EffectiveCritChance.With(AttackDamageHand.MainHand), CriticalStrike.Chance.With(AttackDamageHand.MainHand).Value.AsPercentage * Stat.ChanceToHit.With(AttackDamageHand.MainHand).Value.AsPercentage }, { TotalOverride, _stat.EffectiveCritChance.With(AttackDamageHand.OffHand), CriticalStrike.Chance.With(AttackDamageHand.OffHand).Value.AsPercentage * Stat.ChanceToHit.With(AttackDamageHand.OffHand).Value.AsPercentage }, { TotalOverride, _stat.EffectiveCritChance.With(DamageSource.Spell), CriticalStrike.Chance.With(DamageSource.Spell).Value.AsPercentage }, { TotalOverride, _stat.EffectiveCritChance.With(DamageSource.Secondary), CriticalStrike.Chance.With(DamageSource.Secondary).Value.AsPercentage }, // pools { BaseAdd, p => p.Regen, p => _stat.RegenTargetPoolValue(p.BuildPool()) * p.Regen.Percent.Value.AsPercentage }, { TotalOverride, _stat.EffectiveRegen, p => p.Regen.Value * p.RecoveryRate.Value }, { TotalOverride, _stat.EffectiveRecharge, p => p.Recharge.Value * p.RecoveryRate.Value }, { TotalOverride, _stat.RechargeStartDelay, p => 2 / p.Recharge.Start.Value }, { TotalOverride, _stat.EffectiveLeechRate, p => p.Leech.Rate.Value * p.RecoveryRate.Value }, { TotalOverride, _stat.AbsoluteLeechRate, p => _stat.LeechTargetPoolValue(p) * _stat.EffectiveLeechRate(p).Value.AsPercentage }, { TotalOverride, _stat.AbsoluteLeechRateLimit, p => _stat.LeechTargetPoolValue(p.BuildPool()) * p.Leech.RateLimit.Value.AsPercentage }, { TotalOverride, _stat.TimeToReachLeechRateLimit, p => p.Leech.RateLimit.Value / p.Leech.Rate.Value / _stat.CastRate.Value }, // flasks { PercentMore, Flask.LifeRecovery, Flask.Effect.Value * 100 }, { PercentMore, Flask.ManaRecovery, Flask.Effect.Value * 100 }, { PercentMore, Flask.LifeRecovery, Flask.RecoverySpeed.Value * 100 }, { PercentMore, Flask.ManaRecovery, Flask.RecoverySpeed.Value * 100 }, { PercentMore, Flask.Duration, (100 / Flask.RecoverySpeed.Value) - 100 }, // ailments { TotalOverride, _stat.AilmentDealtDamageType(Common.Builders.Effects.Ailment.Ignite), (int)DamageType.Fire }, { TotalOverride, _stat.AilmentDealtDamageType(Common.Builders.Effects.Ailment.Bleed), (int)DamageType.Physical }, { TotalOverride, _stat.AilmentDealtDamageType(Common.Builders.Effects.Ailment.Ignite), (int)DamageType.Chaos }, { TotalOverride, _stat.AilmentCombinedEffectiveChance, ailment => CombineSource(_stat.AilmentEffectiveChance(ailment), CombineHandsByAverage) }, { TotalOverride, _stat.AilmentEffectiveChance, ailment => Ailment.From(ailment).Chance, ailment => _stat.AilmentChanceWithCrits(ailment), _ => _stat.EffectiveCritChance, (ailment, ailmentChance, ailmentChanceWithCrits, critChance) => (ailmentChance.Value.AsPercentage * (1 - critChance.Value) + ailmentChanceWithCrits.Value.AsPercentage * critChance.Value) * (1 - Ailment.From(ailment).Avoidance.For(Enemy).Value.AsPercentage) }, { TotalOverride, _stat.AilmentChanceWithCrits, ailment => Ailment.From(ailment).Chance, (ailment, ailmentChance) => ValueFactory .If(Ailment.From(ailment).CriticalStrikesAlwaysInflict.IsSet).Then(100) .Else(ailmentChance.Value) }, { TotalOverride, Ailment.Chill.On(Self), 1, Ailment.Freeze.IsOn(Self) }, { PercentIncrease, Ailment.Shock.AddStat(Damage.Taken), _stat.IncreasedDamageTakenFromShocks.Value }, { BaseSet, _stat.IncreasedDamageTakenFromShocks, 20 }, { TotalOverride, _stat.IncreasedDamageTakenFromShocks.Maximum, 50 }, { TotalOverride, _stat.IncreasedDamageTakenFromShocks.Minimum, 1 }, { PercentReduce, Ailment.Chill.AddStat(Stat.ActionSpeed), _stat.ReducedActionSpeedFromChill.Value }, { BaseSet, _stat.ReducedActionSpeedFromChill, 10 }, { TotalOverride, _stat.ReducedActionSpeedFromChill.Maximum, 30 }, { TotalOverride, _stat.ReducedActionSpeedFromChill.Minimum, 1 }, // - AilmentEffectiveInstances { TotalOverride, _stat.AilmentEffectiveInstances(Common.Builders.Effects.Ailment.Ignite), Ailment.Ignite.InstancesOn(Enemy).Maximum.Value }, { TotalOverride, _stat.AilmentEffectiveInstances(Common.Builders.Effects.Ailment.Bleed), Ailment.Bleed.InstancesOn(Enemy).Maximum.Value }, { TotalOverride, _stat.AilmentEffectiveInstances(Common.Builders.Effects.Ailment.Poison), Ailment.Poison.Duration.Value *_stat.CastRate.Value * CombineSource(_stat.AilmentEffectiveChance(Common.Builders.Effects.Ailment.Poison), s => CombineByWeightedAverage( s.With(AttackDamageHand.MainHand).Value * Stat.ChanceToHit.With(AttackDamageHand.MainHand).Value.AsPercentage, SkillUsesHandAsMultiplier(AttackDamageHand.MainHand), s.With(AttackDamageHand.OffHand).Value * Stat.ChanceToHit.With(AttackDamageHand.OffHand).Value.AsPercentage, SkillUsesHandAsMultiplier(AttackDamageHand.OffHand))) }, // stun (see https://pathofexile.gamepedia.com/Stun) { PercentLess, Effect.Stun.Duration, Effect.Stun.Recovery.For(Enemy).Value * 100 }, { TotalOverride, _stat.EffectiveStunThreshold, Effect.Stun.Threshold, EffectiveStunThresholdValue }, { BaseSet, Effect.Stun.Chance, _stat.AverageDamage.WithHits, _stat.EffectiveStunThreshold, (damage, threshold) => 200 * damage.Value / (Life.For(Enemy).ValueFor(NodeType.Subtotal) * threshold.Value) }, { TotalOverride, _stat.StunAvoidanceWhileCasting, 1 - (1 - Effect.Stun.Avoidance.Value) * (1 - Effect.Stun.ChanceToAvoidInterruptionWhileCasting.Value) }, // radius { PercentMore, Stat.Radius, Stat.AreaOfEffect.Value.Select(Math.Sqrt, v => $"Sqrt({v})") }, };
protected override IReadOnlyList <MatcherData> CreateCollection() => new FormAndStatMatcherCollection(_modifierBuilder, ValueFactory) { // attributes // offense // - damage { @"adds # to # ({DamageTypeMatchers}) damage", BaseAdd, ValueFactory.FromMinAndMax(Values[0], Values[1]), Reference.AsDamageType.Damage.WithHits }, { @"# to # added ({DamageTypeMatchers}) damage", BaseAdd, ValueFactory.FromMinAndMax(Values[0], Values[1]), Reference.AsDamageType.Damage.WithHits }, { "# to # ({DamageTypeMatchers}) damage", BaseAdd, ValueFactory.FromMinAndMax(Values[0], Values[1]), Reference.AsDamageType.Damage.WithHits }, { @"adds # to # ({DamageTypeMatchers}) damage to attacks", BaseAdd, ValueFactory.FromMinAndMax(Values[0], Values[1]), Reference.AsDamageType.Damage.WithSkills(DamageSource.Attack) }, { @"adds # to # ({DamageTypeMatchers}) damage to unarmed attacks", BaseAdd, ValueFactory.FromMinAndMax(Values[0], Values[1]), Reference.AsDamageType.Damage.WithSkills(DamageSource.Attack).With(Keyword.Melee), Not(MainHand.HasItem) }, { @"adds # to # ({DamageTypeMatchers}) damage to bow attacks", BaseAdd, ValueFactory.FromMinAndMax(Values[0], Values[1]), Reference.AsDamageType.Damage.WithSkills(DamageSource.Attack).With(Keyword.Melee), MainHand.Has(Tags.Bow) }, { @"adds # to # ({DamageTypeMatchers}) damage to spells", BaseAdd, ValueFactory.FromMinAndMax(Values[0], Values[1]), Reference.AsDamageType.Damage.WithSkills(DamageSource.Spell) }, { @"adds # to # ({DamageTypeMatchers}) damage to spells and attacks", BaseAdd, ValueFactory.FromMinAndMax(Values[0], Values[1]), Reference.AsDamageType.Damage.WithSkills(DamageSource.Spell), Reference.AsDamageType.Damage.WithSkills(DamageSource.Attack) }, { @"# to # additional ({DamageTypeMatchers}) damage", BaseAdd, ValueFactory.FromMinAndMax(Values[0], Values[1]), Reference.AsDamageType.Damage.WithHits }, { @"adds # maximum ({DamageTypeMatchers}) damage", BaseAdd, Value.MaximumOnly, Reference.AsDamageType.Damage.WithHits }, { "deal no ({DamageTypeMatchers}) damage", TotalOverride, 0, Reference.AsDamageType.Damage }, { @"explosion deals (base )?({DamageTypeMatchers}) damage equal to #% of the (corpse|monster)'s maximum life", BaseSet, Value.AsPercentage *Life.For(Enemy).Value, Reference.AsDamageType.Damage.WithSkills(DamageSource.Secondary) }, { @"explosion deals # to # base ({DamageTypeMatchers}) damage", BaseSet, ValueFactory.FromMinAndMax(Values[0], Values[1]), Reference.AsDamageType.Damage.WithSkills(DamageSource.Secondary) }, { "deals # to # base ({DamageTypeMatchers}) damage", BaseSet, ValueFactory.FromMinAndMax(Values[0], Values[1]), Reference.AsDamageType.Damage.WithSkills(DamageSource.Spell) }, // - damage taken { "cold damage taken increased by chill effect", PercentIncrease, 100 * (Ailment.ChillEffect.Value - 1), Cold.Damage.Taken.With(DamageSource.OverTime) }, // - damage taken as // - conversion and gain { "(gain )?#% of ({DamageTypeMatchers}) damage (gained |added )?as (extra )?({DamageTypeMatchers}) damage", BaseAdd, Value, References[0].AsDamageType.Damage.WithHitsAndAilments .GainAs(References[1].AsDamageType.Damage.WithHitsAndAilments) }, { "gain #% of ({DamageTypeMatchers}) damage as extra damage of a random element", BaseAdd, Value, Reference.AsDamageType.Damage.WithHitsAndAilments .GainAs(RandomElement.Damage.WithHitsAndAilments) }, { "gain #% of wand ({DamageTypeMatchers}) damage as extra ({DamageTypeMatchers}) damage", BaseAdd, Value, References[0].AsDamageType.Damage.With(AttackDamageHand.MainHand) .GainAs(References[1].AsDamageType.Damage.With(AttackDamageHand.MainHand)) .WithCondition(MainHand.Has(Tags.Wand)), References[0].AsDamageType.Damage.With(AttackDamageHand.OffHand) .GainAs(References[1].AsDamageType.Damage.With(AttackDamageHand.OffHand)) .WithCondition(OffHand.Has(Tags.Wand)) }, { "#% of ({DamageTypeMatchers}) damage converted to ({DamageTypeMatchers}) damage", BaseAdd, Value, References[0].AsDamageType.Damage.WithHitsAndAilments .ConvertTo(References[1].AsDamageType.Damage.WithHitsAndAilments) }, { "({DamageTypeMatchers}) spells have #% of ({DamageTypeMatchers}) damage converted to ({DamageTypeMatchers}) damage", BaseAdd, Value, References[1].AsDamageType.Damage.With(DamageSource.Spell) .ConvertTo(References[2].AsDamageType.Damage.With(DamageSource.Spell)), With(References[0].AsDamageType) }, // - penetration { "damage penetrates #% (of enemy )?({DamageTypeMatchers}) resistances?", BaseAdd, Value, Reference.AsDamageType.Penetration }, { "damage (?<inner>with .*|dealt by .*) penetrates #% ({DamageTypeMatchers}) resistances?", BaseAdd, Value, Reference.AsDamageType.Penetration, "${inner}" }, { "penetrates? #% ({DamageTypeMatchers}) resistances?", BaseAdd, Value, Reference.AsDamageType.Penetration }, { "({KeywordMatchers}) damage penetrates #% ({DamageTypeMatchers}) resistances?", BaseAdd, Value, References[1].AsDamageType.Penetration.With(References[0].AsKeyword) }, { "({KeywordMatchers}) damage (?<inner>with .*|dealt by .*) penetrates #% ({DamageTypeMatchers}) resistances?", BaseAdd, Value, References[1].AsDamageType.Penetration.With(References[1].AsKeyword), "${inner}" }, { "hits ignore enemy monster ({DamageTypeMatchers}) resistance", TotalOverride, 1, Reference.AsDamageType.IgnoreResistance }, // - exposure { @"(?<damageType>({DamageTypeMatchers})) exposure applies #% to \k<damageType> resistance", BaseSet, Value, Reference.AsDamageType.Exposure }, // - crit { @"\+#% critical strike chance", BaseAdd, Value, CriticalStrike.Chance }, { "no critical strike multiplier, no damage multiplier for ailments from critical strikes", TotalOverride, 0, CriticalStrike.Multiplier }, { "never deal critical strikes", TotalOverride, 0, CriticalStrike.Chance }, // - speed { "actions are #% slower", PercentLess, Value, Stat.ActionSpeed }, { @"\+# seconds to attack time", BaseAdd, Value, Stat.BaseCastTime.With(DamageSource.Attack) }, // - projectiles { "fires # additional projectiles", BaseAdd, Value, Projectile.Count }, { "fires # additional arrows", BaseAdd, Value, Projectile.Count, With(Keyword.Attack) }, { "fires an additional projectile", BaseAdd, 1, Projectile.Count }, { "fires an additional arrow", BaseAdd, 1, Projectile.Count, With(Keyword.Attack) }, { "skills fire an additional projectile", BaseAdd, 1, Projectile.Count }, { "skills fire # additional projectiles", BaseAdd, Value, Projectile.Count }, { "supported skills fire # additional projectiles", BaseAdd, Value, Projectile.Count }, { "pierces # additional targets", BaseAdd, Value, Projectile.PierceCount }, { "projectiles pierce an additional target", BaseAdd, 1, Projectile.PierceCount }, { "arrows pierce an additional target", BaseAdd, 1, Projectile.PierceCount, With(Keyword.Attack) }, { "projectiles pierce # (additional )?targets", BaseAdd, Value, Projectile.PierceCount }, { "arrows pierce # (additional )?targets", BaseAdd, Value, Projectile.PierceCount, With(Keyword.Attack) }, { "projectiles from supported skills pierce # additional targets", BaseAdd, Value, Projectile.PierceCount }, { "projectiles pierce all nearby targets", TotalOverride, double.PositiveInfinity, Projectile.PierceCount, Enemy.IsNearby }, { "projectiles pierce all targets", TotalOverride, double.PositiveInfinity, Projectile.PierceCount }, { @"chains \+# times", BaseAdd, Value, Projectile.ChainCount }, { @"(supported )?skills chain \+# times", BaseAdd, Value, Projectile.ChainCount }, // - other { "your hits can't be evaded", TotalOverride, 100, Stat.ChanceToHit }, { "can't be evaded", TotalOverride, 100, Stat.ChanceToHit }, // defense // - life, mana, defences { "maximum life becomes #", TotalOverride, Value, Life }, { "removes all mana", TotalOverride, 0, Mana }, { "converts all evasion rating to armour", TotalOverride, 100, Evasion.ConvertTo(Armour) }, { "cannot evade enemy attacks", TotalOverride, 0, Evasion.Chance }, { @"\+# evasion rating", BaseAdd, Value, Evasion }, // - resistances { "immune to ({DamageTypeMatchers}) damage", TotalOverride, 100, Reference.AsDamageType.Resistance }, { @"\+#% elemental resistances", BaseAdd, Value, Elemental.Resistance }, { @"\+?#% physical damage reduction", BaseAdd, Value, Physical.Resistance }, // - leech { "leech energy shield instead of life", TotalOverride, 100, Life.Leech.Of(Damage).ConvertTo(EnergyShield.Leech.Of(Damage)) }, { "gain life from leech instantly", TotalOverride, 1, Life.Leech.IsInstant }, { "leech #% of damage as life", BaseAdd, Value, Life.Leech.Of(Damage) }, { "cannot leech mana", TotalOverride, 0, Mana.Leech.Of(Damage) }, // - block { "#% of block chance applied to spells", BaseAdd, Value.PercentOf(Block.AttackChance), Block.SpellChance }, // - other { "chaos damage does not bypass energy shield", TotalOverride, 100, Chaos.DamageTakenFrom(EnergyShield).Before(Life) }, { "#% of chaos damage does not bypass energy shield", BaseAdd, Value, Chaos.DamageTakenFrom(EnergyShield).Before(Life) }, { "#% of physical damage bypasses energy shield", BaseSubtract, Value, Physical.DamageTakenFrom(EnergyShield).Before(Life) }, { "you take #% reduced extra damage from critical strikes", PercentReduce, Value, CriticalStrike.ExtraDamageTaken }, { "you take no extra damage from critical strikes", PercentLess, 100, CriticalStrike.ExtraDamageTaken }, // regen and recharge // (need to be FormAndStatMatcher because they also exist with flat values) { "#%( of)? ({PoolStatMatchers}) regenerated per second", BaseAdd, Value, Reference.AsPoolStat.Regen.Percent }, { "#% of ({PoolStatMatchers}) and ({PoolStatMatchers}) regenerated per second", BaseAdd, Value, References[0].AsPoolStat.Regen.Percent, References[1].AsPoolStat.Regen.Percent }, { "regenerate #%( of)?( their| your)? ({PoolStatMatchers}) per second", BaseAdd, Value, Reference.AsPoolStat.Regen.Percent }, { "# ({PoolStatMatchers}) regenerated per second", BaseAdd, Value, Reference.AsPoolStat.Regen }, { "#% faster start of energy shield recharge", PercentIncrease, Value, EnergyShield.Recharge.Start }, { "life regeneration has no effect", PercentLess, 100, Life.Regen }, { "life regeneration is applied to energy shield instead", TotalOverride, (int)Pool.EnergyShield, Life.Regen.TargetPool }, // gain (need to be FormAndStatMatcher because they also exist with flat values) { "#% of ({PoolStatMatchers}) gained", BaseAdd, Value.PercentOf(Reference.AsStat), Reference.AsPoolStat.Gain }, { "recover #% of( their)? ({PoolStatMatchers})", BaseAdd, Value.PercentOf(Reference.AsStat), Reference.AsPoolStat.Gain }, { "removes #% of ({PoolStatMatchers})", BaseSubtract, Value.PercentOf(Reference.AsStat), Reference.AsPoolStat.Gain }, { @"\+# ({PoolStatMatchers}) gained", BaseAdd, Value, Reference.AsPoolStat.Gain }, { @"gain \+# ({PoolStatMatchers})", BaseAdd, Value, Reference.AsPoolStat.Gain }, { "replenishes energy shield by #% of armour", BaseAdd, Value.PercentOf(Armour), EnergyShield.Gain }, { "recover ({PoolStatMatchers}) equal to #% of your evasion rating", BaseAdd, Value.PercentOf(Evasion), Reference.AsPoolStat.Gain }, // charges { "#% chance to gain a power, frenzy or endurance charge", BaseAdd, Value / 3, Charge.Power.ChanceToGain, Charge.Frenzy.ChanceToGain, Charge.Endurance.ChanceToGain }, { "(?<!chance to |when you )gain an? ({ChargeTypeMatchers})", BaseAdd, 100, Reference.AsChargeType.ChanceToGain }, { "(?<!chance to |when you )gain a power or frenzy charge", BaseAdd, 50, Charge.Power.ChanceToGain, Charge.Frenzy.ChanceToGain }, // skills { "base duration is # seconds", BaseSet, Value, Stat.Duration }, { @"\+# seconds to base duration", BaseAdd, Value, Stat.Duration }, { "base secondary duration is # seconds", BaseSet, Value, Stat.SecondaryDuration }, { "#% increased duration(?! of)", PercentIncrease, Value, ApplyOnce(Stat.Duration, Stat.SecondaryDuration) }, { "#% reduced duration(?! of)", PercentReduce, Value, ApplyOnce(Stat.Duration, Stat.SecondaryDuration) }, { "skills cost no mana", TotalOverride, 0, Mana.Cost }, { "you can cast an additional brand", BaseAdd, 1, Skills[Keyword.Brand].CombinedInstances }, // traps, mines, totems { "trap lasts # seconds", BaseSet, Value, Stat.Trap.Duration }, { "mine lasts # seconds", BaseSet, Value, Stat.Mine.Duration }, { "totem lasts # seconds", BaseSet, Value, Stat.Totem.Duration }, { "detonating mines is instant", TotalOverride, 0, Stat.BaseCastTime, With(Skills.DetonateMines) }, // minions { "can summon up to # golem at a time", BaseSet, Value, Golems.CombinedInstances.Maximum }, // buffs { "(?<!while |chance to )you have ({BuffMatchers})", TotalOverride, 1, Reference.AsBuff.NotAsBuffOn(Self) }, { "(?<!while |chance to )gain ({BuffMatchers})", TotalOverride, 1, Reference.AsBuff.On(Self) }, { "you can have one additional curse", BaseAdd, 1, Buff.CurseLimit }, { "enemies can have # additional curse", BaseAdd, Value, Buff.CurseLimit.For(Enemy) }, { "you can apply an additional curse", BaseAdd, 1, Buff.CurseLimit.For(Enemy) }, { "unaffected by curses", PercentLess, 100, Buffs(targets: Self).With(Keyword.Curse).Effect }, { "unaffected by ({SkillMatchers})", PercentLess, 100, Reference.AsSkill.Buff.EffectOn(Self).For(Entity.Any) }, { "immun(e|ity) to curses", TotalOverride, 0, Buffs(targets: Self).With(Keyword.Curse).On }, { "monsters are hexproof", TotalOverride, 0, Buffs(Self, Enemy).With(Keyword.Curse).On, Flag.IgnoreHexproof.IsSet.Not }, { "grants? fortify", TotalOverride, 1, Buff.Fortify.On(Self) }, { "gain elemental conflux", TotalOverride, 1, Buff.Conflux.Elemental.On(Self) }, { "creates consecrated ground", TotalOverride, 1, Buff.Conflux.Elemental.On(Self) }, { "(?<!chance to )impale enemies", TotalOverride, 100, Buff.Impale.Chance }, { "({BuffMatchers}) lasts # seconds", BaseSet, Value, Reference.AsBuff.Duration }, { "supported auras do not affect you", TotalOverride, 0, Skills.ModifierSourceSkill.Buff.EffectOn(Self) }, { "totems cannot gain ({BuffMatchers})", TotalOverride, 0, Reference.AsBuff.On(Entity.Totem) }, // flags // ailments { "causes bleeding", TotalOverride, 100, Ailment.Bleed.Chance }, { "bleed is applied", TotalOverride, 100, Ailment.Bleed.Chance }, { "always poison", TotalOverride, 100, Ailment.Poison.Chance }, { "always ({AilmentMatchers}) enemies", TotalOverride, 100, Reference.AsAilment.Chance }, { "cannot cause bleeding", TotalOverride, 0, Ailment.Bleed.Chance }, { "cannot ignite", TotalOverride, 0, Ailment.Ignite.Chance }, { "cannot (apply|inflict) shock", TotalOverride, 0, Ailment.Shock.Chance }, { "cannot inflict elemental ailments", TotalOverride, 0, Ailment.Elemental.Select(s => s.Chance) }, { "(you )?can afflict an additional ignite on an enemy", BaseAdd, 1, Ailment.Ignite.InstancesOn(Enemy).Maximum }, { "(you are )?immune to ({AilmentMatchers})", TotalOverride, 100, Reference.AsAilment.Avoidance }, { "cannot be ({AilmentMatchers})", TotalOverride, 100, Reference.AsAilment.Avoidance }, { "cannot be ({AilmentMatchers}) or ({AilmentMatchers})", TotalOverride, 100, References[0].AsAilment.Avoidance, References[1].AsAilment.Avoidance }, { "(immune to|cannot be affected by|immunity to) elemental ailments", TotalOverride, 100, Ailment.Elemental.Select(a => a.Avoidance) }, { "poison you inflict with critical strikes deals #% more damage", PercentMore, Value, CriticalStrike.Multiplier.With(Ailment.Poison) }, // stun { "(you )?cannot be stunned", TotalOverride, 100, Effect.Stun.Avoidance }, { "additional #% chance to be stunned", BaseAdd, Value, Effect.Stun.Chance.For(Entity.OpponentOfSelf) }, // item quantity/quality // range and area of effect // other { "knocks back enemies", TotalOverride, 100, Effect.Knockback.Chance }, { "knocks enemies back", TotalOverride, 100, Effect.Knockback.Chance }, { "knockback(?! distance)", TotalOverride, 100, Effect.Knockback.Chance }, };