diff --git a/src/Classes/ModStore.lua b/src/Classes/ModStore.lua index 3523140724..ba89af6b2d 100644 --- a/src/Classes/ModStore.lua +++ b/src/Classes/ModStore.lua @@ -246,7 +246,11 @@ function ModStoreClass:HasMod(modType, cfg, ...) end function ModStoreClass:GetCondition(var, cfg, noMod) - return self.conditions[var] or (self.parent and self.parent:GetCondition(var, cfg, true)) or (not noMod and self:Flag(cfg, conditionName[var])) + if (cfg and cfg.overrideCond and cfg.overrideCond[var] ~= nil) then + return cfg.overrideCond[var] + else + return self.conditions[var] or (self.parent and self.parent:GetCondition(var, cfg, true)) or (not noMod and self:Flag(cfg, conditionName[var])) + end end function ModStoreClass:GetMultiplier(var, cfg, noMod) diff --git a/src/Data/ModCache.lua b/src/Data/ModCache.lua index c68a19fa2c..3ccf0f7c35 100755 --- a/src/Data/ModCache.lua +++ b/src/Data/ModCache.lua @@ -5057,7 +5057,7 @@ c["Leech Life 20% slower"]={{[1]={flags=0,keywordFlags=0,name="LifeLeechRate",ty c["Leech Life 25% faster"]={{[1]={flags=0,keywordFlags=0,name="LifeLeechRate",type="INC",value=25}},nil} c["Leech Life 5% slower"]={{[1]={flags=0,keywordFlags=0,name="LifeLeechRate",type="INC",value=-5}},nil} c["Leech from Critical Hits is instant"]={{[1]={[1]={type="Condition",var="CriticalStrike"},flags=0,keywordFlags=0,name="InstantLifeLeech",type="BASE",value=100},[2]={[1]={type="Condition",var="CriticalStrike"},flags=0,keywordFlags=0,name="InstantManaLeech",type="BASE",value=100},[3]={[1]={type="Condition",var="CriticalStrike"},flags=0,keywordFlags=0,name="InstantEnergyShieldLeech",type="BASE",value=100}},nil} -c["Leech recovers based on Chaos Damage as well as Physical Damage"]={nil,"Leech recovers based on Chaos Damage as well as Physical Damage "} +c["Leech recovers based on Chaos Damage as well as Physical Damage"]={{[1]={flags=0,keywordFlags=0,name="LifeLeechBasedOnChaosDamage",type="FLAG",value=true},[2]={flags=0,keywordFlags=0,name="ManaLeechBasedOnChaosDamage",type="FLAG",value=true},[3]={flags=0,keywordFlags=0,name="EnergyShieldLeechBasedOnChaosDamage",type="FLAG",value=true}},nil} c["Leeches 0.1% of Physical Damage as Life"]={{[1]={flags=0,keywordFlags=0,name="PhysicalDamageLifeLeech",type="BASE",value=0.1}},nil} c["Leeches 1% of maximum Life when you Cast a Spell"]={nil,"Leeches 1% of maximum Life when you Cast a Spell "} c["Leeches 10% of Physical Damage as Life"]={{[1]={flags=0,keywordFlags=0,name="PhysicalDamageLifeLeech",type="BASE",value=10}},nil} @@ -5089,8 +5089,8 @@ c["Life Leech effects are not removed when Unreserved Life is Filled"]={{[1]={fl c["Life Leech from your Hits also applies to your Companion"]={nil,"Life Leech from your Hits also applies to your Companion "} c["Life Leech is Converted to Energy Shield Leech"]={{[1]={flags=0,keywordFlags=0,name="GhostReaver",type="FLAG",value=true}},nil} c["Life Leech is Instant"]={{[1]={flags=0,keywordFlags=0,name="InstantLifeLeech",type="BASE",value=100}},nil} -c["Life Leech recovers based on your Chaos damage instead of Physical damage"]={nil,"Life Leech recovers based on your Chaos damage instead of Physical damage "} -c["Life Leech recovers based on your Elemental damage as well as Physical damage"]={{[1]={flags=0,keywordFlags=0,name="PhysicalAsElementalDamageLifeLeech",type="FLAG",value=true}},nil} +c["Life Leech recovers based on your Chaos damage instead of Physical damage"]={{[1]={flags=0,keywordFlags=0,name="LifeLeechBasedOnChaosDamage",type="FLAG",value=true},[2]={flags=0,keywordFlags=0,name="Condition:NoLifeLeechFromPhysicalDamage",type="FLAG",value=true}},nil} +c["Life Leech recovers based on your Elemental damage as well as Physical damage"]={{[1]={flags=0,keywordFlags=0,name="LifeLeechBasedOnElementalDamage",type="FLAG",value=true}},nil} c["Life Leeched from Empowered Attacks is Instant"]={nil,"Life Leeched from Empowered Attacks is Instant "} c["Life Recharges"]={nil,"Life Recharges "} c["Life Recharges instead of Energy Shield"]={{[1]={flags=0,keywordFlags=0,name="EnergyShieldRechargeAppliesToLife",type="FLAG",value=true}},nil} @@ -5142,7 +5142,7 @@ c["Mana Flasks gain 0.1 charges per Second"]={{[1]={flags=0,keywordFlags=0,name= c["Mana Flasks gain 0.22 charges per Second"]={{[1]={flags=0,keywordFlags=0,name="ManaFlaskChargesGenerated",type="BASE",value=0.22}},nil} c["Mana Flasks gain 0.25 charges per Second"]={{[1]={flags=0,keywordFlags=0,name="ManaFlaskChargesGenerated",type="BASE",value=0.25}},nil} c["Mana Flasks used while on Low Mana apply Recovery Instantly"]={{[1]={[1]={type="Condition",var="LowMana"},flags=0,keywordFlags=0,name="ManaFlaskInstantRecovery",type="BASE",value=100}},nil} -c["Mana Leech recovers based on Elemental Damage Types instead of Physical Damage"]={nil,"Mana Leech recovers based on Elemental Damage Types instead of Physical Damage "} +c["Mana Leech recovers based on Elemental Damage Types instead of Physical Damage"]={{[1]={flags=0,keywordFlags=0,name="ManaLeechBasedOnElementalDamage",type="FLAG",value=true},[2]={flags=0,keywordFlags=0,name="Condition:NoManaLeechFromPhysicalDamage",type="FLAG",value=true}},nil} c["Mana Recovery from Regeneration is not applied"]={{[1]={flags=0,keywordFlags=0,name="UnaffectedByManaRegen",type="FLAG",value=true}},nil} c["Mana Recovery other than Regeneration cannot Recover Mana"]={nil,"Mana Recovery other than Regeneration cannot Recover Mana "} c["Mark Skills have 10% increased Use Speed"]={{}," Use Speed "} diff --git a/src/Modules/CalcOffence.lua b/src/Modules/CalcOffence.lua index d7a745415f..8f2531ca5d 100644 --- a/src/Modules/CalcOffence.lua +++ b/src/Modules/CalcOffence.lua @@ -3693,25 +3693,60 @@ function calcs.offence(env, actor, activeSkill) local lifeLeech = 0 local energyShieldLeech = 0 local manaLeech = 0 - if skillFlags.mine or skillFlags.trap or skillFlags.totem then - lifeLeech = skillModList:Sum("BASE", cfg, "DamageLifeLeechToPlayer") - else - if skillModList:Flag(nil, "LifeLeechBasedOnChaosDamage") then - if damageType == "Chaos" then - lifeLeech = skillModList:Sum("BASE", cfg, "DamageLeech", "DamageLifeLeech", "PhysicalDamageLifeLeech", "LightningDamageLifeLeech", "ColdDamageLifeLeech", "FireDamageLifeLeech", "ChaosDamageLifeLeech", "ElementalDamageLifeLeech") + enemyDB:Sum("BASE", cfg, "SelfDamageLifeLeech") / 100 + + -- Determine base leech value according to resource (using function to avoid repetition) + ---@param resource string "Life" | "Mana" | "EnergyShield" + ---@param dmgType string "Physical" | "Cold" | "Fire" | "Lightning" | "Chaos" + ---@return number + local function getBaseLeech(resource, dmgType) + local leech = 0 + if (not skillModList:Flag(cfg, "Condition:No" .. resource .. "LeechFrom" .. dmgType .. "Damage" )) and not (isElemental[dmgType] and skillModList:Flag(cfg, "No" .. resource .. "LeechFromElementalDamage" )) then + -- Check if converted physical leech (most PoE2 leech is physical only by default) + local convertModName, convertFlag + if isElemental[dmgType] and skillModList:Flag(cfg, resource .. "LeechBasedOnElementalDamage") then + convertFlag = resource .. "LeechBasedOnElementalDamage" + convertModName = "ElementalDamage" .. resource .. "Leech" + elseif skillModList:Flag(cfg, resource .. "LeechBasedOn".. dmgType .. "Damage") then + convertFlag = resource .. "LeechBasedOn" .. dmgType .. "Damage" + convertModName = dmgType .. "Damage" .. resource .. "Leech" end - else - if pass == 1 and damageType == "Physical" and skillModList:Flag(nil, "PhysicalAsElementalDamageLifeLeech") then - skillModList:NewMod("ElementalDamageLifeLeech", "BASE", skillModList:Sum("BASE", cfg, "PhysicalDamageLifeLeech"), "Mystic Harvest") + if convertModName and convertFlag then + local tempCfg = copyTable(cfg, true) + tempCfg.overrideCond = { ["No" .. resource .. "LeechFromPhysicalDamage"] = false } -- Need to force Condition to `false`, to calculate original phys leech values + local physLeechMods = skillModList:Tabulate("BASE", tempCfg , "PhysicalDamage" .. resource .. "Leech") + for _, entry in ipairs(physLeechMods) do + -- Add new leech mods for that damage type with the same conditions, source, etc. + local newMod = copyTable(entry.mod) + newMod.name = convertModName + -- Tags that specifically disable Physical Damage leech need to be removed + local hasNoPhysLeech, tagIndex = modLib.hasTag(newMod, { type = "Condition", var = "No" .. resource .. "LeechFromPhysicalDamage", neg = true }) + if hasNoPhysLeech then + t_remove(newMod, tagIndex) + end + if not skillModList:ReplaceModInternal(newMod) then -- using `ReplaceModInternal` instead of `ReplaceMod`, so I don't have to unpack the mod first + skillModList:AddMod(newMod) + end + end + end + leech = skillModList:Sum("BASE", cfg, "Damage" .. resource .. "Leech", dmgType.."Damage" .. resource .. "Leech", isElemental[dmgType] and "ElementalDamage" .. resource .. "Leech" or nil) + enemyDB:Sum("BASE", cfg, "SelfDamage" .. resource .. "Leech") / 100 + elseif skillModList:Flag(cfg, "Condition:No" .. resource .. "LeechFrom" .. dmgType .. "Damage" ) then + -- dmgType leech should not apply, but still needs to exist for possible conversion so adding additional condition tag instead + local noLeechFlagTag = { type = "Condition", var = "No" .. resource .. "LeechFrom" .. dmgType .. "Damage", neg = true } + for _, entry in ipairs(skillModList:Tabulate("BASE", cfg, dmgType .. "Damage" .. resource .. "Leech")) do + if not modLib.hasTag(entry.mod, noLeechFlagTag) then + t_insert(entry.mod, noLeechFlagTag ) + end end - lifeLeech = skillModList:Sum("BASE", cfg, "DamageLeech", "DamageLifeLeech", damageType.."DamageLifeLeech", isElemental[damageType] and "ElementalDamageLifeLeech" or nil) + enemyDB:Sum("BASE", cfg, "SelfDamageLifeLeech") / 100 - end - energyShieldLeech = skillModList:Sum("BASE", cfg, "DamageEnergyShieldLeech", damageType.."DamageEnergyShieldLeech", isElemental[damageType] and "ElementalDamageEnergyShieldLeech" or nil) + enemyDB:Sum("BASE", cfg, "SelfDamageEnergyShieldLeech") / 100 - if pass == 1 and damageType == "Physical" and skillModList:Flag(nil, "PhysicalAsAllDamageManaLeech") then - skillModList:NewMod("ElementalDamageManaLeech", "BASE", skillModList:Sum("BASE", cfg, "PhysicalDamageLifeLeech"), "Ravenous Doubts") - skillModList:NewMod("ChaosDamageManaLeech", "BASE", skillModList:Sum("BASE", cfg, "PhysicalDamageLifeLeech"), "Ravenous Doubts") end - manaLeech = skillModList:Sum("BASE", cfg, "DamageLeech", "DamageManaLeech", damageType.."DamageManaLeech", isElemental[damageType] and "ElementalDamageManaLeech" or nil) + enemyDB:Sum("BASE", cfg, "SelfDamageManaLeech") / 100 + return leech and leech or 0 + end + + if skillFlags.mine or skillFlags.trap or skillFlags.totem then + lifeLeech = skillModList:Sum("BASE", cfg, "DamageLifeLeechToPlayer") + else + lifeLeech = getBaseLeech("Life", damageType) + energyShieldLeech = getBaseLeech("EnergyShield", damageType) + manaLeech = getBaseLeech("Mana", damageType) end if ghostReaver and not noLifeLeech then diff --git a/src/Modules/CalcSections.lua b/src/Modules/CalcSections.lua index 59b630cabb..a6f3549b73 100644 --- a/src/Modules/CalcSections.lua +++ b/src/Modules/CalcSections.lua @@ -1101,6 +1101,7 @@ return { { label = "Off Hand", notFlag = "totem", flag = "weapon2Attack", modName = { "DamageLeech", "DamageLifeLeech", "PhysicalDamageLifeLeech", "LightningDamageLifeLeech", "ColdDamageLifeLeech", "FireDamageLifeLeech", "ChaosDamageLifeLeech", "ElementalDamageLifeLeech" }, modType = "BASE", cfg = "weapon2" }, { label = "Totem modifiers", flag = "totem", modName = { "DamageLifeLeechToPlayer" }, modType = "BASE", cfg = "skill" }, { label = "Enemy modifiers", modName = { "SelfDamageLifeLeech" }, modType = "BASE", enemy = true }, + { label = "Leech Conversion", modName = { "LifeLeechBasedOnPhysicalDamage", "LifeLeechBasedOnColdDamage", "LifeLeechBasedOnFireDamage", "LifeLeechBasedOnLightningDamage", "LifeLeechBasedOnElementalDamage", "LifeLeechBasedOnChaosDamage", "Condition:NoLifeLeechFromPhysicalDamage", "Condition:NoLifeLeechFromColdDamage", "Condition:NoLifeLeechFromFireDamage", "Condition:NoLifeLeechFromLightningDamage", "Condition:NoLifeLeechFromElementalDamage", "Condition:NoLifeLeechFromChaosDamage" }, cfg = "skill" }, }, }, { label = "Life Leech per Hit", flagList = { "leechLife", "showAverage" }, { format = "{1:output:LifeLeechPerHit}", { breakdown = "LifeLeech" }, @@ -1109,6 +1110,7 @@ return { { label = "Off Hand", notFlag = "totem", flag = "weapon2Attack", modName = { "DamageLeech", "DamageLifeLeech", "PhysicalDamageLifeLeech", "LightningDamageLifeLeech", "ColdDamageLifeLeech", "FireDamageLifeLeech", "ChaosDamageLifeLeech", "ElementalDamageLifeLeech" }, modType = "BASE", cfg = "weapon2" }, { label = "Totem modifiers", flag = "totem", modName = { "DamageLifeLeechToPlayer" }, modType = "BASE", cfg = "skill" }, { label = "Enemy modifiers", modName = { "SelfDamageLifeLeech" }, modType = "BASE", enemy = true }, + { label = "Leech Conversion", modName = { "LifeLeechBasedOnPhysicalDamage", "LifeLeechBasedOnColdDamage", "LifeLeechBasedOnFireDamage", "LifeLeechBasedOnLightningDamage", "LifeLeechBasedOnElementalDamage", "LifeLeechBasedOnChaosDamage", "Condition:NoLifeLeechFromPhysicalDamage", "Condition:NoLifeLeechFromColdDamage", "Condition:NoLifeLeechFromFireDamage", "Condition:NoLifeLeechFromLightningDamage", "Condition:NoLifeLeechFromElementalDamage", "Condition:NoLifeLeechFromChaosDamage" }, cfg = "skill" }, }, }, { label = "Life Gain Rate", notFlag = "showAverage", haveOutput = "LifeOnHitRate", { format = "{1:output:LifeOnHitRate}", { label = "Player modifiers", notFlag = "attack", modName = "LifeOnHit", modType = "BASE", cfg = "skill" }, @@ -1136,6 +1138,7 @@ return { { label = "Off Hand", notFlag = "totem", flag = "weapon2Attack", modName = { "DamageEnergyShieldLeech", "PhysicalDamageEnergyShieldLeech", "LightningDamageEnergyShieldLeech", "ColdDamageEnergyShieldLeech", "FireDamageEnergyShieldLeech", "ChaosDamageEnergyShieldLeech", "ElementalDamageEnergyShieldLeech" }, modType = "BASE", cfg = "weapon2" }, { label = "Totem modifiers", flag = "totem", modName = { "DamageEnergyShieldLeechToPlayer" }, modType = "BASE", cfg = "skill" }, { label = "Enemy modifiers", modName = { "SelfDamageEnergyShieldLeech" }, modType = "BASE", enemy = true }, + { label = "Leech Conversion", modName = { "EnergyShieldLeechBasedOnPhysicalDamage", "EnergyShieldLeechBasedOnColdDamage", "EnergyShieldLeechBasedOnFireDamage", "EnergyShieldLeechBasedOnLightningDamage", "EnergyShieldLeechBasedOnElementalDamage", "EnergyShieldLeechBasedOnChaosDamage", "Condition:NoEnergyShieldLeechFromPhysicalDamage", "Condition:NoEnergyShieldLeechFromColdDamage", "Condition:NoEnergyShieldLeechFromFireDamage", "Condition:NoEnergyShieldLeechFromLightningDamage", "Condition:NoEnergyShieldLeechFromElementalDamage", "Condition:NoEnergyShieldLeechFromChaosDamage" }, cfg = "skill" }, }, }, { label = "ES Leech per Hit", flagList = { "leechES", "showAverage" }, { format = "{1:output:EnergyShieldLeechPerHit}", { breakdown = "EnergyShieldLeech" }, @@ -1144,6 +1147,7 @@ return { { label = "Off Hand", notFlag = "totem", flag = "weapon2Attack", modName = { "DamageEnergyShieldLeech", "PhysicalDamageEnergyShieldLeech", "LightningDamageEnergyShieldLeech", "ColdDamageEnergyShieldLeech", "FireDamageEnergyShieldLeech", "ChaosDamageEnergyShieldLeech", "ElementalDamageEnergyShieldLeech" }, modType = "BASE", cfg = "weapon2" }, { label = "Totem modifiers", flag = "totem", modName = { "DamageEnergyShieldLeechToPlayer" }, modType = "BASE", cfg = "skill" }, { label = "Enemy modifiers", modName = { "SelfDamageEnergyShieldLeech" }, modType = "BASE", enemy = true }, + { label = "Leech Conversion", modName = { "EnergyShieldLeechBasedOnPhysicalDamage", "EnergyShieldLeechBasedOnColdDamage", "EnergyShieldLeechBasedOnFireDamage", "EnergyShieldLeechBasedOnLightningDamage", "EnergyShieldLeechBasedOnElementalDamage", "EnergyShieldLeechBasedOnChaosDamage", "Condition:NoEnergyShieldLeechFromPhysicalDamage", "Condition:NoEnergyShieldLeechFromColdDamage", "Condition:NoEnergyShieldLeechFromFireDamage", "Condition:NoEnergyShieldLeechFromLightningDamage", "Condition:NoEnergyShieldLeechFromElementalDamage", "Condition:NoEnergyShieldLeechFromChaosDamage" }, cfg = "skill" }, }, }, { label = "ES Gain Rate", notFlag = "showAverage", haveOutput = "EnergyShieldOnHitRate", { format = "{1:output:EnergyShieldOnHitRate}", { label = "Player modifiers", notFlag = "attack", modName = "EnergyShieldOnHit", modType = "BASE", cfg = "skill" }, @@ -1170,6 +1174,7 @@ return { { label = "Main Hand", flag = "weapon1Attack", modName = { "DamageLeech", "DamageManaLeech", "PhysicalDamageManaLeech", "LightningDamageManaLeech", "ColdDamageManaLeech", "FireDamageManaLeech", "ChaosDamageManaLeech", "ElementalDamageManaLeech" }, modType = "BASE", cfg = "weapon1" }, { label = "Off Hand", flag = "weapon2Attack", modName = { "DamageLeech", "DamageManaLeech", "PhysicalDamageManaLeech", "LightningDamageManaLeech", "ColdDamageManaLeech", "FireDamageManaLeech", "ChaosDamageManaLeech", "ElementalDamageManaLeech" }, modType = "BASE", cfg = "weapon2" }, { label = "Enemy modifiers", modName = { "SelfDamageManaLeech" }, modType = "BASE", cfg = "skill", enemy = true }, + { label = "Leech Conversion", modName = { "ManaLeechBasedOnPhysicalDamage", "ManaLeechBasedOnColdDamage", "ManaLeechBasedOnFireDamage", "ManaLeechBasedOnLightningDamage", "ManaLeechBasedOnElementalDamage", "ManaLeechBasedOnChaosDamage", "Condition:NoManaLeechFromPhysicalDamage", "Condition:NoManaLeechFromColdDamage", "Condition:NoManaLeechFromFireDamage", "Condition:NoManaLeechFromLightningDamage", "Condition:NoManaLeechFromElementalDamage", "Condition:NoManaLeechFromChaosDamage" }, cfg = "skill" }, }, }, { label = "Mana Leech per Hit", flagList = { "leechMana", "showAverage" }, { format = "{1:output:ManaLeechPerHit}", { breakdown = "ManaLeech" }, @@ -1177,6 +1182,7 @@ return { { label = "Main Hand", flag = "weapon1Attack", modName = { "DamageLeech", "DamageManaLeech", "PhysicalDamageManaLeech", "LightningDamageManaLeech", "ColdDamageManaLeech", "FireDamageManaLeech", "ChaosDamageManaLeech", "ElementalDamageManaLeech" }, modType = "BASE", cfg = "weapon1" }, { label = "Off Hand", flag = "weapon2Attack", modName = { "DamageLeech", "DamageManaLeech", "PhysicalDamageManaLeech", "LightningDamageManaLeech", "ColdDamageManaLeech", "FireDamageManaLeech", "ChaosDamageManaLeech", "ElementalDamageManaLeech" }, modType = "BASE", cfg = "weapon2" }, { label = "Enemy modifiers", modName = { "SelfDamageManaLeech" }, modType = "BASE", enemy = true }, + { label = "Leech Conversion", modName = { "ManaLeechBasedOnPhysicalDamage", "ManaLeechBasedOnColdDamage", "ManaLeechBasedOnFireDamage", "ManaLeechBasedOnLightningDamage", "ManaLeechBasedOnElementalDamage", "ManaLeechBasedOnChaosDamage", "Condition:NoManaLeechFromPhysicalDamage", "Condition:NoManaLeechFromColdDamage", "Condition:NoManaLeechFromFireDamage", "Condition:NoManaLeechFromLightningDamage", "Condition:NoManaLeechFromElementalDamage", "Condition:NoManaLeechFromChaosDamage" }, cfg = "skill" }, }, }, { label = "Mana Gain Rate", notFlag = "showAverage", haveOutput = "ManaOnHitRate", { format = "{1:output:ManaOnHitRate}", { label = "Player modifiers", notFlag = "attack", modName = "ManaOnHit", modType = "BASE", cfg = "skill" }, diff --git a/src/Modules/ModParser.lua b/src/Modules/ModParser.lua index 70353de369..4423b1561f 100644 --- a/src/Modules/ModParser.lua +++ b/src/Modules/ModParser.lua @@ -2396,6 +2396,10 @@ local specialModList = { flag("IgniteToChaos", { type = "SkillType", skillType = SkillType.Spell },{ type = "SkillType", skillType = SkillType[firstToUpper(spellType)]}), mod("SkillData", "LIST", { key = "IgniteToChaos", value = true }, { type = "SkillType", skillType = SkillType.Spell },{ type = "SkillType", skillType = SkillType[firstToUpper(spellType)]}), } end, + ["mana leech recovers based on elemental damage types instead of physical damage"] = { + flag("ManaLeechBasedOnElementalDamage"), + flag("Condition:NoManaLeechFromPhysicalDamage"), + }, -- Legacy support ["(%d+)%% chance to defend with double armour"] = function(numChance) return { mod("ArmourDefense", "MAX", 100, "Armour Mastery: Max Calc", { type = "Condition", var = "ArmourMax" }), @@ -2453,12 +2457,56 @@ local specialModList = { ["leeche?s? ([%d%.]+)%% of (%a+) damage as life"] = function(num, _, dmgType) return { mod(firstToUpper(dmgType) .. "DamageLifeLeech", "BASE", num), } end, + ["leeche?s? ([%d%.]+)%% of (%a+) damage as energy shield"] = function(num, _, dmgType) return { + mod(firstToUpper(dmgType) .. "DamageEnergyShieldLeech", "BASE", num), + } end, ["leeche?s? ([%d%.]+)%% of (%a+) attack damage as mana"] = function(num, _, dmgType) return { mod(firstToUpper(dmgType) .. "DamageManaLeech", "BASE", num, nil, ModFlag.Attack, 0), } end, ["leeche?s? ([%d%.]+)%% of (%a+) attack damage as life"] = function(num, _, dmgType) return { mod(firstToUpper(dmgType) .. "DamageLifeLeech", "BASE", num, nil, ModFlag.Attack, 0), } end, + ["leeche?s? ([%d%.]+)%% of (%a+) attack damage as energy shield"] = function(num, _, dmgType) return { + mod(firstToUpper(dmgType) .. "DamageEnergyShieldLeech", "BASE", num, nil, ModFlag.Attack, 0), + } end, + ["(%a+%s?%a*) leech recovers based on y?o?u?r? ?(%a+) damage as well as physical damage"] = function(_, resource, dmgType) + local resourceTable = { + ["life"] = "Life", + ["mana"] = "Mana", + ["energy shield"] = "EnergyShield", + } + if resourceTable[resource] then + return { flag(resourceTable[resource] .. "LeechBasedOn" .. firstToUpper(dmgType) .. "Damage"), } + else + return { nil } + end + end, + ["(%a+%s?%a*) leech recovers based on y?o?u?r? ?(%a+) damage instead of physical damage"] = function(_, resource, dmgType) + local resourceTable = { + ["life"] = "Life", + ["mana"] = "Mana", + ["energy shield"] = "EnergyShield", + } + if resourceTable[resource] then + return { flag(resourceTable[resource] .. "LeechBasedOn" .. firstToUpper(dmgType) .. "Damage"), flag("Condition:No" .. resourceTable[resource] .. "LeechFromPhysicalDamage"), } + else + return { nil } + end + end, + ["leech recovers based on y?o?u?r? ?(%a+) damage as well as physical damage"] = function(_, dmgType) + return { + flag("LifeLeechBasedOn" .. firstToUpper(dmgType) .. "Damage"), + flag("ManaLeechBasedOn" .. firstToUpper(dmgType) .. "Damage"), + flag("EnergyShieldLeechBasedOn" .. firstToUpper(dmgType) .. "Damage"), + } + end, + ["leech recovers based on y?o?u?r? ?(%a+) damage instead of physical damage"] = function(_, dmgType) + return { + flag("LifeLeechBasedOn" .. firstToUpper(dmgType) .. "Damage"), flag("Condition:NoLifeLeechFromPhysicalDamage"), + flag("ManaLeechBasedOn" .. firstToUpper(dmgType) .. "Damage"), flag("Condition:NoManaLeechFromPhysicalDamage"), + flag("EnergyShieldLeechBasedOn" .. firstToUpper(dmgType) .. "Damage"), flag("Condition:NoEnergyShieldLeechFromPhysicalDamage"), + } + end, -- Amazon ["chance to hit with attacks can exceed 100%%"] = {flag("Condition:HitChanceCanExceed100", { type = "Skilltype", skillType = SkillType.Attack})}, ["gain additional critical hit chance equal to (%d+)%% of excess chance to hit with attacks"] = function(num) return { mod("CritChance", "BASE", 0.01 * num, { type = "Multiplier", var = "ExcessHitChance" }, { type = "SkillType", skillType = SkillType.Attack})} end, @@ -2468,7 +2516,6 @@ local specialModList = { mod(firstToUpper(dmgType) .. "Min", "BASE", 1, nil, ModFlag.Attack, { type = "PercentStat", stat = "AccuracyOnWeapon 2", percent = num }, { type = "SkillType", skillType = SkillType.NonWeaponAttack, neg = true } , { type = "Condition", var = "OffHandAttack" }), mod(firstToUpper(dmgType) .. "Max", "BASE", 1, nil, ModFlag.Attack, { type = "PercentStat", stat = "AccuracyOnWeapon 2", percent = num }, { type = "SkillType", skillType = SkillType.NonWeaponAttack, neg = true } , { type = "Condition", var = "OffHandAttack" }), } end, - ["life leech recovers based on your elemental damage as well as physical damage"] = { flag("PhysicalAsElementalDamageLifeLeech"), }, ["evasion rating from equipped helmet, gloves and boots is doubled"] = { mod("Evasion", "MORE", 100, { type = "SlotName", slotNameList = { "Helmet", "Boots", "Gloves" } }) }, ["evasion rating from equipped body armour is halved"] = { mod("Evasion", "MORE", -50, { type = "SlotName", slotName = "Body Armour" }) }, -- Ascendant @@ -3042,7 +3089,10 @@ local specialModList = { mod("DamageGainAs"..firstToUpper(strType), "BASE", tonumber(num2) * (num / 100), nil, ModFlag.Hit, 0), } end, ["effect and duration of flames of chayula on you is doubled"] = { mod("Multiplier:FlameEffect", "BASE", 1) } , - ["mana leech recovers based on other damage types damage as well as physical damage"] = { flag("PhysicalAsAllDamageManaLeech") }, + ["mana leech recovers based on other damage types damage as well as physical damage"] = { -- legacy wording + flag("ManaLeechBasedOnElementalDamage"), + flag("ManaLeechBasedOnChaosDamage"), + }, -- Monk - Invoker ["critical hits ignore non%-negative enemy monster elemental resistances"] = { flag("IgnoreNonNegativeEleRes", { type = "Condition", var = "CriticalStrike" }) }, ["(%d+)%% chance on shocking enemies to created shocked ground"] = { mod("ShockBase", "BASE", data.nonDamagingAilment["Shock"].default, { type = "ActorCondition", actor = "enemy", var = "OnShockedGround" }) }, @@ -4459,7 +4509,10 @@ local specialModList = { mod("InstantLifeLeech", "BASE", 100, { type = "Condition", var = "CriticalStrike" }), mod("InstantManaLeech", "BASE", 100, { type = "Condition", var = "CriticalStrike" }) }, - ["with 5 corrupted items equipped: life leech recovers based on your chaos damage instead"] = { flag("LifeLeechBasedOnChaosDamage", { type = "MultiplierThreshold", var = "CorruptedItem", threshold = 5 }) }, + ["with 5 corrupted items equipped: life leech recovers based on your chaos damage instead"] = { + flag("LifeLeechBasedOnChaosDamage", { type = "MultiplierThreshold", var = "CorruptedItem", threshold = 5 }), + flag("Condition:NoLifeLeechFromPhysicalDamage", { type = "MultiplierThreshold", var = "CorruptedItem", threshold = 5 }), + }, ["you have vaal pact if you've dealt a critical hit recently"] = { mod("Keystone", "LIST", "Vaal Pact", { type = "Condition", var = "CritRecently" }) }, ["you have vaal pact while at maximum endurance charges"] = { mod("Keystone", "LIST", "Vaal Pact", { type = "StatThreshold", stat = "EnduranceCharges", thresholdStat = "EnduranceChargesMax" }) }, ["you have vaal pact while focus?sed"] = { mod("Keystone", "LIST", "Vaal Pact", { type = "Condition", var = "Focused" }) }, diff --git a/src/Modules/ModTools.lua b/src/Modules/ModTools.lua index 27f73271fc..049e9d58e5 100644 --- a/src/Modules/ModTools.lua +++ b/src/Modules/ModTools.lua @@ -233,4 +233,36 @@ function modLib.setSource(mod, source) mod.value.mod.source = source end return mod +end + +-- Check if a mod contains a specific tag already +-- Note: All keys AND values need to be matched exactly +---@param mod table individual mod that is to be checked +---@param searchTag table exact tag you're searching for, e.g. { type = "Condition", var = "Shocked" } +---@return boolean @returns `true` if tag is found, otherwise `false` +---@return number @returns position as `number` if tag exists or `0` if not +function modLib.hasTag(mod, searchTag) + local numSearchKeys = 0 -- Apparently Lua doesn't have a built in functionality to return the number of keys in a table(?) so have to determine manually + for _, __ in pairs(searchTag) do + numSearchKeys = numSearchKeys + 1 + end + + for i, tag in ipairs(mod) do + local numTagKeys = 0 -- Total number of keys in tag + local numMatchedKeys = 0 -- Number of keys matching with searchTag + if type(tag) == "table" then + for key, value in pairs(searchTag) do + numTagKeys = numTagKeys + 1 + if tag[key] and (tag[key] == value) then + numMatchedKeys = numMatchedKeys + 1 + else + break -- this is not the correct tag + end + end + end + if (numSearchKeys > 0) and (numSearchKeys == numMatchedKeys) and (numSearchKeys == numTagKeys) then + return true, i + end + end + return false, 0 end \ No newline at end of file