Add support for various "Infamous" (Mercenary exclusive) item modifiers (#8698)

* Add move speed penalty & debuff tag to Debilitated

Debilitated so far only cause 10% less damage
(presumably because it was only applied to players), but should also
apply 20% less movement speed

Also added the "globalEffect" / "debuff" tag in case of future
interactions

* Add "debilitated" to flagTypes in ModParser

This enables "Your are debilitated", "Nearby enemies are debilitated",
"minions are debilitated" etc.

No effect on ModCache as no such mod exists on uniques or tree
(only "Infamous" mod atm)

* Add support for "Your warcries cover enemies in ash"

* Add support for "per two fortification on you"

NOTE: This doesn't correctly work when putting the item on an AG because
actor = "player" is necessary. actor = "enemy" did also not work and is
less safe. Not specifying an actor would make it based on monster
fortification stacks in some cases. "player" is the best solution until
we have actor = "self"

* Add support for Trap & Mine Chain mod

"Skills used by your Traps and Mines Chain 2 additional times"
- Added "skills used by your traps and mines" preFlag
- Added "chain " as modName (extra space to avoid match with "chaining")
- Added "(%d+) additional times?" to formList

* Add generic "projectiles " preFlag

This in combination with previous commit enables mods like:
"Projectiles chain 2 additional times"

Also included modCache to check it doesn't break any existing mods

* Add support for "socketed gems xyz"

Previous preFlag was limited to "have/deal/gain" etc. which did not work
with e.g. "chain" modName. So now "Socketed gems chain 1 additional time"
works.

Changing the lua pattern from "[hgd][ae][via][enl] "
to "([hgd]?[ae]?[via]?[enl]?%s?)" would probably work in a lot of places
, but I didn't want to do that much testing. It doesn't break any other
"socketed gems xyz" mods for now.

* Add support for many other mods

* Support Blink / Mirror Arrow mod

* Generic parsing for seconds

* Position

---------

Co-authored-by: majochem <majochem@users.noreply.github.com>
Co-authored-by: LocalIdentity <localidentity2@gmail.com>
This commit is contained in:
majochem
2025-06-26 10:01:08 +02:00
committed by GitHub
parent f238d97618
commit 18c08505a3
5 changed files with 59 additions and 30 deletions

View File

@@ -8305,7 +8305,7 @@ c["Gain 2 Endurance, Frenzy or Power Charges every 6 seconds Adds 70 to 130 Fire
c["Gain 2 Grasping Vines each second while stationary"]={{[1]={[1]={limit=10,limitTotal=true,type="Multiplier",var="StationarySeconds"},[2]={type="Condition",var="Stationary"},flags=0,keywordFlags=0,name="Multiplier:GraspingVinesCount",type="BASE",value=2}},nil}
c["Gain 2 Mana per Enemy Hit with Attacks"]={{[1]={flags=4,keywordFlags=65536,name="ManaOnHit",type="BASE",value=2}},nil}
c["Gain 2 Power Charges on Using a Warcry"]={{}," Power Charges on Using a "}
c["Gain 2 Power Charges on Using a Warcry Warcries grant Arcane Surge to you and Allies, with 10% increased effect per 5 power, up to 50%"]={{[1]={flags=0,keywordFlags=4,name="Condition:ArcaneSurge",type="BASE",value=2}}," Power Charges on Using a Warcries grant , with 10% increased effect per 5 power, up to 50% "}
c["Gain 2 Power Charges on Using a Warcry Warcries grant Arcane Surge to you and Allies, with 10% increased effect per 5 power, up to 50%"]={{[1]={flags=0,keywordFlags=0,name="ExtraAura",type="LIST",value={mod={flags=0,keywordFlags=4,name="Condition:ArcaneSurge",type="BASE",value=2}}}}," Power Charges on Using a Warcries grant , with 10% increased effect per 5 power, up to 50% "}
c["Gain 2 Rage on Hit with Axes"]={{[1]={flags=0,keywordFlags=0,name="Condition:CanGainRage",type="FLAG",value=true}},nil}
c["Gain 2 Rage on Hit with Axes or Swords"]={{[1]={flags=0,keywordFlags=0,name="Condition:CanGainRage",type="FLAG",value=true}},nil}
c["Gain 2 Rage on Melee Hit"]={{[1]={flags=0,keywordFlags=0,name="Condition:CanGainRage",type="FLAG",value=true}},nil}
@@ -8402,8 +8402,7 @@ c["Gain 40% of Physical Attack Damage as Extra Fire Damage"]={{[1]={flags=1,keyw
c["Gain 40% of Physical Damage as Extra Chaos Damage"]={{[1]={flags=0,keywordFlags=0,name="PhysicalDamageGainAsChaos",type="BASE",value=40}},nil}
c["Gain 40% of Physical Damage as Extra Damage of a random Element while you are Ignited"]={{[1]={[1]={type="Condition",var="Ignited"},flags=0,keywordFlags=0,name="PhysicalDamageGainAsRandom",type="BASE",value=40}},nil}
c["Gain 40% of Physical Damage as a Random Element if you've cast Elemental Weakness in the past 10 seconds"]={{[1]={[1]={type="Condition",var="SelfCastElementalWeakness"},flags=0,keywordFlags=0,name="PhysicalDamageGainAsRandom",type="BASE",value=40}},nil}
c["Gain 5 Life for each Ignited Enemy hit with Attacks"]={{[1]={flags=0,keywordFlags=65536,name="Life",type="BASE",value=5}}," for each Ignited Enemy hit "}
c["Gain 5 Life for each Ignited Enemy hit with Attacks 15% increased Ignite Duration on Enemies"]={{[1]={flags=0,keywordFlags=65536,name="Life",type="BASE",value=5}}," for each Ignited Enemy hit 15% increased Ignite Duration "}
c["Gain 5 Life for each Ignited Enemy hit with Attacks"]={{[1]={[1]={actor="enemy",type="ActorCondition",var="Ignited"},flags=5,keywordFlags=0,name="LifeOnHit",type="BASE",value=5}},nil}
c["Gain 5 Life per Enemy Hit with Attacks"]={{[1]={flags=4,keywordFlags=65536,name="LifeOnHit",type="BASE",value=5}},nil}
c["Gain 5 Mana per Enemy Killed"]={{[1]={flags=0,keywordFlags=0,name="ManaOnKill",type="BASE",value=5}},nil}
c["Gain 5 Rage on Hit with Retaliation Skills"]={{[1]={flags=0,keywordFlags=0,name="Condition:CanGainRage",type="FLAG",value=true}},nil}
@@ -9834,13 +9833,13 @@ c["Projectiles Pierce 6 additional Targets"]={{[1]={flags=0,keywordFlags=0,name=
c["Projectiles Pierce all Burning Enemies"]={{[1]={[1]={actor="enemy",type="ActorCondition",var="Burning"},flags=0,keywordFlags=0,name="PierceAllTargets",type="FLAG",value=true}},nil}
c["Projectiles Pierce all Targets while you have Phasing"]={{[1]={[1]={type="Condition",var="Phasing"},flags=0,keywordFlags=0,name="PierceAllTargets",type="FLAG",value=true}},nil}
c["Projectiles Pierce an additional Target"]={{[1]={flags=0,keywordFlags=0,name="PierceCount",type="BASE",value=1}},nil}
c["Projectiles Return to you at end of flight"]={nil,"Projectiles Return to you at end of flight "}
c["Projectiles Return to you at end of flight Projectiles are fired in random directions"]={nil,"Projectiles Return to you at end of flight Projectiles are fired in random directions "}
c["Projectiles Return to you from final target"]={nil,"Projectiles Return to you from final target "}
c["Projectiles are fired in random directions"]={nil,"Projectiles are fired in random directions "}
c["Projectiles Return to you at end of flight"]={nil,"Return to you at end of flight "}
c["Projectiles Return to you at end of flight Projectiles are fired in random directions"]={nil,"Return to you at end of flight Projectiles are fired in random directions "}
c["Projectiles Return to you from final target"]={nil,"Return to you from final target "}
c["Projectiles are fired in random directions"]={nil,"fired in random directions "}
c["Projectiles cannot Pierce, Fork or Chain"]={{[1]={flags=1024,keywordFlags=0,name="CannotPierce",type="FLAG",value=true},[2]={flags=1024,keywordFlags=0,name="CannotChain",type="FLAG",value=true},[3]={flags=1024,keywordFlags=0,name="CannotFork",type="FLAG",value=true}},nil}
c["Projectiles cannot collide with Enemies at Close Range"]={nil,"Projectiles cannot collide with Enemies at Close Range "}
c["Projectiles cannot collide with Enemies at Close Range Far Shot"]={nil,"Projectiles cannot collide with Enemies at Close Range Far Shot "}
c["Projectiles cannot collide with Enemies at Close Range"]={nil,"cannot collide with Enemies at Close Range "}
c["Projectiles cannot collide with Enemies at Close Range Far Shot"]={nil,"cannot collide with Enemies at Close Range Far Shot "}
c["Projectiles cannot continue after colliding with targets"]={{[1]={flags=1024,keywordFlags=0,name="CannotPierce",type="FLAG",value=true},[2]={flags=1024,keywordFlags=0,name="CannotChain",type="FLAG",value=true},[3]={flags=1024,keywordFlags=0,name="CannotFork",type="FLAG",value=true},[4]={flags=1024,keywordFlags=0,name="CannotSplit",type="FLAG",value=true}},nil}
c["Projectiles deal 15% increased Damage with Hits and Ailments for each remaining Chain"]={{[1]={[1]={stat="ChainRemaining",type="PerStat"},[2]={skillType=3,type="SkillType"},flags=0,keywordFlags=786432,name="Damage",type="INC",value=15}},nil}
c["Projectiles deal 20% increased Damage with Hits and Ailments for each Enemy Pierced"]={{[1]={[1]={stat="PiercedCount",type="PerStat"},[2]={skillType=3,type="SkillType"},flags=0,keywordFlags=786432,name="Damage",type="INC",value=20}},nil}
@@ -9860,14 +9859,14 @@ c["Projectiles from Attacks have 20% chance to Poison on Hit while you have a Be
c["Projectiles from Attacks have 20% chance to inflict Bleeding on Hit while you have a Bestial Minion"]={{[1]={[1]={skillType=47,type="SkillType"},[2]={type="Condition",var="HaveBestialMinion"},flags=0,keywordFlags=0,name="BleedChance",type="BASE",value=20}},nil}
c["Projectiles from Spells cannot Pierce"]={{[1]={flags=2,keywordFlags=0,name="CannotPierce",type="FLAG",value=true}},nil}
c["Projectiles gain 20% of Non-Chaos Damage as extra Chaos Damage per Chain"]={{[1]={[1]={stat="Chain",type="PerStat"},flags=1024,keywordFlags=0,name="NonChaosDamageGainAsChaos",type="BASE",value=20}},nil}
c["Projectiles gain Damage as they travel farther, dealing up"]={nil,"Projectiles gain Damage as they travel farther, dealing up "}
c["Projectiles gain Damage as they travel farther, dealing up"]={nil,"Damage as they travel farther, dealing up "}
c["Projectiles gain Damage as they travel farther, dealing up to 30% more Damage with Hits and Ailments"]={{[1]={[1]={ramp={[1]={[1]=35,[2]=0},[2]={[1]=70,[2]=1}},type="DistanceRamp"},flags=0,keywordFlags=786432,name="Damage",type="MORE",value=30}},nil}
c["Projectiles gain Damage as they travel farther, dealing up to 60% increased Damage with Hits to targets"]={{[1]={[1]={ramp={[1]={[1]=35,[2]=0},[2]={[1]=70,[2]=1}},type="DistanceRamp"},flags=1028,keywordFlags=0,name="Damage",type="INC",value=60}},nil}
c["Projectiles have 20% chance to be able to Chain when colliding with terrain"]={{}," to be able to Chain when colliding with terrain "}
c["Projectiles have 20% chance to be able to Chain when colliding with terrain Projectiles gain Damage as they travel farther, dealing up"]={{[1]={flags=1024,keywordFlags=0,name="ProjectileCount",type="BASE",value=20}}," to be able to Chain when colliding with terrain gain Damage as they travel farther, dealing up "}
c["Projectiles have 20% chance to be able to Chain when colliding with terrain"]={{[1]={flags=1024,keywordFlags=0,name="ChainCountMax",type="BASE",value=20}}," to be able to when colliding with terrain "}
c["Projectiles have 20% chance to be able to Chain when colliding with terrain Projectiles gain Damage as they travel farther, dealing up"]={{[1]={flags=1024,keywordFlags=0,name="ChainCountMax",type="BASE",value=20}}," to be able to when colliding with terrain Projectiles gain Damage as they travel farther, dealing up "}
c["Projectiles have 25% chance for an additional Projectile when Forking"]={{[1]={flags=1024,keywordFlags=0,name="ProjectileCount",type="BASE",value=25}}," for an additional when Forking "}
c["Projectiles have 30% chance to be able to Chain when colliding with terrain"]={{}," to be able to Chain when colliding with terrain "}
c["Projectiles have 4% chance to be able to Chain when colliding with terrain per Searching Eye Jewel affecting you, up to a maximum of 20%"]={{}," to be able to Chain when colliding with terrain "}
c["Projectiles have 30% chance to be able to Chain when colliding with terrain"]={{[1]={flags=1024,keywordFlags=0,name="ChainCountMax",type="BASE",value=30}}," to be able to when colliding with terrain "}
c["Projectiles have 4% chance to be able to Chain when colliding with terrain per Searching Eye Jewel affecting you, up to a maximum of 20%"]={{[1]={[1]={limit=20,limitTotal=true,type="Multiplier",var="SearchingEyeJewel"},flags=1024,keywordFlags=0,name="ChainCountMax",type="BASE",value=4}}," to be able to when colliding with terrain "}
c["Projectiles have 50% chance for an additional Projectile when Forking"]={{[1]={flags=1024,keywordFlags=0,name="ProjectileCount",type="BASE",value=50}}," for an additional when Forking "}
c["Projectiles have 50% chance to Return to you"]={{}," to Return to you "}
c["Projectiles have 50% chance to Return to you Projectiles are fired in random directions"]={{[1]={flags=1024,keywordFlags=0,name="ProjectileCount",type="BASE",value=50}}," to Return to you are fired in random directions "}
@@ -11174,8 +11173,8 @@ c["Socketed Gems are supported by Level 5 Blind"]={{[1]={[1]={slotName="{SlotNam
c["Socketed Gems are supported by level 30 Infernal Legion"]={{[1]={[1]={slotName="{SlotName}",type="SocketedIn"},flags=0,keywordFlags=0,name="ExtraSupport",type="LIST",value={level=30,skillId="SupportInfernalLegion"}}},nil}
c["Socketed Gems deal 63 to 94 additional Fire Damage"]={{[1]={[1]={slotName="{SlotName}",type="SocketedIn"},flags=0,keywordFlags=0,name="ExtraSkillMod",type="LIST",value={mod={flags=0,keywordFlags=0,name="FireMin",type="BASE",value=63}}},[2]={[1]={slotName="{SlotName}",type="SocketedIn"},flags=0,keywordFlags=0,name="ExtraSkillMod",type="LIST",value={mod={flags=0,keywordFlags=0,name="FireMax",type="BASE",value=94}}}},nil}
c["Socketed Gems fire 4 additional Projectiles"]={{[1]={[1]={slotName="{SlotName}",type="SocketedIn"},flags=0,keywordFlags=0,name="ExtraSkillMod",type="LIST",value={mod={flags=0,keywordFlags=0,name="ProjectileCount",type="BASE",value=4}}}},nil}
c["Socketed Gems fire Projectiles in a Nova"]={nil,"Socketed Gems fire Projectiles in a Nova "}
c["Socketed Gems fire Projectiles in a Nova +20 to All Attributes"]={nil,"Socketed Gems fire Projectiles in a Nova +20 to All Attributes "}
c["Socketed Gems fire Projectiles in a Nova"]={nil,"fire Projectiles in a Nova "}
c["Socketed Gems fire Projectiles in a Nova +20 to All Attributes"]={nil,"fire Projectiles in a Nova +20 to All Attributes "}
c["Socketed Gems fire an additional Projectile"]={{[1]={[1]={slotName="{SlotName}",type="SocketedIn"},flags=0,keywordFlags=0,name="ExtraSkillMod",type="LIST",value={mod={flags=0,keywordFlags=0,name="ProjectileCount",type="BASE",value=1}}}},nil}
c["Socketed Gems have 10% chance to cause Enemies to Flee on Hit"]={{}," to cause Enemies to Flee "}
c["Socketed Gems have 10% chance to cause Enemies to Flee on Hit Trigger Level 1 Intimidating Cry on Hit"]={{}," to cause Enemies to Flee Trigger Level 1on Hit "}

View File

@@ -2415,7 +2415,7 @@ function calcs.offence(env, actor, activeSkill)
-- Exerted Attack members
local exertedDoubleDamage = env.modDB:Sum("BASE", cfg, "ExertDoubleDamageChance")
local exertingWarcryCount = env.modDB:Sum("BASE", nil, "ExertingWarcryCount")
local exertingWarcryCount = env.modDB:Sum("BASE", nil, "Multiplier:ExertingWarcryCount")
globalOutput.OffensiveWarcryEffect = 1
globalOutput.MaxOffensiveWarcryEffect = 1
globalOutput.TheoreticalOffensiveWarcryEffect = 1
@@ -2841,7 +2841,12 @@ function calcs.offence(env, actor, activeSkill)
output.CritChance = (1 - (1 - output.CritChance / 100) ^ (critRolls + 1)) * 100
end
local preHitCheckCritChance = output.CritChance
local pre3rdUseCritChance= output.CritChance
if env.mode_effective then
if skillModList:Flag(skillCfg, "Every3UseCrit") then
output.CritChance = (2 * output.CritChance + 100) / 3
end
preHitCheckCritChance = output.CritChance
output.CritChance = output.CritChance * output.AccuracyHitChance / 100
end
if breakdown and output.CritChance ~= baseCrit then
@@ -2863,9 +2868,14 @@ function calcs.offence(env, actor, activeSkill)
local overCap = preCapCritChance - 100
t_insert(breakdown.CritChance, s_format("Crit is overcapped by %.2f%% (%d%% increased Critical Strike Chance)", overCap, overCap / more / (baseCrit + base) * 100))
end
if env.mode_effective and critRolls ~= 0 then
t_insert(breakdown.CritChance, "Crit Chance is Lucky:")
t_insert(breakdown.CritChance, s_format("1 - (1 - %.4f)^ %d", preLuckyCritChance / 100, critRolls + 1))
if env.mode_effective and (critRolls ~= 0 or skillModList:Flag(skillCfg, "Every3UseCrit")) then
if critRolls ~= 0 then
t_insert(breakdown.CritChance, "Crit Chance is Lucky:")
t_insert(breakdown.CritChance, s_format("1 - (1 - %.4f)^ %d", preLuckyCritChance / 100, critRolls + 1))
end
if skillModList:Flag(skillCfg, "Every3UseCrit") then
t_insert(breakdown.CritChance, s_format("+ %.2f%% ^8(crit every 3rd use)", (2 * pre3rdUseCritChance + 100) / 3 - pre3rdUseCritChance))
end
t_insert(breakdown.CritChance, s_format("= %.2f%%", preHitCheckCritChance))
end
if env.mode_effective and output.AccuracyHitChance < 100 then
@@ -5500,7 +5510,7 @@ function calcs.offence(env, actor, activeSkill)
end
local averageWarcryCount = output.GlobalWarcryUptimeRatio / 100
if activeSkill.skillModList:Flag(nil, "Condition:WarcryMaxHit") then
averageWarcryCount = env.modDB:Sum("BASE", nil, "ExertingWarcryCount")
averageWarcryCount = env.modDB:Sum("BASE", nil, "Multiplier:ExertingWarcryCount")
end
if averageWarcryCount and dmgType and dmgMult then
local dmgBreakdown, totalDmgTaken = calcs.applyDmgTakenConversion(activeSkill, output, breakdown, dmgType, (output.Life or 0) * dmgMult/100 * averageWarcryCount)

View File

@@ -1120,6 +1120,9 @@ function calcs.perform(env, skipEHP)
if env.player.itemList["Weapon 2"] and env.player.itemList["Weapon 2"].type == "Quiver" then
env.minion.modDB:ScaleAddList(env.player.itemList["Weapon 2"].modList, m_max(modDB:Sum("BASE", nil, "WidowHailMultiplier"), 1))
end
if modDB:Flag(nil, "BlinkAndMirrorUseGloves") and env.player.itemList["Gloves"] then
env.minion.modDB:AddList(env.player.itemList["Gloves"].modList)
end
end
if env.minion.itemSet or env.minion.uses then
for slotName, slot in pairs(env.build.itemsTab.slots) do
@@ -2003,7 +2006,7 @@ function calcs.perform(env, skipEHP)
local extraExertions = modStore:Sum("BASE", nil, "ExtraExertedAttacks") or 0
local exertMultiplier = modStore:More(nil, "ExtraExertedAttacks")
env.player.modDB:NewMod("Num"..warcryName.."Exerts", "BASE", m_floor((baseExerts + extraExertions) * exertMultiplier))
env.player.modDB:NewMod("ExertingWarcryCount", "BASE", 1)
env.player.modDB:NewMod("Multiplier:ExertingWarcryCount", "BASE", 1)
end
if not activeSkill.skillModList:Flag(nil, "CannotShareWarcryBuffs") then
local warcryPower = modDB:Override(nil, "WarcryPower") or m_max((modDB:Sum("BASE", nil, "WarcryPower") or 0) * (1 + (modDB:Sum("INC", nil, "WarcryPower") or 0)/100), (modDB:Sum("BASE", nil, "MinimumWarcryPower") or 0))

View File

@@ -61,7 +61,8 @@ function calcs.initModDB(env, modDB)
modDB:NewMod("DamageTaken", "INC", 10, "Base", ModFlag.Attack, { type = "Condition", var = "Intimidated", neg = true}, { type = "Condition", var = "Party:Intimidated"})
modDB:NewMod("DamageTaken", "INC", 10, "Base", ModFlag.Spell, { type = "Condition", var = "Unnerved"})
modDB:NewMod("DamageTaken", "INC", 10, "Base", ModFlag.Spell, { type = "Condition", var = "Unnerved", neg = true}, { type = "Condition", var = "Party:Unnerved"})
modDB:NewMod("Damage", "MORE", -10, "Base", { type = "Condition", var = "Debilitated"})
modDB:NewMod("Damage", "MORE", -10, "Base", { type = "Condition", var = "Debilitated"}, { type = "GlobalEffect", effectName = "Debilitated", effectType = "Debuff"})
modDB:NewMod("MovementSpeed", "MORE", -20, "Base", { type = "Condition", var = "Debilitated"}, { type = "GlobalEffect", effectName = "Debilitated", effectType = "Debuff"})
modDB:NewMod("Condition:Burning", "FLAG", true, "Base", { type = "IgnoreCond" }, { type = "Condition", var = "Ignited" })
modDB:NewMod("Condition:Poisoned", "FLAG", true, "Base", { type = "IgnoreCond" }, { type = "MultiplierThreshold", var = "PoisonStack", threshold = 1 })
modDB:NewMod("Blind", "FLAG", true, "Base", { type = "Condition", var = "Blinded" })

View File

@@ -74,6 +74,7 @@ local formList = {
["^([%+%-][%d%.]+)%%? base"] = "BASE",
["^([%+%-]?[%d%.]+)%%? additional"] = "BASE",
["(%d+) additional hits?"] = "BASE",
["(%d+) additional times?"] = "BASE",
["^throw up to (%d+)"] = "BASE",
["^you gain ([%d%.]+)"] = "GAIN",
["^gains? ([%d%.]+)%% of"] = "GAIN",
@@ -537,6 +538,7 @@ local modNameList = {
["projectiles"] = "ProjectileCount",
["projectile speed"] = "ProjectileSpeed",
["arrow speed"] = { "ProjectileSpeed", flags = ModFlag.Bow },
["chain "] = "ChainCountMax",
-- Totem/trap/mine/brand modifiers
["totem placement speed"] = "TotemPlacementSpeed",
["totem life"] = "TotemLife",
@@ -676,6 +678,7 @@ local modNameList = {
["damage over time multiplier"] = "DotMultiplier",
-- Crit/accuracy/speed modifiers
["critical strike chance"] = "CritChance",
["critical hit chance"] = "CritChance",
["attack critical strike chance"] = { "CritChance", flags = ModFlag.Attack },
["critical strike multiplier"] = "CritMultiplier",
["attack critical strike multiplier"] = { "CritMultiplier", flags = ModFlag.Attack },
@@ -961,6 +964,7 @@ local modFlagList = {
["of movement skills used"] = { keywordFlags = KeywordFlag.Movement },
["of travel skills"] = { tag = { type = "SkillType", skillType = SkillType.Travel } },
["of banner skills"] = { tag = { type = "SkillType", skillType = SkillType.Banner } },
["lightning skills"] = { keywordFlags = KeywordFlag.Lightning },
["with lightning skills"] = { keywordFlags = KeywordFlag.Lightning },
["with cold skills"] = { keywordFlags = KeywordFlag.Cold },
["with fire skills"] = { keywordFlags = KeywordFlag.Fire },
@@ -1049,6 +1053,7 @@ local preFlagList = {
["^while a pinnacle atlas boss is in your presence, minions [hd][ae][va][el] "] = { addToMinion = true, playerTag = { type = "ActorCondition", actor = "enemy", var = "PinnacleBoss" } },
["^minions leech "] = { addToMinion = true },
["^minions' attacks deal "] = { addToMinion = true, flags = ModFlag.Attack },
["^minions created recently have "] = { addToMinion = true, playerTag = { type = "Condition", var = "MinionsCreatedRecently" } },
["^golems [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillType", skillType = SkillType.Golem } },
["^summoned golems [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillType", skillType = SkillType.Golem } },
["^golem skills have "] = { tag = { type = "SkillType", skillType = SkillType.Golem } },
@@ -1094,6 +1099,7 @@ local preFlagList = {
["^skills which throw traps ([hgd][ae][via][enl])? "] = { keywordFlags = KeywordFlag.Trap },
["^skills used by mines [hgd][ae][via][enl] "] = { keywordFlags = KeywordFlag.Mine },
["^skills which throw mines ([hgd][ae][via][enl])? "] = { keywordFlags = KeywordFlag.Mine },
["^skills used by your traps and mines ([hgd]?[ae]?[via]?[enl]?[ ]?)"] = { keywordFlags = bor(KeywordFlag.Trap, KeywordFlag.Mine) },
-- Local damage
["^attacks with this weapon "] = { tagList = { { type = "Condition", var = "{Hand}Attack" }, { type = "SkillType", skillType = SkillType.Attack } } },
["^attacks with this weapon [hd][ae][va][el] "] = { tagList = { { type = "Condition", var = "{Hand}Attack" }, { type = "SkillType", skillType = SkillType.Attack } } },
@@ -1108,6 +1114,7 @@ local preFlagList = {
["^arrows [hd][ae][va][el] "] = { keywordFlags = KeywordFlag.Bow },
["^bow skills [hdf][aei][var][el] "] = { keywordFlags = KeywordFlag.Bow },
["^projectiles [hdf][aei][var][el] "] = { flags = ModFlag.Projectile },
["^projectiles "] = { flags = ModFlag.Projectile },
["^melee attacks have "] = { flags = ModFlag.Melee },
["^movement attack skills have "] = { flags = ModFlag.Attack, keywordFlags = KeywordFlag.Movement },
["^travel skills have "] = { tag = { type = "SkillType", skillType = SkillType.Travel } },
@@ -1141,7 +1148,7 @@ local preFlagList = {
-- Slot specific
["^left ring slot: "] = { tag = { type = "SlotNumber", num = 1 } },
["^right ring slot: "] = { tag = { type = "SlotNumber", num = 2 } },
["^socketed gems [hgd][ae][via][enl] "] = { addToSkill = { type = "SocketedIn", slotName = "{SlotName}" } },
["^socketed gems ([hgd]?[ae]?[via]?[enl]?%s?)"] = { addToSkill = { type = "SocketedIn", slotName = "{SlotName}" } },
["^socketed skills [hgd][ae][via][enl] "] = { addToSkill = { type = "SocketedIn", slotName = "{SlotName}" } },
["^socketed travel skills [hgd][ae][via][enl] "] = { addToSkill = { type = "SocketedIn", slotName = "{SlotName}", keyword = "travel" } },
["^socketed warcry skills [hgd][ae][via][enl] "] = { addToSkill = { type = "SocketedIn", slotName = "{SlotName}", keyword = "warcry" } },
@@ -1216,7 +1223,7 @@ local preFlagList = {
["attacks with energy blades "] = { flags = ModFlag.Attack, tag = { type = "Condition", var = "AffectedByEnergyBlade" } },
["^for each nearby corpse, "] = { tag = { type = "Multiplier", var = "NearbyCorpse" } },
["^enemies in your link beams have "] = { tag = { type = "Condition", var = "BetweenYouAndLinkedTarget" }, applyToEnemy = true },
["^consecrated ground you create also grants "] = { tag = { type = "Condition", var = "OnConsecratedGround" } },
["^consecrated ground you create a?l?s?o? ?grants "] = { tag = { type = "Condition", var = "OnConsecratedGround" } },
-- While in the presence of...
["^while a unique enemy is in your presence, "] = { tag = { type = "ActorCondition", actor = "enemy", var = "RareOrUnique" } },
["^while a pinnacle atlas boss is in your presence, "] = { tag = { type = "ActorCondition", actor = "enemy", var = "PinnacleBoss" } },
@@ -1233,6 +1240,7 @@ local modTagList = {
["with critical strikes"] = { tag = { type = "Condition", var = "CriticalStrike" } },
["while affected by auras you cast"] = { tag = { type = "Condition", var = "AffectedByAura" } },
["for you and nearby allies"] = { newAura = true },
["to you and allies"] = { newAura = true },
-- Multipliers
["per power charge"] = { tag = { type = "Multiplier", var = "PowerCharge" } },
["per frenzy charge"] = { tag = { type = "Multiplier", var = "FrenzyCharge" } },
@@ -1329,7 +1337,6 @@ local modTagList = {
["per enemy killed by you or your totems recently"] = { tag = { type = "Multiplier", varList = { "EnemyKilledRecently","EnemyKilledByTotemsRecently" } } },
["per nearby enemy, up to %+?(%d+)%%"] = function(num) return { tag = { type = "Multiplier", var = "NearbyEnemies", limit = num, limitTotal = true } } end,
["per enemy in close range"] = { tagList = { { type = "Condition", var = "AtCloseRange" }, { type = "Multiplier", var = "NearbyEnemies" } } },
["to you and allies"] = { },
["per red socket"] = { tag = { type = "Multiplier", var = "RedSocketIn{SlotName}" } },
["per green socket on main hand weapon"] = { tag = { type = "Multiplier", var = "GreenSocketInWeapon 1" } },
["per green socket on"] = { tag = { type = "Multiplier", var = "GreenSocketInWeapon 1" } },
@@ -1408,6 +1415,7 @@ local modTagList = {
["per (%d+)%% missing cold resistance, up to a maximum of (%d+)%%"] = function(num, _, limit) return { tag = { type = "PerStat", stat = "MissingColdResist", div = num, globalLimit = tonumber(limit), globalLimitKey = "ReplicaNebulisCold" } } end,
["per endurance, frenzy or power charge"] = { tag = { type = "PerStat", stat = "TotalCharges" } },
["per fortification"] = { tag = { type = "PerStat", stat = "FortificationStacks" } },
["per two fortification on you"] = { tag = { type = "PerStat", stat= "FortificationStacks", div = 2, actor = "player" } },
["per fortification above 20"] = { tag = { type = "PerStat", stat = "FortificationStacksOver20" } },
["per totem"] = { tag = { type = "PerStat", stat = "TotemsSummoned" } },
["per summoned totem"] = { tag = { type = "PerStat", stat = "TotemsSummoned" } },
@@ -1446,6 +1454,7 @@ local modTagList = {
["against targets they pierce"] = { tag = { type = "StatThreshold", stat = "PierceCount", threshold = 1 } },
["against pierced targets"] = { tag = { type = "StatThreshold", stat = "PierceCount", threshold = 1 } },
["to targets they pierce"] = { tag = { type = "StatThreshold", stat = "PierceCount", threshold = 1 } },
["that fire a single projectile"] = { tag = { type = "StatThreshold", stat = "ProjectileCount", threshold = 1, upper = true } },
["w?h?i[lf]e? you have at least (%d+) devotion"] = function(num) return { tag = { type = "StatThreshold", stat = "Devotion", threshold = num } } end,
["while you have at least (%d+) rage"] = function(num) return { tag = { type = "MultiplierThreshold", var = "Rage", threshold = num } } end,
["while affected by a unique abyss jewel"] = { tag = { type = "MultiplierThreshold", var = "UniqueAbyssJewels", threshold = 1 } },
@@ -1713,6 +1722,7 @@ local modTagList = {
["when you warcry"] = { tag = { type = "Condition", var = "UsedWarcryRecently" } },
["if you[' ]h?a?ve warcried recently"] = { tag = { type = "Condition", var = "UsedWarcryRecently" } },
["for each time you[' ]h?a?ve warcried recently"] = { tag = { type = "Multiplier", var = "WarcryUsedRecently" } },
["for each warcry exerting them"] = { tag = { type = "Multiplier", var = "ExertingWarcryCount" } },
["when you warcry"] = { tag = { type = "Condition", var = "UsedWarcryRecently" } },
["if you[' ]h?a?ve warcried in the past 8 seconds"] = { tag = { type = "Condition", var = "UsedWarcryInPast8Seconds" } },
["for each second you've been affected by a warcry buff, up to a maximum of (%d+)%%"] = function(num) return { tag = { type = "Multiplier", var = "AffectedByWarcryBuffDuration", limit = num, limitTotal = true } } end,
@@ -2077,6 +2087,8 @@ local specialModList = {
["removes all energy shield"] = { mod("EnergyShield", "MORE", -100) },
["skills cost life instead of mana"] = { flag("CostLifeInsteadOfMana") },
["skills reserve life instead of mana"] = { flag("BloodMagicReserved") },
["your skills that throw mines reserve life instead of mana"] = { flag("BloodMagicReserved", nil, 0, KeywordFlag.Mine) },
["your travel skills critically strike once every 3 uses"] = { flag("Every3UseCrit", { type = "SkillType", skillType = SkillType.Travel }) },
["non%-aura skills cost no mana or life while focus?sed"] = {
mod("ManaCost", "MORE", -100, { type = "Condition", var = "Focused" }, { type = "SkillType", skillType = SkillType.Aura, neg = true }),
mod("LifeCost", "MORE", -100, { type = "Condition", var = "Focused" }, { type = "SkillType", skillType = SkillType.Aura, neg = true })
@@ -3474,6 +3486,7 @@ local specialModList = {
["(%d+)%% chance on hitting an enemy for all impales on that enemy to last for an additional hit"] = function(num) return {
mod("ImpaleAdditionalDurationChance", "BASE", num)
} end,
["projectiles gain impale effect as they travel farther, causing impales they inflict to have up to (%d+)%% increased effect"] = function(num) return { mod("ImpaleEffect", "INC", num, nil, ModFlag.Projectile, { type = "DistanceRamp", ramp = { {35,0},{70,1} } }) } end,
-- Poison and Bleed
["(%d+)%% increased damage with bleeding inflicted on poisoned enemies"] = function(num) return {
mod("Damage", "INC", num, nil, 0, KeywordFlag.Bleed, { type = "ActorCondition", actor = "enemy", var = "Poisoned" })
@@ -3862,10 +3875,11 @@ local specialModList = {
["(%d+)%% more maximum life if you have at least (%d+) life masteries allocated"] = function(num, _, thresh) return {
mod("Life", "MORE", num, { type = "MultiplierThreshold", var = "AllocatedLifeMastery", threshold = tonumber(thresh) }),
} end,
["left ring slot: cover enemies in ash for 5 seconds when you ignite them"] = { mod("CoveredInAshEffect", "BASE", 20, { type = "SlotNumber", num = 1 }, { type = "ActorCondition", actor = "enemy", var = "Ignited" }) },
["right ring slot: cover enemies in frost for 5 seconds when you freeze them"] = { mod("CoveredInFrostEffect", "BASE", 20, { type = "SlotNumber", num = 2 }, { type = "ActorCondition", actor = "enemy", var = "Frozen" }) },
["left ring slot: cover enemies in ash for (%d+) seconds when you ignite them"] = { mod("CoveredInAshEffect", "BASE", 20, { type = "SlotNumber", num = 1 }, { type = "ActorCondition", actor = "enemy", var = "Ignited" }) },
["right ring slot: cover enemies in frost for (%d+) seconds when you freeze them"] = { mod("CoveredInFrostEffect", "BASE", 20, { type = "SlotNumber", num = 2 }, { type = "ActorCondition", actor = "enemy", var = "Frozen" }) },
["nearby enemies are covered in ash"] = { mod("CoveredInAshEffect", "BASE", 20) },
["nearby enemies are covered in ash if you haven't moved in the past (%d+) seconds"] = function(num) return { mod("CoveredInAshEffect", "BASE", 20, { type = "MultiplierThreshold", var = "StationarySeconds", threshold = num }, { type = "Condition", var = "Stationary" }) } end,
["your warcries cover enemies in ash for (%d+) seconds"] = { mod("CoveredInAshEffect", "BASE", 20, { type = "Condition", var = "UsedWarcryRecently" }) },
["enemies near targets you shatter have (%d+)%% chance to be covered in frost for (%d+) seconds"] = { mod("CoveredInFrostEffect", "BASE", 20, { type = "Condition", var = "ShatteredEnemyRecently" }) },
["([%a%s]+) has (%d+)%% increased effect"] = function(_, skill, num) return { mod("BuffEffect", "INC", num, { type = "SkillId", skillId = gemIdLookup[skill]}) } end,
["debuffs on you expire (%d+)%% faster"] = function(num) return { mod("SelfDebuffExpirationRate", "BASE", num) } end,
@@ -3936,8 +3950,6 @@ local specialModList = {
["your strength is added to your minions"] = { mod("StrengthAddedToMinions", "BASE", 100) },
["half of your strength is added to your minions"] = { mod("StrengthAddedToMinions", "BASE", 50) },
["minions' accuracy rating is equal to yours"] = { flag("MinionAccuracyEqualsAccuracy") },
["minions created recently have (%d+)%% increased attack and cast speed"] = function(num) return { mod("MinionModifier", "LIST", { mod = mod("Speed", "INC", num) }, { type = "Condition", var = "MinionsCreatedRecently" }) } end,
["minions created recently have (%d+)%% increased movement speed"] = function(num) return { mod("MinionModifier", "LIST", { mod = mod("MovementSpeed", "INC", num) }, { type = "Condition", var = "MinionsCreatedRecently" }) } end,
["minions poison enemies on hit"] = { mod("MinionModifier", "LIST", { mod = mod("PoisonChance", "BASE", 100) }) },
["minions have (%d+)%% chance to poison enemies on hit"] = function(num) return { mod("MinionModifier", "LIST", { mod = mod("PoisonChance", "BASE", num) }) } end,
["(%d+)%% increased minion damage if you have hit recently"] = function(num) return { mod("MinionModifier", "LIST", { mod = mod("Damage", "INC", num) }, { type = "Condition", var = "HitRecently" }) } end,
@@ -4081,6 +4093,7 @@ local specialModList = {
} end,
["skeleton warriors are permanent minions and follow you"] = { flag("RaisedSkeletonPermanentDuration", { type = "SkillName", skillName = "Summon Skeletons" }) }, -- typo never existed except in some items generated by PoB
["summoned skeleton warriors are permanent and follow you"] = { flag("RaisedSkeletonPermanentDuration", { type = "SkillName", skillName = "Summon Skeletons" }) },
["your blink and mirror arrow clones use your gloves"] = { flag("BlinkAndMirrorUseGloves") },
-- Projectiles
["skills chain %+(%d) times"] = function(num) return { mod("ChainCountMax", "BASE", num) } end,
["arrows chain %+(%d) times"] = function(num) return { mod("ChainCountMax", "BASE", num, nil, ModFlag.Bow) } end,
@@ -4208,6 +4221,7 @@ local specialModList = {
} end,
["(%d+) life gained for each cursed enemy hit by your attacks"] = function(num) return { mod("LifeOnHit", "BASE", num, nil, bor(ModFlag.Attack, ModFlag.Hit), { type = "ActorCondition", actor = "enemy", var = "Cursed" }) } end,
["gain (%d+) life per cursed enemy hit with attacks"] = function(num) return { mod("LifeOnHit", "BASE", num, nil, bor(ModFlag.Attack, ModFlag.Hit), { type = "ActorCondition", actor = "enemy", var = "Cursed" }) } end,
["gain (%d+) life for each ignited enemy hit with attacks"] = function(num) return { mod("LifeOnHit", "BASE", num, nil, bor(ModFlag.Attack, ModFlag.Hit), { type = "ActorCondition", actor = "enemy", var = "Ignited" }) } end,
["(%d+) mana gained for each cursed enemy hit by your attacks"] = function(num) return { mod("ManaOnHit", "BASE", num, nil, bor(ModFlag.Attack, ModFlag.Hit), { type = "ActorCondition", actor = "enemy", var = "Cursed" }) } end,
["gain (%d+) mana per cursed enemy hit with attacks"] = function(num) return { mod("ManaOnHit", "BASE", num, nil, bor(ModFlag.Attack, ModFlag.Hit), { type = "ActorCondition", actor = "enemy", var = "Cursed" }) } end,
["gain (%d+) life per blinded enemy hit with this weapon"] = function(num) return { mod("LifeOnHit", "BASE", num, nil, ModFlag.Hit,{ type = "ActorCondition", actor = "enemy", var = "Blinded" }, { type = "Condition", var = "{Hand}Attack" }) } end,
@@ -4284,6 +4298,7 @@ local specialModList = {
["replenishes energy shield by (%d+)%% of armour when you block"] = function(num) return { mod("EnergyShieldOnBlock", "BASE", 1, { type = "PercentStat", stat = "Armour", percent = num }) } end,
["recover energy shield equal to (%d+)%% of armour when you block"] = function(num) return { mod("EnergyShieldOnBlock", "BASE", 1, { type = "PercentStat", stat = "Armour", percent = num }) } end,
["(%d+)%% of damage taken while affected by clarity recouped as mana"] = function(num) return { mod("ManaRecoup", "BASE", num, { type = "Condition", var = "AffectedByClarity" }) } end,
["(%d+)%% of damage taken while frozen recouped as life"] = function(num) return { mod("LifeRecoup", "BASE", num, { type = "Condition", var = "Frozen" }) } end,
["recoup effects instead occur over 3 seconds"] = { flag("3SecondRecoup") },
["life recoup effects instead occur over 3 seconds"] = { flag("3SecondLifeRecoup") },
["([%d%.]+)%% of physical damage prevented from hits in the past (%d+) seconds is regenerated as life per second"] = function(num, _, duration) return {
@@ -5443,6 +5458,7 @@ local flagTypes = {
["hindered,? with (%d+)%% reduced movement speed"] = "Condition:Hindered",
["unnerved"] = "Condition:Unnerved",
["malediction"] = "HasMalediction",
["debilitated"] = "Condition:Debilitated"
}
-- Build active skill name lookup