Пример #1
0
 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 },
 };
Пример #2
0
 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 },
 };