new FormAndStatMatcherCollection(_modifierBuilder, ValueFactory)
 {
     // attributes
     // offense
     // - damage
     {
         @"adds # to # ({DamageTypeMatchers}) damage",
         BaseAdd, ValueFactory.FromMinAndMax(Values[0], Values[1]), Reference.AsDamageType.Damage.WithHits
     },
Esempio n. 2
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 },
 };
 protected override IReadOnlyList <MatcherData> CreateCollection() =>
 new ValueConversionMatcherCollection(_modifierBuilder)
 {
     // action
     { "for each enemy you've killed recently", Kill.CountRecently },
     { "per enemy killed recently, up to #%", CappedMultiplier(Kill.CountRecently, Value) },
     {
         "per enemy killed by you or your totems recently",
         Kill.CountRecently + Kill.By(Entity.Totem).CountRecently
     },
     {
         "for each enemy you or your minions have killed recently, up to #%( per second)?",
         CappedMultiplier(Kill.CountRecently + Kill.By(Entity.Minion).CountRecently, Value)
     },
     { "for each hit you've blocked recently", Block.CountRecently },
     { "for each corpse consumed recently", Action.ConsumeCorpse.CountRecently },
     // equipment
     { "for each magic item( you have)? equipped", Equipment.Count(e => e.Has(FrameType.Magic)) },
     // stats
     { "per # accuracy rating", PerStat(Stat.Accuracy.With(AttackDamageHand.MainHand)) },
     { "per #%? ({StatMatchers})(?! leech)", PerStat(stat: Reference.AsStat, divideBy: Value) },
     {
         "per # ({StatMatchers}), up to #%",
         CappedMultiplier((Reference.AsStat.Value / Values[0]).Floor(), Values[1])
     },
     { "per # ({StatMatchers}) ceiled", PerStatCeiled(stat: Reference.AsStat, divideBy: Value) },
     { "per ({StatMatchers})(?! leech)", PerStat(stat: Reference.AsStat) },
     { "per ({StatMatchers}), up to #%", CappedMultiplier(Reference.AsStat.Value, Value) },
     {
         "per # ({StatMatchers}) on helmet",
         PerStat(Reference.AsStat.ValueFor(NodeType.Base, new ModifierSource.Local.Item(ItemSlot.Helm)),
                 divideBy: Value)
     },
     {
         "per # ({StatMatchers}) on body armour",
         PerStat(
             Reference.AsStat.ValueFor(NodeType.Base, new ModifierSource.Local.Item(ItemSlot.BodyArmour)),
             divideBy: Value)
     },
     { "per grand spectrum", PerStat(stat: Stat.GrandSpectrumJewelsSocketed) },
     { "per level", PerStat(Stat.Level) },
     { "per (stage|fuse charge)", PerStat(Stat.SkillStage) },
     { "for each (stage|blade)", PerStat(Stat.SkillStage) },
     { @"per stage, up to \+#", CappedMultiplier(Stat.SkillStage.Value, Value) },
     { "per stage after the first", PerStatAfterFirst(Stat.SkillStage) },
     {
         "per ({ChargeTypeMatchers}) removed",
         Reference.AsChargeType.Amount.Value - Reference.AsChargeType.Amount.Minimum.Value
     },
     {
         "when placed, (?<inner>.*) per stage",
         Stat.BannerStage.Value, Flag.IsBannerPlanted, "${inner}"
     },
     { "per nearby enemy", Enemy.CountNearby },
     { "per one hundred nearby enemies", Enemy.CountNearby / 100 },
     { @"per nearby enemy, up to \+#%?", CappedMultiplier(Enemy.CountNearby, Value) },
     {
         "per # unreserved maximum mana, up to #%",
         CappedMultiplier(((Mana.Value - Mana.Reservation.Value) / Values[0]).Floor(), Values[1])
     },
     {
         "per # mana spent recently, up to #%",
         CappedMultiplier(Action.SpendMana(Values[0]).CountRecently, Values[1])
     },
     {
         "per # additional melee range",
         PerStat(Stat.Range.With(AttackDamageHand.MainHand).ValueFor(NodeType.BaseAdd), Value)
     },
     // buffs
     { "per buff on you", Buffs(targets: Self).Count() },
     { "per curse on you", Buffs(targets: Self).With(Keyword.Curse).Count() },
     { "per curse on enemy", Buffs(targets: Enemy).With(Keyword.Curse).Count() },
     { "for each curse on that enemy,", Buffs(targets: Enemy).With(Keyword.Curse).Count() },
     { "for each impale on enemy", Buff.Impale.StackCount.For(Enemy).Value },
     // ailments
     { "for each poison on the enemy", Ailment.Poison.InstancesOn(Enemy).Value },
     { "per poison on enemy", Ailment.Poison.InstancesOn(Enemy).Value },
     { "per poison on you", Ailment.Poison.InstancesOn(Self).Value },
     { "per poison(?= affecting enemies)", Ailment.Poison.InstancesOn(Enemy).Value },
     {
         @"per poison affecting enemy, up to \+#%",
         CappedMultiplier(Ailment.Poison.InstancesOn(Enemy).Value, Value)
     },
     {
         "for each poison on the enemy, up to #",
         CappedMultiplier(Ailment.Poison.InstancesOn(Enemy).Value, Value)
     },
     { "per elemental ailment on the enemy", Ailment.Elemental.Count(b => b.IsOn(Enemy)) },
     // skills
     { "for each zombie you own", Skills.RaiseZombie.Instances.Value },
     { "for each raised zombie", Skills.RaiseZombie.Instances.Value },
     { "for each summoned golem", Golems.CombinedInstances.Value },
     { "per summoned golem", Golems.CombinedInstances.Value },
     { "for each golem you have summoned", Golems.CombinedInstances.Value },
     { "for each type of golem you have summoned", Golems.CombinedInstances.Value },
     {
         "per minion, up to #%",
         CappedMultiplier(Skills[Keyword.Minion].CombinedInstances.Value, Value)
     },
     {
         "for each skill you've used Recently, up to #%",
         CappedMultiplier(AllSkills.Cast.CountRecently, Value)
     },
     // traps, mines, totems
     { "for each trap", Traps.CombinedInstances.Value },
     { "for each mine", Mines.CombinedInstances.Value },
     { "for each trap and mine you have", Traps.CombinedInstances.Value + Mines.CombinedInstances.Value },
     { "(per|for each) totem", Totems.CombinedInstances.Value },
     {
         "each mine( from supported skills)? applies (?<inner>.*) to( hits against)? enemies near it, up to( a maximum of)? #%",
         CappedMultiplier(MineAura(), Value),
         "${inner}"
     },
     {
         "each mine (?<inner>.*) to( hits against)? enemies near it, up to( a maximum of)? # to #",
         CappedMultiplier(MineAura(), ValueFactory.FromMinAndMax(Values[0], Values[1])),
         "${inner}"
     },
     // jewels
     {
         "(per|for every) # ({AttributeStatMatchers}) (allocated|(from|on) allocated passives) in radius",
         PerStat(PassiveTree.AllocatedInModifierSourceJewelRadius(Reference.AsStat), Value)
     },
     {
         "(per|for every) # ({AttributeStatMatchers}) (from|on) unallocated passives in radius",
         PerStat(PassiveTree.UnallocatedInModifierSourceJewelRadius(Reference.AsStat), Value)
     },
     // unique
     {
         "for each poison you have inflicted recently",
         Stat.UniqueAmount("# of Poisons inflicted Recently")
     },
     {
         "for each remaining chain",
         AtLeastZero(Projectile.ChainCount.Value - Stat.UniqueAmount("Projectile.ChainedCount"))
     },
     { "per chain", Stat.UniqueAmount("Projectile.ChainedCount") },
     { "for each enemy pierced", Stat.UniqueAmount("Projectile.PiercedCount") },
     {
         "for each (of your mines|mine) detonated recently, up to #%( per second)?",
         CappedMultiplier(Stat.UniqueAmount("# of Mines Detonated Recently"), Value)
     },
     {
         "for each (of your traps|trap) triggered recently, up to #%( per second)?",
         CappedMultiplier(Stat.UniqueAmount("# of Traps Triggered Recently"), Value)
     },
     {
         "for each time you've blocked in the past 10 seconds",
         Stat.UniqueAmount("# of times blocked in the past 10 seconds")
     },
     {
         "for each endurance charge lost recently, up to #%",
         CappedMultiplier(Stat.UniqueAmount("# of Endurance Charges lost recently"), Value)
     },
     {
         "for each nearby corpse, (?<inner>.*) up to #%? per second",
         CappedMultiplier(Stat.UniqueAmount("# of nearby Corpses"), Value), "${inner}"
     },
     {
         "for each prior mine in detonation sequence",
         CappedMultiplier(
             Stat.UniqueAmount("# of prior mines in detonation sequence"),
             Mines.CombinedInstances.Maximum.Value)
     },
     {
         "for every # prior mines in detonation sequence",
         CappedMultiplier(
             (Stat.UniqueAmount("# of prior mines in detonation sequence") / Value).Floor(),
             Mines.CombinedInstances.Maximum.Value)
     },
 };     // add
Esempio n. 4
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 },
 };