Skip to content

Instantly share code, notes, and snippets.

@tntmarket
Created March 9, 2021 21:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tntmarket/18f9cc0531876f9f104b5e0820388a31 to your computer and use it in GitHub Desktop.
Save tntmarket/18f9cc0531876f9f104b5e0820388a31 to your computer and use it in GitHub Desktop.
The error started happening after I copied over these changes
diff --git b/Modules/Build.lua a/Modules/Build.lua
index 5fc85bf2..e84397b6 100644
--- b/Modules/Build.lua
+++ a/Modules/Build.lua
@@ -602,9 +602,19 @@ function buildMode:Init(dbFileName, buildName, buildXML, convertBuild)
-- Initialise class dropdown
for classId, class in pairs(self.latestTree.classes) do
+ local ascendancies = {}
+ -- Initialise ascendancy dropdown
+ for i = 0, #class.classes do
+ local ascendClass = class.classes[i]
+ t_insert(ascendancies, {
+ label = ascendClass.name,
+ ascendClassId = i,
+ })
+ end
t_insert(self.controls.classDrop.list, {
label = class.name,
classId = classId,
+ ascendencies = ascendancies,
})
end
table.sort(self.controls.classDrop.list, function(a, b) return a.label < b.label end)
@@ -784,17 +794,8 @@ function buildMode:OnFrame(inputEvents)
end
self:ProcessControlsInput(inputEvents, main.viewPort)
- -- Update contents of ascendancy class dropdown
- wipeTable(self.controls.ascendDrop.list)
- for i = 0, #self.spec.curClass.classes do
- local ascendClass = self.spec.curClass.classes[i]
- t_insert(self.controls.ascendDrop.list, {
- label = ascendClass.name,
- ascendClassId = i,
- })
- end
-
self.controls.classDrop:SelByValue(self.spec.curClassId, "classId")
+ self.controls.ascendDrop.list = self.controls.classDrop:GetSelValue("ascendencies")
self.controls.ascendDrop:SelByValue(self.spec.curAscendClassId, "ascendClassId")
for _, diff in pairs({ "bandit", "pantheonMajorGod", "pantheonMinorGod" }) do
@@ -810,7 +811,13 @@ function buildMode:OnFrame(inputEvents)
self.calcsTab:BuildOutput()
self:RefreshStatList()
end
- if main.showThousandsSidebar ~= self.lastShowThousandsSidebar then
+ if main.showThousandsSeparators ~= self.lastShowThousandsSeparators then
+ self:RefreshStatList()
+ end
+ if main.thousandsSeparator ~= self.lastShowThousandsSeparator then
+ self:RefreshStatList()
+ end
+ if main.decimalSeparator ~= self.lastShowDecimalSeparator then
self:RefreshStatList()
end
if main.showTitlebarName ~= self.lastShowTitlebarName then
@@ -1093,12 +1100,12 @@ function buildMode:FormatStat(statData, statVal)
local val = statVal * ((statData.pc or statData.mod) and 100 or 1) - (statData.mod and 100 or 0)
local color = (statVal >= 0 and "^7" or colorCodes.NEGATIVE)
local valStr = s_format("%"..statData.fmt, val)
- if main.showThousandsSidebar then
- valStr = color .. formatNumSep(valStr)
- else
- valStr = color .. valStr
- end
- self.lastShowThousandsSidebar = main.showThousandsSidebar
+ valStr:gsub("%.", main.decimalSeparator)
+ valStr = color .. formatNumSep(valStr)
+
+ self.lastShowThousandsSeparators = main.showThousandsSeparators
+ self.lastShowThousandsSeparator = main.thousandsSeparator
+ self.lastShowDecimalSeparator = main.decimalSeparator
self.lastShowTitlebarName = main.showTitlebarName
return valStr
end
@@ -1167,9 +1174,9 @@ function buildMode:CompareStatList(tooltip, statList, actor, baseOutput, compare
local color = ((statData.lowerIsBetter and diff < 0) or (not statData.lowerIsBetter and diff > 0)) and colorCodes.POSITIVE or colorCodes.NEGATIVE
local val = diff * ((statData.pc or statData.mod) and 100 or 1)
local valStr = s_format("%+"..statData.fmt, val) -- Can't use self:FormatStat, because it doesn't have %+. Adding that would have complicated a simple function
- if main.showThousandsCalcs then
- valStr = formatNumSep(valStr)
- end
+
+ valStr = formatNumSep(valStr)
+
local line = string.format("%s%s %s", color, valStr, statData.label)
local pcPerPt = ""
if statData.compPercent and statVal1 ~= 0 and statVal2 ~= 0 then
diff --git b/Modules/CalcActiveSkill.lua a/Modules/CalcActiveSkill.lua
index 387271d7..2d5aa0c0 100644
--- b/Modules/CalcActiveSkill.lua
+++ a/Modules/CalcActiveSkill.lua
@@ -671,7 +671,7 @@ function calcs.createMinionSkills(env, activeSkill)
}
if #activeEffect.grantedEffect.levels > 1 then
for level, levelData in ipairs(activeEffect.grantedEffect.levels) do
- if levelData[1] > minion.level then
+ if levelData.levelRequirement > minion.level then
break
else
activeEffect.level = level
diff --git b/Modules/CalcBreakdown.lua a/Modules/CalcBreakdown.lua
index 6f913e32..e69def81 100644
--- b/Modules/CalcBreakdown.lua
+++ a/Modules/CalcBreakdown.lua
@@ -122,7 +122,11 @@ function breakdown.effMult(damageType, resist, pen, taken, mult, takenMore, sour
if pen ~= 0 then
t_insert(out, "Effective resistance:")
t_insert(out, s_format("%d%% ^8(resistance)", resist))
- t_insert(out, s_format("- %d%% ^8(penetration)", pen))
+ if pen < 0 then
+ t_insert(out, s_format("+ %d%% ^8(penetration)", -pen))
+ else
+ t_insert(out, s_format("- %d%% ^8(penetration)", pen))
+ end
t_insert(out, s_format("= %d%%", resist - pen))
end
breakdown.multiChain(out, {
diff --git b/Modules/CalcDefence-2_6.lua a/Modules/CalcDefence-2_6.lua
deleted file mode 100644
index 28ac6b4b..00000000
--- b/Modules/CalcDefence-2_6.lua
+++ /dev/null
@@ -1,608 +0,0 @@
--- Path of Building
---
--- Module: Calc Defence
--- Performs defence calculations.
---
-local calcs = ...
-
-local pairs = pairs
-local ipairs = ipairs
-local t_insert = table.insert
-local m_ceil = math.ceil
-local m_floor = math.floor
-local m_min = math.min
-local m_max = math.max
-local s_format = string.format
-
-local tempTable1 = { }
-
-local isElemental = { Fire = true, Cold = true, Lightning = true }
-
--- List of all damage types, ordered according to the conversion sequence
-local dmgTypeList = {"Physical", "Lightning", "Cold", "Fire", "Chaos"}
-
-local resistTypeList = { "Fire", "Cold", "Lightning", "Chaos" }
-
--- Calculate hit chance
-function calcs.hitChance(evasion, accuracy)
- local rawChance = accuracy / (accuracy + (evasion / 4) ^ 0.8) * 100
- return m_max(m_min(round(rawChance), 95), 5)
-end
-
--- Calculate physical damage reduction from armour
-function calcs.armourReduction(armour, raw)
- return round(armour / (armour + raw * 10) * 100)
-end
-
--- Performs all defensive calculations
-function calcs.defence(env, actor)
- local modDB = actor.modDB
- local enemyDB = actor.enemy.modDB
- local output = actor.output
- local breakdown = actor.breakdown
-
- local condList = modDB.conditions
-
- -- Action Speed
- output.ActionSpeedMod = 1 + (m_max(-75, modDB:Sum("INC", nil, "TemporalChainsActionSpeed")) + modDB:Sum("INC", nil, "ActionSpeed")) / 100
- if modDB:Flag(nil, "ActionSpeedCannotBeBelowBase") then
- output.ActionSpeedMod = m_max(1, output.ActionSpeedMod)
- end
-
- -- Resistances
- output.PhysicalResist = m_min(90, modDB:Sum("BASE", nil, "PhysicalDamageReduction"))
- for _, elem in ipairs(resistTypeList) do
- local max, total
- if elem == "Chaos" and modDB:Flag(nil, "ChaosInoculation") then
- max = 100
- total = 100
- else
- max = modDB:Override(nil, elem.."ResistMax") or m_min(100, modDB:Sum("BASE", nil, elem.."ResistMax"))
- total = modDB:Override(nil, elem.."Resist") or modDB:Sum("BASE", nil, elem.."Resist", isElemental[elem] and "ElementalResist")
- end
- output[elem.."Resist"] = m_min(total, max)
- output[elem.."ResistTotal"] = total
- output[elem.."ResistOverCap"] = m_max(0, total - max)
- if breakdown then
- breakdown[elem.."Resist"] = {
- "Max: "..max.."%",
- "Total: "..total.."%",
- "In hideout: "..(total + 60).."%",
- }
- end
- end
-
- -- Primary defences: Energy shield, evasion and armour
- do
- local ironReflexes = modDB:Flag(nil, "IronReflexes")
- local energyShield = 0
- local armour = 0
- local evasion = 0
- if breakdown then
- breakdown.EnergyShield = { slots = { } }
- breakdown.Armour = { slots = { } }
- breakdown.Evasion = { slots = { } }
- end
- local energyShieldBase = modDB:Sum("BASE", nil, "EnergyShield")
- if energyShieldBase > 0 then
- energyShield = energyShield + energyShieldBase * calcLib.mod(modDB, nil, "EnergyShield", "Defences")
- if breakdown then
- breakdown.slot("Global", nil, nil, energyShieldBase, nil, "EnergyShield", "Defences")
- end
- end
- local armourBase = modDB:Sum("BASE", nil, "Armour", "ArmourAndEvasion")
- if armourBase > 0 then
- armour = armour + armourBase * calcLib.mod(modDB, nil, "Armour", "ArmourAndEvasion", "Defences")
- if breakdown then
- breakdown.slot("Global", nil, nil, armourBase, nil, "Armour", "ArmourAndEvasion", "Defences")
- end
- end
- local evasionBase = modDB:Sum("BASE", nil, "Evasion", "ArmourAndEvasion")
- if evasionBase > 0 then
- if ironReflexes then
- armour = armour + evasionBase * calcLib.mod(modDB, nil, "Armour", "Evasion", "ArmourAndEvasion", "Defences")
- if breakdown then
- breakdown.slot("Conversion", "Evasion to Armour", nil, evasionBase, nil, "Armour", "Evasion", "ArmourAndEvasion", "Defences")
- end
- else
- evasion = evasion + evasionBase * calcLib.mod(modDB, nil, "Evasion", "ArmourAndEvasion", "Defences")
- if breakdown then
- breakdown.slot("Global", nil, nil, evasionBase, nil, "Evasion", "ArmourAndEvasion", "Defences")
- end
- end
- end
- local gearEnergyShield = 0
- local gearArmour = 0
- local gearEvasion = 0
- local slotCfg = wipeTable(tempTable1)
- for _, slot in pairs({"Helmet","Body Armour","Gloves","Boots","Weapon 2","Weapon 3"}) do
- local armourData = actor.itemList[slot] and actor.itemList[slot].armourData
- if armourData then
- slotCfg.slotName = slot
- energyShieldBase = armourData.EnergyShield or 0
- if energyShieldBase > 0 then
- energyShield = energyShield + energyShieldBase * calcLib.mod(modDB, slotCfg, "EnergyShield", "Defences")
- gearEnergyShield = gearEnergyShield + energyShieldBase
- if breakdown then
- breakdown.slot(slot, nil, slotCfg, energyShieldBase, nil, "EnergyShield", "Defences")
- end
- end
- armourBase = armourData.Armour or 0
- if armourBase > 0 then
- if slot == "Body Armour" and modDB:Flag(nil, "Unbreakable") then
- armourBase = armourBase * 2
- end
- armour = armour + armourBase * calcLib.mod(modDB, slotCfg, "Armour", "ArmourAndEvasion", "Defences")
- gearArmour = gearArmour + armourBase
- if breakdown then
- breakdown.slot(slot, nil, slotCfg, armourBase, nil, "Armour", "ArmourAndEvasion", "Defences")
- end
- end
- evasionBase = armourData.Evasion or 0
- if evasionBase > 0 then
- if ironReflexes then
- armour = armour + evasionBase * calcLib.mod(modDB, slotCfg, "Armour", "Evasion", "ArmourAndEvasion", "Defences")
- gearArmour = gearArmour + evasionBase
- if breakdown then
- breakdown.slot(slot, nil, slotCfg, evasionBase, nil, "Armour", "Evasion", "ArmourAndEvasion", "Defences")
- end
- else
- evasion = evasion + evasionBase * calcLib.mod(modDB, slotCfg, "Evasion", "ArmourAndEvasion", "Defences")
- gearEvasion = gearEvasion + evasionBase
- if breakdown then
- breakdown.slot(slot, nil, slotCfg, evasionBase, nil, "Evasion", "ArmourAndEvasion", "Defences")
- end
- end
- end
- end
- end
- local convManaToES = modDB:Sum("BASE", nil, "ManaGainAsEnergyShield")
- if convManaToES > 0 then
- energyShieldBase = modDB:Sum("BASE", nil, "Mana") * convManaToES / 100
- energyShield = energyShield + energyShieldBase * calcLib.mod(modDB, nil, "Mana", "EnergyShield", "Defences")
- if breakdown then
- breakdown.slot("Conversion", "Mana to Energy Shield", nil, energyShieldBase, nil, "EnergyShield", "Defences", "Mana")
- end
- end
- local convLifeToES = modDB:Sum("BASE", nil, "LifeConvertToEnergyShield", "LifeGainAsEnergyShield")
- if convLifeToES > 0 then
- energyShieldBase = modDB:Sum("BASE", nil, "Life") * convLifeToES / 100
- local total
- if modDB:Flag(nil, "ChaosInoculation") then
- total = 1
- else
- total = energyShieldBase * calcLib.mod(modDB, nil, "Life", "EnergyShield", "Defences")
- end
- energyShield = energyShield + total
- if breakdown then
- breakdown.slot("Conversion", "Life to Energy Shield", nil, energyShieldBase, total, "EnergyShield", "Defences", "Life")
- end
- end
- output.EnergyShield = round(energyShield)
- output.Armour = round(armour)
- output.Evasion = round(evasion)
- output.LowestOfArmourAndEvasion = m_min(output.Armour, output.Evasion)
- output["Gear:EnergyShield"] = gearEnergyShield
- output["Gear:Armour"] = gearArmour
- output["Gear:Evasion"] = gearEvasion
- if modDB:Flag(nil, "CannotEvade") then
- output.EvadeChance = 0
- output.MeleeEvadeChance = 0
- output.ProjectileEvadeChance = 0
- else
- local enemyAccuracy = round(calcLib.val(enemyDB, "Accuracy"))
- output.EvadeChance = 100 - calcs.hitChance(output.Evasion, enemyAccuracy) * calcLib.mod(enemyDB, nil, "HitChance")
- if breakdown then
- breakdown.EvadeChance = {
- s_format("Enemy level: %d ^8(%s the Configuration tab)", env.enemyLevel, env.configInput.enemyLevel and "overridden from" or "can be overridden in"),
- s_format("Average enemy accuracy: %d", enemyAccuracy),
- s_format("Approximate evade chance: %d%%", output.EvadeChance),
- }
- end
- output.MeleeEvadeChance = m_max(5, m_min(95, output.EvadeChance * calcLib.mod(modDB, nil, "EvadeChance", "MeleeEvadeChance")))
- output.ProjectileEvadeChance = m_max(5, m_min(95, output.EvadeChance * calcLib.mod(modDB, nil, "EvadeChance", "ProjectileEvadeChance")))
- end
- end
-
- -- Recovery modifiers
- output.LifeRecoveryMod = calcLib.mod(modDB, nil, "LifeRecovery")
- output.LifeRecoveryRateMod = calcLib.mod(modDB, nil, "LifeRecovery", "LifeRecoveryRate")
- output.ManaRecoveryMod = calcLib.mod(modDB, nil, "ManaRecovery")
- output.ManaRecoveryRateMod = calcLib.mod(modDB, nil, "ManaRecovery", "ManaRecoveryRate")
- output.EnergyShieldRecoveryMod = calcLib.mod(modDB, nil, "EnergyShieldRecovery")
- output.EnergyShieldRecoveryRateMod = calcLib.mod(modDB, nil, "EnergyShieldRecovery", "EnergyShieldRecoveryRate")
-
- -- Leech caps
- if modDB:Flag(nil, "GhostReaver") then
- output.MaxEnergyShieldLeechRate = output.EnergyShield * modDB:Sum("BASE", nil, "MaxLifeLeechRate") / 100
- if breakdown then
- breakdown.MaxEnergyShieldLeechRate = {
- s_format("%d ^8(maximum energy shield)", output.EnergyShield),
- s_format("x %d%% ^8(percenage of life to maximum leech rate)", modDB:Sum("BASE", nil, "MaxLifeLeechRate")),
- s_format("= %.1f", output.MaxEnergyShieldLeechRate)
- }
- end
- else
- output.MaxLifeLeechRate = output.Life * modDB:Sum("BASE", nil, "MaxLifeLeechRate") / 100
- if breakdown then
- breakdown.MaxLifeLeechRate = {
- s_format("%d ^8(maximum life)", output.Life),
- s_format("x %d%% ^8(percenage of life to maximum leech rate)", modDB:Sum("BASE", nil, "MaxLifeLeechRate")),
- s_format("= %.1f", output.MaxLifeLeechRate)
- }
- end
- end
- output.MaxManaLeechRate = output.Mana * modDB:Sum("BASE", nil, "MaxManaLeechRate") / 100
- if breakdown then
- breakdown.MaxManaLeechRate = {
- s_format("%d ^8(maximum mana)", output.Mana),
- s_format("x %d%% ^8(percenage of mana to maximum leech rate)", modDB:Sum("BASE", nil, "MaxManaLeechRate")),
- s_format("= %.1f", output.MaxManaLeechRate)
- }
- end
-
- -- Mana, life and energy shield regen
- if modDB:Flag(nil, "NoManaRegen") then
- output.ManaRegen = 0
- else
- local base = modDB:Sum("BASE", nil, "ManaRegen") + output.Mana * modDB:Sum("BASE", nil, "ManaRegenPercent") / 100
- local inc = modDB:Sum("INC", nil, "ManaRegen")
- local more = modDB:More(nil, "ManaRegen")
- local regen = base * (1 + inc/100) * more
- output.ManaRegen = round(regen * output.ManaRecoveryRateMod, 1)
- if breakdown then
- breakdown.ManaRegen = { }
- breakdown.multiChain(breakdown.ManaRegen, {
- label = "Mana Regeneration:",
- base = s_format("%.1f ^8(base)", base),
- { "%.2f ^8(increased/reduced)", 1 + inc/100 },
- { "%.2f ^8(more/less)", more },
- total = s_format("= %.1f ^8per second", regen),
- })
- breakdown.multiChain(breakdown.ManaRegen, {
- label = "Effective Mana Regeneration:",
- base = s_format("%.1f", regen),
- { "%.2f ^8(recovery rate modifier)", output.ManaRecoveryRateMod },
- total = s_format("= %.1f ^8per second", output.ManaRegen),
- })
- end
- end
- output.TotalRegen = 0
- if modDB:Flag(nil, "NoLifeRegen") then
- output.LifeRegen = 0
- elseif modDB:Flag(nil, "ZealotsOath") then
- output.LifeRegen = 0
- local lifeBase = modDB:Sum("BASE", nil, "LifeRegen")
- if lifeBase > 0 then
- modDB:NewMod("EnergyShieldRegen", "BASE", lifeBase, "Zealot's Oath")
- end
- local lifePercent = modDB:Sum("BASE", nil, "LifeRegenPercent")
- if lifePercent > 0 then
- modDB:NewMod("EnergyShieldRegenPercent", "BASE", lifePercent, "Zealot's Oath")
- end
- else
- local lifeBase = modDB:Sum("BASE", nil, "LifeRegen")
- local lifePercent = modDB:Sum("BASE", nil, "LifeRegenPercent")
- if lifePercent > 0 then
- lifeBase = lifeBase + output.Life * lifePercent / 100
- end
- if lifeBase > 0 then
- output.LifeRegen = lifeBase * output.LifeRecoveryRateMod
- output.LifeRegenPercent = round(output.LifeRegen / output.Life * 100, 1)
- output.TotalRegen = output.TotalRegen + output.LifeRegen
- else
- output.LifeRegen = 0
- end
- end
- if modDB:Flag(nil, "NoEnergyShieldRegen") then
- output.EnergyShieldRegen = 0
- else
- local esBase = modDB:Sum("BASE", nil, "EnergyShieldRegen")
- local esPercent = modDB:Sum("BASE", nil, "EnergyShieldRegenPercent")
- if esPercent > 0 then
- esBase = esBase + output.EnergyShield * esPercent / 100
- end
- if esBase > 0 then
- output.EnergyShieldRegen = esBase * output.EnergyShieldRecoveryRateMod
- output.EnergyShieldRegenPercent = round(output.EnergyShieldRegen / output.EnergyShield * 100, 1)
- if not modDB:Flag(nil, "EnergyShieldProtectsMana") then
- output.TotalRegen = output.TotalRegen + output.EnergyShieldRegen
- end
- else
- output.EnergyShieldRegen = 0
- end
- end
-
- -- Energy Shield Recharge
- if modDB:Flag(nil, "NoEnergyShieldRecharge") then
- output.EnergyShieldRecharge = 0
- else
- local inc = modDB:Sum("INC", nil, "EnergyShieldRecharge")
- local more = modDB:More(nil, "EnergyShieldRecharge")
- local recharge = output.EnergyShield * 0.2 * (1 + inc/100) * more
- output.EnergyShieldRecharge = round(recharge * output.EnergyShieldRecoveryRateMod)
- output.EnergyShieldRechargeDelay = 2 / (1 + modDB:Sum("INC", nil, "EnergyShieldRechargeFaster") / 100)
- if breakdown then
- breakdown.EnergyShieldRecharge = { }
- breakdown.multiChain(breakdown.EnergyShieldRecharge, {
- label = "Recharge rate:",
- base = s_format("%.1f ^8(20%% per second)", output.EnergyShield * 0.2),
- { "%.2f ^8(increased/reduced)", 1 + inc/100 },
- { "%.2f ^8(more/less)", more },
- total = s_format("= %.1f ^8per second", recharge),
- })
- breakdown.multiChain(breakdown.EnergyShieldRecharge, {
- label = "Effective Recharge rate:",
- base = s_format("%.1f", recharge),
- { "%.2f ^8(recovery rate modifier)", output.EnergyShieldRecoveryRateMod },
- total = s_format("= %.1f ^8per second", output.EnergyShieldRecharge),
- })
- if output.EnergyShieldRechargeDelay ~= 2 then
- breakdown.EnergyShieldRechargeDelay = {
- "2.00s ^8(base)",
- s_format("/ %.2f ^8(faster start)", 1 + modDB:Sum("INC", nil, "EnergyShieldRechargeFaster") / 100),
- s_format("= %.2fs", output.EnergyShieldRechargeDelay)
- }
- end
- end
- end
-
- -- Mind over Matter
- output.MindOverMatter = modDB:Sum("BASE", nil, "DamageTakenFromManaBeforeLife")
- if output.MindOverMatter and breakdown then
- local sourcePool = output.ManaUnreserved or 0
- if modDB:Flag(nil, "EnergyShieldProtectsMana") then
- sourcePool = sourcePool + output.EnergyShield
- end
- local lifeProtected = sourcePool / (output.MindOverMatter / 100) * (1 - output.MindOverMatter / 100)
- local effectiveLife = m_max(output.Life - lifeProtected, 0) + m_min(output.Life, lifeProtected) / (1 - output.MindOverMatter / 100)
- breakdown.MindOverMatter = {
- s_format("Total life protected:"),
- s_format("%d ^8(unreserved mana%s)", sourcePool, modDB:Flag(nil, "EnergyShieldProtectsMana") and " + total energy shield" or ""),
- s_format("/ %.2f ^8(portion taken from mana)", output.MindOverMatter / 100),
- s_format("x %.2f ^8(portion taken from life)", 1 - output.MindOverMatter / 100),
- s_format("= %d", lifeProtected),
- s_format("Effective life: %d", effectiveLife)
- }
- end
-
- -- Damage taken multipliers/Degen calculations
- for _, damageType in ipairs(dmgTypeList) do
- local baseTakenInc = modDB:Sum("INC", nil, "DamageTaken", damageType.."DamageTaken")
- local baseTakenMore = modDB:More(nil, "DamageTaken", damageType.."DamageTaken")
- if isElemental[damageType] then
- baseTakenInc = baseTakenInc + modDB:Sum("INC", nil, "ElementalDamageTaken")
- baseTakenMore = baseTakenMore * modDB:More(nil, "ElementalDamageTaken")
- end
- do
- -- Hit
- local takenInc = baseTakenInc + modDB:Sum("INC", nil, "DamageTakenWhenHit", damageType.."DamageTakenWhenHit")
- local takenMore = baseTakenMore * modDB:More(nil, "DamageTakenWhenHit", damageType.."DamageTakenWhenHit")
- if isElemental[damageType] then
- takenInc = takenInc + modDB:Sum("INC", nil, "ElementalDamageTakenWhenHit")
- takenMore = takenMore * modDB:More(nil, "ElementalDamageTakenWhenHit")
- end
- output[damageType.."TakenHit"] = (1 + takenInc / 100) * takenMore
- end
- do
- -- Dot
- local takenInc = baseTakenInc + modDB:Sum("INC", nil, "DamageTakenOverTime", damageType.."DamageTakenOverTime")
- local takenMore = baseTakenMore * modDB:More(nil, "DamageTakenOverTime", damageType.."DamageTakenOverTime")
- if isElemental[damageType] then
- takenInc = takenInc + modDB:Sum("INC", nil, "ElementalDamageTakenOverTime")
- takenMore = takenMore * modDB:More(nil, "ElementalDamageTakenOverTime")
- end
- local resist = output[damageType.."Resist"]
- output[damageType.."TakenDotMult"] = (1 - resist / 100) * (1 + takenInc / 100) * takenMore
- if breakdown then
- breakdown[damageType.."TakenDotMult"] = { }
- breakdown.multiChain(breakdown[damageType.."TakenDotMult"], {
- label = "DoT Multiplier:",
- { "%.2f ^8(%s)", (1 - output[damageType.."Resist"] / 100), damageType == "Physical" and "physical damage reduction" or "resistance" },
- { "%.2f ^8(increased/reduced damage taken)", (1 + takenInc / 100) },
- { "%.2f ^8(more/less damage taken)", takenMore },
- total = s_format("= %.2f", output[damageType.."TakenDotMult"]),
- })
- end
- -- Degens
- local baseVal = modDB:Sum("BASE", nil, damageType.."Degen")
- if baseVal > 0 then
- local total = baseVal * output[damageType.."TakenDotMult"]
- output[damageType.."Degen"] = total
- output.TotalDegen = (output.TotalDegen or 0) + total
- if breakdown then
- breakdown.TotalDegen = breakdown.TotalDegen or {
- rowList = { },
- colList = {
- { label = "Type", key = "type" },
- { label = "Base", key = "base" },
- { label = "Multiplier", key = "mult" },
- { label = "Total", key = "total" },
- }
- }
- t_insert(breakdown.TotalDegen.rowList, {
- type = damageType,
- base = s_format("%.1f", baseVal),
- mult = s_format("x %.2f", output[damageType.."TakenDotMult"]),
- total = s_format("%.1f", total),
- })
- end
- end
- end
- end
- if output.TotalDegen and output.TotalRegen > 0 then
- output.NetRegen = output.TotalRegen - output.TotalDegen
- if breakdown then
- breakdown.NetRegen = {
- s_format("%.1f ^8(total life%s regen)", output.TotalRegen, modDB:Flag(nil, "EnergyShieldProtectsMana") and "" or " + energy shield"),
- s_format("- %.1f ^8(total degen)", output.TotalDegen),
- s_format("= %.1f", output.NetRegen),
- }
- end
- end
-
- -- Incoming hit damage multipliers
- actor.damageShiftTable = wipeTable(actor.damageShiftTable)
- for _, damageType in ipairs(dmgTypeList) do
- -- Build damage shift table
- local shiftTable = { }
- local destTotal = 0
- for _, destType in ipairs(dmgTypeList) do
- if destType ~= damageType then
- shiftTable[destType] = modDB:Sum("BASE", nil, damageType.."DamageTakenAs"..destType, isElemental[damageType] and "ElementalDamageTakenAs"..destType or nil)
- destTotal = destTotal + shiftTable[destType]
- end
- end
- if destTotal > 100 then
- local factor = 100 / destTotal
- for destType, portion in pairs(shiftTable) do
- shiftTable[destType] = portion * factor
- end
- destTotal = 100
- end
- shiftTable[damageType] = 100 - destTotal
- actor.damageShiftTable[damageType] = shiftTable
-
- -- Calculate incoming damage multiplier
- local mult = 0
- if breakdown then
- breakdown[damageType.."TakenHitMult"] = {
- label = "Hit Damage taken as",
- rowList = { },
- colList = {
- { label = "Type", key = "type" },
- { label = "Migitation", key = "resist" },
- { label = "Taken", key = "taken" },
- { label = "Final", key = "final" },
- },
- }
- end
- for _, destType in ipairs(dmgTypeList) do
- local portion = shiftTable[destType]
- if portion > 0 then
- local resist = output[destType.."Resist"]
- if damageType == "Physical" and destType == "Physical" then
- -- Factor in armour for Physical taken as Physical
- local damage = env.configInput.enemyPhysicalHit or env.data.monsterDamageTable[env.enemyLevel] * 1.5
- local armourReduct = calcs.armourReduction(output.Armour, damage * portion / 100)
- resist = m_min(90, resist + armourReduct)
- output.PhysicalDamageReduction = resist
- if breakdown then
- breakdown.PhysicalDamageReduction = {
- s_format("Enemy Physical Hit Damage: %d ^8(%s the Configuration tab)", damage, env.configInput.enemyPhysicalHit and "overridden from" or "can be overridden in"),
- }
- if portion < 100 then
- t_insert(breakdown.PhysicalDamageReduction, s_format("Portion taken as Physical: %d%%", portion))
- end
- t_insert(breakdown.PhysicalDamageReduction, s_format("Reduction from Armour: %d%%", armourReduct))
- end
- end
- local takenMult = output[destType.."TakenHit"]
- local final = portion / 100 * (1 - resist / 100) * takenMult
- mult = mult + final
- if breakdown then
- t_insert(breakdown[damageType.."TakenHitMult"].rowList, {
- type = s_format("%d%% as %s", portion, destType),
- resist = s_format("x %.2f", 1 - resist / 100),
- taken = takenMult ~= 1 and s_format("x %.2f", takenMult),
- final = s_format("x %.2f", final),
- })
- end
- end
- end
- output[damageType.."TakenHitMult"] = mult
- end
-
- -- Other defences: block, dodge, stun recovery/avoidance
- do
- output.MovementSpeedMod = calcLib.mod(modDB, nil, "MovementSpeed")
- if modDB:Flag(nil, "MovementSpeedCannotBeBelowBase") then
- output.MovementSpeedMod = m_max(output.MovementSpeedMod, 1)
- end
- output.EffectiveMovementSpeedMod = output.MovementSpeedMod * output.ActionSpeedMod
- if breakdown then
- breakdown.EffectiveMovementSpeedMod = { }
- breakdown.multiChain(breakdown.EffectiveMovementSpeedMod, {
- { "%.2f ^8(movement speed modifier)", output.MovementSpeedMod },
- { "%.2f ^8(action speed modifier)", output.ActionSpeedMod },
- total = s_format("= %.2f ^8(effective movement speed modifier)", output.EffectiveMovementSpeedMod)
- })
- end
- output.BlockChanceMax = modDB:Sum("BASE", nil, "BlockChanceMax")
- local baseBlockChance = 0
- if actor.itemList["Weapon 2"] and actor.itemList["Weapon 2"].armourData then
- baseBlockChance = baseBlockChance + actor.itemList["Weapon 2"].armourData.BlockChance
- end
- if actor.itemList["Weapon 3"] and actor.itemList["Weapon 3"].armourData then
- baseBlockChance = baseBlockChance + actor.itemList["Weapon 3"].armourData.BlockChance
- end
- output.BlockChance = m_min((baseBlockChance + modDB:Sum("BASE", nil, "BlockChance")) * calcLib.mod(modDB, nil, "BlockChance"), output.BlockChanceMax)
- output.SpellBlockChance = m_min(modDB:Sum("BASE", nil, "SpellBlockChance") * calcLib.mod(modDB, nil, "SpellBlockChance") + output.BlockChance * modDB:Sum("BASE", nil, "BlockChanceConv") / 100, output.BlockChanceMax)
- if breakdown then
- breakdown.BlockChance = breakdown.simple(baseBlockChance, nil, output.BlockChance, "BlockChance")
- breakdown.SpellBlockChance = breakdown.simple(output.BlockChance * modDB:Sum("BASE", nil, "BlockChanceConv") / 100, nil, output.SpellBlockChance, "SpellBlockChance")
- end
- if modDB:Flag(nil, "CannotBlockAttacks") then
- output.BlockChance = 0
- end
- output.AttackDodgeChance = m_min(modDB:Sum("BASE", nil, "AttackDodgeChance"), 75)
- output.SpellDodgeChance = m_min(modDB:Sum("BASE", nil, "SpellDodgeChance"), 75)
- if env.mode_effective and modDB:Flag(nil, "DodgeChanceIsUnlucky") then
- output.AttackDodgeChance = output.AttackDodgeChance / 100 * output.AttackDodgeChance
- output.SpellDodgeChance = output.SpellDodgeChance / 100 * output.SpellDodgeChance
- end
- output.MeleeAvoidChance = 100 - (1 - output.MeleeEvadeChance / 100) * (1 - output.AttackDodgeChance / 100) * (1 - output.BlockChance / 100) * 100
- output.ProjectileAvoidChance = 100 - (1 - output.ProjectileEvadeChance / 100) * (1 - output.AttackDodgeChance / 100) * (1 - output.BlockChance / 100) * 100
- output.SpellAvoidChance = 100 - (1 - output.SpellDodgeChance / 100) * (1 - output.SpellBlockChance / 100) * 100
- if breakdown then
- breakdown.MeleeAvoidChance = { }
- breakdown.multiChain(breakdown.MeleeAvoidChance, {
- { "%.2f ^8(chance for evasion to fail)", 1 - output.MeleeEvadeChance / 100 },
- { "%.2f ^8(chance for dodge to fail)", 1 - output.AttackDodgeChance / 100 },
- { "%.2f ^8(chance for block to fail)", 1 - output.BlockChance / 100 },
- total = s_format("= %d%% ^8(chance to be hit by a melee attack)", 100 - output.MeleeAvoidChance),
- })
- breakdown.ProjectileAvoidChance = { }
- breakdown.multiChain(breakdown.ProjectileAvoidChance, {
- { "%.2f ^8(chance for evasion to fail)", 1 - output.ProjectileEvadeChance / 100 },
- { "%.2f ^8(chance for dodge to fail)", 1 - output.AttackDodgeChance / 100 },
- { "%.2f ^8(chance for block to fail)", 1 - output.BlockChance / 100 },
- total = s_format("= %d%% ^8(chance to be hit by a projectile attack)", 100 - output.ProjectileAvoidChance),
- })
- breakdown.SpellAvoidChance = { }
- breakdown.multiChain(breakdown.SpellAvoidChance, {
- { "%.2f ^8(chance for dodge to fail)", 1 - output.SpellDodgeChance / 100 },
- { "%.2f ^8(chance for block to fail)", 1 - output.SpellBlockChance / 100 },
- total = s_format("= %d%% ^8(chance to be hit by a spell)", 100 - output.SpellAvoidChance),
- })
- end
- local stunChance = 100 - modDB:Sum("BASE", nil, "AvoidStun")
- if output.EnergyShield > output.Life * 2 then
- stunChance = stunChance * 0.5
- end
- output.StunAvoidChance = 100 - stunChance
- if output.StunAvoidChance >= 100 then
- output.StunDuration = 0
- output.BlockDuration = 0
- else
- output.StunDuration = 0.35 / (1 + modDB:Sum("INC", nil, "StunRecovery") / 100)
- output.BlockDuration = 0.35 / (1 + modDB:Sum("INC", nil, "StunRecovery", "BlockRecovery") / 100)
- if breakdown then
- breakdown.StunDuration = {
- "0.35s ^8(base)",
- s_format("/ %.2f ^8(increased/reduced recovery)", 1 + modDB:Sum("INC", nil, "StunRecovery") / 100),
- s_format("= %.2fs", output.StunDuration)
- }
- breakdown.BlockDuration = {
- "0.35s ^8(base)",
- s_format("/ %.2f ^8(increased/reduced recovery)", 1 + modDB:Sum("INC", nil, "StunRecovery", "BlockRecovery") / 100),
- s_format("= %.2fs", output.BlockDuration)
- }
- end
- end
- output.LightRadiusMod = calcLib.mod(modDB, nil, "LightRadius")
- if breakdown then
- breakdown.LightRadiusMod = breakdown.mod(nil, "LightRadius")
- end
- end
-end
\ No newline at end of file
diff --git b/Modules/CalcDefence-3_0.lua a/Modules/CalcDefence-3_0.lua
deleted file mode 100644
index 47dfd7d6..00000000
--- b/Modules/CalcDefence-3_0.lua
+++ /dev/null
@@ -1,647 +0,0 @@
--- Path of Building
---
--- Module: Calc Defence
--- Performs defence calculations.
---
-local calcs = ...
-
-local pairs = pairs
-local ipairs = ipairs
-local t_insert = table.insert
-local m_ceil = math.ceil
-local m_floor = math.floor
-local m_min = math.min
-local m_max = math.max
-local s_format = string.format
-
-local tempTable1 = { }
-
-local isElemental = { Fire = true, Cold = true, Lightning = true }
-
--- List of all damage types, ordered according to the conversion sequence
-local dmgTypeList = {"Physical", "Lightning", "Cold", "Fire", "Chaos"}
-
-local resistTypeList = { "Fire", "Cold", "Lightning", "Chaos" }
-
--- Calculate hit chance
-function calcs.hitChance(evasion, accuracy)
- local rawChance = accuracy / (accuracy + (evasion / 4) ^ 0.8) * 115
- return m_max(m_min(round(rawChance), 100), 5)
-end
-
--- Calculate physical damage reduction from armour
-function calcs.armourReduction(armour, raw)
- return round(armour / (armour + raw * 10) * 100)
-end
-
--- Performs all defensive calculations
-function calcs.defence(env, actor)
- local modDB = actor.modDB
- local enemyDB = actor.enemy.modDB
- local output = actor.output
- local breakdown = actor.breakdown
-
- local condList = modDB.conditions
-
- -- Action Speed
- output.ActionSpeedMod = 1 + (m_max(-75, modDB:Sum("INC", nil, "TemporalChainsActionSpeed")) + modDB:Sum("INC", nil, "ActionSpeed")) / 100
- if modDB:Flag(nil, "ActionSpeedCannotBeBelowBase") then
- output.ActionSpeedMod = m_max(1, output.ActionSpeedMod)
- end
-
- -- Resistances
- output.PhysicalResist = m_min(90, modDB:Sum("BASE", nil, "PhysicalDamageReduction"))
- output.PhysicalResistWhenHit = m_min(90, output.PhysicalResist + modDB:Sum("BASE", nil, "PhysicalDamageReductionWhenHit"))
- for _, elem in ipairs(resistTypeList) do
- local max, total
- if elem == "Chaos" and modDB:Flag(nil, "ChaosInoculation") then
- max = 100
- total = 100
- else
- max = modDB:Override(nil, elem.."ResistMax") or m_min(100, modDB:Sum("BASE", nil, elem.."ResistMax", isElemental[elem] and "ElementalResistMax"))
- total = modDB:Override(nil, elem.."Resist")
- if not total then
- local base = modDB:Sum("BASE", nil, elem.."Resist", isElemental[elem] and "ElementalResist")
- total = base * calcLib.mod(modDB, nil, elem.."Resist", isElemental[elem] and "ElementalResist")
- end
- end
- local final = m_min(total, max)
- output[elem.."Resist"] = final
- output[elem.."ResistTotal"] = total
- output[elem.."ResistOverCap"] = m_max(0, total - max)
- output[elem.."ResistOver75"] = m_max(0, final - 75)
- if breakdown then
- breakdown[elem.."Resist"] = {
- "Max: "..max.."%",
- "Total: "..total.."%",
- }
- end
- end
-
- -- Primary defences: Energy shield, evasion and armour
- do
- local ironReflexes = modDB:Flag(nil, "IronReflexes")
- local energyShield = 0
- local armour = 0
- local evasion = 0
- if breakdown then
- breakdown.EnergyShield = { slots = { } }
- breakdown.Armour = { slots = { } }
- breakdown.Evasion = { slots = { } }
- end
- local energyShieldBase, armourBase, evasionBase
- local gearEnergyShield = 0
- local gearArmour = 0
- local gearEvasion = 0
- local slotCfg = wipeTable(tempTable1)
- for _, slot in pairs({"Helmet","Body Armour","Gloves","Boots","Weapon 2","Weapon 3"}) do
- local armourData = actor.itemList[slot] and actor.itemList[slot].armourData
- if armourData then
- slotCfg.slotName = slot
- energyShieldBase = armourData.EnergyShield or 0
- if energyShieldBase > 0 then
- output["EnergyShieldOn"..slot] = energyShieldBase
- energyShield = energyShield + energyShieldBase * calcLib.mod(modDB, slotCfg, "EnergyShield", "Defences")
- gearEnergyShield = gearEnergyShield + energyShieldBase
- if breakdown then
- breakdown.slot(slot, nil, slotCfg, energyShieldBase, nil, "EnergyShield", "Defences")
- end
- end
- armourBase = armourData.Armour or 0
- if armourBase > 0 then
- output["ArmourOn"..slot] = armourBase
- if slot == "Body Armour" and modDB:Flag(nil, "Unbreakable") then
- armourBase = armourBase * 2
- end
- armour = armour + armourBase * calcLib.mod(modDB, slotCfg, "Armour", "ArmourAndEvasion", "Defences")
- gearArmour = gearArmour + armourBase
- if breakdown then
- breakdown.slot(slot, nil, slotCfg, armourBase, nil, "Armour", "ArmourAndEvasion", "Defences")
- end
- end
- evasionBase = armourData.Evasion or 0
- if evasionBase > 0 then
- output["EvasionOn"..slot] = evasionBase
- if ironReflexes then
- armour = armour + evasionBase * calcLib.mod(modDB, slotCfg, "Armour", "Evasion", "ArmourAndEvasion", "Defences")
- gearArmour = gearArmour + evasionBase
- if breakdown then
- breakdown.slot(slot, nil, slotCfg, evasionBase, nil, "Armour", "Evasion", "ArmourAndEvasion", "Defences")
- end
- else
- evasion = evasion + evasionBase * calcLib.mod(modDB, slotCfg, "Evasion", "ArmourAndEvasion", "Defences")
- gearEvasion = gearEvasion + evasionBase
- if breakdown then
- breakdown.slot(slot, nil, slotCfg, evasionBase, nil, "Evasion", "ArmourAndEvasion", "Defences")
- end
- end
- end
- end
- end
- energyShieldBase = modDB:Sum("BASE", nil, "EnergyShield")
- if energyShieldBase > 0 then
- energyShield = energyShield + energyShieldBase * calcLib.mod(modDB, nil, "EnergyShield", "Defences")
- if breakdown then
- breakdown.slot("Global", nil, nil, energyShieldBase, nil, "EnergyShield", "Defences")
- end
- end
- armourBase = modDB:Sum("BASE", nil, "Armour", "ArmourAndEvasion")
- if armourBase > 0 then
- armour = armour + armourBase * calcLib.mod(modDB, nil, "Armour", "ArmourAndEvasion", "Defences")
- if breakdown then
- breakdown.slot("Global", nil, nil, armourBase, nil, "Armour", "ArmourAndEvasion", "Defences")
- end
- end
- evasionBase = modDB:Sum("BASE", nil, "Evasion", "ArmourAndEvasion")
- if evasionBase > 0 then
- if ironReflexes then
- armour = armour + evasionBase * calcLib.mod(modDB, nil, "Armour", "Evasion", "ArmourAndEvasion", "Defences")
- if breakdown then
- breakdown.slot("Conversion", "Evasion to Armour", nil, evasionBase, nil, "Armour", "Evasion", "ArmourAndEvasion", "Defences")
- end
- else
- evasion = evasion + evasionBase * calcLib.mod(modDB, nil, "Evasion", "ArmourAndEvasion", "Defences")
- if breakdown then
- breakdown.slot("Global", nil, nil, evasionBase, nil, "Evasion", "ArmourAndEvasion", "Defences")
- end
- end
- end
- local convManaToES = modDB:Sum("BASE", nil, "ManaGainAsEnergyShield")
- if convManaToES > 0 then
- energyShieldBase = modDB:Sum("BASE", nil, "Mana") * convManaToES / 100
- energyShield = energyShield + energyShieldBase * calcLib.mod(modDB, nil, "Mana", "EnergyShield", "Defences")
- if breakdown then
- breakdown.slot("Conversion", "Mana to Energy Shield", nil, energyShieldBase, nil, "EnergyShield", "Defences", "Mana")
- end
- end
- local convLifeToES = modDB:Sum("BASE", nil, "LifeConvertToEnergyShield", "LifeGainAsEnergyShield")
- if convLifeToES > 0 then
- energyShieldBase = modDB:Sum("BASE", nil, "Life") * convLifeToES / 100
- local total
- if modDB:Flag(nil, "ChaosInoculation") then
- total = 1
- else
- total = energyShieldBase * calcLib.mod(modDB, nil, "Life", "EnergyShield", "Defences")
- end
- energyShield = energyShield + total
- if breakdown then
- breakdown.slot("Conversion", "Life to Energy Shield", nil, energyShieldBase, total, "EnergyShield", "Defences", "Life")
- end
- end
- output.EnergyShield = m_max(round(energyShield), 0)
- output.Armour = m_max(round(armour), 0)
- output.Evasion = m_max(round(evasion), 0)
- output.LowestOfArmourAndEvasion = m_min(output.Armour, output.Evasion)
- output["Gear:EnergyShield"] = gearEnergyShield
- output["Gear:Armour"] = gearArmour
- output["Gear:Evasion"] = gearEvasion
- if modDB:Flag(nil, "CannotEvade") then
- output.EvadeChance = 0
- output.MeleeEvadeChance = 0
- output.ProjectileEvadeChance = 0
- else
- local enemyAccuracy = round(calcLib.val(enemyDB, "Accuracy"))
- output.EvadeChance = 100 - (calcs.hitChance(output.Evasion, enemyAccuracy) - modDB:Sum("BASE", nil, "EvadeChance")) * calcLib.mod(enemyDB, nil, "HitChance")
- if breakdown then
- breakdown.EvadeChance = {
- s_format("Enemy level: %d ^8(%s the Configuration tab)", env.enemyLevel, env.configInput.enemyLevel and "overridden from" or "can be overridden in"),
- s_format("Average enemy accuracy: %d", enemyAccuracy),
- s_format("Approximate evade chance: %d%%", output.EvadeChance),
- }
- end
- output.MeleeEvadeChance = m_max(0, m_min(95, output.EvadeChance * calcLib.mod(modDB, nil, "EvadeChance", "MeleeEvadeChance")))
- output.ProjectileEvadeChance = m_max(0, m_min(95, output.EvadeChance * calcLib.mod(modDB, nil, "EvadeChance", "ProjectileEvadeChance")))
- end
- end
-
- -- Recovery modifiers
- output.LifeRecoveryMod = calcLib.mod(modDB, nil, "LifeRecovery")
- output.LifeRecoveryRateMod = calcLib.mod(modDB, nil, "LifeRecovery", "LifeRecoveryRate")
- output.ManaRecoveryMod = calcLib.mod(modDB, nil, "ManaRecovery")
- output.ManaRecoveryRateMod = calcLib.mod(modDB, nil, "ManaRecovery", "ManaRecoveryRate")
- output.EnergyShieldRecoveryMod = calcLib.mod(modDB, nil, "EnergyShieldRecovery")
- output.EnergyShieldRecoveryRateMod = calcLib.mod(modDB, nil, "EnergyShieldRecovery", "EnergyShieldRecoveryRate")
-
- -- Leech caps
- output.MaxLifeLeechInstance = output.Life * calcLib.val(modDB, "MaxLifeLeechInstance") / 100
- output.MaxLifeLeechRate = output.Life * calcLib.val(modDB, "MaxLifeLeechRate") / 100
- if breakdown then
- breakdown.MaxLifeLeechRate = {
- s_format("%d ^8(maximum life)", output.Life),
- s_format("x %d%% ^8(percentage of life to maximum leech rate)", calcLib.val(modDB, "MaxLifeLeechRate")),
- s_format("= %.1f", output.MaxLifeLeechRate)
- }
- end
- output.MaxEnergyShieldLeechInstance = output.EnergyShield * calcLib.val(modDB, "MaxEnergyShieldLeechInstance") / 100
- output.MaxEnergyShieldLeechRate = output.EnergyShield * calcLib.val(modDB, "MaxEnergyShieldLeechRate") / 100
- if breakdown then
- breakdown.MaxEnergyShieldLeechRate = {
- s_format("%d ^8(maximum energy shield)", output.EnergyShield),
- s_format("x %d%% ^8(percentage of energy shield to maximum leech rate)", calcLib.val(modDB, "MaxEnergyShieldLeechRate")),
- s_format("= %.1f", output.MaxEnergyShieldLeechRate)
- }
- end
- output.MaxManaLeechInstance = output.Mana * calcLib.val(modDB, "MaxManaLeechInstance") / 100
- output.MaxManaLeechRate = output.Mana * calcLib.val(modDB, "MaxManaLeechRate") / 100
- if breakdown then
- breakdown.MaxManaLeechRate = {
- s_format("%d ^8(maximum mana)", output.Mana),
- s_format("x %d%% ^8(percentage of mana to maximum leech rate)", modDB:Sum("BASE", nil, "MaxManaLeechRate")),
- s_format("= %.1f", output.MaxManaLeechRate)
- }
- end
-
- -- Mana, life and energy shield regen
- if modDB:Flag(nil, "NoManaRegen") then
- output.ManaRegen = 0
- else
- local base = modDB:Sum("BASE", nil, "ManaRegen") + output.Mana * modDB:Sum("BASE", nil, "ManaRegenPercent") / 100
- local inc = modDB:Sum("INC", nil, "ManaRegen")
- local more = modDB:More(nil, "ManaRegen")
- local regen = base * (1 + inc/100) * more
- output.ManaRegen = round(regen * output.ManaRecoveryRateMod, 1) - modDB:Sum("BASE", nil, "ManaDegen")
- if breakdown then
- breakdown.ManaRegen = { }
- breakdown.multiChain(breakdown.ManaRegen, {
- label = "Mana Regeneration:",
- base = s_format("%.1f ^8(base)", base),
- { "%.2f ^8(increased/reduced)", 1 + inc/100 },
- { "%.2f ^8(more/less)", more },
- total = s_format("= %.1f ^8per second", regen),
- })
- breakdown.multiChain(breakdown.ManaRegen, {
- label = "Effective Mana Regeneration:",
- base = s_format("%.1f", regen),
- { "%.2f ^8(recovery rate modifier)", output.ManaRecoveryRateMod },
- total = s_format("= %.1f ^8per second", output.ManaRegen),
- })
- end
- end
- if modDB:Flag(nil, "NoLifeRegen") then
- output.LifeRegen = 0
- elseif modDB:Flag(nil, "ZealotsOath") then
- output.LifeRegen = 0
- local lifeBase = modDB:Sum("BASE", nil, "LifeRegen")
- if lifeBase > 0 then
- modDB:NewMod("EnergyShieldRegen", "BASE", lifeBase, "Zealot's Oath")
- end
- local lifePercent = modDB:Sum("BASE", nil, "LifeRegenPercent")
- if lifePercent > 0 then
- modDB:NewMod("EnergyShieldRegenPercent", "BASE", lifePercent, "Zealot's Oath")
- end
- else
- local lifeBase = modDB:Sum("BASE", nil, "LifeRegen")
- local lifePercent = modDB:Sum("BASE", nil, "LifeRegenPercent")
- if lifePercent > 0 then
- lifeBase = lifeBase + output.Life * lifePercent / 100
- end
- if lifeBase > 0 then
- output.LifeRegen = lifeBase * output.LifeRecoveryRateMod
- else
- output.LifeRegen = 0
- end
- end
- output.LifeRegen = output.LifeRegen - modDB:Sum("BASE", nil, "LifeDegen")
- output.LifeRegenPercent = round(output.LifeRegen / output.Life * 100, 1)
- if modDB:Flag(nil, "NoEnergyShieldRegen") then
- output.EnergyShieldRegen = 0
- else
- local esBase = modDB:Sum("BASE", nil, "EnergyShieldRegen")
- local esPercent = modDB:Sum("BASE", nil, "EnergyShieldRegenPercent")
- if esPercent > 0 then
- esBase = esBase + output.EnergyShield * esPercent / 100
- end
- if esBase > 0 then
- output.EnergyShieldRegen = esBase * output.EnergyShieldRecoveryRateMod * calcLib.mod(modDB, nil, "EnergyShieldRegen")
- output.EnergyShieldRegenPercent = round(output.EnergyShieldRegen / output.EnergyShield * 100, 1)
- else
- output.EnergyShieldRegen = 0
- end
- end
-
- -- Energy Shield Recharge
- if modDB:Flag(nil, "NoEnergyShieldRecharge") then
- output.EnergyShieldRecharge = 0
- else
- local inc = modDB:Sum("INC", nil, "EnergyShieldRecharge")
- local more = modDB:More(nil, "EnergyShieldRecharge")
- local recharge = output.EnergyShield * 0.2 * (1 + inc/100) * more
- output.EnergyShieldRecharge = round(recharge * output.EnergyShieldRecoveryRateMod)
- output.EnergyShieldRechargeDelay = 2 / (1 + modDB:Sum("INC", nil, "EnergyShieldRechargeFaster") / 100)
- if breakdown then
- breakdown.EnergyShieldRecharge = { }
- breakdown.multiChain(breakdown.EnergyShieldRecharge, {
- label = "Recharge rate:",
- base = s_format("%.1f ^8(20%% per second)", output.EnergyShield * 0.2),
- { "%.2f ^8(increased/reduced)", 1 + inc/100 },
- { "%.2f ^8(more/less)", more },
- total = s_format("= %.1f ^8per second", recharge),
- })
- breakdown.multiChain(breakdown.EnergyShieldRecharge, {
- label = "Effective Recharge rate:",
- base = s_format("%.1f", recharge),
- { "%.2f ^8(recovery rate modifier)", output.EnergyShieldRecoveryRateMod },
- total = s_format("= %.1f ^8per second", output.EnergyShieldRecharge),
- })
- if output.EnergyShieldRechargeDelay ~= 2 then
- breakdown.EnergyShieldRechargeDelay = {
- "2.00s ^8(base)",
- s_format("/ %.2f ^8(faster start)", 1 + modDB:Sum("INC", nil, "EnergyShieldRechargeFaster") / 100),
- s_format("= %.2fs", output.EnergyShieldRechargeDelay)
- }
- end
- end
- end
-
- -- Mind over Matter
- output.MindOverMatter = modDB:Sum("BASE", nil, "DamageTakenFromManaBeforeLife")
- if output.MindOverMatter and breakdown then
- local sourcePool = output.ManaUnreserved or 0
- if modDB:Flag(nil, "EnergyShieldProtectsMana") then
- sourcePool = sourcePool + output.EnergyShield
- end
- local lifeProtected = sourcePool / (output.MindOverMatter / 100) * (1 - output.MindOverMatter / 100)
- local effectiveLife = m_max(output.Life - lifeProtected, 0) + m_min(output.Life, lifeProtected) / (1 - output.MindOverMatter / 100)
- breakdown.MindOverMatter = {
- s_format("Total life protected:"),
- s_format("%d ^8(unreserved mana%s)", sourcePool, modDB:Flag(nil, "EnergyShieldProtectsMana") and " + total energy shield" or ""),
- s_format("/ %.2f ^8(portion taken from mana)", output.MindOverMatter / 100),
- s_format("x %.2f ^8(portion taken from life)", 1 - output.MindOverMatter / 100),
- s_format("= %d", lifeProtected),
- s_format("Effective life: %d", effectiveLife)
- }
- end
-
- -- Damage taken multipliers/Degen calculations
- for _, damageType in ipairs(dmgTypeList) do
- local baseTakenInc = modDB:Sum("INC", nil, "DamageTaken", damageType.."DamageTaken")
- local baseTakenMore = modDB:More(nil, "DamageTaken", damageType.."DamageTaken")
- if isElemental[damageType] then
- baseTakenInc = baseTakenInc + modDB:Sum("INC", nil, "ElementalDamageTaken")
- baseTakenMore = baseTakenMore * modDB:More(nil, "ElementalDamageTaken")
- end
- do
- -- Hit
- local takenInc = baseTakenInc + modDB:Sum("INC", nil, "DamageTakenWhenHit", damageType.."DamageTakenWhenHit")
- local takenMore = baseTakenMore * modDB:More(nil, "DamageTakenWhenHit", damageType.."DamageTakenWhenHit")
- if isElemental[damageType] then
- takenInc = takenInc + modDB:Sum("INC", nil, "ElementalDamageTakenWhenHit")
- takenMore = takenMore * modDB:More(nil, "ElementalDamageTakenWhenHit")
- end
- output[damageType.."TakenHit"] = (1 + takenInc / 100) * takenMore
- end
- do
- -- Dot
- local takenInc = baseTakenInc + modDB:Sum("INC", nil, "DamageTakenOverTime", damageType.."DamageTakenOverTime")
- local takenMore = baseTakenMore * modDB:More(nil, "DamageTakenOverTime", damageType.."DamageTakenOverTime")
- if isElemental[damageType] then
- takenInc = takenInc + modDB:Sum("INC", nil, "ElementalDamageTakenOverTime")
- takenMore = takenMore * modDB:More(nil, "ElementalDamageTakenOverTime")
- end
- local resist = output[damageType.."Resist"]
- output[damageType.."TakenDotMult"] = (1 - resist / 100) * (1 + takenInc / 100) * takenMore
- if breakdown then
- breakdown[damageType.."TakenDotMult"] = { }
- breakdown.multiChain(breakdown[damageType.."TakenDotMult"], {
- label = "DoT Multiplier:",
- { "%.2f ^8(%s)", (1 - output[damageType.."Resist"] / 100), damageType == "Physical" and "physical damage reduction" or "resistance" },
- { "%.2f ^8(increased/reduced damage taken)", (1 + takenInc / 100) },
- { "%.2f ^8(more/less damage taken)", takenMore },
- total = s_format("= %.2f", output[damageType.."TakenDotMult"]),
- })
- end
- -- Degens
- local baseVal = modDB:Sum("BASE", nil, damageType.."Degen")
- if baseVal > 0 then
- local total = baseVal * output[damageType.."TakenDotMult"]
- output[damageType.."Degen"] = total
- output.TotalDegen = (output.TotalDegen or 0) + total
- if breakdown then
- breakdown.TotalDegen = breakdown.TotalDegen or {
- rowList = { },
- colList = {
- { label = "Type", key = "type" },
- { label = "Base", key = "base" },
- { label = "Multiplier", key = "mult" },
- { label = "Total", key = "total" },
- }
- }
- t_insert(breakdown.TotalDegen.rowList, {
- type = damageType,
- base = s_format("%.1f", baseVal),
- mult = s_format("x %.2f", output[damageType.."TakenDotMult"]),
- total = s_format("%.1f", total),
- })
- end
- end
- end
- end
- if output.TotalDegen then
- if output.MindOverMatter > 0 and output.LifeRegen >= output.EnergyShieldRegen then
- local lifeDegen = output.TotalDegen * (1 - output.MindOverMatter / 100)
- local manaDegen = output.TotalDegen * output.MindOverMatter / 100
- output.NetLifeRegen = output.LifeRegen - lifeDegen
- output.NetManaRegen = output.ManaRegen - manaDegen
- if breakdown then
- breakdown.NetLifeRegen = {
- s_format("%.1f ^8(total life regen)", output.LifeRegen),
- s_format("- %.1f ^8(total life degen)", lifeDegen),
- s_format("= %.1f", output.NetLifeRegen),
- }
- breakdown.NetManaRegen = {
- s_format("%.1f ^8(total mana regen)", output.ManaRegen),
- s_format("- %.1f ^8(total mana degen)", manaDegen),
- s_format("= %.1f", output.NetManaRegen),
- }
- end
- else
- local totalRegen = output.LifeRegen + (modDB:Flag(nil, "EnergyShieldProtectsMana") and 0 or output.EnergyShieldRegen)
- output.NetLifeRegen = totalRegen - output.TotalDegen
- if breakdown then
- breakdown.NetLifeRegen = {
- s_format("%.1f ^8(total life%s regen)", totalRegen, modDB:Flag(nil, "EnergyShieldProtectsMana") and "" or " + energy shield"),
- s_format("- %.1f ^8(total degen)", output.TotalDegen),
- s_format("= %.1f", output.NetLifeRegen),
- }
- end
- end
- end
-
- -- Incoming hit damage multipliers
- actor.damageShiftTable = wipeTable(actor.damageShiftTable)
- for _, damageType in ipairs(dmgTypeList) do
- -- Build damage shift table
- local shiftTable = { }
- local destTotal = 0
- for _, destType in ipairs(dmgTypeList) do
- if destType ~= damageType then
- shiftTable[destType] = modDB:Sum("BASE", nil, damageType.."DamageTakenAs"..destType, isElemental[damageType] and "ElementalDamageTakenAs"..destType or nil)
- destTotal = destTotal + shiftTable[destType]
- end
- end
- if destTotal > 100 then
- local factor = 100 / destTotal
- for destType, portion in pairs(shiftTable) do
- shiftTable[destType] = portion * factor
- end
- destTotal = 100
- end
- shiftTable[damageType] = 100 - destTotal
- actor.damageShiftTable[damageType] = shiftTable
-
- -- Calculate incoming damage multiplier
- local mult = 0
- if breakdown then
- breakdown[damageType.."TakenHitMult"] = {
- label = "Hit Damage taken as",
- rowList = { },
- colList = {
- { label = "Type", key = "type" },
- { label = "Mitigation", key = "resist" },
- { label = "Taken", key = "taken" },
- { label = "Final", key = "final" },
- },
- }
- end
- for _, destType in ipairs(dmgTypeList) do
- local portion = shiftTable[destType]
- if portion > 0 then
- local resist = output[destType.."ResistWhenHit"] or output[destType.."Resist"]
- if damageType == "Physical" and destType == "Physical" then
- -- Factor in armour for Physical taken as Physical
- local damage = env.configInput.enemyPhysicalHit or env.data.monsterDamageTable[env.enemyLevel] * 1.5
- local armourReduct = calcs.armourReduction(output.Armour, damage * portion / 100)
- resist = m_min(90, resist + armourReduct)
- output.PhysicalDamageReduction = resist
- if breakdown then
- breakdown.PhysicalDamageReduction = {
- s_format("Enemy Physical Hit Damage: %d ^8(%s the Configuration tab)", damage, env.configInput.enemyPhysicalHit and "overridden from" or "can be overridden in"),
- }
- if portion < 100 then
- t_insert(breakdown.PhysicalDamageReduction, s_format("Portion taken as Physical: %d%%", portion))
- end
- t_insert(breakdown.PhysicalDamageReduction, s_format("Reduction from Armour: %d%%", armourReduct))
- end
- end
- local takenMult = output[destType.."TakenHit"]
- local final = portion / 100 * (1 - resist / 100) * takenMult
- mult = mult + final
- if breakdown then
- t_insert(breakdown[damageType.."TakenHitMult"].rowList, {
- type = s_format("%d%% as %s", portion, destType),
- resist = s_format("x %.2f", 1 - resist / 100),
- taken = takenMult ~= 1 and s_format("x %.2f", takenMult),
- final = s_format("x %.2f", final),
- })
- end
- end
- end
- output[damageType.."TakenHitMult"] = mult
- end
-
- -- Other defences: block, dodge, stun recovery/avoidance
- do
- output.MovementSpeedMod = calcLib.mod(modDB, nil, "MovementSpeed")
- if modDB:Flag(nil, "MovementSpeedCannotBeBelowBase") then
- output.MovementSpeedMod = m_max(output.MovementSpeedMod, 1)
- end
- output.EffectiveMovementSpeedMod = output.MovementSpeedMod * output.ActionSpeedMod
- if breakdown then
- breakdown.EffectiveMovementSpeedMod = { }
- breakdown.multiChain(breakdown.EffectiveMovementSpeedMod, {
- { "%.2f ^8(movement speed modifier)", output.MovementSpeedMod },
- { "%.2f ^8(action speed modifier)", output.ActionSpeedMod },
- total = s_format("= %.2f ^8(effective movement speed modifier)", output.EffectiveMovementSpeedMod)
- })
- end
- output.BlockChanceMax = modDB:Sum("BASE", nil, "BlockChanceMax")
- local baseBlockChance = 0
- if actor.itemList["Weapon 2"] and actor.itemList["Weapon 2"].armourData then
- baseBlockChance = baseBlockChance + actor.itemList["Weapon 2"].armourData.BlockChance
- end
- if actor.itemList["Weapon 3"] and actor.itemList["Weapon 3"].armourData then
- baseBlockChance = baseBlockChance + actor.itemList["Weapon 3"].armourData.BlockChance
- end
- output.ShieldBlockChance = baseBlockChance
- output.BlockChance = m_min((baseBlockChance + modDB:Sum("BASE", nil, "BlockChance")) * calcLib.mod(modDB, nil, "BlockChance"), output.BlockChanceMax)
- if modDB:Flag(nil, "SpellBlockChanceMaxIsBlockChanceMax") then
- output.SpellBlockChanceMax = output.BlockChanceMax
- else
- output.SpellBlockChanceMax = modDB:Sum("BASE", nil, "SpellBlockChanceMax")
- end
- if modDB:Flag(nil, "SpellBlockChanceIsBlockChance") then
- output.SpellBlockChance = output.BlockChance
- else
- output.SpellBlockChance = m_min(modDB:Sum("BASE", nil, "SpellBlockChance") * calcLib.mod(modDB, nil, "SpellBlockChance"), output.SpellBlockChanceMax)
- end
- if breakdown then
- breakdown.BlockChance = breakdown.simple(baseBlockChance, nil, output.BlockChance, "BlockChance")
- breakdown.SpellBlockChance = breakdown.simple(0, nil, output.SpellBlockChance, "SpellBlockChance")
- end
- if modDB:Flag(nil, "CannotBlockAttacks") then
- output.BlockChance = 0
- end
- if modDB:Flag(nil, "CannotBlockSpells") then
- output.SpellBlockChance = 0
- end
- output.AttackDodgeChance = m_min(modDB:Sum("BASE", nil, "AttackDodgeChance"), 75)
- output.SpellDodgeChance = m_min(modDB:Sum("BASE", nil, "SpellDodgeChance"), 75)
- if env.mode_effective and modDB:Flag(nil, "DodgeChanceIsUnlucky") then
- output.AttackDodgeChance = output.AttackDodgeChance / 100 * output.AttackDodgeChance
- output.SpellDodgeChance = output.SpellDodgeChance / 100 * output.SpellDodgeChance
- end
- output.MeleeAvoidChance = 100 - (1 - output.MeleeEvadeChance / 100) * (1 - output.AttackDodgeChance / 100) * (1 - output.BlockChance / 100) * 100
- output.ProjectileAvoidChance = 100 - (1 - output.ProjectileEvadeChance / 100) * (1 - output.AttackDodgeChance / 100) * (1 - output.BlockChance / 100) * 100
- output.SpellAvoidChance = 100 - (1 - output.SpellDodgeChance / 100) * (1 - output.SpellBlockChance / 100) * 100
- if breakdown then
- breakdown.MeleeAvoidChance = { }
- breakdown.multiChain(breakdown.MeleeAvoidChance, {
- { "%.2f ^8(chance for evasion to fail)", 1 - output.MeleeEvadeChance / 100 },
- { "%.2f ^8(chance for dodge to fail)", 1 - output.AttackDodgeChance / 100 },
- { "%.2f ^8(chance for block to fail)", 1 - output.BlockChance / 100 },
- total = s_format("= %d%% ^8(chance to be hit by a melee attack)", 100 - output.MeleeAvoidChance),
- })
- breakdown.ProjectileAvoidChance = { }
- breakdown.multiChain(breakdown.ProjectileAvoidChance, {
- { "%.2f ^8(chance for evasion to fail)", 1 - output.ProjectileEvadeChance / 100 },
- { "%.2f ^8(chance for dodge to fail)", 1 - output.AttackDodgeChance / 100 },
- { "%.2f ^8(chance for block to fail)", 1 - output.BlockChance / 100 },
- total = s_format("= %d%% ^8(chance to be hit by a projectile attack)", 100 - output.ProjectileAvoidChance),
- })
- breakdown.SpellAvoidChance = { }
- breakdown.multiChain(breakdown.SpellAvoidChance, {
- { "%.2f ^8(chance for dodge to fail)", 1 - output.SpellDodgeChance / 100 },
- { "%.2f ^8(chance for block to fail)", 1 - output.SpellBlockChance / 100 },
- total = s_format("= %d%% ^8(chance to be hit by a spell)", 100 - output.SpellAvoidChance),
- })
- end
- local stunChance = 100 - m_min(modDB:Sum("BASE", nil, "AvoidStun"), 100)
- if output.EnergyShield > output.Life * 2 then
- stunChance = stunChance * 0.5
- end
- output.StunAvoidChance = 100 - stunChance
- if output.StunAvoidChance >= 100 then
- output.StunDuration = 0
- output.BlockDuration = 0
- else
- output.StunDuration = 0.35 / (1 + modDB:Sum("INC", nil, "StunRecovery") / 100)
- output.BlockDuration = 0.35 / (1 + modDB:Sum("INC", nil, "StunRecovery", "BlockRecovery") / 100)
- if breakdown then
- breakdown.StunDuration = {
- "0.35s ^8(base)",
- s_format("/ %.2f ^8(increased/reduced recovery)", 1 + modDB:Sum("INC", nil, "StunRecovery") / 100),
- s_format("= %.2fs", output.StunDuration)
- }
- breakdown.BlockDuration = {
- "0.35s ^8(base)",
- s_format("/ %.2f ^8(increased/reduced recovery)", 1 + modDB:Sum("INC", nil, "StunRecovery", "BlockRecovery") / 100),
- s_format("= %.2fs", output.BlockDuration)
- }
- end
- end
- output.LightRadiusMod = calcLib.mod(modDB, nil, "LightRadius")
- if breakdown then
- breakdown.LightRadiusMod = breakdown.mod(nil, "LightRadius")
- end
- end
-end
diff --git b/Modules/CalcDefence.lua a/Modules/CalcDefence.lua
index 07b27cfa..dc6d9cb1 100644
--- b/Modules/CalcDefence.lua
+++ a/Modules/CalcDefence.lua
@@ -235,6 +235,15 @@ function calcs.defence(env, actor)
breakdown.slot("Conversion", "Life to Energy Shield", nil, energyShieldBase, total, "EnergyShield", "Defences", "Life")
end
end
+ local convEvasionToArmour = modDB:Sum("BASE", nil, "EvasionGainAsArmour")
+ if convEvasionToArmour > 0 then
+ armourBase = modDB:Sum("BASE", nil, "Evasion") * convEvasionToArmour / 100
+ local total = armourBase * calcLib.mod(modDB, nil, "Evasion", "Armour", "ArmourAndEvasion", "Defences")
+ armour = armour + total
+ if breakdown then
+ breakdown.slot("Conversion", "Evasion to Armour", nil, armourBase, total, "Armour", "ArmourAndEvasion", "Defences", "Evasion")
+ end
+ end
output.EnergyShield = modDB:Override(nil, "EnergyShield") or m_max(round(energyShield), 0)
output.Armour = m_max(round(armour), 0)
output.DoubleArmourChance = m_min(modDB:Sum("BASE", nil, "DoubleArmourChance"), 100)
@@ -320,7 +329,7 @@ function calcs.defence(env, actor)
output.SpellProjectileBlockChance = 0
end
output.AverageBlockChance = (output.BlockChance + output.ProjectileBlockChance + output.SpellBlockChance + output.SpellProjectileBlockChance) / 4
- output.BlockEffect = modDB:Sum("BASE", nil, "BlockEffect")
+ output.BlockEffect = m_max(100 - modDB:Sum("BASE", nil, "BlockEffect"), 0)
if output.BlockEffect == 0 then
output.BlockEffect = 100
else
@@ -427,13 +436,13 @@ function calcs.defence(env, actor)
lifeBase = lifeBase + output.Life * lifePercent / 100
end
if lifeBase > 0 then
- output.LifeRegen = lifeBase * output.LifeRecoveryRateMod * (1 + modDB:Sum("MORE", nil, "LifeRegen") / 100)
+ output.LifeRegen = lifeBase * output.LifeRecoveryRateMod * modDB:More(nil, "LifeRegen")
else
output.LifeRegen = 0
end
-- Don't add life recovery mod for this
if output.LifeRegen and modDB:Flag(nil, "LifeRegenerationRecoversEnergyShield") then
- modDB:NewMod("EnergyShieldRecovery", "BASE", lifeBase * (1 + modDB:Sum("MORE", nil, "LifeRegen") / 100), "Life Regeneration Recovers Energy Shield")
+ modDB:NewMod("EnergyShieldRecovery", "BASE", lifeBase * modDB:More(nil, "LifeRegen"), "Life Regeneration Recovers Energy Shield")
end
end
output.LifeRegen = output.LifeRegen - modDB:Sum("BASE", nil, "LifeDegen") + modDB:Sum("BASE", nil, "LifeRecovery") * output.LifeRecoveryRateMod
diff --git b/Modules/CalcOffence-2_6.lua a/Modules/CalcOffence-2_6.lua
deleted file mode 100644
index d25f9401..00000000
--- b/Modules/CalcOffence-2_6.lua
+++ /dev/null
@@ -1,1782 +0,0 @@
--- Path of Building
---
--- Module: Calc Offence
--- Performs offence calculations.
---
-local calcs = ...
-
-local pairs = pairs
-local ipairs = ipairs
-local unpack = unpack
-local t_insert = table.insert
-local m_floor = math.floor
-local m_min = math.min
-local m_max = math.max
-local m_sqrt = math.sqrt
-local m_pi = math.pi
-local bor = bit.bor
-local band = bit.band
-local bnot = bit.bnot
-local s_format = string.format
-
-local tempTable1 = { }
-local tempTable2 = { }
-local tempTable3 = { }
-
-local isElemental = { Fire = true, Cold = true, Lightning = true }
-
--- List of all damage types, ordered according to the conversion sequence
-local dmgTypeList = {"Physical", "Lightning", "Cold", "Fire", "Chaos"}
-
--- Calculate min/max damage of a hit for the given damage type
-local function calcHitDamage(activeSkill, source, cfg, breakdown, damageType, ...)
- local skillModList = activeSkill.skillModList
-
- local damageTypeMin = damageType.."Min"
- local damageTypeMax = damageType.."Max"
-
- -- Calculate base values
- local damageEffectiveness = activeSkill.activeEffect.grantedEffectLevel.damageEffectiveness or activeSkill.skillData.damageEffectiveness or 1
- local addedMin = skillModList:Sum("BASE", cfg, damageTypeMin)
- local addedMax = skillModList:Sum("BASE", cfg, damageTypeMax)
- local baseMin = (source[damageTypeMin] or 0) + addedMin * damageEffectiveness
- local baseMax = (source[damageTypeMax] or 0) + addedMax * damageEffectiveness
-
- if breakdown and not (...) and baseMin ~= 0 and baseMax ~= 0 then
- t_insert(breakdown, "Base damage:")
- local plus = ""
- if (source[damageTypeMin] or 0) ~= 0 or (source[damageTypeMax] or 0) ~= 0 then
- t_insert(breakdown, s_format("%d to %d ^8(base damage from %s)", source[damageTypeMin], source[damageTypeMax], source.type and "weapon" or "skill"))
- plus = "+ "
- end
- if addedMin ~= 0 or addedMax ~= 0 then
- if damageEffectiveness ~= 1 then
- t_insert(breakdown, s_format("%s(%d to %d) x %.2f ^8(added damage multiplied by damage effectiveness)", plus, addedMin, addedMax, damageEffectiveness))
- else
- t_insert(breakdown, s_format("%s%d to %d ^8(added damage)", plus, addedMin, addedMax))
- end
- end
- t_insert(breakdown, s_format("= %.1f to %.1f", baseMin, baseMax))
- end
-
- -- Calculate conversions
- local addMin, addMax = 0, 0
- local conversionTable = activeSkill.conversionTable
- for _, otherType in ipairs(dmgTypeList) do
- if otherType == damageType then
- -- Damage can only be converted from damage types that preceed this one in the conversion sequence, so stop here
- break
- end
- local convMult = conversionTable[otherType][damageType]
- if convMult > 0 then
- -- Damage is being converted/gained from the other damage type
- local min, max = calcHitDamage(activeSkill, source, cfg, breakdown, otherType, damageType, ...)
- addMin = addMin + min * convMult
- addMax = addMax + max * convMult
- end
- end
- if addMin ~= 0 and addMax ~= 0 then
- addMin = round(addMin)
- addMax = round(addMax)
- end
-
- if baseMin == 0 and baseMax == 0 then
- -- No base damage for this type, don't need to calculate modifiers
- if breakdown and (addMin ~= 0 or addMax ~= 0) then
- t_insert(breakdown.damageTypes, {
- source = damageType,
- convSrc = (addMin ~= 0 or addMax ~= 0) and (addMin .. " to " .. addMax),
- total = addMin .. " to " .. addMax,
- convDst = (...) and s_format("%d%% to %s", conversionTable[damageType][...] * 100, ...),
- })
- end
- return addMin, addMax
- end
-
- -- Build lists of applicable modifier names
- local addElemental = isElemental[damageType]
- local modNames = { damageType.."Damage", "Damage" }
- for i = 1, select('#', ...) do
- local dstElem = select(i, ...)
- -- Add modifiers for damage types to which this damage is being converted
- addElemental = addElemental or isElemental[dstElem]
- t_insert(modNames, dstElem.."Damage")
- end
- if addElemental then
- -- Damage is elemental or is being converted to elemental damage, add global elemental modifiers
- t_insert(modNames, "ElementalDamage")
- end
-
- -- Combine modifiers
- local inc = 1 + skillModList:Sum("INC", cfg, unpack(modNames)) / 100
- local more = m_floor(skillModList:More(cfg, unpack(modNames)) * 100 + 0.50000001) / 100
-
- if breakdown then
- t_insert(breakdown.damageTypes, {
- source = damageType,
- base = baseMin .. " to " .. baseMax,
- inc = (inc ~= 1 and "x "..inc),
- more = (more ~= 1 and "x "..more),
- convSrc = (addMin ~= 0 or addMax ~= 0) and (addMin .. " to " .. addMax),
- total = (round(baseMin * inc * more) + addMin) .. " to " .. (round(baseMax * inc * more) + addMax),
- convDst = (...) and s_format("%d%% to %s", conversionTable[damageType][...] * 100, ...),
- })
- end
-
- return (round(baseMin * inc * more) + addMin),
- (round(baseMax * inc * more) + addMax)
-end
-
--- Performs all offensive calculations
-function calcs.offence(env, actor, activeSkill)
- local enemyDB = actor.enemy.modDB
- local output = actor.output
- local breakdown = actor.breakdown
-
- local skillModList = activeSkill.skillModList
- local skillData = activeSkill.skillData
- local skillFlags = activeSkill.skillFlags
- local skillCfg = activeSkill.skillCfg
- if skillData.showAverage then
- skillFlags.showAverage = true
- else
- skillFlags.notAverage = true
- end
-
- if skillFlags.disable then
- -- Skill is disabled
- output.CombinedDPS = 0
- return
- end
-
- -- Update skill data
- for _, value in ipairs(skillModList:List(skillCfg, "SkillData")) do
- if value.merge == "MAX" then
- skillData[value.key] = m_max(value.value, skillData[value.key] or 0)
- else
- skillData[value.key] = value.value
- end
- end
-
- skillCfg.skillCond["SkillIsTriggered"] = skillData.triggered
-
- -- Add addition stat bonuses
- if skillModList:Flag(nil, "IronGrip") then
- skillModList:NewMod("PhysicalDamage", "INC", actor.strDmgBonus, "Strength", bor(ModFlag.Attack, ModFlag.Projectile))
- end
- if skillModList:Flag(nil, "IronWill") then
- skillModList:NewMod("Damage", "INC", actor.strDmgBonus, "Strength", ModFlag.Spell)
- end
-
- if skillModList:Flag(nil, "MinionDamageAppliesToPlayer") then
- -- Minion Damage conversion from The Scourge
- for _, value in ipairs(skillModList:List(skillCfg, "MinionModifier")) do
- if value.mod.name == "Damage" and value.mod.type == "INC" then
- skillModList:AddMod(value.mod)
- end
- end
- end
- if skillModList:Flag(nil, "SpellDamageAppliesToAttacks") then
- -- Spell Damage conversion from Crown of Eyes
- for i, value in ipairs(skillModList:Tabulate("INC", { flags = ModFlag.Spell }, "Damage")) do
- local mod = value.mod
- if band(mod.flags, ModFlag.Spell) ~= 0 then
- skillModList:NewMod("Damage", "INC", mod.value, mod.source, bor(band(mod.flags, bnot(ModFlag.Spell)), ModFlag.Attack), mod.keywordFlags, unpack(mod))
- end
- end
- end
- if skillModList:Flag(nil, "ClawDamageAppliesToUnarmed") then
- -- Claw Damage conversion from Rigwald's Curse
- for i, value in ipairs(skillModList:Tabulate("INC", { flags = ModFlag.Claw }, "PhysicalDamage")) do
- local mod = value.mod
- if band(mod.flags, ModFlag.Claw) ~= 0 then
- skillModList:NewMod("PhysicalDamage", mod.type, mod.value, mod.source, bor(band(mod.flags, bnot(ModFlag.Claw)), ModFlag.Unarmed), mod.keywordFlags, unpack(mod))
- end
- end
- end
- if skillModList:Flag(nil, "ClawAttackSpeedAppliesToUnarmed") then
- -- Claw Attack Speed conversion from Rigwald's Curse
- for i, value in ipairs(skillModList:Tabulate("INC", { flags = bor(ModFlag.Claw, ModFlag.Attack) }, "Speed")) do
- local mod = value.mod
- if band(mod.flags, ModFlag.Claw) ~= 0 and band(mod.flags, ModFlag.Attack) ~= 0 then
- skillModList:NewMod("Speed", mod.type, mod.value, mod.source, bor(band(mod.flags, bnot(ModFlag.Claw)), ModFlag.Unarmed), mod.keywordFlags, unpack(mod))
- end
- end
- end
- if skillModList:Flag(nil, "ClawCritChanceAppliesToUnarmed") then
- -- Claw Crit Chance conversion from Rigwald's Curse
- for i, value in ipairs(skillModList:Tabulate("INC", { flags = ModFlag.Claw }, "Speed")) do
- local mod = value.mod
- if band(mod.flags, ModFlag.Claw) ~= 0 then
- skillModList:NewMod("CritChance", mod.type, mod.value, mod.source, bor(band(mod.flags, bnot(ModFlag.Claw)), ModFlag.Unarmed), mod.keywordFlags, unpack(mod))
- end
- end
- end
-
- local isAttack = skillFlags.attack
-
- -- Calculate skill type stats
- if skillFlags.minion then
- if activeSkill.minion and activeSkill.minion.minionData.limit then
- output.ActiveMinionLimit = m_floor(calcLib.val(skillModList, activeSkill.minion.minionData.limit, skillCfg))
- end
- end
- if skillFlags.chaining then
- output.ChainMax = skillModList:Sum("BASE", skillCfg, "ChainCountMax")
- output.Chain = m_min(output.ChainMax, skillModList:Sum("BASE", skillCfg, "ChainCount"))
- output.ChainRemaining = m_max(0, output.ChainMax - output.Chain)
- end
- if skillFlags.projectile then
- if skillModList:Flag(nil, "PointBlank") then
- skillModList:NewMod("Damage", "MORE", 50, "Point Blank", bor(ModFlag.Attack, ModFlag.Projectile), { type = "DistanceRamp", ramp = {{10,1},{35,0},{150,-1}} })
- end
- output.ProjectileCount = skillModList:Sum("BASE", skillCfg, "ProjectileCount")
- output.PierceChance = m_min(100, skillModList:Sum("BASE", skillCfg, "PierceChance"))
- output.ProjectileSpeedMod = calcLib.mod(skillModList, skillCfg, "ProjectileSpeed")
- if breakdown then
- breakdown.ProjectileSpeedMod = breakdown.mod(skillCfg, "ProjectileSpeed")
- end
- end
- if skillFlags.melee then
- if skillFlags.weapon1Attack then
- actor.weaponRange1 = (actor.weaponData1.range and actor.weaponData1.range + skillModList:Sum("BASE", skillCfg, "MeleeWeaponRange")) or (4 + skillModList:Sum("BASE", skillCfg, "UnarmedRange"))
- end
- if skillFlags.weapon2Attack then
- actor.weaponRange2 = (actor.weaponData2.range and actor.weaponData2.range + skillModList:Sum("BASE", skillCfg, "MeleeWeaponRange")) or (4 + skillModList:Sum("BASE", skillCfg, "UnarmedRange"))
- end
- if activeSkill.skillTypes[SkillType.MeleeSingleTarget] then
- local range = 100
- if skillFlags.weapon1Attack then
- range = m_min(range, actor.weaponRange1)
- end
- if skillFlags.weapon2Attack then
- range = m_min(range, actor.weaponRange2)
- end
- output.WeaponRange = range + 2
- if breakdown then
- breakdown.WeaponRange = {
- radius = output.WeaponRange
- }
- end
- end
- end
- if skillFlags.area or skillData.radius then
- output.AreaOfEffectMod = calcLib.mod(skillModList, skillCfg, "AreaOfEffect")
- if skillData.radiusIsWeaponRange then
- local range = 0
- if skillFlags.weapon1Attack then
- range = m_max(range, actor.weaponRange1)
- end
- if skillFlags.weapon2Attack then
- range = m_max(range, actor.weaponRange2)
- end
- skillData.radius = range + 2
- end
- if skillData.radius then
- skillFlags.area = true
- local baseRadius = skillData.radius + (skillData.radiusExtra or 0) + skillModList:Sum("BASE", skillCfg, "AreaOfEffect")
- output.AreaOfEffectRadius = m_floor(baseRadius * m_sqrt(output.AreaOfEffectMod))
- if breakdown then
- breakdown.AreaOfEffectRadius = breakdown.area(baseRadius, output.AreaOfEffectMod, output.AreaOfEffectRadius)
- end
- if skillData.radiusSecondary then
- baseRadius = skillData.radiusSecondary + (skillData.radiusExtra or 0)
- output.AreaOfEffectRadiusSecondary = m_floor(baseRadius * m_sqrt(output.AreaOfEffectMod))
- if breakdown then
- breakdown.AreaOfEffectRadiusSecondary = breakdown.area(baseRadius, output.AreaOfEffectMod, output.AreaOfEffectRadiusSecondary)
- end
- end
- end
- if breakdown then
- breakdown.AreaOfEffectMod = breakdown.mod(skillCfg, "AreaOfEffect")
- end
- end
- if skillFlags.trap then
- local baseSpeed = 1 / skillModList:Sum("BASE", skillCfg, "TrapThrowingTime")
- output.TrapThrowingSpeed = baseSpeed * calcLib.mod(skillModList, skillCfg, "TrapThrowingSpeed") * output.ActionSpeedMod
- output.TrapThrowingTime = 1 / output.TrapThrowingSpeed
- if breakdown then
- breakdown.TrapThrowingTime = { }
- breakdown.multiChain(breakdown.TrapThrowingTime, {
- label = "Throwing speed:",
- base = s_format("%.2f ^8(base throwing speed)", baseSpeed),
- { "%.2f ^8(increased/reduced throwing speed)", 1 + skillModList:Sum("INC", skillCfg, "TrapThrowingSpeed") / 100 },
- { "%.2f ^8(more/less throwing speed)", skillModList:More(skillCfg, "TrapThrowingSpeed") },
- { "%.2f ^8(action speed modifier)", output.ActionSpeedMod },
- total = s_format("= %.2f ^8per second", output.TrapThrowingSpeed),
- })
- end
- output.ActiveTrapLimit = skillModList:Sum("BASE", skillCfg, "ActiveTrapLimit")
- output.TrapCooldown = (skillData.trapCooldown or skillData.cooldown or 4) / calcLib.mod(skillModList, skillCfg, "CooldownRecovery")
- if breakdown then
- breakdown.TrapCooldown = {
- s_format("%.2fs ^8(base)", skillData.trapCooldown or skillData.cooldown or 4),
- s_format("/ %.2f ^8(increased/reduced cooldown recovery)", 1 + skillModList:Sum("INC", skillCfg, "CooldownRecovery") / 100),
- s_format("= %.2fs", output.TrapCooldown)
- }
- end
- local areaMod = calcLib.mod(skillModList, skillCfg, "TrapTriggerAreaOfEffect")
- output.TrapTriggerRadius = 10 * m_sqrt(areaMod)
- if breakdown then
- breakdown.TrapTriggerRadius = breakdown.area(10, areaMod, output.TrapTriggerRadius)
- end
- elseif skillData.cooldown then
- output.Cooldown = skillData.cooldown / calcLib.mod(skillModList, skillCfg, "CooldownRecovery")
- if breakdown then
- breakdown.Cooldown = {
- s_format("%.2fs ^8(base)", skillData.cooldown),
- s_format("/ %.2f ^8(increased/reduced cooldown recovery)", 1 + skillModList:Sum("INC", skillCfg, "CooldownRecovery") / 100),
- s_format("= %.2fs", output.Cooldown)
- }
- end
- end
- if skillFlags.mine then
- local baseSpeed = 1 / skillModList:Sum("BASE", skillCfg, "MineLayingTime")
- output.MineLayingSpeed = baseSpeed * calcLib.mod(skillModList, skillCfg, "MineLayingSpeed") * output.ActionSpeedMod
- output.MineLayingTime = 1 / output.MineLayingSpeed
- if breakdown then
- breakdown.MineLayingTime = { }
- breakdown.multiChain(breakdown.MineLayingTime, {
- label = "Laying speed:",
- base = s_format("%.2f ^8(base laying speed)", baseSpeed),
- { "%.2f ^8(increased/reduced laying speed)", 1 + skillModList:Sum("INC", skillCfg, "MineLayingSpeed") / 100 },
- { "%.2f ^8(more/less laying speed)", skillModList:More(skillCfg, "MineLayingSpeed") },
- { "%.2f ^8(action speed modifier)", output.ActionSpeedMod },
- total = s_format("= %.2f ^8per second", output.MineLayingSpeed),
- })
- end
- output.ActiveMineLimit = skillModList:Sum("BASE", skillCfg, "ActiveMineLimit")
- local areaMod = calcLib.mod(skillModList, skillCfg, "MineDetonationAreaOfEffect")
- output.MineDetonationRadius = 60 * m_sqrt(areaMod)
- if breakdown then
- breakdown.MineDetonationRadius = breakdown.area(60, areaMod, output.MineDetonationRadius)
- end
- end
- if skillFlags.totem then
- local baseSpeed = 1 / skillModList:Sum("BASE", skillCfg, "TotemPlacementTime")
- output.TotemPlacementSpeed = baseSpeed * calcLib.mod(skillModList, skillCfg, "TotemPlacementSpeed") * output.ActionSpeedMod
- output.TotemPlacementTime = 1 / output.TotemPlacementSpeed
- if breakdown then
- breakdown.TotemPlacementTime = { }
- breakdown.multiChain(breakdown.TotemPlacementTime, {
- label = "Placement speed:",
- base = s_format("%.2f ^8(base placement speed)", baseSpeed),
- { "%.2f ^8(increased/reduced placement speed)", 1 + skillModList:Sum("INC", skillCfg, "TotemPlacementSpeed") / 100 },
- { "%.2f ^8(more/less placement speed)", skillModList:More(skillCfg, "TotemPlacementSpeed") },
- { "%.2f ^8(action speed modifier)", output.ActionSpeedMod },
- total = s_format("= %.2f ^8per second", output.TotemPlacementSpeed),
- })
- end
- output.ActiveTotemLimit = skillModList:Sum("BASE", skillCfg, "ActiveTotemLimit")
- output.TotemLifeMod = calcLib.mod(skillModList, skillCfg, "TotemLife")
- output.TotemLife = round(m_floor(env.data.monsterAllyLifeTable[skillData.totemLevel] * env.data.totemLifeMult[activeSkill.skillTotemId]) * output.TotemLifeMod)
- if breakdown then
- breakdown.TotemLifeMod = breakdown.mod(skillCfg, "TotemLife")
- breakdown.TotemLife = {
- "Totem level: "..skillData.totemLevel,
- env.data.monsterAllyLifeTable[skillData.totemLevel].." ^8(base life for a level "..skillData.totemLevel.." monster)",
- "x "..env.data.totemLifeMult[activeSkill.skillTotemId].." ^8(life multiplier for this totem type)",
- "x "..output.TotemLifeMod.." ^8(totem life modifier)",
- "= "..output.TotemLife,
- }
- end
- end
-
- -- Skill duration
- local debuffDurationMult
- if env.mode_effective then
- debuffDurationMult = 1 / calcLib.mod(enemyDB, skillCfg, "BuffExpireFaster")
- else
- debuffDurationMult = 1
- end
- do
- output.DurationMod = calcLib.mod(skillModList, skillCfg, "Duration")
- if breakdown then
- breakdown.DurationMod = breakdown.mod(skillCfg, "Duration")
- end
- local durationBase = skillData.duration or 0
- if durationBase > 0 then
- output.Duration = durationBase * output.DurationMod
- if skillData.debuff then
- output.Duration = output.Duration * debuffDurationMult
- end
- if breakdown and output.Duration ~= durationBase then
- breakdown.Duration = {
- s_format("%.2fs ^8(base)", durationBase),
- }
- if output.DurationMod ~= 1 then
- t_insert(breakdown.Duration, s_format("x %.2f ^8(duration modifier)", output.DurationMod))
- end
- if skillData.debuff and debuffDurationMult ~= 1 then
- t_insert(breakdown.Duration, s_format("/ %.2f ^8(debuff expires slower/faster)", 1 / debuffDurationMult))
- end
- t_insert(breakdown.Duration, s_format("= %.2fs", output.Duration))
- end
- end
- end
-
- -- Run skill setup function
- do
- local setupFunc = activeSkill.activeEffect.grantedEffect.setupFunc
- if setupFunc then
- setupFunc(activeSkill, output)
- end
- end
-
- -- Cache global damage disabling flags
- local canDeal = { }
- for _, damageType in pairs(dmgTypeList) do
- canDeal[damageType] = not skillModList:Flag(skillCfg, "DealNo"..damageType)
- end
-
- -- Calculate damage conversion percentages
- activeSkill.conversionTable = wipeTable(activeSkill.conversionTable)
- for damageTypeIndex = 1, 4 do
- local damageType = dmgTypeList[damageTypeIndex]
- local globalConv = wipeTable(tempTable1)
- local skillConv = wipeTable(tempTable2)
- local add = wipeTable(tempTable3)
- local globalTotal, skillTotal = 0, 0
- for otherTypeIndex = damageTypeIndex + 1, 5 do
- -- For all possible destination types, check for global and skill conversions
- otherType = dmgTypeList[otherTypeIndex]
- globalConv[otherType] = skillModList:Sum("BASE", skillCfg, damageType.."DamageConvertTo"..otherType, isElemental[damageType] and "ElementalDamageConvertTo"..otherType or nil)
- globalTotal = globalTotal + globalConv[otherType]
- skillConv[otherType] = skillModList:Sum("BASE", skillCfg, "Skill"..damageType.."DamageConvertTo"..otherType)
- skillTotal = skillTotal + skillConv[otherType]
- add[otherType] = skillModList:Sum("BASE", skillCfg, damageType.."DamageGainAs"..otherType, isElemental[damageType] and "ElementalDamageGainAs"..otherType or nil)
- end
- if skillTotal > 100 then
- -- Skill conversion exceeds 100%, scale it down and remove non-skill conversions
- local factor = 100 / skillTotal
- for type, val in pairs(skillConv) do
- -- The game currently doesn't scale this down even though it is supposed to
- --skillConv[type] = val * factor
- end
- for type, val in pairs(globalConv) do
- globalConv[type] = 0
- end
- elseif globalTotal + skillTotal > 100 then
- -- Conversion exceeds 100%, scale down non-skill conversions
- local factor = (100 - skillTotal) / globalTotal
- for type, val in pairs(globalConv) do
- globalConv[type] = val * factor
- end
- globalTotal = globalTotal * factor
- end
- local dmgTable = { }
- for type, val in pairs(globalConv) do
- dmgTable[type] = (globalConv[type] + skillConv[type] + add[type]) / 100
- end
- dmgTable.mult = 1 - m_min((globalTotal + skillTotal) / 100, 1)
- activeSkill.conversionTable[damageType] = dmgTable
- end
- activeSkill.conversionTable["Chaos"] = { mult = 1 }
-
- -- Calculate mana cost (may be slightly off due to rounding differences)
- do
- local more = m_floor(skillModList:More(skillCfg, "ManaCost") * 100 + 0.0001) / 100
- local inc = skillModList:Sum("INC", skillCfg, "ManaCost")
- local base = skillModList:Sum("BASE", skillCfg, "ManaCost")
- local manaCost = activeSkill.activeEffect.grantedEffectLevel.manaCost or 0
- output.ManaCost = m_floor(m_max(0, manaCost * more * (1 + inc / 100) + base))
- if activeSkill.skillTypes[SkillType.ManaCostPercent] and skillFlags.totem then
- output.ManaCost = m_floor(output.Mana * output.ManaCost / 100)
- end
- if breakdown and output.ManaCost ~= manaCost then
- breakdown.ManaCost = {
- s_format("%d ^8(base mana cost)", manaCost)
- }
- if more ~= 1 then
- t_insert(breakdown.ManaCost, s_format("x %.2f ^8(mana cost multiplier)", more))
- end
- if inc ~= 0 then
- t_insert(breakdown.ManaCost, s_format("x %.2f ^8(increased/reduced mana cost)", 1 + inc/100))
- end
- if base ~= 0 then
- t_insert(breakdown.ManaCost, s_format("- %d ^8(- mana cost)", -base))
- end
- t_insert(breakdown.ManaCost, s_format("= %d", output.ManaCost))
- end
- end
-
- -- Configure damage passes
- local passList = { }
- if isAttack then
- output.MainHand = { }
- output.OffHand = { }
- if skillFlags.weapon1Attack then
- if breakdown then
- breakdown.MainHand = LoadModule(calcs.breakdownModule, skillModList, output.MainHand)
- end
- activeSkill.weapon1Cfg.skillStats = output.MainHand
- t_insert(passList, {
- label = "Main Hand",
- source = actor.weaponData1,
- cfg = activeSkill.weapon1Cfg,
- output = output.MainHand,
- breakdown = breakdown and breakdown.MainHand,
- })
- end
- if skillFlags.weapon2Attack then
- if breakdown then
- breakdown.OffHand = LoadModule(calcs.breakdownModule, skillModList, output.OffHand)
- end
- activeSkill.weapon2Cfg.skillStats = output.OffHand
- t_insert(passList, {
- label = "Off Hand",
- source = actor.weaponData2,
- cfg = activeSkill.weapon2Cfg,
- output = output.OffHand,
- breakdown = breakdown and breakdown.OffHand,
- })
- end
- else
- t_insert(passList, {
- label = "Skill",
- source = skillData,
- cfg = skillCfg,
- output = output,
- breakdown = breakdown,
- })
- end
-
- local function combineStat(stat, mode, ...)
- -- Combine stats from Main Hand and Off Hand according to the mode
- if mode == "OR" or not skillFlags.bothWeaponAttack then
- output[stat] = output.MainHand[stat] or output.OffHand[stat]
- elseif mode == "ADD" then
- output[stat] = (output.MainHand[stat] or 0) + (output.OffHand[stat] or 0)
- elseif mode == "AVERAGE" then
- output[stat] = ((output.MainHand[stat] or 0) + (output.OffHand[stat] or 0)) / 2
- elseif mode == "CHANCE" then
- if output.MainHand[stat] and output.OffHand[stat] then
- local mainChance = output.MainHand[...] * output.MainHand.HitChance
- local offChance = output.OffHand[...] * output.OffHand.HitChance
- local mainPortion = mainChance / (mainChance + offChance)
- local offPortion = offChance / (mainChance + offChance)
- output[stat] = output.MainHand[stat] * mainPortion + output.OffHand[stat] * offPortion
- if breakdown then
- if not breakdown[stat] then
- breakdown[stat] = { }
- end
- t_insert(breakdown[stat], "Contribution from Main Hand:")
- t_insert(breakdown[stat], s_format("%.1f", output.MainHand[stat]))
- t_insert(breakdown[stat], s_format("x %.3f ^8(portion of instances created by main hand)", mainPortion))
- t_insert(breakdown[stat], s_format("= %.1f", output.MainHand[stat] * mainPortion))
- t_insert(breakdown[stat], "Contribution from Off Hand:")
- t_insert(breakdown[stat], s_format("%.1f", output.OffHand[stat]))
- t_insert(breakdown[stat], s_format("x %.3f ^8(portion of instances created by off hand)", offPortion))
- t_insert(breakdown[stat], s_format("= %.1f", output.OffHand[stat] * offPortion))
- t_insert(breakdown[stat], "Total:")
- t_insert(breakdown[stat], s_format("%.1f + %.1f", output.MainHand[stat] * mainPortion, output.OffHand[stat] * offPortion))
- t_insert(breakdown[stat], s_format("= %.1f", output[stat]))
- end
- else
- output[stat] = output.MainHand[stat] or output.OffHand[stat]
- end
- elseif mode == "DPS" then
- output[stat] = (output.MainHand[stat] or 0) + (output.OffHand[stat] or 0)
- if not skillData.doubleHitsWhenDualWielding then
- output[stat] = output[stat] / 2
- end
- end
- end
-
- for _, pass in ipairs(passList) do
- local globalOutput, globalBreakdown = output, breakdown
- local source, output, cfg, breakdown = pass.source, pass.output, pass.cfg, pass.breakdown
-
- -- Calculate hit chance
- output.Accuracy = calcLib.val(skillModList, "Accuracy", cfg)
- if breakdown then
- breakdown.Accuracy = breakdown.simple(nil, cfg, output.Accuracy, "Accuracy")
- end
- if not isAttack or skillModList:Flag(cfg, "CannotBeEvaded") or skillData.cannotBeEvaded then
- output.HitChance = 100
- else
- local enemyEvasion = round(calcLib.val(enemyDB, "Evasion"))
- output.HitChance = calcs.hitChance(enemyEvasion, output.Accuracy)
- if breakdown then
- breakdown.HitChance = {
- "Enemy level: "..env.enemyLevel..(env.configInput.enemyLevel and " ^8(overridden from the Configuration tab" or " ^8(can be overridden in the Configuration tab)"),
- "Average enemy evasion: "..enemyEvasion,
- "Approximate hit chance: "..output.HitChance.."%",
- }
- end
- end
-
- -- Calculate attack/cast speed
- if skillData.timeOverride then
- output.Time = skillData.timeOverride
- output.Speed = 1 / output.Time
- else
- local baseSpeed
- if isAttack then
- if skillData.castTimeOverridesAttackTime then
- -- Skill is overriding weapon attack speed
- baseSpeed = 1 / skillData.castTime * (1 + (source.AttackSpeedInc or 0) / 100)
- else
- baseSpeed = source.AttackRate or 1
- end
- else
- baseSpeed = 1 / (skillData.castTime or 1)
- end
- local inc = skillModList:Sum("INC", cfg, "Speed")
- local more = skillModList:More(cfg, "Speed")
- output.Speed = baseSpeed * round((1 + inc/100) * more, 2)
- if skillData.attackRateCap then
- output.Speed = m_min(output.Speed, skillData.attackRateCap)
- end
- if skillFlags.selfCast then
- -- Self-cast skill; apply action speed
- output.Speed = output.Speed * globalOutput.ActionSpeedMod
- end
- if breakdown then
- breakdown.Speed = { }
- breakdown.multiChain(breakdown.Speed, {
- base = s_format("%.2f ^8(base)", baseSpeed),
- { "%.2f ^8(increased/reduced)", 1 + inc/100 },
- { "%.2f ^8(more/less)", more },
- { "%.2f ^8(action speed modifier)", skillFlags.selfCast and globalOutput.ActionSpeedMod or 1 },
- total = s_format("= %.2f ^8per second", output.Speed)
- })
- end
- if output.Speed == 0 then
- output.Time = 0
- else
- output.Time = 1 / output.Speed
- end
- end
- if skillData.hitTimeOverride then
- output.HitTime = skillData.hitTimeOverride
- output.HitSpeed = 1 / output.HitTime
- end
- end
-
- if isAttack then
- -- Combine hit chance and attack speed
- combineStat("HitChance", "AVERAGE")
- combineStat("Speed", "AVERAGE")
- if output.Speed == 0 then
- output.Time = 0
- else
- output.Time = 1 / output.Speed
- end
- if skillFlags.bothWeaponAttack then
- if breakdown then
- breakdown.Speed = {
- "Both weapons:",
- s_format("(%.2f + %.2f) / 2", output.MainHand.Speed, output.OffHand.Speed),
- s_format("= %.2f", output.Speed),
- }
- end
- end
- end
-
- for _, pass in ipairs(passList) do
- local globalOutput, globalBreakdown = output, breakdown
- local source, output, cfg, breakdown = pass.source, pass.output, pass.cfg, pass.breakdown
-
- -- Calculate crit chance, crit multiplier, and their combined effect
- if skillModList:Flag(nil, "NeverCrit") then
- output.PreEffectiveCritChance = 0
- output.CritChance = 0
- output.CritMultiplier = 0
- output.CritEffect = 1
- else
- local baseCrit = source.CritChance or 0
- if baseCrit == 100 then
- output.PreEffectiveCritChance = 100
- output.CritChance = 100
- else
- local base = skillModList:Sum("BASE", cfg, "CritChance")
- local inc = skillModList:Sum("INC", cfg, "CritChance")
- local more = skillModList:More(cfg, "CritChance")
- local enemyExtra = env.mode_effective and enemyDB:Sum("BASE", nil, "SelfExtraCritChance") or 0
- output.CritChance = (baseCrit + base) * (1 + inc / 100) * more
- local preCapCritChance = output.CritChance
- output.CritChance = m_min(output.CritChance, 95)
- if (baseCrit + base) > 0 then
- output.CritChance = m_max(output.CritChance, 5)
- end
- output.PreEffectiveCritChance = output.CritChance
- if enemyExtra ~= 0 then
- output.CritChance = m_min(output.CritChance + enemyExtra, 100)
- end
- local preLuckyCritChance = output.CritChance
- if env.mode_effective and skillModList:Flag(cfg, "CritChanceLucky") then
- output.CritChance = (1 - (1 - output.CritChance / 100) ^ 2) * 100
- end
- local preHitCheckCritChance = output.CritChance
- if env.mode_effective then
- output.CritChance = output.CritChance * output.HitChance / 100
- end
- if breakdown and output.CritChance ~= baseCrit then
- breakdown.CritChance = { }
- if base ~= 0 then
- t_insert(breakdown.CritChance, s_format("(%g + %g) ^8(base)", baseCrit, base))
- else
- t_insert(breakdown.CritChance, s_format("%g ^8(base)", baseCrit + base))
- end
- if inc ~= 0 then
- t_insert(breakdown.CritChance, s_format("x %.2f", 1 + inc/100).." ^8(increased/reduced)")
- end
- if more ~= 1 then
- t_insert(breakdown.CritChance, s_format("x %.2f", more).." ^8(more/less)")
- end
- t_insert(breakdown.CritChance, s_format("= %.2f%% ^8(crit chance)", output.PreEffectiveCritChance))
- if preCapCritChance > 95 then
- local overCap = preCapCritChance - 95
- t_insert(breakdown.CritChance, s_format("Crit is overcapped by %.2f%% (%d%% increased Critical Strike Chance)", overCap, overCap / more / (baseCrit + base) * 100))
- end
- if enemyExtra ~= 0 then
- t_insert(breakdown.CritChance, s_format("+ %g ^8(extra chance for enemy to be crit)", enemyExtra))
- t_insert(breakdown.CritChance, s_format("= %.2f%% ^8(chance to crit against enemy)", preLuckyCritChance))
- end
- if env.mode_effective and skillModList:Flag(cfg, "CritChanceLucky") then
- t_insert(breakdown.CritChance, "Crit Chance is Lucky:")
- t_insert(breakdown.CritChance, s_format("1 - (1 - %.4f) x (1 - %.4f)", preLuckyCritChance / 100, preLuckyCritChance / 100))
- t_insert(breakdown.CritChance, s_format("= %.2f%%", preHitCheckCritChance))
- end
- if env.mode_effective and output.HitChance < 100 then
- t_insert(breakdown.CritChance, "Crit confirmation roll:")
- t_insert(breakdown.CritChance, s_format("%.2f%%", preHitCheckCritChance))
- t_insert(breakdown.CritChance, s_format("x %.2f ^8(chance to hit)", output.HitChance / 100))
- t_insert(breakdown.CritChance, s_format("= %.2f%%", output.CritChance))
- end
- end
- end
- if skillModList:Flag(cfg, "NoCritMultiplier") then
- output.CritMultiplier = 1
- else
- local extraDamage = skillModList:Sum("BASE", cfg, "CritMultiplier") / 100
- if env.mode_effective then
- local enemyInc = 1 + enemyDB:Sum("INC", nil, "SelfCritMultiplier") / 100
- extraDamage = round(extraDamage * enemyInc, 2)
- if breakdown and enemyInc ~= 1 then
- breakdown.CritMultiplier = {
- s_format("%d%% ^8(additional extra damage)", skillModList:Sum("BASE", cfg, "CritMultiplier") / 100),
- s_format("x %.2f ^8(increased/reduced extra crit damage taken by enemy)", enemyInc),
- s_format("= %d%% ^8(extra crit damage)", extraDamage * 100),
- }
- end
- end
- output.CritMultiplier = 1 + m_max(0, extraDamage)
- end
- output.CritEffect = 1 - output.CritChance / 100 + output.CritChance / 100 * output.CritMultiplier
- if breakdown and output.CritEffect ~= 1 then
- breakdown.CritEffect = {
- s_format("(1 - %.4f) ^8(portion of damage from non-crits)", output.CritChance/100),
- s_format("+ (%.4f x %g) ^8(portion of damage from crits)", output.CritChance/100, output.CritMultiplier),
- s_format("= %.3f", output.CritEffect),
- }
- end
- end
-
- -- Calculate hit damage for each damage type
- local totalHitMin, totalHitMax = 0, 0
- local totalCritMin, totalCritMax = 0, 0
- output.LifeLeech = 0
- output.LifeLeechInstant = 0
- output.ManaLeech = 0
- output.ManaLeechInstant = 0
- for pass = 1, 2 do
- -- Pass 1 is critical strike damage, pass 2 is non-critical strike
- cfg.skillCond["CriticalStrike"] = (pass == 1)
- local lifeLeechTotal = 0
- local manaLeechTotal = 0
- local noLifeLeech = skillModList:Flag(cfg, "CannotLeechLife") or enemyDB:Flag(nil, "CannotLeechLifeFromSelf")
- local noManaLeech = skillModList:Flag(cfg, "CannotLeechMana") or enemyDB:Flag(nil, "CannotLeechManaFromSelf")
- for _, damageType in ipairs(dmgTypeList) do
- local min, max
- if skillFlags.hit and canDeal[damageType] then
- if breakdown then
- breakdown[damageType] = { damageTypes = { } }
- end
- min, max = calcHitDamage(activeSkill, source, cfg, breakdown and breakdown[damageType], damageType)
- local convMult = activeSkill.conversionTable[damageType].mult
- local doubleChance = m_min(skillModList:Sum("BASE", cfg, "DoubleDamageChance"), 100)
- if breakdown then
- t_insert(breakdown[damageType], "Hit damage:")
- t_insert(breakdown[damageType], s_format("%d to %d ^8(total damage)", min, max))
- if convMult ~= 1 then
- t_insert(breakdown[damageType], s_format("x %g ^8(%g%% converted to other damage types)", convMult, (1-convMult)*100))
- end
- if doubleChance > 0 then
- t_insert(breakdown[damageType], s_format("x %.2f ^8(chance to deal double damage)", 1 + doubleChance / 100))
- end
- end
- min = min * convMult
- max = max * convMult
- if doubleChance > 0 then
- min = min * (1 + doubleChance / 100)
- max = max * (1 + doubleChance / 100)
- end
- if pass == 1 then
- -- Apply crit multiplier
- min = min * output.CritMultiplier
- max = max * output.CritMultiplier
- end
- if (min ~= 0 or max ~= 0) and env.mode_effective then
- -- Apply enemy resistances and damage taken modifiers
- local resist = 0
- local pen = 0
- local taken = enemyDB:Sum("INC", nil, "DamageTaken", damageType.."DamageTaken")
- if damageType == "Physical" then
- resist = enemyDB:Sum("BASE", nil, "PhysicalDamageReduction")
- else
- resist = enemyDB:Sum("BASE", nil, damageType.."Resist")
- if isElemental[damageType] then
- resist = resist + enemyDB:Sum("BASE", nil, "ElementalResist")
- pen = skillModList:Sum("BASE", cfg, damageType.."Penetration", "ElementalPenetration")
- taken = taken + enemyDB:Sum("INC", nil, "ElementalDamageTaken")
- end
- resist = m_min(resist, 75)
- end
- if skillFlags.projectile then
- taken = taken + enemyDB:Sum("INC", nil, "ProjectileDamageTaken")
- end
- local effMult = (1 + taken / 100)
- if not isElemental[damageType] or not skillModList:Flag(cfg, "IgnoreElementalResistances") then
- effMult = effMult * (1 - (resist - pen) / 100)
- end
- min = min * effMult
- max = max * effMult
- if env.mode == "CALCS" then
- output[damageType.."EffMult"] = effMult
- end
- if breakdown and effMult ~= 1 then
- t_insert(breakdown[damageType], s_format("x %.3f ^8(effective DPS modifier)", effMult))
- breakdown[damageType.."EffMult"] = breakdown.effMult(damageType, resist, pen, taken, effMult)
- end
- end
- if breakdown then
- t_insert(breakdown[damageType], s_format("= %d to %d", min, max))
- end
- if skillFlags.mine or skillFlags.trap or skillFlags.totem then
- if not noLifeLeech then
- local lifeLeech = skillModList:Sum("BASE", cfg, "DamageLifeLeechToPlayer")
- if lifeLeech > 0 then
- lifeLeechTotal = lifeLeechTotal + (min + max) / 2 * lifeLeech / 100
- end
- end
- else
- if not noLifeLeech then
- local lifeLeech
- 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", nil, "SelfDamageLifeLeech") / 100
- else
- lifeLeech = 0
- end
- else
- lifeLeech = skillModList:Sum("BASE", cfg, "DamageLeech", "DamageLifeLeech", damageType.."DamageLifeLeech", isElemental[damageType] and "ElementalDamageLifeLeech" or nil) + enemyDB:Sum("BASE", nil, "SelfDamageLifeLeech") / 100
- end
- if lifeLeech > 0 then
- lifeLeechTotal = lifeLeechTotal + (min + max) / 2 * lifeLeech / 100
- end
- end
- if not noManaLeech then
- local manaLeech = skillModList:Sum("BASE", cfg, "DamageLeech", "DamageManaLeech", damageType.."DamageManaLeech", isElemental[damageType] and "ElementalDamageManaLeech" or nil) + enemyDB:Sum("BASE", nil, "SelfDamageManaLeech") / 100
- if manaLeech > 0 then
- manaLeechTotal = manaLeechTotal + (min + max) / 2 * manaLeech / 100
- end
- end
- end
- else
- min, max = 0, 0
- if breakdown then
- breakdown[damageType] = {
- "You can't deal "..damageType.." damage"
- }
- end
- end
- if pass == 1 then
- output[damageType.."CritAverage"] = (min + max) / 2
- totalCritMin = totalCritMin + min
- totalCritMax = totalCritMax + max
- else
- if env.mode == "CALCS" then
- output[damageType.."Min"] = min
- output[damageType.."Max"] = max
- end
- output[damageType.."HitAverage"] = (min + max) / 2
- totalHitMin = totalHitMin + min
- totalHitMax = totalHitMax + max
- end
- end
- if skillData.lifeLeechPerUse then
- lifeLeechTotal = lifeLeechTotal + skillData.lifeLeechPerUse
- end
- if skillData.manaLeechPerUse then
- manaLeechTotal = manaLeechTotal + skillData.manaLeechPerUse
- end
- local portion = (pass == 1) and (output.CritChance / 100) or (1 - output.CritChance / 100)
- if skillModList:Flag(cfg, "InstantLifeLeech") then
- output.LifeLeechInstant = output.LifeLeechInstant + lifeLeechTotal * portion
- else
- output.LifeLeech = output.LifeLeech + lifeLeechTotal * portion
- end
- if skillModList:Flag(cfg, "InstantManaLeech") then
- output.ManaLeechInstant = output.ManaLeechInstant + manaLeechTotal * portion
- else
- output.ManaLeech = output.ManaLeech + manaLeechTotal * portion
- end
- end
- output.TotalMin = totalHitMin
- output.TotalMax = totalHitMax
-
- if skillModList:Flag(skillCfg, "ElementalEquilibrium") and not env.configInput.EEIgnoreHitDamage and (output.FireHitAverage + output.ColdHitAverage + output.LightningHitAverage > 0) then
- -- Update enemy hit-by-damage-type conditions
- enemyDB.conditions.HitByFireDamage = output.FireHitAverage > 0
- enemyDB.conditions.HitByColdDamage = output.ColdHitAverage > 0
- enemyDB.conditions.HitByLightningDamage = output.LightningHitAverage > 0
- end
-
- if breakdown then
- -- For each damage type, calculate percentage of total damage
- for _, damageType in ipairs(dmgTypeList) do
- if output[damageType.."HitAverage"] > 0 then
- t_insert(breakdown[damageType], s_format("Portion of total damage: %d%%", output[damageType.."HitAverage"] / (totalHitMin + totalHitMax) * 200))
- end
- end
- end
-
- local hitRate = output.HitChance / 100 * (globalOutput.HitSpeed or globalOutput.Speed) * (skillData.dpsMultiplier or 1)
-
- -- Calculate leech
- local function getLeechInstances(amount, total)
- if total == 0 then
- return 0, 0
- end
- local duration = amount / total / 0.02
- return duration, duration * hitRate
- end
- output.LifeLeechDuration, output.LifeLeechInstances = getLeechInstances(output.LifeLeech, skillModList:Flag(nil, "GhostReaver") and globalOutput.EnergyShield or globalOutput.Life)
- output.LifeLeechInstantRate = output.LifeLeechInstant * hitRate
- output.ManaLeechDuration, output.ManaLeechInstances = getLeechInstances(output.ManaLeech, globalOutput.Mana)
- output.ManaLeechInstantRate = output.ManaLeechInstant * hitRate
-
- -- Calculate gain on hit
- if skillFlags.mine or skillFlags.trap or skillFlags.totem then
- output.LifeOnHit = 0
- output.EnergyShieldOnHit = 0
- output.ManaOnHit = 0
- else
- output.LifeOnHit = (skillModList:Sum("BASE", skillCfg, "LifeOnHit") + enemyDB:Sum("BASE", skillCfg, "SelfLifeOnHit")) * globalOutput.LifeRecoveryMod
- output.EnergyShieldOnHit = (skillModList:Sum("BASE", skillCfg, "EnergyShieldOnHit") + enemyDB:Sum("BASE", skillCfg, "SelfEnergyShieldOnHit")) * globalOutput.EnergyShieldRecoveryMod
- output.ManaOnHit = (skillModList:Sum("BASE", skillCfg, "ManaOnHit") + enemyDB:Sum("BASE", skillCfg, "SelfManaOnHit")) * globalOutput.ManaRecoveryMod
- end
- output.LifeOnHitRate = output.LifeOnHit * hitRate
- output.EnergyShieldOnHitRate = output.EnergyShieldOnHit * hitRate
- output.ManaOnHitRate = output.ManaOnHit * hitRate
-
- -- Calculate average damage and final DPS
- output.AverageHit = (totalHitMin + totalHitMax) / 2 * (1 - output.CritChance / 100) + (totalCritMin + totalCritMax) / 2 * output.CritChance / 100
- output.AverageDamage = output.AverageHit * output.HitChance / 100
- output.TotalDPS = output.AverageDamage * (globalOutput.HitSpeed or globalOutput.Speed) * (skillData.dpsMultiplier or 1)
- if breakdown then
- if output.CritEffect ~= 1 then
- breakdown.AverageHit = {
- s_format("%.1f x (1 - %.4f) ^8(damage from non-crits)", (totalHitMin + totalHitMax) / 2, output.CritChance / 100),
- s_format("+ %.1f x %.4f ^8(damage from crits)", (totalCritMin + totalCritMax) / 2, output.CritChance / 100),
- s_format("= %.1f", output.AverageHit),
- }
- end
- if isAttack then
- breakdown.AverageDamage = { }
- t_insert(breakdown.AverageDamage, s_format("%s:", pass.label))
- t_insert(breakdown.AverageDamage, s_format("%.1f ^8(average hit)", output.AverageHit))
- t_insert(breakdown.AverageDamage, s_format("x %.2f ^8(chance to hit)", output.HitChance / 100))
- t_insert(breakdown.AverageDamage, s_format("= %.1f", output.AverageDamage))
- end
- end
- end
-
- if isAttack then
- -- Combine crit stats, average damage and DPS
- combineStat("PreEffectiveCritChance", "AVERAGE")
- combineStat("CritChance", "AVERAGE")
- combineStat("CritMultiplier", "AVERAGE")
- combineStat("AverageDamage", "DPS")
- combineStat("TotalDPS", "DPS")
- combineStat("LifeLeechDuration", "DPS")
- combineStat("LifeLeechInstances", "DPS")
- combineStat("LifeLeechInstant", "DPS")
- combineStat("LifeLeechInstantRate", "DPS")
- combineStat("ManaLeechDuration", "DPS")
- combineStat("ManaLeechInstances", "DPS")
- combineStat("ManaLeechInstant", "DPS")
- combineStat("ManaLeechInstantRate", "DPS")
- combineStat("LifeOnHit", "DPS")
- combineStat("LifeOnHitRate", "DPS")
- combineStat("EnergyShieldOnHit", "DPS")
- combineStat("EnergyShieldOnHitRate", "DPS")
- combineStat("ManaOnHit", "DPS")
- combineStat("ManaOnHitRate", "DPS")
- if skillFlags.bothWeaponAttack then
- if breakdown then
- breakdown.AverageDamage = { }
- t_insert(breakdown.AverageDamage, "Both weapons:")
- if skillData.doubleHitsWhenDualWielding then
- t_insert(breakdown.AverageDamage, s_format("%.1f + %.1f ^8(skill hits with both weapons at once)", output.MainHand.AverageDamage, output.OffHand.AverageDamage))
- else
- t_insert(breakdown.AverageDamage, s_format("(%.1f + %.1f) / 2 ^8(skill alternates weapons)", output.MainHand.AverageDamage, output.OffHand.AverageDamage))
- end
- t_insert(breakdown.AverageDamage, s_format("= %.1f", output.AverageDamage))
- end
- end
- end
- if env.mode == "CALCS" then
- if skillData.showAverage then
- output.DisplayDamage = s_format("%.1f average damage", output.AverageDamage)
- else
- output.DisplayDamage = s_format("%.1f DPS", output.TotalDPS)
- end
- end
- if breakdown then
- if isAttack then
- breakdown.TotalDPS = {
- s_format("%.1f ^8(average damage)", output.AverageDamage),
- output.HitSpeed and s_format("x %.2f ^8(hit rate)", output.HitSpeed) or s_format("x %.2f ^8(attack rate)", output.Speed),
- }
- else
- breakdown.TotalDPS = {
- s_format("%.1f ^8(average hit)", output.AverageDamage),
- output.HitSpeed and s_format("x %.2f ^8(hit rate)", output.HitSpeed) or s_format("x %.2f ^8(cast rate)", output.Speed),
- }
- end
- if skillData.dpsMultiplier then
- t_insert(breakdown.TotalDPS, s_format("x %g ^8(DPS multiplier for this skill)", skillData.dpsMultiplier))
- end
- t_insert(breakdown.TotalDPS, s_format("= %.1f", output.TotalDPS))
- end
-
- -- Calculate leech rates
- if skillModList:Flag(nil, "GhostReaver") then
- output.LifeLeechRate = 0
- output.LifeLeechPerHit = 0
- output.EnergyShieldLeechInstanceRate = output.EnergyShield * 0.02 * calcLib.mod(skillModList, skillCfg, "LifeLeechRate")
- output.EnergyShieldLeechRate = output.LifeLeechInstantRate * output.EnergyShieldRecoveryMod + m_min(output.LifeLeechInstances * output.EnergyShieldLeechInstanceRate, output.MaxEnergyShieldLeechRate) * output.EnergyShieldRecoveryRateMod
- output.EnergyShieldLeechPerHit = output.LifeLeechInstant * output.EnergyShieldRecoveryMod + m_min(output.EnergyShieldLeechInstanceRate, output.MaxEnergyShieldLeechRate) * output.LifeLeechDuration * output.EnergyShieldRecoveryRateMod
- else
- output.LifeLeechInstanceRate = output.Life * 0.02 * calcLib.mod(skillModList, skillCfg, "LifeLeechRate")
- output.LifeLeechRate = output.LifeLeechInstantRate * output.LifeRecoveryMod + m_min(output.LifeLeechInstances * output.LifeLeechInstanceRate, output.MaxLifeLeechRate) * output.LifeRecoveryRateMod
- output.LifeLeechPerHit = output.LifeLeechInstant * output.LifeRecoveryMod + m_min(output.LifeLeechInstanceRate, output.MaxLifeLeechRate) * output.LifeLeechDuration * output.LifeRecoveryRateMod
- output.EnergyShieldLeechRate = 0
- output.EnergyShieldLeechPerHit = 0
- end
- do
- output.ManaLeechInstanceRate = output.Mana * 0.02 * calcLib.mod(skillModList, skillCfg, "ManaLeechRate")
- output.ManaLeechRate = output.ManaLeechInstantRate * output.ManaRecoveryMod + m_min(output.ManaLeechInstances * output.ManaLeechInstanceRate, output.MaxManaLeechRate) * output.ManaRecoveryRateMod
- output.ManaLeechPerHit = output.ManaLeechInstant * output.ManaRecoveryMod + m_min(output.ManaLeechInstanceRate, output.MaxManaLeechRate) * output.ManaLeechDuration * output.ManaRecoveryRateMod
- end
- skillFlags.leechES = output.EnergyShieldLeechRate > 0
- skillFlags.leechLife = output.LifeLeechRate > 0
- skillFlags.leechMana = output.ManaLeechRate > 0
- if skillData.showAverage then
- output.LifeLeechGainPerHit = output.LifeLeechPerHit + output.LifeOnHit
- output.EnergyShieldLeechGainPerHit = output.EnergyShieldLeechPerHit + output.EnergyShieldOnHit
- output.ManaLeechGainPerHit = output.ManaLeechPerHit + output.ManaOnHit
- else
- output.LifeLeechGainRate = output.LifeLeechRate + output.LifeOnHitRate
- output.EnergyShieldLeechGainRate = output.EnergyShieldLeechRate + output.EnergyShieldOnHitRate
- output.ManaLeechGainRate = output.ManaLeechRate + output.ManaOnHitRate
- end
- if breakdown then
- if skillFlags.leechLife then
- breakdown.LifeLeech = breakdown.leech(output.LifeLeechInstant, output.LifeLeechInstantRate, output.LifeLeechInstances, output.Life, "LifeLeechRate", output.MaxLifeLeechRate, output.LifeLeechDuration)
- end
- if skillFlags.leechES then
- breakdown.EnergyShieldLeech = breakdown.leech(output.LifeLeechInstant, output.LifeLeechInstantRate, output.LifeLeechInstances, output.EnergyShield, "LifeLeechRate", output.MaxEnergyShieldLeechRate, output.LifeLeechDuration)
- end
- if skillFlags.leechMana then
- breakdown.ManaLeech = breakdown.leech(output.ManaLeechInstant, output.ManaLeechInstantRate, output.ManaLeechInstances, output.Mana, "ManaLeechRate", output.MaxManaLeechRate, output.ManaLeechDuration)
- end
- end
-
- -- Calculate skill DOT components
- local dotCfg = {
- skillName = skillCfg.skillName,
- skillPart = skillCfg.skillPart,
- skillTypes = skillCfg.skillTypes,
- slotName = skillCfg.slotName,
- flags = bor(band(skillCfg.flags, ModFlag.SourceMask), ModFlag.Dot, skillData.dotIsSpell and ModFlag.Spell or 0, skillData.dotIsArea and ModFlag.Area or 0),
- keywordFlags = skillCfg.keywordFlags
- }
- activeSkill.dotCfg = dotCfg
- output.TotalDot = 0
- for _, damageType in ipairs(dmgTypeList) do
- local baseVal
- if canDeal[damageType] then
- baseVal = skillData[damageType.."Dot"] or 0
- else
- baseVal = 0
- end
- if baseVal > 0 then
- skillFlags.dot = true
- local effMult = 1
- if env.mode_effective then
- local resist = 0
- local taken = enemyDB:Sum("INC", nil, "DamageTaken", "DamageTakenOverTime", damageType.."DamageTaken", damageType.."DamageTakenOverTime")
- if damageType == "Physical" then
- resist = enemyDB:Sum("BASE", nil, "PhysicalDamageReduction")
- else
- resist = enemyDB:Sum("BASE", nil, damageType.."Resist")
- if isElemental[damageType] then
- resist = resist + enemyDB:Sum("BASE", nil, "ElementalResist")
- taken = taken + enemyDB:Sum("INC", nil, "ElementalDamageTaken")
- end
- resist = m_min(resist, 75)
- end
- effMult = (1 - resist / 100) * (1 + taken / 100)
- output[damageType.."DotEffMult"] = effMult
- if breakdown and effMult ~= 1 then
- breakdown[damageType.."DotEffMult"] = breakdown.effMult(damageType, resist, 0, taken, effMult)
- end
- end
- local inc = skillModList:Sum("INC", dotCfg, "Damage", damageType.."Damage", isElemental[damageType] and "ElementalDamage" or nil)
- local more = round(skillModList:More(dotCfg, "Damage", damageType.."Damage", isElemental[damageType] and "ElementalDamage" or nil), 2)
- local total = baseVal * (1 + inc/100) * more * effMult
- output[damageType.."Dot"] = total
- output.TotalDot = output.TotalDot + total
- if breakdown then
- breakdown[damageType.."Dot"] = { }
- breakdown.dot(breakdown[damageType.."Dot"], baseVal, inc, more, nil, nil, effMult, total)
- end
- end
- end
-
- skillFlags.bleed = false
- skillFlags.poison = false
- skillFlags.ignite = false
- skillFlags.igniteCanStack = skillModList:Flag(skillCfg, "IgniteCanStack")
- skillFlags.shock = false
- skillFlags.freeze = false
- for _, pass in ipairs(passList) do
- local globalOutput, globalBreakdown = output, breakdown
- local source, output, cfg, breakdown = pass.source, pass.output, pass.cfg, pass.breakdown
-
- -- Calculate chance to inflict secondary dots/status effects
- cfg.skillCond["CriticalStrike"] = true
- if skillModList:Flag(cfg, "CannotBleed") then
- output.BleedChanceOnCrit = 0
- else
- output.BleedChanceOnCrit = m_min(100, skillModList:Sum("BASE", cfg, "BleedChance"))
- end
- output.PoisonChanceOnCrit = m_min(100, skillModList:Sum("BASE", cfg, "PoisonChance"))
- if skillModList:Flag(cfg, "CannotIgnite") then
- output.IgniteChanceOnCrit = 0
- else
- output.IgniteChanceOnCrit = 100
- end
- if skillModList:Flag(cfg, "CannotShock") then
- output.ShockChanceOnCrit = 0
- else
- output.ShockChanceOnCrit = 100
- end
- if skillModList:Flag(cfg, "CannotFreeze") then
- output.FreezeChanceOnCrit = 0
- else
- output.FreezeChanceOnCrit = 100
- end
- if skillModList:Flag(cfg, "CannotKnockback") then
- output.KnockbackChanceOnCrit = 0
- else
- output.KnockbackChanceOnCrit = skillModList:Sum("BASE", cfg, "EnemyKnockbackChance")
- end
- cfg.skillCond["CriticalStrike"] = false
- if skillModList:Flag(cfg, "CannotBleed") then
- output.BleedChanceOnHit = 0
- else
- output.BleedChanceOnHit = m_min(100, skillModList:Sum("BASE", cfg, "BleedChance"))
- end
- output.PoisonChanceOnHit = m_min(100, skillModList:Sum("BASE", cfg, "PoisonChance"))
- output.ChaosPoisonChance = m_min(100, skillModList:Sum("BASE", cfg, "ChaosPoisonChance"))
- if skillModList:Flag(cfg, "CannotIgnite") then
- output.IgniteChanceOnHit = 0
- else
- output.IgniteChanceOnHit = m_min(100, skillModList:Sum("BASE", cfg, "EnemyIgniteChance") + enemyDB:Sum("BASE", nil, "SelfIgniteChance"))
- end
- if skillModList:Flag(cfg, "CannotShock") then
- output.ShockChanceOnHit = 0
- else
- output.ShockChanceOnHit = m_min(100, skillModList:Sum("BASE", cfg, "EnemyShockChance") + enemyDB:Sum("BASE", nil, "SelfShockChance"))
- end
- if skillModList:Flag(cfg, "CannotFreeze") then
- output.FreezeChanceOnHit = 0
- else
- output.FreezeChanceOnHit = m_min(100, skillModList:Sum("BASE", cfg, "EnemyFreezeChance") + enemyDB:Sum("BASE", nil, "SelfFreezeChance"))
- if skillModList:Flag(cfg, "CritsDontAlwaysFreeze") then
- output.FreezeChanceOnCrit = output.FreezeChanceOnHit
- end
- end
- if skillModList:Flag(cfg, "CannotKnockback") then
- output.KnockbackChanceOnHit = 0
- else
- output.KnockbackChanceOnHit = skillModList:Sum("BASE", cfg, "EnemyKnockbackChance")
- end
- if skillFlags.attack and skillFlags.projectile and skillModList:Flag(cfg, "ArrowsThatPierceCauseBleeding") then
- output.BleedChanceOnHit = 100 - (1 - output.BleedChanceOnHit / 100) * (1 - globalOutput.PierceChance / 100) * 100
- output.BleedChanceOnCrit = 100 - (1 - output.BleedChanceOnCrit / 100) * (1 - globalOutput.PierceChance / 100) * 100
- end
- if env.mode_effective then
- local bleedMult = (1 - enemyDB:Sum("BASE", nil, "AvoidBleed") / 100)
- output.BleedChanceOnHit = output.BleedChanceOnHit * bleedMult
- output.BleedChanceOnCrit = output.BleedChanceOnCrit * bleedMult
- local poisonMult = (1 - enemyDB:Sum("BASE", nil, "AvoidPoison") / 100)
- output.PoisonChanceOnHit = output.PoisonChanceOnHit * poisonMult
- output.PoisonChanceOnCrit = output.PoisonChanceOnCrit * poisonMult
- output.ChaosPoisonChance = output.ChaosPoisonChance * poisonMult
- local igniteMult = (1 - enemyDB:Sum("BASE", nil, "AvoidIgnite") / 100)
- output.IgniteChanceOnHit = output.IgniteChanceOnHit * igniteMult
- output.IgniteChanceOnCrit = output.IgniteChanceOnCrit * igniteMult
- local shockMult = (1 - enemyDB:Sum("BASE", nil, "AvoidShock") / 100)
- output.ShockChanceOnHit = output.ShockChanceOnHit * shockMult
- output.ShockChanceOnCrit = output.ShockChanceOnCrit * shockMult
- local freezeMult = (1 - enemyDB:Sum("BASE", nil, "AvoidFreeze") / 100)
- output.FreezeChanceOnHit = output.FreezeChanceOnHit * freezeMult
- output.FreezeChanceOnCrit = output.FreezeChanceOnCrit * freezeMult
- end
-
- local function calcSecondaryEffectBase(type, sourceHitDmg, sourceCritDmg)
- -- Calculate the inflict chance and base damage of a secondary effect (bleed/poison/ignite/shock/freeze)
- local chanceOnHit, chanceOnCrit = output[type.."ChanceOnHit"], output[type.."ChanceOnCrit"]
- local chanceFromHit = chanceOnHit * (1 - output.CritChance / 100)
- local chanceFromCrit = chanceOnCrit * output.CritChance / 100
- local chance = chanceFromHit + chanceFromCrit
- output[type.."Chance"] = chance
- local baseFromHit = sourceHitDmg * chanceFromHit / (chanceFromHit + chanceFromCrit)
- local baseFromCrit = sourceCritDmg * chanceFromCrit / (chanceFromHit + chanceFromCrit)
- local baseVal = baseFromHit + baseFromCrit
- if breakdown and chance ~= 0 then
- local breakdownChance = breakdown[type.."Chance"] or { }
- breakdown[type.."Chance"] = breakdownChance
- if breakdownChance[1] then
- t_insert(breakdownChance, "")
- end
- if isAttack then
- t_insert(breakdownChance, pass.label..":")
- end
- t_insert(breakdownChance, s_format("Chance on Non-crit: %d%%", chanceOnHit))
- t_insert(breakdownChance, s_format("Chance on Crit: %d%%", chanceOnCrit))
- if chanceOnHit ~= chanceOnCrit then
- t_insert(breakdownChance, "Combined chance:")
- t_insert(breakdownChance, s_format("%d x (1 - %.4f) ^8(chance from non-crits)", chanceOnHit, output.CritChance/100))
- t_insert(breakdownChance, s_format("+ %d x %.4f ^8(chance from crits)", chanceOnCrit, output.CritChance/100))
- t_insert(breakdownChance, s_format("= %.2f", chance))
- end
- end
- if breakdown and baseVal > 0 then
- local breakdownDPS = breakdown[type.."DPS"] or { }
- breakdown[type.."DPS"] = breakdownDPS
- if breakdownDPS[1] then
- t_insert(breakdownDPS, "")
- end
- if isAttack then
- t_insert(breakdownDPS, pass.label..":")
- end
- if sourceHitDmg == sourceCritDmg then
- t_insert(breakdownDPS, "Base damage:")
- t_insert(breakdownDPS, s_format("%.1f ^8(source damage)",sourceHitDmg))
- else
- if baseFromHit > 0 then
- t_insert(breakdownDPS, "Base from Non-crits:")
- t_insert(breakdownDPS, s_format("%.1f ^8(source damage from non-crits)", sourceHitDmg))
- t_insert(breakdownDPS, s_format("x %.3f ^8(portion of instances created by non-crits)", chanceFromHit / (chanceFromHit + chanceFromCrit)))
- t_insert(breakdownDPS, s_format("= %.1f", baseFromHit))
- end
- if baseFromCrit > 0 then
- t_insert(breakdownDPS, "Base from Crits:")
- t_insert(breakdownDPS, s_format("%.1f ^8(source damage from crits)", sourceCritDmg))
- t_insert(breakdownDPS, s_format("x %.3f ^8(portion of instances created by crits)", chanceFromCrit / (chanceFromHit + chanceFromCrit)))
- t_insert(breakdownDPS, s_format("= %.1f", baseFromCrit))
- end
- if baseFromHit > 0 and baseFromCrit > 0 then
- t_insert(breakdownDPS, "Total base damage:")
- t_insert(breakdownDPS, s_format("%.1f + %.1f", baseFromHit, baseFromCrit))
- t_insert(breakdownDPS, s_format("= %.1f", baseVal))
- end
- end
- end
- return baseVal
- end
-
- -- Calculate bleeding chance and damage
- if canDeal.Physical and (output.BleedChanceOnHit + output.BleedChanceOnCrit) > 0 then
- local sourceHitDmg = output.PhysicalHitAverage
- local sourceCritDmg = output.PhysicalCritAverage
- local basePercent = skillData.bleedBasePercent or 10
- local baseVal = calcSecondaryEffectBase("Bleed", sourceHitDmg, sourceCritDmg) * basePercent / 100
- if baseVal > 0 then
- skillFlags.bleed = true
- skillFlags.duration = true
- if not activeSkill.bleedCfg then
- activeSkill.bleedCfg = {
- skillName = skillCfg.skillName,
- skillTypes = skillCfg.skillTypes,
- slotName = skillCfg.slotName,
- flags = bor(band(skillCfg.flags, ModFlag.SourceMask), ModFlag.Dot, skillData.dotIsSpell and ModFlag.Spell or 0),
- keywordFlags = bor(skillCfg.keywordFlags, KeywordFlag.Bleed)
- }
- end
- local dotCfg = activeSkill.bleedCfg
- local effMult = 1
- if env.mode_effective then
- local resist = enemyDB:Sum("BASE", nil, "PhysicalDamageReduction")
- local taken = enemyDB:Sum("INC", dotCfg, "DamageTaken", "DamageTakenOverTime", "PhysicalDamageTaken", "PhysicalDamageTakenOverTime")
- effMult = (1 - resist / 100) * (1 + taken / 100)
- globalOutput["BleedEffMult"] = effMult
- if breakdown and effMult ~= 1 then
- globalBreakdown.BleedEffMult = breakdown.effMult("Physical", resist, 0, taken, effMult)
- end
- end
- local inc = skillModList:Sum("INC", dotCfg, "Damage", "PhysicalDamage")
- local more = round(skillModList:More(dotCfg, "Damage", "PhysicalDamage"), 2)
- output.BleedDPS = baseVal * (1 + inc/100) * more * effMult
- local durationMod = calcLib.mod(skillModList, dotCfg, "Duration") * calcLib.mod(enemyDB, nil, "SelfBleedDuration")
- globalOutput.BleedDuration = 5 * durationMod * debuffDurationMult
- if breakdown then
- t_insert(breakdown.BleedDPS, s_format("x %.2f ^8(bleed deals %d%% per second)", basePercent/100, basePercent))
- t_insert(breakdown.BleedDPS, s_format("= %.1f", baseVal))
- t_insert(breakdown.BleedDPS, "Bleed DPS:")
- breakdown.dot(breakdown.BleedDPS, baseVal, inc, more, nil, nil, effMult, output.BleedDPS)
- if globalOutput.BleedDuration ~= 5 then
- globalBreakdown.BleedDuration = {
- "5.00s ^8(base duration)"
- }
- if durationMod ~= 1 then
- t_insert(globalBreakdown.BleedDuration, s_format("x %.2f ^8(duration modifier)", durationMod))
- end
- if debuffDurationMult ~= 1 then
- t_insert(globalBreakdown.BleedDuration, s_format("/ %.2f ^8(debuff expires slower/faster)", 1 / debuffDurationMult))
- end
- t_insert(globalBreakdown.BleedDuration, s_format("= %.2fs", globalOutput.BleedDuration))
- end
- end
- end
- end
-
- -- Calculate poison chance and damage
- if canDeal.Chaos and (output.PoisonChanceOnHit + output.PoisonChanceOnCrit + output.ChaosPoisonChance) > 0 then
- local sourceHitDmg = output.ChaosHitAverage
- if output.ChaosPoisonChance > 0 and sourceHitDmg > 0 then
- -- Additional chance for chaos; adjust Physical damage and inflict chance
- local chaosChance = m_min(100, output.PoisonChanceOnHit + output.ChaosPoisonChance)
- sourceHitDmg = sourceHitDmg + output.PhysicalHitAverage * output.PoisonChanceOnHit / chaosChance
- output.PoisonChanceOnHit = chaosChance
- else
- sourceHitDmg = sourceHitDmg + output.PhysicalHitAverage
- end
- local sourceCritDmg = output.ChaosCritAverage
- if output.ChaosPoisonChance > 0 and sourceCritDmg > 0 then
- -- Additional chance for chaos; adjust Physical damage and inflict chance
- local chaosChance = m_min(100, output.PoisonChanceOnCrit + output.ChaosPoisonChance)
- sourceCritDmg = sourceCritDmg + output.PhysicalCritAverage * output.PoisonChanceOnCrit / chaosChance
- output.PoisonChanceOnCrit = chaosChance
- else
- sourceCritDmg = sourceCritDmg + output.PhysicalCritAverage
- end
- local baseVal = calcSecondaryEffectBase("Poison", sourceHitDmg, sourceCritDmg * skillModList:More(cfg, "PoisonDamageOnCrit")) * 0.08
- if baseVal > 0 then
- skillFlags.poison = true
- skillFlags.duration = true
- if not activeSkill.poisonCfg then
- activeSkill.poisonCfg = {
- skillName = skillCfg.skillName,
- skillTypes = skillCfg.skillTypes,
- slotName = skillCfg.slotName,
- flags = bor(band(skillCfg.flags, ModFlag.SourceMask), ModFlag.Dot, skillData.dotIsSpell and ModFlag.Spell or 0),
- keywordFlags = bor(skillCfg.keywordFlags, KeywordFlag.Poison)
- }
- end
- local dotCfg = activeSkill.poisonCfg
- local effMult = 1
- if env.mode_effective then
- local resist = m_min(enemyDB:Sum("BASE", nil, "ChaosResist"), 75)
- local taken = enemyDB:Sum("INC", nil, "DamageTaken", "DamageTakenOverTime", "ChaosDamageTaken", "ChaosDamageTakenOverTime")
- effMult = (1 - resist / 100) * (1 + taken / 100)
- globalOutput["PoisonEffMult"] = effMult
- if breakdown and effMult ~= 1 then
- globalBreakdown.PoisonEffMult = breakdown.effMult("Chaos", resist, 0, taken, effMult)
- end
- end
- local inc = skillModList:Sum("INC", dotCfg, "Damage", "ChaosDamage")
- local more = round(skillModList:More(dotCfg, "Damage", "ChaosDamage"), 2)
- output.PoisonDPS = baseVal * (1 + inc/100) * more * effMult
- local durationBase
- if skillData.poisonDurationIsSkillDuration then
- durationBase = skillData.duration
- else
- durationBase = 2
- end
- local durationMod = calcLib.mod(skillModList, dotCfg, "Duration") * calcLib.mod(enemyDB, nil, "SelfPoisonDuration")
- globalOutput.PoisonDuration = durationBase * durationMod * debuffDurationMult
- output.PoisonDamage = output.PoisonDPS * globalOutput.PoisonDuration
- if skillData.showAverage then
- output.TotalPoisonAverageDamage = output.HitChance / 100 * output.PoisonChance / 100 * output.PoisonDamage
- else
- output.TotalPoisonStacks = output.HitChance / 100 * output.PoisonChance / 100 * globalOutput.PoisonDuration * (globalOutput.HitSpeed or globalOutput.Speed) * (skillData.dpsMultiplier or 1)
- output.TotalPoisonDPS = output.PoisonDPS * output.TotalPoisonStacks
- end
- if breakdown then
- t_insert(breakdown.PoisonDPS, "x 0.08 ^8(poison deals 8% per second)")
- t_insert(breakdown.PoisonDPS, s_format("= %.1f", baseVal, 1))
- t_insert(breakdown.PoisonDPS, "Poison DPS:")
- breakdown.dot(breakdown.PoisonDPS, baseVal, inc, more, nil, nil, effMult, output.PoisonDPS)
- if globalOutput.PoisonDuration ~= 2 then
- globalBreakdown.PoisonDuration = {
- s_format("%.2fs ^8(base duration)", durationBase)
- }
- if durationMod ~= 1 then
- t_insert(globalBreakdown.PoisonDuration, s_format("x %.2f ^8(duration modifier)", durationMod))
- end
- if debuffDurationMult ~= 1 then
- t_insert(globalBreakdown.PoisonDuration, s_format("/ %.2f ^8(debuff expires slower/faster)", 1 / debuffDurationMult))
- end
- t_insert(globalBreakdown.PoisonDuration, s_format("= %.2fs", globalOutput.PoisonDuration))
- end
- breakdown.PoisonDamage = { }
- if isAttack then
- t_insert(breakdown.PoisonDamage, pass.label..":")
- end
- t_insert(breakdown.PoisonDamage, s_format("%.1f ^8(damage per second)", output.PoisonDPS))
- t_insert(breakdown.PoisonDamage, s_format("x %.2fs ^8(poison duration)", globalOutput.PoisonDuration))
- t_insert(breakdown.PoisonDamage, s_format("= %.1f ^8damage per poison stack", output.PoisonDamage))
- if not skillData.showAverage then
- breakdown.TotalPoisonStacks = { }
- if isAttack then
- t_insert(breakdown.TotalPoisonStacks, pass.label..":")
- end
- breakdown.multiChain(breakdown.TotalPoisonStacks, {
- base = s_format("%.2fs ^8(poison duration)", globalOutput.PoisonDuration),
- { "%.2f ^8(poison chance)", output.PoisonChance / 100 },
- { "%.2f ^8(hit chance)", output.HitChance / 100 },
- { "%.2f ^8(hits per second)", globalOutput.HitSpeed or globalOutput.Speed },
- { "%g ^8(dps multiplier for this skill)", skillData.dpsMultiplier or 1 },
- total = s_format("= %.1f", output.TotalPoisonStacks),
- })
- end
- end
- end
- end
-
- -- Calculate ignite chance and damage
- if canDeal.Fire and (output.IgniteChanceOnHit + output.IgniteChanceOnCrit) > 0 then
- local sourceHitDmg = 0
- local sourceCritDmg = 0
- if canDeal.Physical and skillModList:Flag(cfg, "PhysicalCanIgnite") then
- sourceHitDmg = sourceHitDmg + output.PhysicalHitAverage
- sourceCritDmg = sourceCritDmg + output.PhysicalCritAverage
- end
- if canDeal.Lightning and skillModList:Flag(cfg, "LightningCanIgnite") then
- sourceHitDmg = sourceHitDmg + output.LightningHitAverage
- sourceCritDmg = sourceCritDmg + output.LightningCritAverage
- end
- if canDeal.Cold and skillModList:Flag(cfg, "ColdCanIgnite") then
- sourceHitDmg = sourceHitDmg + output.ColdHitAverage
- sourceCritDmg = sourceCritDmg + output.ColdCritAverage
- end
- if canDeal.Fire and not skillModList:Flag(cfg, "FireCannotIgnite") then
- sourceHitDmg = sourceHitDmg + output.FireHitAverage
- sourceCritDmg = sourceCritDmg + output.FireCritAverage
- end
- if canDeal.Chaos and skillModList:Flag(cfg, "ChaosCanIgnite") then
- sourceHitDmg = sourceHitDmg + output.ChaosHitAverage
- sourceCritDmg = sourceCritDmg + output.ChaosCritAverage
- end
- local igniteMode = env.configInput.igniteMode or "AVERAGE"
- if igniteMode == "CRIT" then
- output.IgniteChanceOnHit = 0
- end
- if globalBreakdown then
- globalBreakdown.IgniteDPS = {
- s_format("Ignite mode: %s ^8(can be changed in the Configuration tab)", igniteMode == "CRIT" and "Crit Damage" or "Average Damage")
- }
- end
- local baseVal = calcSecondaryEffectBase("Ignite", sourceHitDmg, sourceCritDmg) * 0.2
- if baseVal > 0 then
- skillFlags.ignite = true
- if not activeSkill.igniteCfg then
- activeSkill.igniteCfg = {
- skillName = skillCfg.skillName,
- skillTypes = skillCfg.skillTypes,
- slotName = skillCfg.slotName,
- flags = bor(band(skillCfg.flags, ModFlag.SourceMask), ModFlag.Dot, skillData.dotIsSpell and ModFlag.Spell or 0),
- keywordFlags = skillCfg.keywordFlags,
- }
- end
- local dotCfg = activeSkill.igniteCfg
- local effMult = 1
- if env.mode_effective then
- local resist = m_min(enemyDB:Sum("BASE", nil, "FireResist", "ElementalResist"), 75)
- local taken = enemyDB:Sum("INC", dotCfg, "DamageTaken", "DamageTakenOverTime", "FireDamageTaken", "FireDamageTakenOverTime", "ElementalDamageTaken")
- effMult = (1 - resist / 100) * (1 + taken / 100)
- globalOutput["IgniteEffMult"] = effMult
- if breakdown and effMult ~= 1 then
- globalBreakdown.IgniteEffMult = breakdown.effMult("Fire", resist, 0, taken, effMult)
- end
- end
- local inc = skillModList:Sum("INC", dotCfg, "Damage", "FireDamage", "ElementalDamage")
- local more = round(skillModList:More(dotCfg, "Damage", "FireDamage", "ElementalDamage"), 2)
- local burnRateMod = calcLib.mod(skillModList, cfg, "IgniteBurnFaster") / calcLib.mod(skillModList, cfg, "IgniteBurnSlower")
- output.IgniteDPS = baseVal * (1 + inc/100) * more * burnRateMod * effMult
- local incDur = skillModList:Sum("INC", dotCfg, "EnemyIgniteDuration") + enemyDB:Sum("INC", nil, "SelfIgniteDuration")
- local moreDur = enemyDB:More(nil, "SelfIgniteDuration")
- globalOutput.IgniteDuration = 4 * (1 + incDur / 100) * moreDur / burnRateMod * debuffDurationMult
- if skillFlags.igniteCanStack then
- output.IgniteDamage = output.IgniteDPS * globalOutput.IgniteDuration
- if skillData.showAverage then
- output.TotalIgniteAverageDamage = output.HitChance / 100 * output.IgniteChance / 100 * output.IgniteDamage
- else
- output.TotalIgniteStacks = output.HitChance / 100 * output.IgniteChance / 100 * globalOutput.IgniteDuration * (globalOutput.HitSpeed or globalOutput.Speed) * (skillData.dpsMultiplier or 1)
- output.TotalIgniteDPS = output.IgniteDPS * output.TotalIgniteStacks
- end
- end
- if breakdown then
- t_insert(breakdown.IgniteDPS, "x 0.2 ^8(ignite deals 20% per second)")
- t_insert(breakdown.IgniteDPS, s_format("= %.1f", baseVal, 1))
- t_insert(breakdown.IgniteDPS, "Ignite DPS:")
- breakdown.dot(breakdown.IgniteDPS, baseVal, inc, more, nil, burnRateMod, effMult, output.IgniteDPS)
- if skillFlags.igniteCanStack then
- breakdown.IgniteDamage = { }
- if isAttack then
- t_insert(breakdown.IgniteDamage, pass.label..":")
- end
- t_insert(breakdown.IgniteDamage, s_format("%.1f ^8(damage per second)", output.IgniteDPS))
- t_insert(breakdown.IgniteDamage, s_format("x %.2fs ^8(ignite duration)", globalOutput.IgniteDuration))
- t_insert(breakdown.IgniteDamage, s_format("= %.1f ^8damage per ignite stack", output.IgniteDamage))
- if not skillData.showAverage then
- breakdown.TotalIgniteStacks = { }
- if isAttack then
- t_insert(breakdown.TotalIgniteStacks, pass.label..":")
- end
- breakdown.multiChain(breakdown.TotalIgniteStacks, {
- base = s_format("%.2fs ^8(ignite duration)", globalOutput.IgniteDuration),
- { "%.2f ^8(ignite chance)", output.IgniteChance / 100 },
- { "%.2f ^8(hit chance)", output.HitChance / 100 },
- { "%.2f ^8(hits per second)", globalOutput.HitSpeed or globalOutput.Speed },
- { "%g ^8(dps multiplier for this skill)", skillData.dpsMultiplier or 1 },
- total = s_format("= %.1f", output.TotalIgniteStacks),
- })
- end
- end
- if globalOutput.IgniteDuration ~= 4 then
- globalBreakdown.IgniteDuration = {
- s_format("4.00s ^8(base duration)", durationBase)
- }
- if incDur ~= 0 then
- t_insert(globalBreakdown.IgniteDuration, s_format("x %.2f ^8(increased/reduced duration)", 1 + incDur/100))
- end
- if moreDur ~= 1 then
- t_insert(globalBreakdown.IgniteDuration, s_format("x %.2f ^8(more/less duration)", moreDur))
- end
- if burnRateMod ~= 1 then
- t_insert(globalBreakdown.IgniteDuration, s_format("/ %.2f ^8(rate modifier)", burnRateMod))
- end
- if debuffDurationMult ~= 1 then
- t_insert(globalBreakdown.IgniteDuration, s_format("/ %.2f ^8(debuff expires slower/faster)", 1 / debuffDurationMult))
- end
- t_insert(globalBreakdown.IgniteDuration, s_format("= %.2fs", globalOutput.IgniteDuration))
- end
- end
- end
- end
-
- -- Calculate shock and freeze chance + duration modifier
- if (output.ShockChanceOnHit + output.ShockChanceOnCrit) > 0 then
- local sourceHitDmg = 0
- local sourceCritDmg = 0
- if canDeal.Physical and skillModList:Flag(cfg, "PhysicalCanShock") then
- sourceHitDmg = sourceHitDmg + output.PhysicalHitAverage
- sourceCritDmg = sourceCritDmg + output.PhysicalCritAverage
- end
- if canDeal.Lightning and not skillModList:Flag(cfg, "LightningCannotShock") then
- sourceHitDmg = sourceHitDmg + output.LightningHitAverage
- sourceCritDmg = sourceCritDmg + output.LightningCritAverage
- end
- if canDeal.Cold and skillModList:Flag(cfg, "ColdCanShock") then
- sourceHitDmg = sourceHitDmg + output.ColdHitAverage
- sourceCritDmg = sourceCritDmg + output.ColdCritAverage
- end
- if canDeal.Fire and skillModList:Flag(cfg, "FireCanShock") then
- sourceHitDmg = sourceHitDmg + output.FireHitAverage
- sourceCritDmg = sourceCritDmg + output.FireCritAverage
- end
- if canDeal.Chaos and skillModList:Flag(cfg, "ChaosCanShock") then
- sourceHitDmg = sourceHitDmg + output.ChaosHitAverage
- sourceCritDmg = sourceCritDmg + output.ChaosCritAverage
- end
- local baseVal = calcSecondaryEffectBase("Shock", sourceHitDmg, sourceCritDmg)
- if baseVal > 0 then
- skillFlags.shock = true
- output.ShockDurationMod = 1 + skillModList:Sum("INC", cfg, "EnemyShockDuration") / 100 + enemyDB:Sum("INC", nil, "SelfShockDuration") / 100
- if breakdown then
- t_insert(breakdown.ShockDPS, s_format("For shock to apply, target must have no more than %d life.", baseVal * 20 * output.ShockDurationMod))
- end
- end
- end
- if (output.FreezeChanceOnHit + output.FreezeChanceOnCrit) > 0 then
- local sourceHitDmg = 0
- local sourceCritDmg = 0
- if canDeal.Cold and not skillModList:Flag(cfg, "ColdCannotFreeze") then
- sourceHitDmg = sourceHitDmg + output.ColdHitAverage
- sourceCritDmg = sourceCritDmg + output.ColdCritAverage
- end
- if canDeal.Lightning and skillModList:Flag(cfg, "LightningCanFreeze") then
- sourceHitDmg = sourceHitDmg + output.LightningHitAverage
- sourceCritDmg = sourceCritDmg + output.LightningCritAverage
- end
- local baseVal = calcSecondaryEffectBase("Freeze", sourceHitDmg, sourceCritDmg)
- if baseVal > 0 then
- skillFlags.freeze = true
- output.FreezeDurationMod = 1 + skillModList:Sum("INC", cfg, "EnemyFreezeDuration") / 100 + enemyDB:Sum("INC", nil, "SelfFreezeDuration") / 100
- if breakdown then
- t_insert(breakdown.FreezeDPS, s_format("For freeze to apply, target must have no more than %d life.", baseVal * 20 * output.FreezeDurationMod))
- end
- end
- end
-
- -- Calculate knockback chance/distance
- output.KnockbackChance = m_min(100, output.KnockbackChanceOnHit * (1 - output.CritChance / 100) + output.KnockbackChanceOnCrit * output.CritChance / 100 + enemyDB:Sum("BASE", nil, "SelfKnockbackChance"))
- if output.KnockbackChance > 0 then
- output.KnockbackDistance = round(4 * calcLib.mod(skillModList, cfg, "EnemyKnockbackDistance"))
- if breakdown then
- breakdown.KnockbackDistance = {
- radius = output.KnockbackDistance,
- }
- end
- end
-
- -- Calculate enemy stun modifiers
- local enemyStunThresholdRed = -skillModList:Sum("INC", cfg, "EnemyStunThreshold")
- if enemyStunThresholdRed > 75 then
- output.EnemyStunThresholdMod = 1 - (75 + (enemyStunThresholdRed - 75) * 25 / (enemyStunThresholdRed - 50)) / 100
- else
- output.EnemyStunThresholdMod = 1 - enemyStunThresholdRed / 100
- end
- local incDur = skillModList:Sum("INC", cfg, "EnemyStunDuration")
- local incRecov = enemyDB:Sum("INC", nil, "StunRecovery")
- output.EnemyStunDuration = 0.35 * (1 + incDur / 100) / (1 + incRecov / 100)
- if breakdown then
- if output.EnemyStunDuration ~= 0.35 then
- breakdown.EnemyStunDuration = {
- "0.35s ^8(base duration)"
- }
- if incDur ~= 0 then
- t_insert(breakdown.EnemyStunDuration, s_format("x %.2f ^8(increased/reduced stun duration)", 1 + incDur/100))
- end
- if incRecov ~= 0 then
- t_insert(breakdown.EnemyStunDuration, s_format("/ %.2f ^8(increased/reduced enemy stun recovery)", 1 + incRecov/100))
- end
- t_insert(breakdown.EnemyStunDuration, s_format("= %.2fs", output.EnemyStunDuration))
- end
- end
-
- end
-
- -- Combine secondary effect stats
- if isAttack then
- combineStat("BleedChance", "AVERAGE")
- combineStat("BleedDPS", "CHANCE", "BleedChance")
- combineStat("PoisonChance", "AVERAGE")
- combineStat("PoisonDPS", "CHANCE", "PoisonChance")
- combineStat("PoisonDamage", "CHANCE", "PoisonChance")
- if skillData.showAverage then
- combineStat("TotalPoisonAverageDamage", "DPS")
- else
- combineStat("TotalPoisonStacks", "DPS")
- combineStat("TotalPoisonDPS", "DPS")
- end
- combineStat("IgniteChance", "AVERAGE")
- combineStat("IgniteDPS", "CHANCE", "IgniteChance")
- if skillFlags.igniteCanStack then
- combineStat("IgniteDamage", "CHANCE", "IgniteChance")
- if skillData.showAverage then
- combineStat("TotalIgniteAverageDamage", "DPS")
- else
- combineStat("TotalIgniteStacks", "DPS")
- combineStat("TotalIgniteDPS", "DPS")
- end
- end
- combineStat("ShockChance", "AVERAGE")
- combineStat("ShockDurationMod", "AVERAGE")
- combineStat("FreezeChance", "AVERAGE")
- combineStat("FreezeDurationMod", "AVERAGE")
- end
-
- if skillFlags.hit and skillData.decay then
- -- Calculate DPS for Essence of Delirium's Decay effect
- skillFlags.decay = true
- activeSkill.decayCfg = {
- slotName = skillCfg.slotName,
- skillTypes = skillCfg.skillTypes,
- flags = bor(band(skillCfg.flags, ModFlag.SourceMask), ModFlag.Dot, skillData.dotIsSpell and ModFlag.Spell or 0),
- keywordFlags = skillCfg.keywordFlags,
- }
- local dotCfg = activeSkill.decayCfg
- local effMult = 1
- if env.mode_effective then
- local resist = m_min(enemyDB:Sum("BASE", nil, "ChaosResist"), 75)
- local taken = enemyDB:Sum("INC", nil, "DamageTaken", "DamageTakenOverTime", "ChaosDamageTaken", "ChaosDamageTakenOverTime")
- effMult = (1 - resist / 100) * (1 + taken / 100)
- output["DecayEffMult"] = effMult
- if breakdown and effMult ~= 1 then
- breakdown.DecayEffMult = breakdown.effMult("Chaos", resist, 0, taken, effMult)
- end
- end
- local inc = skillModList:Sum("INC", dotCfg, "Damage", "ChaosDamage")
- local more = round(skillModList:More(dotCfg, "Damage", "ChaosDamage"), 2)
- output.DecayDPS = skillData.decay * (1 + inc/100) * more * effMult
- local durationMod = calcLib.mod(skillModList, dotCfg, "Duration")
- output.DecayDuration = 10 * durationMod * debuffDurationMult
- if breakdown then
- breakdown.DecayDPS = { }
- t_insert(breakdown.DecayDPS, "Decay DPS:")
- breakdown.dot(breakdown.DecayDPS, skillData.decay, inc, more, nil, nil, effMult, output.DecayDPS)
- if output.DecayDuration ~= 2 then
- breakdown.DecayDuration = {
- s_format("%.2fs ^8(base duration)", 10)
- }
- if durationMod ~= 1 then
- t_insert(breakdown.DecayDuration, s_format("x %.2f ^8(duration modifier)", durationMod))
- end
- if debuffDurationMult ~= 1 then
- t_insert(breakdown.DecayDuration, s_format("/ %.2f ^8(debuff expires slower/faster)", 1 / debuffDurationMult))
- end
- t_insert(breakdown.DecayDuration, s_format("= %.2fs", output.DecayDuration))
- end
- end
- end
-
- -- Calculate combined DPS estimate, including DoTs
- local baseDPS = output[(skillData.showAverage and "AverageDamage") or "TotalDPS"] + output.TotalDot
- output.CombinedDPS = baseDPS
- if skillData.showAverage then
- output.CombinedDPS = output.CombinedDPS + (output.TotalPoisonAverageDamage or 0)
- output.WithPoisonAverageDamage = baseDPS + (output.TotalPoisonAverageDamage or 0)
- else
- output.CombinedDPS = output.CombinedDPS + (output.TotalPoisonDPS or 0)
- output.WithPoisonDPS = baseDPS + (output.TotalPoisonDPS or 0)
- end
- if skillFlags.ignite then
- if skillFlags.igniteCanStack then
- if skillData.showAverage then
- output.CombinedDPS = output.CombinedDPS + output.TotalIgniteAverageDamage
- output.WithIgniteAverageDamage = baseDPS + output.TotalIgniteAverageDamage
- else
- output.CombinedDPS = output.CombinedDPS + output.TotalIgniteDPS
- output.WithIgniteDPS = baseDPS + output.TotalIgniteDPS
- end
- else
- output.CombinedDPS = output.CombinedDPS + output.IgniteDPS
- end
- end
- if skillFlags.bleed then
- output.CombinedDPS = output.CombinedDPS + output.BleedDPS
- end
- if skillFlags.decay then
- output.CombinedDPS = output.CombinedDPS + output.DecayDPS
- end
-end
\ No newline at end of file
diff --git b/Modules/CalcOffence-3_0.lua a/Modules/CalcOffence-3_0.lua
deleted file mode 100644
index 8ffce06d..00000000
--- b/Modules/CalcOffence-3_0.lua
+++ /dev/null
@@ -1,2214 +0,0 @@
--- Path of Building
---
--- Module: Calc Offence
--- Performs offence calculations.
---
-local calcs = ...
-
-local pairs = pairs
-local ipairs = ipairs
-local unpack = unpack
-local t_insert = table.insert
-local m_floor = math.floor
-local m_modf = math.modf
-local m_min = math.min
-local m_max = math.max
-local m_sqrt = math.sqrt
-local m_pi = math.pi
-local bor = bit.bor
-local band = bit.band
-local bnot = bit.bnot
-local s_format = string.format
-
-local tempTable1 = { }
-local tempTable2 = { }
-local tempTable3 = { }
-
-local isElemental = { Fire = true, Cold = true, Lightning = true }
-
--- List of all damage types, ordered according to the conversion sequence
-local dmgTypeList = {"Physical", "Lightning", "Cold", "Fire", "Chaos"}
-local dmgTypeFlags = {
- Physical = 0x01,
- Lightning = 0x02,
- Cold = 0x04,
- Fire = 0x08,
- Elemental = 0x0E,
- Chaos = 0x10,
-}
-
--- Magic table for caching the modifier name sets used in calcDamage()
-local damageStatsForTypes = setmetatable({ }, { __index = function(t, k)
- local modNames = { "Damage" }
- for type, flag in pairs(dmgTypeFlags) do
- if band(k, flag) ~= 0 then
- t_insert(modNames, type.."Damage")
- end
- end
- t[k] = modNames
- return modNames
-end })
-
--- Calculate min/max damage for the given damage type
-local function calcDamage(activeSkill, output, cfg, breakdown, damageType, typeFlags, convDst)
- local skillModList = activeSkill.skillModList
-
- typeFlags = bor(typeFlags, dmgTypeFlags[damageType])
-
- -- Calculate conversions
- local addMin, addMax = 0, 0
- local conversionTable = activeSkill.conversionTable
- for _, otherType in ipairs(dmgTypeList) do
- if otherType == damageType then
- -- Damage can only be converted from damage types that preceed this one in the conversion sequence, so stop here
- break
- end
- local convMult = conversionTable[otherType][damageType]
- if convMult > 0 then
- -- Damage is being converted/gained from the other damage type
- local min, max = calcDamage(activeSkill, output, cfg, breakdown, otherType, typeFlags, damageType)
- addMin = addMin + min * convMult
- addMax = addMax + max * convMult
- end
- end
- if addMin ~= 0 and addMax ~= 0 then
- addMin = round(addMin)
- addMax = round(addMax)
- end
-
- local baseMin = output[damageType.."MinBase"]
- local baseMax = output[damageType.."MaxBase"]
- if baseMin == 0 and baseMax == 0 then
- -- No base damage for this type, don't need to calculate modifiers
- if breakdown and (addMin ~= 0 or addMax ~= 0) then
- t_insert(breakdown.damageTypes, {
- source = damageType,
- convSrc = (addMin ~= 0 or addMax ~= 0) and (addMin .. " to " .. addMax),
- total = addMin .. " to " .. addMax,
- convDst = convDst and s_format("%d%% to %s", conversionTable[damageType][convDst] * 100, convDst),
- })
- end
- return addMin, addMax
- end
-
- -- Combine modifiers
- local modNames = damageStatsForTypes[typeFlags]
- local inc = 1 + skillModList:Sum("INC", cfg, unpack(modNames)) / 100
- local more = m_floor(skillModList:More(cfg, unpack(modNames)) * 100 + 0.50000001) / 100
-
- if breakdown then
- t_insert(breakdown.damageTypes, {
- source = damageType,
- base = baseMin .. " to " .. baseMax,
- inc = (inc ~= 1 and "x "..inc),
- more = (more ~= 1 and "x "..more),
- convSrc = (addMin ~= 0 or addMax ~= 0) and (addMin .. " to " .. addMax),
- total = (round(baseMin * inc * more) + addMin) .. " to " .. (round(baseMax * inc * more) + addMax),
- convDst = convDst and conversionTable[damageType][convDst] > 0 and s_format("%d%% to %s", conversionTable[damageType][convDst] * 100, convDst),
- })
- end
-
- return (round(baseMin * inc * more) + addMin),
- (round(baseMax * inc * more) + addMax)
-end
-
-local function calcAilmentSourceDamage(activeSkill, output, cfg, breakdown, damageType, typeFlags)
- local min, max = calcDamage(activeSkill, output, cfg, breakdown, damageType, typeFlags)
- local convMult = activeSkill.conversionTable[damageType].mult
- if breakdown and convMult ~= 1 then
- t_insert(breakdown, "Source damage:")
- t_insert(breakdown, s_format("%d to %d ^8(total damage)", min, max))
- t_insert(breakdown, s_format("x %g ^8(%g%% converted to other damage types)", convMult, (1-convMult)*100))
- t_insert(breakdown, s_format("= %d to %d", min * convMult, max * convMult))
- end
- return min * convMult, max * convMult
-end
-
--- Performs all offensive calculations
-function calcs.offence(env, actor, activeSkill)
- local enemyDB = actor.enemy.modDB
- local output = actor.output
- local breakdown = actor.breakdown
-
- local skillModList = activeSkill.skillModList
- local skillData = activeSkill.skillData
- local skillFlags = activeSkill.skillFlags
- local skillCfg = activeSkill.skillCfg
- if skillData.showAverage then
- skillFlags.showAverage = true
- else
- skillFlags.notAverage = true
- end
-
- if skillFlags.disable then
- -- Skill is disabled
- output.CombinedDPS = 0
- return
- end
-
- local function runSkillFunc(name)
- local func = activeSkill.activeEffect.grantedEffect[name]
- if func then
- func(activeSkill, output)
- end
- end
-
- runSkillFunc("initialFunc")
-
- -- Update skill data
- for _, value in ipairs(skillModList:List(skillCfg, "SkillData")) do
- if value.merge == "MAX" then
- skillData[value.key] = m_max(value.value, skillData[value.key] or 0)
- else
- skillData[value.key] = value.value
- end
- end
-
- skillCfg.skillCond["SkillIsTriggered"] = skillData.triggered
-
- -- Add addition stat bonuses
- if skillModList:Flag(nil, "IronGrip") then
- skillModList:NewMod("PhysicalDamage", "INC", actor.strDmgBonus, "Strength", bor(ModFlag.Attack, ModFlag.Projectile))
- end
- if skillModList:Flag(nil, "IronWill") then
- skillModList:NewMod("Damage", "INC", actor.strDmgBonus, "Strength", ModFlag.Spell)
- end
-
- if skillModList:Flag(nil, "MinionDamageAppliesToPlayer") then
- -- Minion Damage conversion from The Scourge
- for _, value in ipairs(skillModList:List(skillCfg, "MinionModifier")) do
- if value.mod.name == "Damage" and value.mod.type == "INC" then
- skillModList:AddMod(value.mod)
- end
- end
- end
- if skillModList:Flag(nil, "MinionAttackSpeedAppliesToPlayer") then
- -- Minion Attack Speed conversion from Spiritual Command
- for _, value in ipairs(skillModList:List(skillCfg, "MinionModifier")) do
- if value.mod.name == "Speed" and value.mod.type == "INC" and (value.mod.flags == 0 or band(value.mod.flags, ModFlag.Attack) ~= 0) then
- skillModList:NewMod("Speed", "INC", value.mod.value, value.mod.source, ModFlag.Attack, value.mod.keywordFlags, unpack(value.mod))
- end
- end
- end
- local spellConvPercent = 0
- if skillData.spellDamageAppliesToAttackAtPercentValue then
- spellConvPercent = m_max(spellConvPercent, skillData.spellDamageAppliesToAttackAtPercentValue)
- end
- if skillModList:Flag(nil, "SpellDamageAppliesToAttacksAt150Percent") then
- spellConvPercent = m_max(spellConvPercent, 150)
- end
- if skillModList:Flag(nil, "SpellDamageAppliesToAttacks") then
- spellConvPercent = m_max(spellConvPercent, 100)
- end
- if spellConvPercent > 0 then
- -- Spell Damage conversion
- for i, value in ipairs(skillModList:Tabulate("INC", { flags = ModFlag.Spell }, "Damage")) do
- local mod = value.mod
- if band(mod.flags, ModFlag.Spell) ~= 0 then
- skillModList:NewMod("Damage", "INC", mod.value * spellConvPercent / 100, mod.source, bor(band(mod.flags, bnot(ModFlag.Spell)), ModFlag.Attack), mod.keywordFlags, unpack(mod))
- end
- end
- end
- if skillModList:Flag(nil, "ClawDamageAppliesToUnarmed") then
- -- Claw Damage conversion from Rigwald's Curse
- for i, value in ipairs(skillModList:Tabulate("INC", { flags = ModFlag.Claw, keywordFlags = KeywordFlag.Hit }, "Damage")) do
- local mod = value.mod
- if band(mod.flags, ModFlag.Claw) ~= 0 then
- skillModList:NewMod("Damage", mod.type, mod.value, mod.source, bor(band(mod.flags, bnot(ModFlag.Claw)), ModFlag.Unarmed), mod.keywordFlags, unpack(mod))
- end
- end
- end
- if skillModList:Flag(nil, "ClawAttackSpeedAppliesToUnarmed") then
- -- Claw Attack Speed conversion from Rigwald's Curse
- for i, value in ipairs(skillModList:Tabulate("INC", { flags = bor(ModFlag.Claw, ModFlag.Attack, ModFlag.Hit) }, "Speed")) do
- local mod = value.mod
- if band(mod.flags, ModFlag.Claw) ~= 0 and band(mod.flags, ModFlag.Attack) ~= 0 then
- skillModList:NewMod("Speed", mod.type, mod.value, mod.source, bor(band(mod.flags, bnot(ModFlag.Claw)), ModFlag.Unarmed), mod.keywordFlags, unpack(mod))
- end
- end
- end
- if skillModList:Flag(nil, "ClawCritChanceAppliesToUnarmed") then
- -- Claw Crit Chance conversion from Rigwald's Curse
- for i, value in ipairs(skillModList:Tabulate("INC", { flags = bor(ModFlag.Claw, ModFlag.Hit) }, "CritChance")) do
- local mod = value.mod
- if band(mod.flags, ModFlag.Claw) ~= 0 then
- skillModList:NewMod("CritChance", mod.type, mod.value, mod.source, bor(band(mod.flags, bnot(ModFlag.Claw)), ModFlag.Unarmed), mod.keywordFlags, unpack(mod))
- end
- end
- end
- if skillModList:Flag(nil, "LightRadiusAppliesToAccuracy") then
- -- Light Radius conversion from Corona Solaris
- for i, value in ipairs(skillModList:Tabulate("INC", { }, "LightRadius")) do
- local mod = value.mod
- skillModList:NewMod("Accuracy", "INC", mod.value, mod.source, mod.flags, mod.keywordFlags, unpack(mod))
- end
- end
- if skillModList:Flag(nil, "LightRadiusAppliesToAreaOfEffect") then
- -- Light Radius conversion from Wreath of Phrecia
- for i, value in ipairs(skillModList:Tabulate("INC", { }, "LightRadius")) do
- local mod = value.mod
- skillModList:NewMod("AreaOfEffect", "INC", math.floor(mod.value / 2), mod.source, mod.flags, mod.keywordFlags, unpack(mod))
- end
- end
- if skillModList:Flag(nil, "LightRadiusAppliesToDamage") then
- -- Light Radius conversion from Wreath of Phrecia
- for i, value in ipairs(skillModList:Tabulate("INC", { }, "LightRadius")) do
- local mod = value.mod
- skillModList:NewMod("Damage", "INC", mod.value, mod.source, mod.flags, mod.keywordFlags, unpack(mod))
- end
- end
- if skillModList:Flag(nil, "CastSpeedAppliesToTrapThrowingSpeed") then
- -- Cast Speed conversion from Slavedriver's Hand
- for i, value in ipairs(skillModList:Tabulate("INC", { flags = ModFlag.Cast }, "Speed")) do
- local mod = value.mod
- if (mod.flags == 0 or band(mod.flags, ModFlag.Cast) ~= 0) then
- skillModList:NewMod("TrapThrowingSpeed", "INC", mod.value, mod.source, band(mod.flags, bnot(ModFlag.Cast), bnot(ModFlag.Attack)), mod.keywordFlags, unpack(mod))
- end
- end
- end
- if skillData.arrowSpeedAppliesToAreaOfEffect then
- -- Arrow Speed conversion for Galvanic Arrow
- for i, value in ipairs(skillModList:Tabulate("INC", { flags = ModFlag.Bow }, "ProjectileSpeed")) do
- local mod = value.mod
- skillModList:NewMod("AreaOfEffect", "INC", mod.value, mod.source, mod.flags, mod.keywordFlags, unpack(mod))
- end
- end
- if skillModList:Flag(nil, "TransfigurationOfBody") then
- skillModList:NewMod("Damage", "INC", m_floor(skillModList:Sum("INC", nil, "Life") * 0.3), "Transfiguration of Body", ModFlag.Attack)
- end
- if skillModList:Flag(nil, "TransfigurationOfMind") then
- skillModList:NewMod("Damage", "INC", m_floor(skillModList:Sum("INC", nil, "Mana") * 0.3), "Transfiguration of Mind")
- end
- if skillModList:Flag(nil, "TransfigurationOfSoul") then
- skillModList:NewMod("Damage", "INC", m_floor(skillModList:Sum("INC", nil, "EnergyShield") * 0.3), "Transfiguration of Soul", ModFlag.Spell)
- end
- if skillData.gainPercentBaseWandDamage then
- local mult = skillData.gainPercentBaseWandDamage / 100
- if actor.weaponData1.type == "Wand" and actor.weaponData2.type == "Wand" then
- for _, damageType in ipairs(dmgTypeList) do
- skillModList:NewMod(damageType.."Min", "BASE", ((actor.weaponData1[damageType.."Min"] or 0) + (actor.weaponData2[damageType.."Min"] or 0)) / 2 * mult, "Spellslinger")
- skillModList:NewMod(damageType.."Max", "BASE", ((actor.weaponData1[damageType.."Max"] or 0) + (actor.weaponData2[damageType.."Max"] or 0)) / 2 * mult, "Spellslinger")
- end
- elseif actor.weaponData1.type == "Wand" then
- for _, damageType in ipairs(dmgTypeList) do
- skillModList:NewMod(damageType.."Min", "BASE", (actor.weaponData1[damageType.."Min"] or 0) * mult, "Spellslinger")
- skillModList:NewMod(damageType.."Max", "BASE", (actor.weaponData1[damageType.."Max"] or 0) * mult, "Spellslinger")
- end
- elseif actor.weaponData2.type == "Wand" then
- for _, damageType in ipairs(dmgTypeList) do
- skillModList:NewMod(damageType.."Min", "BASE", (actor.weaponData2[damageType.."Min"] or 0) * mult, "Spellslinger")
- skillModList:NewMod(damageType.."Max", "BASE", (actor.weaponData2[damageType.."Max"] or 0) * mult, "Spellslinger")
- end
- end
- end
-
- local isAttack = skillFlags.attack
-
- runSkillFunc("preSkillTypeFunc")
-
- -- Calculate skill type stats
- if skillFlags.minion then
- if activeSkill.minion and activeSkill.minion.minionData.limit then
- output.ActiveMinionLimit = m_floor(calcLib.val(skillModList, activeSkill.minion.minionData.limit, skillCfg))
- end
- end
- if skillFlags.chaining then
- if skillModList:Flag(skillCfg, "CannotChain") then
- output.ChainMaxString = "Cannot chain"
- else
- output.ChainMax = skillModList:Sum("BASE", skillCfg, "ChainCountMax", not skillFlags.projectile and "BeamChainCountMax" or nil)
- output.ChainMaxString = output.ChainMax
- output.Chain = m_min(output.ChainMax, skillModList:Sum("BASE", skillCfg, "ChainCount"))
- output.ChainRemaining = m_max(0, output.ChainMax - output.Chain)
- end
- end
- if skillFlags.projectile then
- if skillModList:Flag(nil, "PointBlank") then
- skillModList:NewMod("Damage", "MORE", 30, "Point Blank", bor(ModFlag.Attack, ModFlag.Projectile), { type = "DistanceRamp", ramp = {{10,1},{35,0},{150,-1}} })
- end
- if skillModList:Flag(nil, "FarShot") then
- skillModList:NewMod("Damage", "MORE", 30, "Far Shot", bor(ModFlag.Attack, ModFlag.Projectile), { type = "DistanceRamp", ramp = {{35,0},{70,1}} })
- end
- local projBase = skillModList:Sum("BASE", skillCfg, "ProjectileCount")
- local projMore = skillModList:More(skillCfg, "ProjectileCount")
- output.ProjectileCount = round((projBase - 1) * projMore + 1)
- if skillModList:Flag(skillCfg, "CannotPierce") then
- output.PierceCountString = "Cannot pierce"
- else
- if skillModList:Flag(skillCfg, "PierceAllTargets") or enemyDB:Flag(nil, "AlwaysPierceSelf") then
- output.PierceCount = 100
- output.PierceCountString = "All targets"
- else
- output.PierceCount = skillModList:Sum("BASE", skillCfg, "PierceCount")
- output.PierceCountString = output.PierceCount
- end
- end
- output.ProjectileSpeedMod = calcLib.mod(skillModList, skillCfg, "ProjectileSpeed")
- if breakdown then
- breakdown.ProjectileSpeedMod = breakdown.mod(skillCfg, "ProjectileSpeed")
- end
- end
- if skillFlags.melee then
- if skillFlags.weapon1Attack then
- actor.weaponRange1 = (actor.weaponData1.range and actor.weaponData1.range + skillModList:Sum("BASE", activeSkill.weapon1Cfg, "MeleeWeaponRange")) or (6 + skillModList:Sum("BASE", skillCfg, "UnarmedRange"))
- end
- if skillFlags.weapon2Attack then
- actor.weaponRange2 = (actor.weaponData2.range and actor.weaponData2.range + skillModList:Sum("BASE", activeSkill.weapon2Cfg, "MeleeWeaponRange")) or (6 + skillModList:Sum("BASE", skillCfg, "UnarmedRange"))
- end
- if activeSkill.skillTypes[SkillType.MeleeSingleTarget] then
- local range = 100
- if skillFlags.weapon1Attack then
- range = m_min(range, actor.weaponRange1)
- end
- if skillFlags.weapon2Attack then
- range = m_min(range, actor.weaponRange2)
- end
- output.WeaponRange = range + 2
- if breakdown then
- breakdown.WeaponRange = {
- radius = output.WeaponRange
- }
- end
- end
- end
- if skillFlags.area or skillData.radius or (skillFlags.mine and activeSkill.skillTypes[SkillType.Aura]) then
- output.AreaOfEffectMod = calcLib.mod(skillModList, skillCfg, "AreaOfEffect")
- if skillData.radiusIsWeaponRange then
- local range = 0
- if skillFlags.weapon1Attack then
- range = m_max(range, actor.weaponRange1)
- end
- if skillFlags.weapon2Attack then
- range = m_max(range, actor.weaponRange2)
- end
- skillData.radius = range + 2
- end
- if skillData.radius then
- skillFlags.area = true
- local baseRadius = skillData.radius + (skillData.radiusExtra or 0) + skillModList:Sum("BASE", skillCfg, "AreaOfEffect")
- output.AreaOfEffectRadius = m_floor(baseRadius * m_sqrt(output.AreaOfEffectMod))
- if breakdown then
- breakdown.AreaOfEffectRadius = breakdown.area(baseRadius, output.AreaOfEffectMod, output.AreaOfEffectRadius, skillData.radiusLabel)
- end
- if skillData.radiusSecondary then
- output.AreaOfEffectModSecondary = calcLib.mod(skillModList, skillCfg, "AreaOfEffect", "AreaOfEffectSecondary")
- baseRadius = skillData.radiusSecondary + (skillData.radiusExtra or 0)
- output.AreaOfEffectRadiusSecondary = m_floor(baseRadius * m_sqrt(output.AreaOfEffectModSecondary))
- if breakdown then
- breakdown.AreaOfEffectRadiusSecondary = breakdown.area(baseRadius, output.AreaOfEffectModSecondary, output.AreaOfEffectRadiusSecondary, skillData.radiusSecondaryLabel)
- end
- end
- if skillData.radiusTertiary then
- output.AreaOfEffectModTertiary = calcLib.mod(skillModList, skillCfg, "AreaOfEffect", "AreaOfEffectTertiary")
- baseRadius = skillData.radiusTertiary + (skillData.radiusExtra or 0)
- output.AreaOfEffectRadiusTertiary = m_floor(baseRadius * m_sqrt(output.AreaOfEffectModTertiary))
- if breakdown then
- breakdown.AreaOfEffectRadiusTertiary = breakdown.area(baseRadius, output.AreaOfEffectModTertiary, output.AreaOfEffectRadiusTertiary, skillData.radiusTertiaryLabel)
- end
- end
- end
- if breakdown then
- breakdown.AreaOfEffectMod = breakdown.mod(skillCfg, "AreaOfEffect")
- end
- end
- if skillFlags.trap then
- local baseSpeed = 1 / skillModList:Sum("BASE", skillCfg, "TrapThrowingTime")
- output.TrapThrowingSpeed = baseSpeed * calcLib.mod(skillModList, skillCfg, "TrapThrowingSpeed") * output.ActionSpeedMod
- output.TrapThrowingTime = 1 / output.TrapThrowingSpeed
- if breakdown then
- breakdown.TrapThrowingTime = { }
- breakdown.multiChain(breakdown.TrapThrowingTime, {
- label = "Throwing speed:",
- base = s_format("%.2f ^8(base throwing speed)", baseSpeed),
- { "%.2f ^8(increased/reduced throwing speed)", 1 + skillModList:Sum("INC", skillCfg, "TrapThrowingSpeed") / 100 },
- { "%.2f ^8(more/less throwing speed)", skillModList:More(skillCfg, "TrapThrowingSpeed") },
- { "%.2f ^8(action speed modifier)", output.ActionSpeedMod },
- total = s_format("= %.2f ^8per second", output.TrapThrowingSpeed),
- })
- end
- output.ActiveTrapLimit = skillModList:Sum("BASE", skillCfg, "ActiveTrapLimit")
- local baseCooldown = skillData.trapCooldown or skillData.cooldown
- if baseCooldown then
- output.TrapCooldown = baseCooldown / calcLib.mod(skillModList, skillCfg, "CooldownRecovery")
- if breakdown then
- breakdown.TrapCooldown = {
- s_format("%.2fs ^8(base)", skillData.trapCooldown or skillData.cooldown or 4),
- s_format("/ %.2f ^8(increased/reduced cooldown recovery)", 1 + skillModList:Sum("INC", skillCfg, "CooldownRecovery") / 100),
- s_format("= %.2fs", output.TrapCooldown)
- }
- end
- end
- local areaMod = calcLib.mod(skillModList, skillCfg, "TrapTriggerAreaOfEffect")
- output.TrapTriggerRadius = 10 * m_sqrt(areaMod)
- if breakdown then
- breakdown.TrapTriggerRadius = breakdown.area(10, areaMod, output.TrapTriggerRadius)
- end
- elseif skillData.cooldown then
- local cooldownOverride = skillModList:Override(skillCfg, "CooldownRecovery")
- output.Cooldown = cooldownOverride or skillData.cooldown / calcLib.mod(skillModList, skillCfg, "CooldownRecovery")
- if breakdown then
- breakdown.Cooldown = {
- s_format("%.2fs ^8(base)", skillData.cooldown),
- s_format("/ %.2f ^8(increased/reduced cooldown recovery)", 1 + skillModList:Sum("INC", skillCfg, "CooldownRecovery") / 100),
- s_format("= %.2fs", output.Cooldown)
- }
- end
- end
- if skillFlags.mine then
- local baseSpeed = 1 / skillModList:Sum("BASE", skillCfg, "MineLayingTime")
- output.MineLayingSpeed = baseSpeed * calcLib.mod(skillModList, skillCfg, "MineLayingSpeed") * output.ActionSpeedMod
- output.MineLayingTime = 1 / output.MineLayingSpeed
- if breakdown then
- breakdown.MineLayingTime = { }
- breakdown.multiChain(breakdown.MineLayingTime, {
- label = "Throwing speed:",
- base = s_format("%.2f ^8(base throwing speed)", baseSpeed),
- { "%.2f ^8(increased/reduced throwing speed)", 1 + skillModList:Sum("INC", skillCfg, "MineLayingSpeed") / 100 },
- { "%.2f ^8(more/less throwing speed)", skillModList:More(skillCfg, "MineLayingSpeed") },
- { "%.2f ^8(action speed modifier)", output.ActionSpeedMod },
- total = s_format("= %.2f ^8per second", output.MineLayingSpeed),
- })
- end
- output.ActiveMineLimit = skillModList:Sum("BASE", skillCfg, "ActiveMineLimit")
- local areaMod = calcLib.mod(skillModList, skillCfg, "MineDetonationAreaOfEffect")
- output.MineDetonationRadius = 60 * m_sqrt(areaMod)
- if breakdown then
- breakdown.MineDetonationRadius = breakdown.area(60, areaMod, output.MineDetonationRadius)
- end
- if activeSkill.skillTypes[SkillType.Aura] then
- output.MineAuraRadius = 35 * m_sqrt(output.AreaOfEffectMod)
- if breakdown then
- breakdown.MineAuraRadius = breakdown.area(35, output.AreaOfEffectMod, output.MineAuraRadius)
- end
- end
- end
- if skillFlags.totem then
- local baseSpeed = 1 / skillModList:Sum("BASE", skillCfg, "TotemPlacementTime")
- output.TotemPlacementSpeed = baseSpeed * calcLib.mod(skillModList, skillCfg, "TotemPlacementSpeed") * output.ActionSpeedMod
- output.TotemPlacementTime = 1 / output.TotemPlacementSpeed
- if breakdown then
- breakdown.TotemPlacementTime = { }
- breakdown.multiChain(breakdown.TotemPlacementTime, {
- label = "Placement speed:",
- base = s_format("%.2f ^8(base placement speed)", baseSpeed),
- { "%.2f ^8(increased/reduced placement speed)", 1 + skillModList:Sum("INC", skillCfg, "TotemPlacementSpeed") / 100 },
- { "%.2f ^8(more/less placement speed)", skillModList:More(skillCfg, "TotemPlacementSpeed") },
- { "%.2f ^8(action speed modifier)", output.ActionSpeedMod },
- total = s_format("= %.2f ^8per second", output.TotemPlacementSpeed),
- })
- end
- output.ActiveTotemLimit = skillModList:Sum("BASE", skillCfg, "ActiveTotemLimit")
- output.TotemLifeMod = calcLib.mod(skillModList, skillCfg, "TotemLife")
- output.TotemLife = round(m_floor(env.data.monsterAllyLifeTable[skillData.totemLevel] * env.data.totemLifeMult[activeSkill.skillTotemId]) * output.TotemLifeMod)
- if breakdown then
- breakdown.TotemLifeMod = breakdown.mod(skillCfg, "TotemLife")
- breakdown.TotemLife = {
- "Totem level: "..skillData.totemLevel,
- env.data.monsterAllyLifeTable[skillData.totemLevel].." ^8(base life for a level "..skillData.totemLevel.." monster)",
- "x "..env.data.totemLifeMult[activeSkill.skillTotemId].." ^8(life multiplier for this totem type)",
- "x "..output.TotemLifeMod.." ^8(totem life modifier)",
- "= "..output.TotemLife,
- }
- end
- end
-
- -- Skill duration
- local debuffDurationMult
- if env.mode_effective then
- debuffDurationMult = 1 / calcLib.mod(enemyDB, skillCfg, "BuffExpireFaster")
- else
- debuffDurationMult = 1
- end
- do
- output.DurationMod = calcLib.mod(skillModList, skillCfg, "Duration", "PrimaryDuration", "SkillAndDamagingAilmentDuration", skillData.mineDurationAppliesToSkill and "MineDuration" or nil)
- if breakdown then
- breakdown.DurationMod = breakdown.mod(skillCfg, "Duration", "PrimaryDuration", "SkillAndDamagingAilmentDuration", skillData.mineDurationAppliesToSkill and "MineDuration" or nil)
- end
- local durationBase = (skillData.duration or 0) + skillModList:Sum("BASE", skillCfg, "Duration", "PrimaryDuration")
- if durationBase > 0 then
- output.Duration = durationBase * output.DurationMod
- if skillData.debuff then
- output.Duration = output.Duration * debuffDurationMult
- end
- if breakdown and output.Duration ~= durationBase then
- breakdown.Duration = {
- s_format("%.2fs ^8(base)", durationBase),
- }
- if output.DurationMod ~= 1 then
- t_insert(breakdown.Duration, s_format("x %.2f ^8(duration modifier)", output.DurationMod))
- end
- if skillData.debuff and debuffDurationMult ~= 1 then
- t_insert(breakdown.Duration, s_format("/ %.2f ^8(debuff expires slower/faster)", 1 / debuffDurationMult))
- end
- t_insert(breakdown.Duration, s_format("= %.2fs", output.Duration))
- end
- end
- durationBase = (skillData.durationSecondary or 0) + skillModList:Sum("BASE", skillCfg, "Duration", "SecondaryDuration")
- if durationBase > 0 then
- local durationMod = calcLib.mod(skillModList, skillCfg, "Duration", "SecondaryDuration", "SkillAndDamagingAilmentDuration", skillData.mineDurationAppliesToSkill and "MineDuration" or nil)
- output.DurationSecondary = durationBase * durationMod
- if skillData.debuffSecondary then
- output.DurationSecondary = output.DurationSecondary * debuffDurationMult
- end
- if breakdown and output.DurationSecondary ~= durationBase then
- breakdown.DurationSecondary = {
- s_format("%.2fs ^8(base)", durationBase),
- }
- if output.DurationMod ~= 1 then
- t_insert(breakdown.DurationSecondary, s_format("x %.2f ^8(duration modifier)", durationMod))
- end
- if skillData.debuffSecondary and debuffDurationMult ~= 1 then
- t_insert(breakdown.DurationSecondary, s_format("/ %.2f ^8(debuff expires slower/faster)", 1 / debuffDurationMult))
- end
- t_insert(breakdown.DurationSecondary, s_format("= %.2fs", output.DurationSecondary))
- end
- end
- durationBase = (skillData.auraDuration or 0)
- if durationBase > 0 then
- local durationMod = calcLib.mod(skillModList, skillCfg, "Duration", "SkillAndDamagingAilmentDuration")
- output.AuraDuration = durationBase * durationMod
- if breakdown and output.AuraDuration ~= durationBase then
- breakdown.AuraDuration = {
- s_format("%.2fs ^8(base)", durationBase),
- s_format("x %.2f ^8(duration modifier)", durationMod),
- s_format("= %.2fs", output.AuraDuration),
- }
- end
- end
- durationBase = (skillData.reserveDuration or 0)
- if durationBase > 0 then
- local durationMod = calcLib.mod(skillModList, skillCfg, "Duration", "SkillAndDamagingAilmentDuration")
- output.ReserveDuration = durationBase * durationMod
- if breakdown and output.ReserveDuration ~= durationBase then
- breakdown.ReserveDuration = {
- s_format("%.2fs ^8(base)", durationBase),
- s_format("x %.2f ^8(duration modifier)", durationMod),
- s_format("= %.2fs", output.ReserveDuration),
- }
- end
- end
- end
-
- -- Calculate mana cost (may be slightly off due to rounding differences)
- do
- local mult = m_floor(skillModList:More(skillCfg, "SupportManaMultiplier") * 100 + 0.0001) / 100
- local more = m_floor(skillModList:More(skillCfg, "ManaCost") * 100 + 0.0001) / 100
- local inc = skillModList:Sum("INC", skillCfg, "ManaCost")
- local base = skillModList:Sum("BASE", skillCfg, "ManaCost")
- local manaCost = activeSkill.activeEffect.grantedEffectLevel.manaCost or 0
- if skillData.baseManaCostIsAtLeastPercentUnreservedMana then
- manaCost = m_max(manaCost, m_floor((output.ManaUnreserved or 0) * skillData.baseManaCostIsAtLeastPercentUnreservedMana / 100))
- end
- output.ManaCost = m_floor(m_max(0, manaCost * mult * more * (1 + inc / 100) + base))
- if activeSkill.skillTypes[SkillType.ManaCostPercent] and skillFlags.totem then
- output.ManaCost = m_floor(output.Mana * output.ManaCost / 100)
- end
- if breakdown and output.ManaCost ~= manaCost then
- breakdown.ManaCost = {
- s_format("%d ^8(base mana cost)", manaCost)
- }
- if mult ~= 1 then
- t_insert(breakdown.ManaCost, s_format("x %.2f ^8(mana cost multiplier)", mult))
- end
- if inc ~= 0 then
- t_insert(breakdown.ManaCost, s_format("x %.2f ^8(increased/reduced mana cost)", 1 + inc/100))
- end
- if more ~= 0 then
- t_insert(breakdown.ManaCost, s_format("x %.2f ^8(more/less mana cost)", more))
- end
- if base ~= 0 then
- t_insert(breakdown.ManaCost, s_format("- %d ^8(- mana cost)", -base))
- end
- t_insert(breakdown.ManaCost, s_format("= %d", output.ManaCost))
- end
- end
-
- runSkillFunc("preDamageFunc")
-
- -- Handle corpse explosions
- if skillData.explodeCorpse and skillData.corpseLife then
- local damageType = skillData.corpseExplosionDamageType or "Fire"
- skillData[damageType.."BonusMin"] = skillData.corpseLife * skillData.corpseExplosionLifeMultiplier
- skillData[damageType.."BonusMax"] = skillData.corpseLife * skillData.corpseExplosionLifeMultiplier
- end
-
- -- Cache global damage disabling flags
- local canDeal = { }
- for _, damageType in pairs(dmgTypeList) do
- canDeal[damageType] = not skillModList:Flag(skillCfg, "DealNo"..damageType)
- end
-
- -- Calculate damage conversion percentages
- activeSkill.conversionTable = wipeTable(activeSkill.conversionTable)
- for damageTypeIndex = 1, 4 do
- local damageType = dmgTypeList[damageTypeIndex]
- local globalConv = wipeTable(tempTable1)
- local skillConv = wipeTable(tempTable2)
- local add = wipeTable(tempTable3)
- local globalTotal, skillTotal = 0, 0
- for otherTypeIndex = damageTypeIndex + 1, 5 do
- -- For all possible destination types, check for global and skill conversions
- otherType = dmgTypeList[otherTypeIndex]
- globalConv[otherType] = skillModList:Sum("BASE", skillCfg, damageType.."DamageConvertTo"..otherType, isElemental[damageType] and "ElementalDamageConvertTo"..otherType or nil, damageType ~= "Chaos" and "NonChaosDamageConvertTo"..otherType or nil)
- globalTotal = globalTotal + globalConv[otherType]
- skillConv[otherType] = skillModList:Sum("BASE", skillCfg, "Skill"..damageType.."DamageConvertTo"..otherType)
- skillTotal = skillTotal + skillConv[otherType]
- add[otherType] = skillModList:Sum("BASE", skillCfg, damageType.."DamageGainAs"..otherType, isElemental[damageType] and "ElementalDamageGainAs"..otherType or nil, damageType ~= "Chaos" and "NonChaosDamageGainAs"..otherType or nil)
- end
- if skillTotal > 100 then
- -- Skill conversion exceeds 100%, scale it down and remove non-skill conversions
- local factor = 100 / skillTotal
- for type, val in pairs(skillConv) do
- -- Overconversion is fixed in 3.0, so I finally get to uncomment this line!
- skillConv[type] = val * factor
- end
- for type, val in pairs(globalConv) do
- globalConv[type] = 0
- end
- elseif globalTotal + skillTotal > 100 then
- -- Conversion exceeds 100%, scale down non-skill conversions
- local factor = (100 - skillTotal) / globalTotal
- for type, val in pairs(globalConv) do
- globalConv[type] = val * factor
- end
- globalTotal = globalTotal * factor
- end
- local dmgTable = { }
- for type, val in pairs(globalConv) do
- dmgTable[type] = (globalConv[type] + skillConv[type] + add[type]) / 100
- end
- dmgTable.mult = 1 - m_min((globalTotal + skillTotal) / 100, 1)
- activeSkill.conversionTable[damageType] = dmgTable
- end
- activeSkill.conversionTable["Chaos"] = { mult = 1 }
-
- -- Configure damage passes
- local passList = { }
- if isAttack then
- output.MainHand = { }
- output.OffHand = { }
- local critOverride = skillModList:Override(cfg, "WeaponBaseCritChance")
- if skillFlags.weapon1Attack then
- if breakdown then
- breakdown.MainHand = LoadModule(calcs.breakdownModule, skillModList, output.MainHand)
- end
- activeSkill.weapon1Cfg.skillStats = output.MainHand
- local source = copyTable(actor.weaponData1)
- if critOverride and source.type and source.type ~= "None" then
- source.CritChance = critOverride
- end
- t_insert(passList, {
- label = "Main Hand",
- source = source,
- cfg = activeSkill.weapon1Cfg,
- output = output.MainHand,
- breakdown = breakdown and breakdown.MainHand,
- })
- end
- if skillFlags.weapon2Attack then
- if breakdown then
- breakdown.OffHand = LoadModule(calcs.breakdownModule, skillModList, output.OffHand)
- end
- activeSkill.weapon2Cfg.skillStats = output.OffHand
- local source = copyTable(actor.weaponData2)
- if critOverride and source.type and source.type ~= "None" then
- source.CritChance = critOverride
- end
- if skillData.setOffHandBaseCritChance then
- source.CritChance = skillData.setOffHandBaseCritChance
- end
- if skillData.setOffHandPhysicalMin and skillData.setOffHandPhysicalMax then
- source.PhysicalMin = skillData.setOffHandPhysicalMin
- source.PhysicalMax = skillData.setOffHandPhysicalMax
- end
- if skillData.setOffHandAttackTime then
- source.AttackRate = 1000 / skillData.setOffHandAttackTime
- end
- t_insert(passList, {
- label = "Off Hand",
- source = source,
- cfg = activeSkill.weapon2Cfg,
- output = output.OffHand,
- breakdown = breakdown and breakdown.OffHand,
- })
- end
- else
- t_insert(passList, {
- label = "Skill",
- source = skillData,
- cfg = skillCfg,
- output = output,
- breakdown = breakdown,
- })
- end
-
- local function combineStat(stat, mode, ...)
- -- Combine stats from Main Hand and Off Hand according to the mode
- if mode == "OR" or not skillFlags.bothWeaponAttack then
- output[stat] = output.MainHand[stat] or output.OffHand[stat]
- elseif mode == "ADD" then
- output[stat] = (output.MainHand[stat] or 0) + (output.OffHand[stat] or 0)
- elseif mode == "AVERAGE" then
- output[stat] = ((output.MainHand[stat] or 0) + (output.OffHand[stat] or 0)) / 2
- elseif mode == "CHANCE" then
- if output.MainHand[stat] and output.OffHand[stat] then
- local mainChance = output.MainHand[...] * output.MainHand.HitChance
- local offChance = output.OffHand[...] * output.OffHand.HitChance
- local mainPortion = mainChance / (mainChance + offChance)
- local offPortion = offChance / (mainChance + offChance)
- output[stat] = output.MainHand[stat] * mainPortion + output.OffHand[stat] * offPortion
- if breakdown then
- if not breakdown[stat] then
- breakdown[stat] = { }
- end
- t_insert(breakdown[stat], "Contribution from Main Hand:")
- t_insert(breakdown[stat], s_format("%.1f", output.MainHand[stat]))
- t_insert(breakdown[stat], s_format("x %.3f ^8(portion of instances created by main hand)", mainPortion))
- t_insert(breakdown[stat], s_format("= %.1f", output.MainHand[stat] * mainPortion))
- t_insert(breakdown[stat], "Contribution from Off Hand:")
- t_insert(breakdown[stat], s_format("%.1f", output.OffHand[stat]))
- t_insert(breakdown[stat], s_format("x %.3f ^8(portion of instances created by off hand)", offPortion))
- t_insert(breakdown[stat], s_format("= %.1f", output.OffHand[stat] * offPortion))
- t_insert(breakdown[stat], "Total:")
- t_insert(breakdown[stat], s_format("%.1f + %.1f", output.MainHand[stat] * mainPortion, output.OffHand[stat] * offPortion))
- t_insert(breakdown[stat], s_format("= %.1f", output[stat]))
- end
- else
- output[stat] = output.MainHand[stat] or output.OffHand[stat]
- end
- elseif mode == "DPS" then
- output[stat] = (output.MainHand[stat] or 0) + (output.OffHand[stat] or 0)
- if not skillData.doubleHitsWhenDualWielding then
- output[stat] = output[stat] / 2
- end
- end
- end
-
- for _, pass in ipairs(passList) do
- local globalOutput, globalBreakdown = output, breakdown
- local source, output, cfg, breakdown = pass.source, pass.output, pass.cfg, pass.breakdown
-
- -- Calculate hit chance
- output.Accuracy = calcLib.val(skillModList, "Accuracy", cfg)
- if breakdown then
- breakdown.Accuracy = breakdown.simple(nil, cfg, output.Accuracy, "Accuracy")
- end
- if not isAttack or skillModList:Flag(cfg, "CannotBeEvaded") or skillData.cannotBeEvaded or (env.mode_effective and enemyDB:Flag(nil, "CannotEvade")) then
- output.HitChance = 100
- else
- local enemyEvasion = round(calcLib.val(enemyDB, "Evasion"))
- output.HitChance = calcs.hitChance(enemyEvasion, output.Accuracy)
- if breakdown then
- breakdown.HitChance = {
- "Enemy level: "..env.enemyLevel..(env.configInput.enemyLevel and " ^8(overridden from the Configuration tab" or " ^8(can be overridden in the Configuration tab)"),
- "Average enemy evasion: "..enemyEvasion,
- "Approximate hit chance: "..output.HitChance.."%",
- }
- end
- end
-
- -- Calculate attack/cast speed
- if activeSkill.activeEffect.grantedEffect.castTime == 0 and not skillData.castTimeOverride then
- output.Time = 0
- output.Speed = 0
- elseif skillData.timeOverride then
- output.Time = skillData.timeOverride
- output.Speed = 1 / output.Time
- elseif skillData.fixedCastTime then
- output.Time = activeSkill.activeEffect.grantedEffect.castTime
- output.Speed = 1 / output.Time
- else
- local baseTime
- if isAttack then
- if skillData.castTimeOverridesAttackTime then
- -- Skill is overriding weapon attack speed
- baseTime = activeSkill.activeEffect.grantedEffect.castTime / (1 + (source.AttackSpeedInc or 0) / 100)
- else
- baseTime = 1 / ( source.AttackRate or 1 ) + skillModList:Sum("BASE", cfg, "Speed")
- end
- else
- baseTime = skillData.castTimeOverride or activeSkill.activeEffect.grantedEffect.castTime or 1
- end
- local inc = skillModList:Sum("INC", cfg, "Speed")
- local more = skillModList:More(cfg, "Speed")
- output.Speed = 1 / baseTime * round((1 + inc/100) * more, 2)
- if skillData.attackRateCap then
- output.Speed = m_min(output.Speed, skillData.attackRateCap)
- end
- if skillFlags.selfCast then
- -- Self-cast skill; apply action speed
- output.Speed = output.Speed * globalOutput.ActionSpeedMod
- end
- if breakdown then
- breakdown.Speed = { }
- breakdown.multiChain(breakdown.Speed, {
- base = s_format("%.2f ^8(base)", 1 / baseTime),
- { "%.2f ^8(increased/reduced)", 1 + inc/100 },
- { "%.2f ^8(more/less)", more },
- { "%.2f ^8(action speed modifier)", skillFlags.selfCast and globalOutput.ActionSpeedMod or 1 },
- total = s_format("= %.2f ^8per second", output.Speed)
- })
- end
- if output.Speed == 0 then
- output.Time = 0
- else
- output.Time = 1 / output.Speed
- end
- end
- if skillData.hitTimeOverride then
- output.HitTime = skillData.hitTimeOverride
- output.HitSpeed = 1 / output.HitTime
- end
- end
-
- if isAttack then
- -- Combine hit chance and attack speed
- combineStat("HitChance", "AVERAGE")
- combineStat("Speed", "AVERAGE")
- combineStat("HitSpeed", "OR")
- if output.Speed == 0 then
- output.Time = 0
- else
- output.Time = 1 / output.Speed
- end
- if skillFlags.bothWeaponAttack then
- if breakdown then
- breakdown.Speed = {
- "Both weapons:",
- s_format("(%.2f + %.2f) / 2", output.MainHand.Speed, output.OffHand.Speed),
- s_format("= %.2f", output.Speed),
- }
- end
- end
- end
-
- for _, pass in ipairs(passList) do
- local globalOutput, globalBreakdown = output, breakdown
- local source, output, cfg, breakdown = pass.source, pass.output, pass.cfg, pass.breakdown
-
- -- Calculate crit chance, crit multiplier, and their combined effect
- if skillModList:Flag(nil, "NeverCrit") then
- output.PreEffectiveCritChance = 0
- output.CritChance = 0
- output.CritMultiplier = 0
- output.BonusCritDotMultiplier = 0
- output.CritEffect = 1
- else
- local baseCrit = source.CritChance or 0
- if baseCrit == 100 then
- output.PreEffectiveCritChance = 100
- output.CritChance = 100
- else
- local base = skillModList:Sum("BASE", cfg, "CritChance") + (env.mode_effective and enemyDB:Sum("BASE", nil, "SelfCritChance") or 0)
- local inc = skillModList:Sum("INC", cfg, "CritChance") + (env.mode_effective and enemyDB:Sum("INC", nil, "SelfCritChance") or 0)
- local more = skillModList:More(cfg, "CritChance")
- output.CritChance = (baseCrit + base) * (1 + inc / 100) * more
- local preCapCritChance = output.CritChance
- output.CritChance = m_min(output.CritChance, 100)
- if (baseCrit + base) > 0 then
- output.CritChance = m_max(output.CritChance, 0)
- end
- output.PreEffectiveCritChance = output.CritChance
- local preLuckyCritChance = output.CritChance
- if env.mode_effective and skillModList:Flag(cfg, "CritChanceLucky") then
- output.CritChance = (1 - (1 - output.CritChance / 100) ^ 2) * 100
- end
- local preHitCheckCritChance = output.CritChance
- if env.mode_effective then
- output.CritChance = output.CritChance * output.HitChance / 100
- end
- if breakdown and output.CritChance ~= baseCrit then
- breakdown.CritChance = { }
- if base ~= 0 then
- t_insert(breakdown.CritChance, s_format("(%g + %g) ^8(base)", baseCrit, base))
- else
- t_insert(breakdown.CritChance, s_format("%g ^8(base)", baseCrit + base))
- end
- if inc ~= 0 then
- t_insert(breakdown.CritChance, s_format("x %.2f", 1 + inc/100).." ^8(increased/reduced)")
- end
- if more ~= 1 then
- t_insert(breakdown.CritChance, s_format("x %.2f", more).." ^8(more/less)")
- end
- t_insert(breakdown.CritChance, s_format("= %.2f%% ^8(crit chance)", output.PreEffectiveCritChance))
- if preCapCritChance > 100 then
- 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 skillModList:Flag(cfg, "CritChanceLucky") then
- t_insert(breakdown.CritChance, "Crit Chance is Lucky:")
- t_insert(breakdown.CritChance, s_format("1 - (1 - %.4f) x (1 - %.4f)", preLuckyCritChance / 100, preLuckyCritChance / 100))
- t_insert(breakdown.CritChance, s_format("= %.2f%%", preHitCheckCritChance))
- end
- if env.mode_effective and output.HitChance < 100 then
- t_insert(breakdown.CritChance, "Crit confirmation roll:")
- t_insert(breakdown.CritChance, s_format("%.2f%%", preHitCheckCritChance))
- t_insert(breakdown.CritChance, s_format("x %.2f ^8(chance to hit)", output.HitChance / 100))
- t_insert(breakdown.CritChance, s_format("= %.2f%%", output.CritChance))
- end
- end
- end
- if skillModList:Flag(cfg, "NoCritMultiplier") then
- output.CritMultiplier = 1
- else
- local extraDamage = skillModList:Sum("BASE", cfg, "CritMultiplier") / 100
- local multiOverride = skillModList:Override(skillCfg, "CritMultiplier")
- if multiOverride then
- extraDamage = (multiOverride - 100) / 100
- end
- if env.mode_effective then
- local enemyInc = 1 + enemyDB:Sum("INC", nil, "SelfCritMultiplier") / 100
- extraDamage = round(extraDamage * enemyInc, 2)
- if breakdown and enemyInc ~= 1 then
- breakdown.CritMultiplier = {
- s_format("%d%% ^8(additional extra damage)", skillModList:Sum("BASE", cfg, "CritMultiplier") / 100),
- s_format("x %.2f ^8(increased/reduced extra crit damage taken by enemy)", enemyInc),
- s_format("= %d%% ^8(extra crit damage)", extraDamage * 100),
- }
- end
- end
- output.CritMultiplier = 1 + m_max(0, extraDamage)
- end
- output.CritEffect = 1 - output.CritChance / 100 + output.CritChance / 100 * output.CritMultiplier
- output.BonusCritDotMultiplier = (skillModList:Sum("BASE", cfg, "CritMultiplier") - 50) * skillModList:Sum("BASE", cfg, "CritMultiplierAppliesToDegen") / 10000
- if breakdown and output.CritEffect ~= 1 then
- breakdown.CritEffect = {
- s_format("(1 - %.4f) ^8(portion of damage from non-crits)", output.CritChance/100),
- s_format("+ (%.4f x %g) ^8(portion of damage from crits)", output.CritChance/100, output.CritMultiplier),
- s_format("= %.3f", output.CritEffect),
- }
- end
- end
-
- -- Calculate Double Damage + Ruthless Blow chance/multipliers
- output.DoubleDamageChance = m_min(skillModList:Sum("BASE", cfg, "DoubleDamageChance") + (env.mode_effective and enemyDB:Sum("BASE", cfg, "SelfDoubleDamageChance") or 0), 100)
- output.DoubleDamageEffect = 1 + output.DoubleDamageChance / 100
- output.RuthlessBlowMaxCount = skillModList:Sum("BASE", cfg, "RuthlessBlowMaxCount")
- if output.RuthlessBlowMaxCount > 0 then
- output.RuthlessBlowChance = round(100 / output.RuthlessBlowMaxCount)
- else
- output.RuthlessBlowChance = 0
- end
- output.RuthlessBlowMultiplier = 1 + skillModList:Sum("BASE", cfg, "RuthlessBlowMultiplier") / 100
- output.RuthlessBlowEffect = 1 - output.RuthlessBlowChance / 100 + output.RuthlessBlowChance / 100 * output.RuthlessBlowMultiplier
-
- -- Calculate base hit damage
- for _, damageType in ipairs(dmgTypeList) do
- local damageTypeMin = damageType.."Min"
- local damageTypeMax = damageType.."Max"
- local baseMultiplier = activeSkill.activeEffect.grantedEffectLevel.baseMultiplier or 1
- local damageEffectiveness = activeSkill.activeEffect.grantedEffectLevel.damageEffectiveness or skillData.damageEffectiveness or 1
- local addedMin = skillModList:Sum("BASE", cfg, damageTypeMin) + enemyDB:Sum("BASE", cfg, "Self"..damageTypeMin)
- local addedMax = skillModList:Sum("BASE", cfg, damageTypeMax) + enemyDB:Sum("BASE", cfg, "Self"..damageTypeMax)
- local baseMin = ((source[damageTypeMin] or 0) + (source[damageType.."BonusMin"] or 0)) * baseMultiplier + addedMin * damageEffectiveness
- local baseMax = ((source[damageTypeMax] or 0) + (source[damageType.."BonusMax"] or 0)) * baseMultiplier + addedMax * damageEffectiveness
- output[damageTypeMin.."Base"] = baseMin
- output[damageTypeMax.."Base"] = baseMax
- if breakdown then
- breakdown[damageType] = { damageTypes = { } }
- if baseMin ~= 0 and baseMax ~= 0 then
- t_insert(breakdown[damageType], "Base damage:")
- local plus = ""
- if (source[damageTypeMin] or 0) ~= 0 or (source[damageTypeMax] or 0) ~= 0 then
- if baseMultiplier ~= 1 then
- t_insert(breakdown[damageType], s_format("(%d to %d) x %.2f ^8(base damage from %s multiplied by base damage multiplier)", source[damageTypeMin], source[damageTypeMax], baseMultiplier, source.type and "weapon" or "skill"))
- else
- t_insert(breakdown[damageType], s_format("%d to %d ^8(base damage from %s)", source[damageTypeMin], source[damageTypeMax], source.type and "weapon" or "skill"))
- end
- plus = "+ "
- end
- if addedMin ~= 0 or addedMax ~= 0 then
- if damageEffectiveness ~= 1 then
- t_insert(breakdown[damageType], s_format("%s(%d to %d) x %.2f ^8(added damage multiplied by damage effectiveness)", plus, addedMin, addedMax, damageEffectiveness))
- else
- t_insert(breakdown[damageType], s_format("%s%d to %d ^8(added damage)", plus, addedMin, addedMax))
- end
- end
- t_insert(breakdown[damageType], s_format("= %.1f to %.1f", baseMin, baseMax))
- end
- end
- end
-
- -- Calculate hit damage for each damage type
- local totalHitMin, totalHitMax = 0, 0
- local totalCritMin, totalCritMax = 0, 0
- local ghostReaver = skillModList:Flag(nil, "GhostReaver")
- output.LifeLeech = 0
- output.LifeLeechInstant = 0
- output.EnergyShieldLeech = 0
- output.EnergyShieldLeechInstant = 0
- output.ManaLeech = 0
- output.ManaLeechInstant = 0
- for pass = 1, 2 do
- -- Pass 1 is critical strike damage, pass 2 is non-critical strike
- cfg.skillCond["CriticalStrike"] = (pass == 1)
- local lifeLeechTotal = 0
- local energyShieldLeechTotal = 0
- local manaLeechTotal = 0
- local noLifeLeech = skillModList:Flag(cfg, "CannotLeechLife") or enemyDB:Flag(nil, "CannotLeechLifeFromSelf")
- local noEnergyShieldLeech = skillModList:Flag(cfg, "CannotLeechEnergyShield") or enemyDB:Flag(nil, "CannotLeechEnergyShieldFromSelf")
- local noManaLeech = skillModList:Flag(cfg, "CannotLeechMana") or enemyDB:Flag(nil, "CannotLeechManaFromSelf")
- for _, damageType in ipairs(dmgTypeList) do
- local min, max
- if skillFlags.hit and canDeal[damageType] then
- min, max = calcDamage(activeSkill, output, cfg, pass == 2 and breakdown and breakdown[damageType], damageType, 0)
- local convMult = activeSkill.conversionTable[damageType].mult
- if pass == 2 and breakdown then
- t_insert(breakdown[damageType], "Hit damage:")
- t_insert(breakdown[damageType], s_format("%d to %d ^8(total damage)", min, max))
- if convMult ~= 1 then
- t_insert(breakdown[damageType], s_format("x %g ^8(%g%% converted to other damage types)", convMult, (1-convMult)*100))
- end
- if output.DoubleDamageEffect ~= 1 then
- t_insert(breakdown[damageType], s_format("x %.2f ^8(chance to deal double damage)", output.DoubleDamageEffect))
- end
- if output.RuthlessBlowEffect ~= 1 then
- t_insert(breakdown[damageType], s_format("x %.2f ^8(ruthless blow effect modifier)", output.RuthlessBlowEffect))
- end
- end
- local allMult = convMult * output.DoubleDamageEffect * output.RuthlessBlowEffect
- if pass == 1 then
- -- Apply crit multiplier
- allMult = allMult * output.CritMultiplier
- end
- min = min * allMult
- max = max * allMult
- if (min ~= 0 or max ~= 0) and env.mode_effective then
- -- Apply enemy resistances and damage taken modifiers
- local resist = 0
- local pen = 0
- local takenInc = enemyDB:Sum("INC", cfg, "DamageTaken", damageType.."DamageTaken")
- local takenMore = enemyDB:More(cfg, "DamageTaken", damageType.."DamageTaken")
- if damageType == "Physical" then
- resist = m_max(0, enemyDB:Sum("BASE", nil, "PhysicalDamageReduction") + skillModList:Sum("BASE", cfg, "EnemyPhysicalDamageReduction"))
- else
- resist = enemyDB:Sum("BASE", nil, damageType.."Resist")
- if isElemental[damageType] then
- resist = resist + enemyDB:Sum("BASE", nil, "ElementalResist")
- pen = skillModList:Sum("BASE", cfg, damageType.."Penetration", "ElementalPenetration")
- takenInc = takenInc + enemyDB:Sum("INC", nil, "ElementalDamageTaken")
- elseif damageType == "Chaos" then
- pen = skillModList:Sum("BASE", cfg, "ChaosPenetration")
- end
- resist = m_min(resist, 75)
- end
- if skillFlags.projectile then
- takenInc = takenInc + enemyDB:Sum("INC", nil, "ProjectileDamageTaken")
- end
- if skillFlags.trap or skillFlags.mine then
- takenInc = takenInc + enemyDB:Sum("INC", nil, "TrapMineDamageTaken")
- end
- local effMult = (1 + takenInc / 100) * takenMore
- if not skillModList:Flag(cfg, "Ignore"..damageType.."Resistance", isElemental[damageType] and "IgnoreElementalResistances" or nil) and not enemyDB:Flag(nil, "SelfIgnore"..damageType.."Resistance") then
- effMult = effMult * (1 - (resist - pen) / 100)
- end
- min = min * effMult
- max = max * effMult
- if env.mode == "CALCS" then
- output[damageType.."EffMult"] = effMult
- end
- if pass == 2 and breakdown and effMult ~= 1 then
- t_insert(breakdown[damageType], s_format("x %.3f ^8(effective DPS modifier)", effMult))
- breakdown[damageType.."EffMult"] = breakdown.effMult(damageType, resist, pen, takenInc, effMult, takenMore)
- end
- end
- if pass == 2 and breakdown then
- t_insert(breakdown[damageType], s_format("= %d to %d", min, max))
- end
- if skillFlags.mine or skillFlags.trap or skillFlags.totem then
- if not noLifeLeech then
- local lifeLeech = skillModList:Sum("BASE", cfg, "DamageLifeLeechToPlayer")
- if lifeLeech > 0 then
- lifeLeechTotal = lifeLeechTotal + (min + max) / 2 * lifeLeech / 100
- end
- end
- else
- if not noLifeLeech then
- local lifeLeech
- 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
- else
- lifeLeech = 0
- end
- else
- lifeLeech = skillModList:Sum("BASE", cfg, "DamageLeech", "DamageLifeLeech", damageType.."DamageLifeLeech", isElemental[damageType] and "ElementalDamageLifeLeech" or nil) + enemyDB:Sum("BASE", cfg, "SelfDamageLifeLeech") / 100
- end
- if lifeLeech > 0 then
- lifeLeechTotal = lifeLeechTotal + (min + max) / 2 * lifeLeech / 100
- end
- end
- if not noEnergyShieldLeech then
- local energyShieldLeech = skillModList:Sum("BASE", cfg, "DamageEnergyShieldLeech", damageType.."DamageEnergyShieldLeech", isElemental[damageType] and "ElementalDamageEnergyShieldLeech" or nil) + enemyDB:Sum("BASE", cfg, "SelfDamageEnergyShieldLeech") / 100
- if energyShieldLeech > 0 then
- energyShieldLeechTotal = energyShieldLeechTotal + (min + max) / 2 * energyShieldLeech / 100
- end
- end
- if not noManaLeech then
- local manaLeech = skillModList:Sum("BASE", cfg, "DamageLeech", "DamageManaLeech", damageType.."DamageManaLeech", isElemental[damageType] and "ElementalDamageManaLeech" or nil) + enemyDB:Sum("BASE", cfg, "SelfDamageManaLeech") / 100
- if manaLeech > 0 then
- manaLeechTotal = manaLeechTotal + (min + max) / 2 * manaLeech / 100
- end
- end
- end
- else
- min, max = 0, 0
- if breakdown then
- breakdown[damageType] = {
- "You can't deal "..damageType.." damage"
- }
- end
- end
- if pass == 1 then
- output[damageType.."CritAverage"] = (min + max) / 2
- totalCritMin = totalCritMin + min
- totalCritMax = totalCritMax + max
- else
- if env.mode == "CALCS" then
- output[damageType.."Min"] = min
- output[damageType.."Max"] = max
- end
- output[damageType.."HitAverage"] = (min + max) / 2
- totalHitMin = totalHitMin + min
- totalHitMax = totalHitMax + max
- end
- end
- if skillData.lifeLeechPerUse then
- lifeLeechTotal = lifeLeechTotal + skillData.lifeLeechPerUse
- end
- if skillData.manaLeechPerUse then
- manaLeechTotal = manaLeechTotal + skillData.manaLeechPerUse
- end
- local portion = (pass == 1) and (output.CritChance / 100) or (1 - output.CritChance / 100)
- if skillModList:Flag(cfg, "InstantLifeLeech") and not ghostReaver then
- output.LifeLeechInstant = output.LifeLeechInstant + lifeLeechTotal * portion
- else
- output.LifeLeech = output.LifeLeech + lifeLeechTotal * portion
- end
- if skillModList:Flag(cfg, "InstantEnergyShieldLeech") then
- output.EnergyShieldLeechInstant = output.EnergyShieldLeechInstant + energyShieldLeechTotal * portion
- else
- output.EnergyShieldLeech = output.EnergyShieldLeech + energyShieldLeechTotal * portion
- end
- if skillModList:Flag(cfg, "InstantManaLeech") then
- output.ManaLeechInstant = output.ManaLeechInstant + manaLeechTotal * portion
- else
- output.ManaLeech = output.ManaLeech + manaLeechTotal * portion
- end
- end
- output.TotalMin = totalHitMin
- output.TotalMax = totalHitMax
-
- if skillModList:Flag(skillCfg, "ElementalEquilibrium") and not env.configInput.EEIgnoreHitDamage and (output.FireHitAverage + output.ColdHitAverage + output.LightningHitAverage > 0) then
- -- Update enemy hit-by-damage-type conditions
- enemyDB.conditions.HitByFireDamage = output.FireHitAverage > 0
- enemyDB.conditions.HitByColdDamage = output.ColdHitAverage > 0
- enemyDB.conditions.HitByLightningDamage = output.LightningHitAverage > 0
- end
-
- if breakdown then
- -- For each damage type, calculate percentage of total damage
- for _, damageType in ipairs(dmgTypeList) do
- if output[damageType.."HitAverage"] > 0 then
- t_insert(breakdown[damageType], s_format("Portion of total damage: %d%%", output[damageType.."HitAverage"] / (totalHitMin + totalHitMax) * 200))
- end
- end
- end
-
- local hitRate = output.HitChance / 100 * (globalOutput.HitSpeed or globalOutput.Speed) * (skillData.dpsMultiplier or 1)
-
- -- Calculate leech
- local function getLeechInstances(amount, total)
- if total == 0 then
- return 0, 0
- end
- local duration = amount / total / 0.02
- return duration, duration * hitRate
- end
- if ghostReaver then
- output.EnergyShieldLeech = output.EnergyShieldLeech + output.LifeLeech
- output.EnergyShieldLeechInstant = output.EnergyShieldLeechInstant + output.LifeLeechInstant
- output.LifeLeech = 0
- output.LifeLeechInstant = 0
- end
- output.LifeLeech = m_min(output.LifeLeech, globalOutput.MaxLifeLeechInstance)
- output.LifeLeechDuration, output.LifeLeechInstances = getLeechInstances(output.LifeLeech, globalOutput.Life)
- output.LifeLeechInstantRate = output.LifeLeechInstant * hitRate
- output.EnergyShieldLeech = m_min(output.EnergyShieldLeech, globalOutput.MaxEnergyShieldLeechInstance)
- output.EnergyShieldLeechDuration, output.EnergyShieldLeechInstances = getLeechInstances(output.EnergyShieldLeech, globalOutput.EnergyShield)
- output.EnergyShieldLeechInstantRate = output.EnergyShieldLeechInstant * hitRate
- output.ManaLeech = m_min(output.ManaLeech, globalOutput.MaxManaLeechInstance)
- output.ManaLeechDuration, output.ManaLeechInstances = getLeechInstances(output.ManaLeech, globalOutput.Mana)
- output.ManaLeechInstantRate = output.ManaLeechInstant * hitRate
-
- -- Calculate gain on hit
- if skillFlags.mine or skillFlags.trap or skillFlags.totem then
- output.LifeOnHit = 0
- output.EnergyShieldOnHit = 0
- output.ManaOnHit = 0
- else
- output.LifeOnHit = (skillModList:Sum("BASE", cfg, "LifeOnHit") + enemyDB:Sum("BASE", cfg, "SelfLifeOnHit")) * globalOutput.LifeRecoveryMod
- output.EnergyShieldOnHit = (skillModList:Sum("BASE", cfg, "EnergyShieldOnHit") + enemyDB:Sum("BASE", cfg, "SelfEnergyShieldOnHit")) * globalOutput.EnergyShieldRecoveryMod
- output.ManaOnHit = (skillModList:Sum("BASE", cfg, "ManaOnHit") + enemyDB:Sum("BASE", cfg, "SelfManaOnHit")) * globalOutput.ManaRecoveryMod
- end
- output.LifeOnHitRate = output.LifeOnHit * hitRate
- output.EnergyShieldOnHitRate = output.EnergyShieldOnHit * hitRate
- output.ManaOnHitRate = output.ManaOnHit * hitRate
-
- -- Calculate average damage and final DPS
- output.AverageHit = (totalHitMin + totalHitMax) / 2 * (1 - output.CritChance / 100) + (totalCritMin + totalCritMax) / 2 * output.CritChance / 100
- output.AverageDamage = output.AverageHit * output.HitChance / 100
- output.TotalDPS = output.AverageDamage * (globalOutput.HitSpeed or globalOutput.Speed) * (skillData.dpsMultiplier or 1)
- if breakdown then
- if output.CritEffect ~= 1 then
- breakdown.AverageHit = {
- s_format("%.1f x (1 - %.4f) ^8(damage from non-crits)", (totalHitMin + totalHitMax) / 2, output.CritChance / 100),
- s_format("+ %.1f x %.4f ^8(damage from crits)", (totalCritMin + totalCritMax) / 2, output.CritChance / 100),
- s_format("= %.1f", output.AverageHit),
- }
- end
- if isAttack then
- breakdown.AverageDamage = { }
- t_insert(breakdown.AverageDamage, s_format("%s:", pass.label))
- t_insert(breakdown.AverageDamage, s_format("%.1f ^8(average hit)", output.AverageHit))
- t_insert(breakdown.AverageDamage, s_format("x %.2f ^8(chance to hit)", output.HitChance / 100))
- t_insert(breakdown.AverageDamage, s_format("= %.1f", output.AverageDamage))
- end
- end
- end
-
- if isAttack then
- -- Combine crit stats, average damage and DPS
- combineStat("PreEffectiveCritChance", "AVERAGE")
- combineStat("CritChance", "AVERAGE")
- combineStat("CritMultiplier", "AVERAGE")
- combineStat("AverageDamage", "DPS")
- combineStat("TotalDPS", "DPS")
- combineStat("LifeLeechDuration", "DPS")
- combineStat("LifeLeechInstances", "DPS")
- combineStat("LifeLeechInstant", "DPS")
- combineStat("LifeLeechInstantRate", "DPS")
- combineStat("EnergyShieldLeechDuration", "DPS")
- combineStat("EnergyShieldLeechInstances", "DPS")
- combineStat("EnergyShieldLeechInstant", "DPS")
- combineStat("EnergyShieldLeechInstantRate", "DPS")
- combineStat("ManaLeechDuration", "DPS")
- combineStat("ManaLeechInstances", "DPS")
- combineStat("ManaLeechInstant", "DPS")
- combineStat("ManaLeechInstantRate", "DPS")
- combineStat("LifeOnHit", "DPS")
- combineStat("LifeOnHitRate", "DPS")
- combineStat("EnergyShieldOnHit", "DPS")
- combineStat("EnergyShieldOnHitRate", "DPS")
- combineStat("ManaOnHit", "DPS")
- combineStat("ManaOnHitRate", "DPS")
- if skillFlags.bothWeaponAttack then
- if breakdown then
- breakdown.AverageDamage = { }
- t_insert(breakdown.AverageDamage, "Both weapons:")
- if skillData.doubleHitsWhenDualWielding then
- t_insert(breakdown.AverageDamage, s_format("%.1f + %.1f ^8(skill hits with both weapons at once)", output.MainHand.AverageDamage, output.OffHand.AverageDamage))
- else
- t_insert(breakdown.AverageDamage, s_format("(%.1f + %.1f) / 2 ^8(skill alternates weapons)", output.MainHand.AverageDamage, output.OffHand.AverageDamage))
- end
- t_insert(breakdown.AverageDamage, s_format("= %.1f", output.AverageDamage))
- end
- end
- end
- if env.mode == "CALCS" then
- if skillData.showAverage then
- output.DisplayDamage = s_format("%.1f average damage", output.AverageDamage)
- else
- output.DisplayDamage = s_format("%.1f DPS", output.TotalDPS)
- end
- end
- if breakdown then
- if isAttack then
- breakdown.TotalDPS = {
- s_format("%.1f ^8(average damage)", output.AverageDamage),
- output.HitSpeed and s_format("x %.2f ^8(hit rate)", output.HitSpeed) or s_format("x %.2f ^8(attack rate)", output.Speed),
- }
- else
- breakdown.TotalDPS = {
- s_format("%.1f ^8(average hit)", output.AverageDamage),
- output.HitSpeed and s_format("x %.2f ^8(hit rate)", output.HitSpeed) or s_format("x %.2f ^8(cast rate)", output.Speed),
- }
- end
- if skillData.dpsMultiplier then
- t_insert(breakdown.TotalDPS, s_format("x %g ^8(DPS multiplier for this skill)", skillData.dpsMultiplier))
- end
- t_insert(breakdown.TotalDPS, s_format("= %.1f", output.TotalDPS))
- end
-
- -- Calculate leech rates
- output.LifeLeechInstanceRate = output.Life * 0.02 * calcLib.mod(skillModList, skillCfg, "LifeLeechRate")
- output.LifeLeechRate = output.LifeLeechInstantRate * output.LifeRecoveryMod + m_min(output.LifeLeechInstances * output.LifeLeechInstanceRate, output.MaxLifeLeechRate) * output.LifeRecoveryRateMod
- output.LifeLeechPerHit = output.LifeLeechInstant * output.LifeRecoveryMod + m_min(output.LifeLeechInstanceRate, output.MaxLifeLeechRate) * output.LifeLeechDuration * output.LifeRecoveryRateMod
- output.EnergyShieldLeechInstanceRate = output.EnergyShield * 0.02 * calcLib.mod(skillModList, skillCfg, "EnergyShieldLeechRate")
- output.EnergyShieldLeechRate = output.EnergyShieldLeechInstantRate * output.EnergyShieldRecoveryMod + m_min(output.EnergyShieldLeechInstances * output.EnergyShieldLeechInstanceRate, output.MaxEnergyShieldLeechRate) * output.EnergyShieldRecoveryRateMod
- output.EnergyShieldLeechPerHit = output.EnergyShieldLeechInstant * output.EnergyShieldRecoveryMod + m_min(output.EnergyShieldLeechInstanceRate, output.MaxEnergyShieldLeechRate) * output.EnergyShieldLeechDuration * output.EnergyShieldRecoveryRateMod
- output.ManaLeechInstanceRate = output.Mana * 0.02 * calcLib.mod(skillModList, skillCfg, "ManaLeechRate")
- output.ManaLeechRate = output.ManaLeechInstantRate * output.ManaRecoveryMod + m_min(output.ManaLeechInstances * output.ManaLeechInstanceRate, output.MaxManaLeechRate) * output.ManaRecoveryRateMod
- output.ManaLeechPerHit = output.ManaLeechInstant * output.ManaRecoveryMod + m_min(output.ManaLeechInstanceRate, output.MaxManaLeechRate) * output.ManaLeechDuration * output.ManaRecoveryRateMod
- skillFlags.leechLife = output.LifeLeechRate > 0
- skillFlags.leechES = output.EnergyShieldLeechRate > 0
- skillFlags.leechMana = output.ManaLeechRate > 0
- if skillData.showAverage then
- output.LifeLeechGainPerHit = output.LifeLeechPerHit + output.LifeOnHit
- output.EnergyShieldLeechGainPerHit = output.EnergyShieldLeechPerHit + output.EnergyShieldOnHit
- output.ManaLeechGainPerHit = output.ManaLeechPerHit + output.ManaOnHit
- else
- output.LifeLeechGainRate = output.LifeLeechRate + output.LifeOnHitRate
- output.EnergyShieldLeechGainRate = output.EnergyShieldLeechRate + output.EnergyShieldOnHitRate
- output.ManaLeechGainRate = output.ManaLeechRate + output.ManaOnHitRate
- end
- if breakdown then
- if skillFlags.leechLife then
- breakdown.LifeLeech = breakdown.leech(output.LifeLeechInstant, output.LifeLeechInstantRate, output.LifeLeechInstances, output.Life, "LifeLeechRate", output.MaxLifeLeechRate, output.LifeLeechDuration)
- end
- if skillFlags.leechES then
- breakdown.EnergyShieldLeech = breakdown.leech(output.EnergyShieldLeechInstant, output.EnergyShieldLeechInstantRate, output.EnergyShieldLeechInstances, output.EnergyShield, "EnergyShieldLeechRate", output.MaxEnergyShieldLeechRate, output.EnergyShieldLeechDuration)
- end
- if skillFlags.leechMana then
- breakdown.ManaLeech = breakdown.leech(output.ManaLeechInstant, output.ManaLeechInstantRate, output.ManaLeechInstances, output.Mana, "ManaLeechRate", output.MaxManaLeechRate, output.ManaLeechDuration)
- end
- end
-
- -- Calculate skill DOT components
- local dotCfg = {
- skillName = skillCfg.skillName,
- skillPart = skillCfg.skillPart,
- skillTypes = skillCfg.skillTypes,
- slotName = skillCfg.slotName,
- flags = bor(ModFlag.Dot, skillData.dotIsSpell and ModFlag.Spell or 0, skillData.dotIsArea and ModFlag.Area or 0, skillData.dotIsProjectile and ModFlag.Projectile or 0),
- keywordFlags = band(skillCfg.keywordFlags, bnot(KeywordFlag.Hit)),
- }
- activeSkill.dotCfg = dotCfg
- output.TotalDot = 0
- for _, damageType in ipairs(dmgTypeList) do
- local dotTypeCfg = copyTable(dotCfg, true)
- dotTypeCfg.keywordFlags = bor(dotTypeCfg.keywordFlags, KeywordFlag[damageType.."Dot"])
- activeSkill["dot"..damageType.."Cfg"] = dotTypeCfg
- local baseVal
- if canDeal[damageType] then
- baseVal = skillData[damageType.."Dot"] or 0
- else
- baseVal = 0
- end
- if baseVal > 0 then
- skillFlags.dot = true
- local effMult = 1
- if env.mode_effective then
- local resist = 0
- local takenInc = enemyDB:Sum("INC", dotTypeCfg, "DamageTaken", "DamageTakenOverTime", damageType.."DamageTaken", damageType.."DamageTakenOverTime")
- local takenMore = enemyDB:More(dotTypeCfg, "DamageTaken", "DamageTakenOverTime", damageType.."DamageTaken", damageType.."DamageTakenOverTime")
- if damageType == "Physical" then
- resist = enemyDB:Sum("BASE", nil, "PhysicalDamageReduction")
- else
- resist = enemyDB:Sum("BASE", nil, damageType.."Resist")
- if isElemental[damageType] then
- resist = resist + enemyDB:Sum("BASE", dotTypeCfg, "ElementalResist")
- takenInc = takenInc + enemyDB:Sum("INC", dotTypeCfg, "ElementalDamageTaken")
- end
- resist = m_min(resist, 75)
- end
- effMult = (1 - resist / 100) * (1 + takenInc / 100) * takenMore
- output[damageType.."DotEffMult"] = effMult
- if breakdown and effMult ~= 1 then
- breakdown[damageType.."DotEffMult"] = breakdown.effMult(damageType, resist, 0, takenInc, effMult, takenMore)
- end
- end
- local inc = skillModList:Sum("INC", dotTypeCfg, "Damage", damageType.."Damage", isElemental[damageType] and "ElementalDamage" or nil)
- local more = round(skillModList:More(dotTypeCfg, "Damage", damageType.."Damage", isElemental[damageType] and "ElementalDamage" or nil), 2)
- local mult = skillModList:Sum("BASE", dotTypeCfg, "DotMultiplier", damageType.."DotMultiplier")
- local total = baseVal * (1 + inc/100) * more * (1 + mult/100) * effMult
- if activeSkill.skillTypes[SkillType.Aura] then
- total = total * calcLib.mod(skillModList, dotTypeCfg, "AuraEffect")
- end
- output[damageType.."Dot"] = total
- output.TotalDot = output.TotalDot + total
- if breakdown then
- breakdown[damageType.."Dot"] = { }
- breakdown.dot(breakdown[damageType.."Dot"], baseVal, inc, more, mult, nil, effMult, total)
- end
- end
- end
-
- skillFlags.bleed = false
- skillFlags.poison = false
- skillFlags.ignite = false
- skillFlags.igniteCanStack = skillModList:Flag(skillCfg, "IgniteCanStack")
- skillFlags.shock = false
- skillFlags.freeze = false
- for _, pass in ipairs(passList) do
- local globalOutput, globalBreakdown = output, breakdown
- local source, output, cfg, breakdown = pass.source, pass.output, pass.cfg, pass.breakdown
-
- -- Calculate chance to inflict secondary dots/status effects
- cfg.skillCond["CriticalStrike"] = true
- if not skillFlags.attack or skillModList:Flag(cfg, "CannotBleed") then
- output.BleedChanceOnCrit = 0
- else
- output.BleedChanceOnCrit = m_min(100, skillModList:Sum("BASE", cfg, "BleedChance") + enemyDB:Sum("BASE", nil, "SelfBleedChance"))
- end
- if not skillFlags.hit or skillModList:Flag(cfg, "CannotPoison") then
- output.PoisonChanceOnCrit = 0
- else
- output.PoisonChanceOnCrit = m_min(100, skillModList:Sum("BASE", cfg, "PoisonChance") + enemyDB:Sum("BASE", nil, "SelfPoisonChance"))
- end
- if not skillFlags.hit or skillModList:Flag(cfg, "CannotIgnite") then
- output.IgniteChanceOnCrit = 0
- else
- output.IgniteChanceOnCrit = 100
- end
- if not skillFlags.hit or skillModList:Flag(cfg, "CannotShock") then
- output.ShockChanceOnCrit = 0
- else
- output.ShockChanceOnCrit = 100
- end
- if not skillFlags.hit or skillModList:Flag(cfg, "CannotFreeze") then
- output.FreezeChanceOnCrit = 0
- else
- output.FreezeChanceOnCrit = 100
- end
- if not skillFlags.hit or skillModList:Flag(cfg, "CannotKnockback") then
- output.KnockbackChanceOnCrit = 0
- else
- output.KnockbackChanceOnCrit = skillModList:Sum("BASE", cfg, "EnemyKnockbackChance")
- end
- cfg.skillCond["CriticalStrike"] = false
- if not skillFlags.attack or skillModList:Flag(cfg, "CannotBleed") then
- output.BleedChanceOnHit = 0
- else
- output.BleedChanceOnHit = m_min(100, skillModList:Sum("BASE", cfg, "BleedChance") + enemyDB:Sum("BASE", nil, "SelfBleedChance"))
- end
- if not skillFlags.hit or skillModList:Flag(cfg, "CannotPoison") then
- output.PoisonChanceOnHit = 0
- output.ChaosPoisonChance = 0
- else
- output.PoisonChanceOnHit = m_min(100, skillModList:Sum("BASE", cfg, "PoisonChance") + enemyDB:Sum("BASE", nil, "SelfPoisonChance"))
- output.ChaosPoisonChance = m_min(100, skillModList:Sum("BASE", cfg, "ChaosPoisonChance"))
- end
- if not skillFlags.hit or skillModList:Flag(cfg, "CannotIgnite") then
- output.IgniteChanceOnHit = 0
- else
- output.IgniteChanceOnHit = m_min(100, skillModList:Sum("BASE", cfg, "EnemyIgniteChance") + enemyDB:Sum("BASE", nil, "SelfIgniteChance"))
- end
- if not skillFlags.hit or skillModList:Flag(cfg, "CannotShock") then
- output.ShockChanceOnHit = 0
- else
- output.ShockChanceOnHit = m_min(100, skillModList:Sum("BASE", cfg, "EnemyShockChance") + enemyDB:Sum("BASE", nil, "SelfShockChance"))
- end
- if not skillFlags.hit or skillModList:Flag(cfg, "CannotFreeze") then
- output.FreezeChanceOnHit = 0
- else
- output.FreezeChanceOnHit = m_min(100, skillModList:Sum("BASE", cfg, "EnemyFreezeChance") + enemyDB:Sum("BASE", nil, "SelfFreezeChance"))
- if skillModList:Flag(cfg, "CritsDontAlwaysFreeze") then
- output.FreezeChanceOnCrit = output.FreezeChanceOnHit
- end
- end
- if not skillFlags.hit or skillModList:Flag(cfg, "CannotKnockback") then
- output.KnockbackChanceOnHit = 0
- else
- output.KnockbackChanceOnHit = skillModList:Sum("BASE", cfg, "EnemyKnockbackChance")
- end
- if env.mode_effective then
- local bleedMult = (1 - enemyDB:Sum("BASE", nil, "AvoidBleed") / 100)
- output.BleedChanceOnHit = output.BleedChanceOnHit * bleedMult
- output.BleedChanceOnCrit = output.BleedChanceOnCrit * bleedMult
- local poisonMult = (1 - enemyDB:Sum("BASE", nil, "AvoidPoison") / 100)
- output.PoisonChanceOnHit = output.PoisonChanceOnHit * poisonMult
- output.PoisonChanceOnCrit = output.PoisonChanceOnCrit * poisonMult
- output.ChaosPoisonChance = output.ChaosPoisonChance * poisonMult
- local igniteMult = (1 - enemyDB:Sum("BASE", nil, "AvoidIgnite") / 100)
- output.IgniteChanceOnHit = output.IgniteChanceOnHit * igniteMult
- output.IgniteChanceOnCrit = output.IgniteChanceOnCrit * igniteMult
- local shockMult = (1 - enemyDB:Sum("BASE", nil, "AvoidShock") / 100)
- output.ShockChanceOnHit = output.ShockChanceOnHit * shockMult
- output.ShockChanceOnCrit = output.ShockChanceOnCrit * shockMult
- local freezeMult = (1 - enemyDB:Sum("BASE", nil, "AvoidFreeze") / 100)
- output.FreezeChanceOnHit = output.FreezeChanceOnHit * freezeMult
- output.FreezeChanceOnCrit = output.FreezeChanceOnCrit * freezeMult
- end
-
- local function calcAilmentDamage(type, sourceHitDmg, sourceCritDmg)
- -- Calculate the inflict chance and base damage of a secondary effect (bleed/poison/ignite/shock/freeze)
- local chanceOnHit, chanceOnCrit = output[type.."ChanceOnHit"], output[type.."ChanceOnCrit"]
- local chanceFromHit = chanceOnHit * (1 - output.CritChance / 100)
- local chanceFromCrit = chanceOnCrit * output.CritChance / 100
- local chance = chanceFromHit + chanceFromCrit
- output[type.."Chance"] = chance
- local baseFromHit = sourceHitDmg * chanceFromHit / (chanceFromHit + chanceFromCrit)
- local baseFromCrit = sourceCritDmg * chanceFromCrit / (chanceFromHit + chanceFromCrit)
- local baseVal = baseFromHit + baseFromCrit
- if breakdown and chance ~= 0 then
- local breakdownChance = breakdown[type.."Chance"] or { }
- breakdown[type.."Chance"] = breakdownChance
- if breakdownChance[1] then
- t_insert(breakdownChance, "")
- end
- if isAttack then
- t_insert(breakdownChance, pass.label..":")
- end
- t_insert(breakdownChance, s_format("Chance on Non-crit: %d%%", chanceOnHit))
- t_insert(breakdownChance, s_format("Chance on Crit: %d%%", chanceOnCrit))
- if chanceOnHit ~= chanceOnCrit then
- t_insert(breakdownChance, "Combined chance:")
- t_insert(breakdownChance, s_format("%d x (1 - %.4f) ^8(chance from non-crits)", chanceOnHit, output.CritChance/100))
- t_insert(breakdownChance, s_format("+ %d x %.4f ^8(chance from crits)", chanceOnCrit, output.CritChance/100))
- t_insert(breakdownChance, s_format("= %.2f", chance))
- end
- end
- if breakdown and baseVal > 0 then
- local breakdownDPS = breakdown[type.."DPS"] or { }
- breakdown[type.."DPS"] = breakdownDPS
- if breakdownDPS[1] then
- t_insert(breakdownDPS, "")
- end
- if isAttack then
- t_insert(breakdownDPS, pass.label..":")
- end
- if sourceHitDmg == sourceCritDmg then
- t_insert(breakdownDPS, "Total damage:")
- t_insert(breakdownDPS, s_format("%.1f ^8(source damage)",sourceHitDmg))
- else
- if baseFromHit > 0 then
- t_insert(breakdownDPS, "Damage from Non-crits:")
- t_insert(breakdownDPS, s_format("%.1f ^8(source damage from non-crits)", sourceHitDmg))
- t_insert(breakdownDPS, s_format("x %.3f ^8(portion of instances created by non-crits)", chanceFromHit / (chanceFromHit + chanceFromCrit)))
- t_insert(breakdownDPS, s_format("= %.1f", baseFromHit))
- end
- if baseFromCrit > 0 then
- t_insert(breakdownDPS, "Damage from Crits:")
- t_insert(breakdownDPS, s_format("%.1f ^8(source damage from crits)", sourceCritDmg))
- t_insert(breakdownDPS, s_format("x %.3f ^8(portion of instances created by crits)", chanceFromCrit / (chanceFromHit + chanceFromCrit)))
- t_insert(breakdownDPS, s_format("= %.1f", baseFromCrit))
- end
- if baseFromHit > 0 and baseFromCrit > 0 then
- t_insert(breakdownDPS, "Total damage:")
- t_insert(breakdownDPS, s_format("%.1f + %.1f", baseFromHit, baseFromCrit))
- t_insert(breakdownDPS, s_format("= %.1f", baseVal))
- end
- end
- end
- return baseVal
- end
-
- -- Calculate bleeding chance and damage
- if canDeal.Physical and (output.BleedChanceOnHit + output.BleedChanceOnCrit) > 0 then
- if not activeSkill.bleedCfg then
- activeSkill.bleedCfg = {
- skillName = skillCfg.skillName,
- skillPart = skillCfg.skillPart,
- skillTypes = skillCfg.skillTypes,
- slotName = skillCfg.slotName,
- flags = bor(ModFlag.Dot, ModFlag.Ailment, band(cfg.flags, ModFlag.WeaponMask), band(cfg.flags, ModFlag.Melee) ~= 0 and ModFlag.MeleeHit or 0),
- keywordFlags = bor(band(cfg.keywordFlags, bnot(KeywordFlag.Hit)), KeywordFlag.Bleed, KeywordFlag.Ailment, KeywordFlag.PhysicalDot),
- skillCond = { },
- }
- end
- local dotCfg = activeSkill.bleedCfg
- local sourceHitDmg, sourceCritDmg
- if breakdown then
- breakdown.BleedPhysical = { damageTypes = { } }
- end
- for pass = 1, 2 do
- if not skillModList:Flag(dotCfg, "AilmentsAreNeverFromCrit") then
- dotCfg.skillCond["CriticalStrike"] = (pass == 1)
- end
- local min, max = calcAilmentSourceDamage(activeSkill, output, dotCfg, pass == 2 and breakdown and breakdown.BleedPhysical, "Physical", 0)
- output.BleedPhysicalMin = min
- output.BleedPhysicalMax = max
- if pass == 1 then
- sourceCritDmg = (min + max) / 2 * (1 + skillModList:Sum("BASE", dotCfg, "DotMultiplier", "PhysicalDotMultiplier") / 100 + output.BonusCritDotMultiplier)
- else
- sourceHitDmg = (min + max) / 2 * (1 + skillModList:Sum("BASE", dotCfg, "DotMultiplier", "PhysicalDotMultiplier") / 100)
- end
- end
- local basePercent = skillData.bleedBasePercent or 70
- local baseVal = calcAilmentDamage("Bleed", sourceHitDmg, sourceCritDmg) * basePercent / 100 * output.RuthlessBlowEffect
- if baseVal > 0 then
- skillFlags.bleed = true
- skillFlags.duration = true
- local effMult = 1
- if env.mode_effective then
- local resist = enemyDB:Sum("BASE", nil, "PhysicalDamageReduction")
- local takenInc = enemyDB:Sum("INC", dotCfg, "DamageTaken", "DamageTakenOverTime", "PhysicalDamageTaken", "PhysicalDamageTakenOverTime")
- local takenMore = enemyDB:More(dotCfg, "DamageTaken", "DamageTakenOverTime", "PhysicalDamageTaken", "PhysicalDamageTakenOverTime")
- effMult = (1 - resist / 100) * (1 + takenInc / 100) * takenMore
- globalOutput["BleedEffMult"] = effMult
- if breakdown and effMult ~= 1 then
- globalBreakdown.BleedEffMult = breakdown.effMult("Physical", resist, 0, takenInc, effMult, takenMore)
- end
- end
- local mult = skillModList:Sum("BASE", dotCfg, "PhysicalDotMultiplier", "BleedMultiplier")
- local effectMod = calcLib.mod(skillModList, dotCfg, "AilmentEffect")
- local rateMod = calcLib.mod(skillModList, cfg, "BleedFaster")
- output.BleedDPS = baseVal * effectMod * rateMod * effMult
- local durationBase
- if skillData.bleedDurationIsSkillDuration then
- durationBase = skillData.duration
- else
- durationBase = 5
- end
- local durationMod = calcLib.mod(skillModList, dotCfg, "EnemyBleedDuration", "SkillAndDamagingAilmentDuration", skillData.bleedIsSkillEffect and "Duration" or nil) * calcLib.mod(enemyDB, nil, "SelfBleedDuration")
- globalOutput.BleedDuration = durationBase * durationMod / rateMod * debuffDurationMult
- if breakdown then
- t_insert(breakdown.BleedDPS, s_format("x %.2f ^8(bleed deals %d%% per second)", basePercent/100, basePercent))
- if effectMod ~= 1 then
- t_insert(breakdown.BleedDPS, s_format("x %.2f ^8(ailment effect modifier)", effectMod))
- end
- if output.RuthlessBlowEffect ~= 0 then
- t_insert(breakdown.BleedDPS, s_format("x %.2f ^8(ruthless blow effect modifier)", output.RuthlessBlowEffect))
- end
- t_insert(breakdown.BleedDPS, s_format("= %.1f", baseVal))
- breakdown.multiChain(breakdown.BleedDPS, {
- label = "Bleed DPS:",
- base = s_format("%.1f ^8(total damage per second)", baseVal),
- { "%.2f ^8(ailment effect modifier)", effectMod },
- { "%.2f ^8(damage rate modifier)", rateMod },
- { "%.3f ^8(effective DPS modifier)", effMult },
- total = s_format("= %.1f ^8per second", output.BleedDPS),
- })
- if globalOutput.BleedDuration ~= durationBase then
- globalBreakdown.BleedDuration = {
- s_format("%.2fs ^8(base duration)", durationBase)
- }
- if durationMod ~= 1 then
- t_insert(globalBreakdown.BleedDuration, s_format("x %.2f ^8(duration modifier)", durationMod))
- end
- if rateMod ~= 1 then
- t_insert(globalBreakdown.BleedDuration, s_format("/ %.2f ^8(damage rate modifier)", rateMod))
- end
- if debuffDurationMult ~= 1 then
- t_insert(globalBreakdown.BleedDuration, s_format("/ %.2f ^8(debuff expires slower/faster)", 1 / debuffDurationMult))
- end
- t_insert(globalBreakdown.BleedDuration, s_format("= %.2fs", globalOutput.BleedDuration))
- end
- end
- end
- end
-
- -- Calculate poison chance and damage
- if canDeal.Chaos and (output.PoisonChanceOnHit + output.PoisonChanceOnCrit + output.ChaosPoisonChance) > 0 then
- if not activeSkill.poisonCfg then
- activeSkill.poisonCfg = {
- skillName = skillCfg.skillName,
- skillPart = skillCfg.skillPart,
- skillTypes = skillCfg.skillTypes,
- slotName = skillCfg.slotName,
- flags = bor(ModFlag.Dot, ModFlag.Ailment, band(cfg.flags, ModFlag.WeaponMask), band(cfg.flags, ModFlag.Melee) ~= 0 and ModFlag.MeleeHit or 0),
- keywordFlags = bor(band(cfg.keywordFlags, bnot(KeywordFlag.Hit)), KeywordFlag.Poison, KeywordFlag.Ailment, KeywordFlag.ChaosDot),
- skillCond = { },
- }
- end
- local dotCfg = activeSkill.poisonCfg
- local sourceHitDmg, sourceCritDmg
- if breakdown then
- breakdown.PoisonPhysical = { damageTypes = { } }
- breakdown.PoisonLightning = { damageTypes = { } }
- breakdown.PoisonCold = { damageTypes = { } }
- breakdown.PoisonFire = { damageTypes = { } }
- breakdown.PoisonChaos = { damageTypes = { } }
- end
- for pass = 1, 2 do
- if not skillModList:Flag(dotCfg, "AilmentsAreNeverFromCrit") then
- dotCfg.skillCond["CriticalStrike"] = (pass == 1)
- end
- local totalMin, totalMax = 0, 0
- do
- local min, max = calcAilmentSourceDamage(activeSkill, output, dotCfg, pass == 2 and breakdown and breakdown.PoisonChaos, "Chaos", 0)
- output.PoisonChaosMin = min
- output.PoisonChaosMax = max
- totalMin = totalMin + min
- totalMax = totalMax + max
- end
- local nonChaosMult = 1
- if output.ChaosPoisonChance > 0 and output.PoisonChaosMax > 0 then
- -- Additional chance for chaos
- local chance = (pass == 1) and "PoisonChanceOnCrit" or "PoisonChanceOnHit"
- local chaosChance = m_min(100, output[chance] + output.ChaosPoisonChance)
- nonChaosMult = output[chance] / chaosChance
- output[chance] = chaosChance
- end
- if canDeal.Lightning and skillModList:Flag(cfg, "LightningCanPoison") then
- local min, max = calcAilmentSourceDamage(activeSkill, output, dotCfg, pass == 2 and breakdown and breakdown.PoisonLightning, "Lightning", dmgTypeFlags.Chaos)
- output.PoisonLightningMin = min
- output.PoisonLightningMax = max
- totalMin = totalMin + min * nonChaosMult
- totalMax = totalMax + max * nonChaosMult
- end
- if canDeal.Cold and skillModList:Flag(cfg, "ColdCanPoison") then
- local min, max = calcAilmentSourceDamage(activeSkill, output, dotCfg, pass == 2 and breakdown and breakdown.PoisonCold, "Cold", dmgTypeFlags.Chaos)
- output.PoisonColdMin = min
- output.PoisonColdMax = max
- totalMin = totalMin + min * nonChaosMult
- totalMax = totalMax + max * nonChaosMult
- end
- if canDeal.Fire and skillModList:Flag(cfg, "FireCanPoison") then
- local min, max = calcAilmentSourceDamage(activeSkill, output, dotCfg, pass == 2 and breakdown and breakdown.PoisonFire, "Fire", dmgTypeFlags.Chaos)
- output.PoisonFireMin = min
- output.PoisonFireMax = max
- totalMin = totalMin + min * nonChaosMult
- totalMax = totalMax + max * nonChaosMult
- end
- if canDeal.Physical then
- local min, max = calcAilmentSourceDamage(activeSkill, output, dotCfg, pass == 2 and breakdown and breakdown.PoisonPhysical, "Physical", dmgTypeFlags.Chaos)
- output.PoisonPhysicalMin = min
- output.PoisonPhysicalMax = max
- totalMin = totalMin + min * nonChaosMult
- totalMax = totalMax + max * nonChaosMult
- end
- if pass == 1 then
- sourceCritDmg = (totalMin + totalMax) / 2 * (1 + skillModList:Sum("BASE", dotCfg, "DotMultiplier", "ChaosDotMultiplier") / 100 + output.BonusCritDotMultiplier)
- else
- sourceHitDmg = (totalMin + totalMax) / 2 * (1 + skillModList:Sum("BASE", dotCfg, "DotMultiplier", "ChaosDotMultiplier") / 100)
- end
- end
- local baseVal = calcAilmentDamage("Poison", sourceHitDmg, sourceCritDmg) * 0.20
- if baseVal > 0 then
- skillFlags.poison = true
- skillFlags.duration = true
- local effMult = 1
- if env.mode_effective then
- local resist = m_min(enemyDB:Sum("BASE", nil, "ChaosResist"), 75)
- local takenInc = enemyDB:Sum("INC", dotCfg, "DamageTaken", "DamageTakenOverTime", "ChaosDamageTaken", "ChaosDamageTakenOverTime")
- local takenMore = enemyDB:More(dotCfg, "DamageTaken", "DamageTakenOverTime", "ChaosDamageTaken", "ChaosDamageTakenOverTime")
- effMult = (1 - resist / 100) * (1 + takenInc / 100) * takenMore
- globalOutput["PoisonEffMult"] = effMult
- if breakdown and effMult ~= 1 then
- globalBreakdown.PoisonEffMult = breakdown.effMult("Chaos", resist, 0, takenInc, effMult, takenMore)
- end
- end
- local effectMod = calcLib.mod(skillModList, dotCfg, "AilmentEffect")
- local rateMod = calcLib.mod(skillModList, cfg, "PoisonFaster")
- output.PoisonDPS = baseVal * effectMod * rateMod * effMult
- local durationBase
- if skillData.poisonDurationIsSkillDuration then
- durationBase = skillData.duration
- else
- durationBase = 2
- end
- local durationMod = calcLib.mod(skillModList, dotCfg, "EnemyPoisonDuration", "SkillAndDamagingAilmentDuration", skillData.poisonIsSkillEffect and "Duration" or nil) * calcLib.mod(enemyDB, nil, "SelfPoisonDuration")
- globalOutput.PoisonDuration = durationBase * durationMod / rateMod * debuffDurationMult
- output.PoisonDamage = output.PoisonDPS * globalOutput.PoisonDuration
- if skillData.showAverage then
- output.TotalPoisonAverageDamage = output.HitChance / 100 * output.PoisonChance / 100 * output.PoisonDamage
- else
- output.TotalPoisonStacks = output.HitChance / 100 * output.PoisonChance / 100 * globalOutput.PoisonDuration * (globalOutput.HitSpeed or globalOutput.Speed) * (skillData.dpsMultiplier or 1)
- output.TotalPoisonDPS = output.PoisonDPS * output.TotalPoisonStacks
- end
- if breakdown then
- t_insert(breakdown.PoisonDPS, "x 0.20 ^8(poison deals 20% per second)")
- t_insert(breakdown.PoisonDPS, s_format("= %.1f", baseVal, 1))
- breakdown.multiChain(breakdown.PoisonDPS, {
- label = "Poison DPS:",
- base = s_format("%.1f ^8(total damage per second)", baseVal),
- { "%.2f ^8(ailment effect modifier)", effectMod },
- { "%.2f ^8(damage rate modifier)", rateMod },
- { "%.3f ^8(effective DPS modifier)", effMult },
- total = s_format("= %.1f ^8per second", output.PoisonDPS),
- })
- if globalOutput.PoisonDuration ~= 2 then
- globalBreakdown.PoisonDuration = {
- s_format("%.2fs ^8(base duration)", durationBase)
- }
- if durationMod ~= 1 then
- t_insert(globalBreakdown.PoisonDuration, s_format("x %.2f ^8(duration modifier)", durationMod))
- end
- if rateMod ~= 1 then
- t_insert(globalBreakdown.PoisonDuration, s_format("/ %.2f ^8(damage rate modifier)", rateMod))
- end
- if debuffDurationMult ~= 1 then
- t_insert(globalBreakdown.PoisonDuration, s_format("/ %.2f ^8(debuff expires slower/faster)", 1 / debuffDurationMult))
- end
- t_insert(globalBreakdown.PoisonDuration, s_format("= %.2fs", globalOutput.PoisonDuration))
- end
- breakdown.PoisonDamage = { }
- if isAttack then
- t_insert(breakdown.PoisonDamage, pass.label..":")
- end
- t_insert(breakdown.PoisonDamage, s_format("%.1f ^8(damage per second)", output.PoisonDPS))
- t_insert(breakdown.PoisonDamage, s_format("x %.2fs ^8(poison duration)", globalOutput.PoisonDuration))
- t_insert(breakdown.PoisonDamage, s_format("= %.1f ^8damage per poison stack", output.PoisonDamage))
- if not skillData.showAverage then
- breakdown.TotalPoisonStacks = { }
- if isAttack then
- t_insert(breakdown.TotalPoisonStacks, pass.label..":")
- end
- breakdown.multiChain(breakdown.TotalPoisonStacks, {
- base = s_format("%.2fs ^8(poison duration)", globalOutput.PoisonDuration),
- { "%.2f ^8(poison chance)", output.PoisonChance / 100 },
- { "%.2f ^8(hit chance)", output.HitChance / 100 },
- { "%.2f ^8(hits per second)", globalOutput.HitSpeed or globalOutput.Speed },
- { "%g ^8(dps multiplier for this skill)", skillData.dpsMultiplier or 1 },
- total = s_format("= %.1f", output.TotalPoisonStacks),
- })
- end
- end
- end
- end
-
- -- Calculate ignite chance and damage
- if canDeal.Fire and (output.IgniteChanceOnHit + output.IgniteChanceOnCrit) > 0 then
- if not activeSkill.igniteCfg then
- activeSkill.igniteCfg = {
- skillName = skillCfg.skillName,
- skillPart = skillCfg.skillPart,
- skillTypes = skillCfg.skillTypes,
- slotName = skillCfg.slotName,
- flags = bor(ModFlag.Dot, ModFlag.Ailment, band(cfg.flags, ModFlag.WeaponMask), band(cfg.flags, ModFlag.Melee) ~= 0 and ModFlag.MeleeHit or 0),
- keywordFlags = bor(band(cfg.keywordFlags, bnot(KeywordFlag.Hit)), KeywordFlag.Ignite, KeywordFlag.Ailment, KeywordFlag.FireDot),
- skillCond = { },
- }
- end
- local dotCfg = activeSkill.igniteCfg
- local sourceHitDmg, sourceCritDmg
- if breakdown then
- breakdown.IgnitePhysical = { damageTypes = { } }
- breakdown.IgniteLightning = { damageTypes = { } }
- breakdown.IgniteCold = { damageTypes = { } }
- breakdown.IgniteFire = { damageTypes = { } }
- breakdown.IgniteChaos = { damageTypes = { } }
- end
- for pass = 1, 2 do
- if not skillModList:Flag(dotCfg, "AilmentsAreNeverFromCrit") then
- dotCfg.skillCond["CriticalStrike"] = (pass == 1)
- end
- local totalMin, totalMax = 0, 0
- if canDeal.Physical and skillModList:Flag(cfg, "PhysicalCanIgnite") then
- local min, max = calcAilmentSourceDamage(activeSkill, output, dotCfg, pass == 2 and breakdown and breakdown.IgnitePhysical, "Physical", dmgTypeFlags.Fire)
- output.IgnitePhysicalMin = min
- output.IgnitePhysicalMax = max
- totalMin = totalMin + min
- totalMax = totalMax + max
- end
- if canDeal.Lightning and skillModList:Flag(cfg, "LightningCanIgnite") then
- local min, max = calcAilmentSourceDamage(activeSkill, output, dotCfg, pass == 2 and breakdown and breakdown.IgniteLightning, "Lightning", dmgTypeFlags.Fire)
- output.IgniteLightningMin = min
- output.IgniteLightningMax = max
- totalMin = totalMin + min
- totalMax = totalMax + max
- end
- if canDeal.Cold and skillModList:Flag(cfg, "ColdCanIgnite") then
- local min, max = calcAilmentSourceDamage(activeSkill, output, dotCfg, pass == 2 and breakdown and breakdown.IgniteCold, "Cold", dmgTypeFlags.Fire)
- output.IgniteColdMin = min
- output.IgniteColdMax = max
- totalMin = totalMin + min
- totalMax = totalMax + max
- end
- if canDeal.Fire and not skillModList:Flag(cfg, "FireCannotIgnite") then
- local min, max = calcAilmentSourceDamage(activeSkill, output, dotCfg, pass == 2 and breakdown and breakdown.IgniteFire, "Fire", 0)
- output.IgniteFireMin = min
- output.IgniteFireMax = max
- totalMin = totalMin + min
- totalMax = totalMax + max
- end
- if canDeal.Chaos and skillModList:Flag(cfg, "ChaosCanIgnite") then
- local min, max = calcAilmentSourceDamage(activeSkill, output, dotCfg, pass == 2 and breakdown and breakdown.IgniteChaos, "Chaos", dmgTypeFlags.Fire)
- output.IgniteChaosMin = min
- output.IgniteChaosMax = max
- totalMin = totalMin + min
- totalMax = totalMax + max
- end
- if pass == 1 then
- sourceCritDmg = (totalMin + totalMax) / 2 * (1 + skillModList:Sum("BASE", dotCfg, "DotMultiplier", "FireDotMultiplier") / 100 + output.BonusCritDotMultiplier)
- else
- sourceHitDmg = (totalMin + totalMax) / 2 * (1 + skillModList:Sum("BASE", dotCfg, "DotMultiplier", "FireDotMultiplier") / 100)
- end
- end
- local igniteMode = env.configInput.igniteMode or "AVERAGE"
- if igniteMode == "CRIT" then
- output.IgniteChanceOnHit = 0
- end
- if globalBreakdown then
- globalBreakdown.IgniteDPS = {
- s_format("Ignite mode: %s ^8(can be changed in the Configuration tab)", igniteMode == "CRIT" and "Crit Damage" or "Average Damage")
- }
- end
- local baseVal = calcAilmentDamage("Ignite", sourceHitDmg, sourceCritDmg) * 0.5
- if baseVal > 0 then
- skillFlags.ignite = true
- local effMult = 1
- if env.mode_effective then
- local resist = m_min(enemyDB:Sum("BASE", nil, "FireResist", "ElementalResist"), 75)
- local takenInc = enemyDB:Sum("INC", dotCfg, "DamageTaken", "DamageTakenOverTime", "FireDamageTaken", "FireDamageTakenOverTime", "ElementalDamageTaken")
- local takenMore = enemyDB:More(dotCfg, "DamageTaken", "DamageTakenOverTime", "FireDamageTaken", "FireDamageTakenOverTime", "ElementalDamageTaken")
- effMult = (1 - resist / 100) * (1 + takenInc / 100) * takenMore
- globalOutput["IgniteEffMult"] = effMult
- if breakdown and effMult ~= 1 then
- globalBreakdown.IgniteEffMult = breakdown.effMult("Fire", resist, 0, takenInc, effMult, takenMore)
- end
- end
- local effectMod = calcLib.mod(skillModList, dotCfg, "AilmentEffect")
- local rateMod = calcLib.mod(skillModList, cfg, "IgniteBurnFaster") / calcLib.mod(skillModList, cfg, "IgniteBurnSlower")
- output.IgniteDPS = baseVal * effectMod * rateMod * effMult
- local incDur = skillModList:Sum("INC", dotCfg, "EnemyIgniteDuration", "SkillAndDamagingAilmentDuration") + enemyDB:Sum("INC", nil, "SelfIgniteDuration")
- local moreDur = enemyDB:More(nil, "SelfIgniteDuration")
- globalOutput.IgniteDuration = 4 * (1 + incDur / 100) * moreDur / rateMod * debuffDurationMult
- if skillFlags.igniteCanStack then
- output.IgniteDamage = output.IgniteDPS * globalOutput.IgniteDuration
- if skillData.showAverage then
- output.TotalIgniteAverageDamage = output.HitChance / 100 * output.IgniteChance / 100 * output.IgniteDamage
- else
- output.TotalIgniteStacks = output.HitChance / 100 * output.IgniteChance / 100 * globalOutput.IgniteDuration * (globalOutput.HitSpeed or globalOutput.Speed) * (skillData.dpsMultiplier or 1)
- output.TotalIgniteDPS = output.IgniteDPS * output.TotalIgniteStacks
- end
- end
- if breakdown then
- t_insert(breakdown.IgniteDPS, "x 0.5 ^8(ignite deals 50% per second)")
- t_insert(breakdown.IgniteDPS, s_format("= %.1f", baseVal, 1))
- breakdown.multiChain(breakdown.IgniteDPS, {
- label = "Ignite DPS:",
- base = s_format("%.1f ^8(total damage per second)", baseVal),
- { "%.2f ^8(ailment effect modifier)", effectMod },
- { "%.2f ^8(burn rate modifier)", rateMod },
- { "%.3f ^8(effective DPS modifier)", effMult },
- total = s_format("= %.1f ^8per second", output.IgniteDPS),
- })
- if skillFlags.igniteCanStack then
- breakdown.IgniteDamage = { }
- if isAttack then
- t_insert(breakdown.IgniteDamage, pass.label..":")
- end
- t_insert(breakdown.IgniteDamage, s_format("%.1f ^8(damage per second)", output.IgniteDPS))
- t_insert(breakdown.IgniteDamage, s_format("x %.2fs ^8(ignite duration)", globalOutput.IgniteDuration))
- t_insert(breakdown.IgniteDamage, s_format("= %.1f ^8damage per ignite stack", output.IgniteDamage))
- if not skillData.showAverage then
- breakdown.TotalIgniteStacks = { }
- if isAttack then
- t_insert(breakdown.TotalIgniteStacks, pass.label..":")
- end
- breakdown.multiChain(breakdown.TotalIgniteStacks, {
- base = s_format("%.2fs ^8(ignite duration)", globalOutput.IgniteDuration),
- { "%.2f ^8(ignite chance)", output.IgniteChance / 100 },
- { "%.2f ^8(hit chance)", output.HitChance / 100 },
- { "%.2f ^8(hits per second)", globalOutput.HitSpeed or globalOutput.Speed },
- { "%g ^8(dps multiplier for this skill)", skillData.dpsMultiplier or 1 },
- total = s_format("= %.1f", output.TotalIgniteStacks),
- })
- end
- end
- if globalOutput.IgniteDuration ~= 4 then
- globalBreakdown.IgniteDuration = {
- s_format("4.00s ^8(base duration)", durationBase)
- }
- if incDur ~= 0 then
- t_insert(globalBreakdown.IgniteDuration, s_format("x %.2f ^8(increased/reduced duration)", 1 + incDur/100))
- end
- if moreDur ~= 1 then
- t_insert(globalBreakdown.IgniteDuration, s_format("x %.2f ^8(more/less duration)", moreDur))
- end
- if rateMod ~= 1 then
- t_insert(globalBreakdown.IgniteDuration, s_format("/ %.2f ^8(burn rate modifier)", rateMod))
- end
- if debuffDurationMult ~= 1 then
- t_insert(globalBreakdown.IgniteDuration, s_format("/ %.2f ^8(debuff expires slower/faster)", 1 / debuffDurationMult))
- end
- t_insert(globalBreakdown.IgniteDuration, s_format("= %.2fs", globalOutput.IgniteDuration))
- end
- end
- end
- end
-
- -- Calculate shock and freeze chance + duration modifier
- -- FIXME Completely fucking wrong now
- if (output.ShockChanceOnHit + output.ShockChanceOnCrit) > 0 then
- local sourceHitDmg = 0
- local sourceCritDmg = 0
- if canDeal.Physical and skillModList:Flag(cfg, "PhysicalCanShock") then
- sourceHitDmg = sourceHitDmg + output.PhysicalHitAverage
- sourceCritDmg = sourceCritDmg + output.PhysicalCritAverage
- end
- if canDeal.Lightning and not skillModList:Flag(cfg, "LightningCannotShock") then
- sourceHitDmg = sourceHitDmg + output.LightningHitAverage
- sourceCritDmg = sourceCritDmg + output.LightningCritAverage
- end
- if canDeal.Cold and skillModList:Flag(cfg, "ColdCanShock") then
- sourceHitDmg = sourceHitDmg + output.ColdHitAverage
- sourceCritDmg = sourceCritDmg + output.ColdCritAverage
- end
- if canDeal.Fire and skillModList:Flag(cfg, "FireCanShock") then
- sourceHitDmg = sourceHitDmg + output.FireHitAverage
- sourceCritDmg = sourceCritDmg + output.FireCritAverage
- end
- if canDeal.Chaos and skillModList:Flag(cfg, "ChaosCanShock") then
- sourceHitDmg = sourceHitDmg + output.ChaosHitAverage
- sourceCritDmg = sourceCritDmg + output.ChaosCritAverage
- end
- local baseVal = calcAilmentDamage("Shock", sourceHitDmg, sourceCritDmg)
- if baseVal > 0 then
- skillFlags.shock = true
- output.ShockDurationMod = 1 + skillModList:Sum("INC", cfg, "EnemyShockDuration") / 100 + enemyDB:Sum("INC", nil, "SelfShockDuration") / 100
- if breakdown then
- t_insert(breakdown.ShockDPS, s_format("For shock to apply, target must have no more than %d life.", baseVal * 20 * output.ShockDurationMod))
- end
- end
- end
- if (output.FreezeChanceOnHit + output.FreezeChanceOnCrit) > 0 then
- local sourceHitDmg = 0
- local sourceCritDmg = 0
- if canDeal.Cold and not skillModList:Flag(cfg, "ColdCannotFreeze") then
- sourceHitDmg = sourceHitDmg + output.ColdHitAverage
- sourceCritDmg = sourceCritDmg + output.ColdCritAverage
- end
- if canDeal.Lightning and skillModList:Flag(cfg, "LightningCanFreeze") then
- sourceHitDmg = sourceHitDmg + output.LightningHitAverage
- sourceCritDmg = sourceCritDmg + output.LightningCritAverage
- end
- local baseVal = calcAilmentDamage("Freeze", sourceHitDmg, sourceCritDmg)
- if baseVal > 0 then
- skillFlags.freeze = true
- output.FreezeDurationMod = 1 + skillModList:Sum("INC", cfg, "EnemyFreezeDuration") / 100 + enemyDB:Sum("INC", nil, "SelfFreezeDuration") / 100
- if breakdown then
- t_insert(breakdown.FreezeDPS, s_format("For freeze to apply, target must have no more than %d life.", baseVal * 20 * output.FreezeDurationMod))
- end
- end
- end
-
- -- Calculate knockback chance/distance
- output.KnockbackChance = m_min(100, output.KnockbackChanceOnHit * (1 - output.CritChance / 100) + output.KnockbackChanceOnCrit * output.CritChance / 100 + enemyDB:Sum("BASE", nil, "SelfKnockbackChance"))
- if output.KnockbackChance > 0 then
- output.KnockbackDistance = round(4 * calcLib.mod(skillModList, cfg, "EnemyKnockbackDistance"))
- if breakdown then
- breakdown.KnockbackDistance = {
- radius = output.KnockbackDistance,
- }
- end
- end
-
- -- Calculate enemy stun modifiers
- local enemyStunThresholdRed = -skillModList:Sum("INC", cfg, "EnemyStunThreshold")
- if enemyStunThresholdRed > 75 then
- output.EnemyStunThresholdMod = 1 - (75 + (enemyStunThresholdRed - 75) * 25 / (enemyStunThresholdRed - 50)) / 100
- else
- output.EnemyStunThresholdMod = 1 - enemyStunThresholdRed / 100
- end
- local base = skillData.baseStunDuration or 0.35
- local incDur = skillModList:Sum("INC", cfg, "EnemyStunDuration")
- local incRecov = enemyDB:Sum("INC", nil, "StunRecovery")
- output.EnemyStunDuration = base * (1 + incDur / 100) / (1 + incRecov / 100)
- if breakdown then
- if output.EnemyStunDuration ~= base then
- breakdown.EnemyStunDuration = {
- s_format("%.2fs ^8(base duration)", base),
- }
- if incDur ~= 0 then
- t_insert(breakdown.EnemyStunDuration, s_format("x %.2f ^8(increased/reduced stun duration)", 1 + incDur/100))
- end
- if incRecov ~= 0 then
- t_insert(breakdown.EnemyStunDuration, s_format("/ %.2f ^8(increased/reduced enemy stun recovery)", 1 + incRecov/100))
- end
- t_insert(breakdown.EnemyStunDuration, s_format("= %.2fs", output.EnemyStunDuration))
- end
- end
- end
-
- -- Combine secondary effect stats
- if isAttack then
- combineStat("BleedChance", "AVERAGE")
- combineStat("BleedDPS", "CHANCE", "BleedChance")
- combineStat("PoisonChance", "AVERAGE")
- combineStat("PoisonDPS", "CHANCE", "PoisonChance")
- combineStat("PoisonDamage", "CHANCE", "PoisonChance")
- if skillData.showAverage then
- combineStat("TotalPoisonAverageDamage", "DPS")
- else
- combineStat("TotalPoisonStacks", "DPS")
- combineStat("TotalPoisonDPS", "DPS")
- end
- combineStat("IgniteChance", "AVERAGE")
- combineStat("IgniteDPS", "CHANCE", "IgniteChance")
- if skillFlags.igniteCanStack then
- combineStat("IgniteDamage", "CHANCE", "IgniteChance")
- if skillData.showAverage then
- combineStat("TotalIgniteAverageDamage", "DPS")
- else
- combineStat("TotalIgniteStacks", "DPS")
- combineStat("TotalIgniteDPS", "DPS")
- end
- end
- combineStat("ShockChance", "AVERAGE")
- combineStat("ShockDurationMod", "AVERAGE")
- combineStat("FreezeChance", "AVERAGE")
- combineStat("FreezeDurationMod", "AVERAGE")
- end
-
- if skillFlags.hit and skillData.decay and canDeal.Chaos then
- -- Calculate DPS for Essence of Delirium's Decay effect
- skillFlags.decay = true
- activeSkill.decayCfg = {
- skillName = skillCfg.skillName,
- skillPart = skillCfg.skillPart,
- skillTypes = skillCfg.skillTypes,
- slotName = skillCfg.slotName,
- flags = ModFlag.Dot,
- keywordFlags = bor(band(skillCfg.keywordFlags, bnot(KeywordFlag.Hit)), KeywordFlag.ChaosDot),
- }
- local dotCfg = activeSkill.decayCfg
- local effMult = 1
- if env.mode_effective then
- local resist = m_min(enemyDB:Sum("BASE", nil, "ChaosResist"), 75)
- local takenInc = enemyDB:Sum("INC", nil, "DamageTaken", "DamageTakenOverTime", "ChaosDamageTaken", "ChaosDamageTakenOverTime")
- local takenMore = enemyDB:More(nil, "DamageTaken", "DamageTakenOverTime", "ChaosDamageTaken", "ChaosDamageTakenOverTime")
- effMult = (1 - resist / 100) * (1 + takenInc / 100) * takenMore
- output["DecayEffMult"] = effMult
- if breakdown and effMult ~= 1 then
- breakdown.DecayEffMult = breakdown.effMult("Chaos", resist, 0, takenInc, effMult, takenMore)
- end
- end
- local inc = skillModList:Sum("INC", dotCfg, "Damage", "ChaosDamage")
- local more = round(skillModList:More(dotCfg, "Damage", "ChaosDamage"), 2)
- local mult = skillModList:Sum("BASE", dotTypeCfg, "DotMultiplier", "ChaosDotMultiplier")
- output.DecayDPS = skillData.decay * (1 + inc/100) * more * (1 + mult/100) * effMult
- local durationMod = calcLib.mod(skillModList, dotCfg, "Duration", "SkillAndDamagingAilmentDuration")
- output.DecayDuration = 10 * durationMod * debuffDurationMult
- if breakdown then
- breakdown.DecayDPS = { }
- t_insert(breakdown.DecayDPS, "Decay DPS:")
- breakdown.dot(breakdown.DecayDPS, skillData.decay, inc, more, mult, nil, effMult, output.DecayDPS)
- if output.DecayDuration ~= 2 then
- breakdown.DecayDuration = {
- s_format("%.2fs ^8(base duration)", 10)
- }
- if durationMod ~= 1 then
- t_insert(breakdown.DecayDuration, s_format("x %.2f ^8(duration modifier)", durationMod))
- end
- if debuffDurationMult ~= 1 then
- t_insert(breakdown.DecayDuration, s_format("/ %.2f ^8(debuff expires slower/faster)", 1 / debuffDurationMult))
- end
- t_insert(breakdown.DecayDuration, s_format("= %.2fs", output.DecayDuration))
- end
- end
- end
-
- -- Calculate combined DPS estimate, including DoTs
- local baseDPS = output[(skillData.showAverage and "AverageDamage") or "TotalDPS"] + output.TotalDot
- output.CombinedDPS = baseDPS
- if skillData.showAverage then
- output.CombinedDPS = output.CombinedDPS + (output.TotalPoisonAverageDamage or 0)
- output.WithPoisonAverageDamage = baseDPS + (output.TotalPoisonAverageDamage or 0)
- else
- output.CombinedDPS = output.CombinedDPS + (output.TotalPoisonDPS or 0)
- output.WithPoisonDPS = baseDPS + (output.TotalPoisonDPS or 0)
- end
- if skillFlags.ignite then
- if skillFlags.igniteCanStack then
- if skillData.showAverage then
- output.CombinedDPS = output.CombinedDPS + output.TotalIgniteAverageDamage
- output.WithIgniteAverageDamage = baseDPS + output.TotalIgniteAverageDamage
- else
- output.CombinedDPS = output.CombinedDPS + output.TotalIgniteDPS
- output.WithIgniteDPS = baseDPS + output.TotalIgniteDPS
- end
- else
- output.CombinedDPS = output.CombinedDPS + output.IgniteDPS
- end
- end
- if skillFlags.bleed then
- output.CombinedDPS = output.CombinedDPS + output.BleedDPS
- end
- if skillFlags.decay then
- output.CombinedDPS = output.CombinedDPS + output.DecayDPS
- end
-end
diff --git b/Modules/CalcOffence.lua a/Modules/CalcOffence.lua
index 6f37249a..819449b0 100644
--- b/Modules/CalcOffence.lua
+++ a/Modules/CalcOffence.lua
@@ -344,6 +344,23 @@ function calcs.offence(env, actor, activeSkill)
return modifiers
end
+ -- additional charge based modifiers
+ if skillModList:Flag(nil, "UseEnduranceCharges") and skillModList:Flag(nil, "EnduranceChargesConvertToBrutalCharges") then
+ local tripleDmgChancePerEndurance = modDB:Sum("BASE", nil, "PerBrutalTripleDamageChance")
+ modDB:NewMod("TripleDamageChance", "BASE", tripleDmgChancePerEndurance, { type = "Multiplier", var = "BrutalCharge" } )
+ end
+ if skillModList:Flag(nil, "UseFrenzyCharges") and skillModList:Flag(nil, "FrenzyChargesConvertToAfflictionCharges") then
+ local dmgPerAffliction = modDB:Sum("BASE", nil, "PerAfflictionAilmentDamage")
+ local effectPerAffliction = modDB:Sum("BASE", nil, "PerAfflictionNonDamageEffect")
+ modDB:NewMod("Damage", "MORE", dmgPerAffliction, "Affliction Charges", 0, KeywordFlag.Ailment, { type = "Multiplier", var = "AfflictionCharge" } )
+ modDB:NewMod("EnemyChillEffect", "MORE", effectPerAffliction, "Affliction Charges", { type = "Multiplier", var = "AfflictionCharge" } )
+ modDB:NewMod("EnemyShockEffect", "MORE", effectPerAffliction, "Affliction Charges", { type = "Multiplier", var = "AfflictionCharge" } )
+ modDB:NewMod("EnemyFreezeEffect", "MORE", effectPerAffliction, "Affliction Charges", { type = "Multiplier", var = "AfflictionCharge" } )
+ modDB:NewMod("EnemyScorchEffect", "MORE", effectPerAffliction, "Affliction Charges", { type = "Multiplier", var = "AfflictionCharge" } )
+ modDB:NewMod("EnemyBrittleEffect", "MORE", effectPerAffliction, "Affliction Charges", { type = "Multiplier", var = "AfflictionCharge" } )
+ modDB:NewMod("EnemySapEffect", "MORE", effectPerAffliction, "Affliction Charges", { type = "Multiplier", var = "AfflictionCharge" } )
+ end
+
-- account for Battlemage
-- Note: we check conditions of Main Hand weapon using actor.itemList as actor.weaponData1 is populated with unarmed values when no weapon slotted.
if skillModList:Flag(nil, "WeaponDamageAppliesToSpells") and actor.itemList["Weapon 1"] and actor.itemList["Weapon 1"].weaponData and actor.itemList["Weapon 1"].weaponData[1] then
@@ -1162,9 +1179,15 @@ function calcs.offence(env, actor, activeSkill)
output.Time = activeSkill.activeEffect.grantedEffect.castTime
output.Speed = 1 / output.Time
elseif skillData.triggerTime and skillData.triggered then
- output.Time = skillData.triggerTime / (1 + skillModList:Sum("INC", cfg, "CooldownRecovery") / 100) * (skillModList:Sum("BASE", cfg, "CastWhileChannellingSpellsLinked") or 1)
+ local cwcLinkedSpells = skillModList:Sum("BASE", cfg, "CastWhileChannellingSpellsLinked")
+ if cwcLinkedSpells > 0 then
+ output.Time = skillData.triggerTime / (1 + skillModList:Sum("INC", cfg, "CooldownRecovery") / 100) * cwcLinkedSpells
+ else
+ output.Time = skillData.triggerTime / (1 + skillModList:Sum("INC", cfg, "CooldownRecovery") / 100)
+ end
output.TriggerTime = output.Time
output.Speed = 1 / output.Time
+ skillData.showAverage = false
elseif skillData.triggeredByBrand and skillData.triggered then
output.Time = 1 / (1 + skillModList:Sum("INC", cfg, "Speed", "BrandActivationFrequency") / 100) / skillModList:More(cfg, "BrandActivationFrequency") * (skillModList:Sum("BASE", cfg, "ArcanistSpellsLinked") or 1)
output.TriggerTime = output.Time
@@ -1186,12 +1209,20 @@ function calcs.offence(env, actor, activeSkill)
local inc = skillModList:Sum("INC", cfg, "Speed")
local more = skillModList:More(cfg, "Speed")
output.Speed = 1 / baseTime * round((1 + inc/100) * more, 2)
- if skillData.attackRateCap then
- output.Speed = m_min(output.Speed, skillData.attackRateCap)
- end
+ output.CastRate = output.Speed
+ output.Repeats = 1 + (skillModList:Sum("BASE", cfg, "RepeatCount") or 0)
if skillFlags.selfCast then
-- Self-cast skill; apply action speed
output.Speed = output.Speed * globalOutput.ActionSpeedMod
+ output.CastRate = output.Speed
+ end
+ if output.Cooldown then
+ output.Speed = m_min(output.Speed, 1 / output.Cooldown * output.Repeats)
+ end
+ if output.Cooldown and skillFlags.selfCast then
+ skillFlags.notAverage = true
+ skillFlags.showAverage = false
+ skillData.showAverage = false
end
output.Speed = m_min(output.Speed, data.misc.ServerTickRate)
if output.Speed == 0 then
@@ -1206,8 +1237,18 @@ function calcs.offence(env, actor, activeSkill)
{ "%.2f ^8(increased/reduced)", 1 + inc/100 },
{ "%.2f ^8(more/less)", more },
{ "%.2f ^8(action speed modifier)", skillFlags.selfCast and globalOutput.ActionSpeedMod or 1 },
- total = s_format("= %.2f ^8per second", output.Speed)
+ total = s_format("= %.2f ^8casts per second", output.CastRate)
})
+ if output.Cooldown and (1 / output.Cooldown) < output.CastRate then
+ t_insert(breakdown.Speed, s_format("\n"))
+ t_insert(breakdown.Speed, s_format("1 / %.2f ^8(skill cooldown)", output.Cooldown))
+ if output.Repeats > 1 then
+ t_insert(breakdown.Speed, s_format("x %d ^8(repeat count)", output.Repeats))
+ end
+ t_insert(breakdown.Speed, s_format("= %.2f ^8(casts per second)", output.Repeats / output.Cooldown))
+ t_insert(breakdown.Speed, s_format("\n"))
+ t_insert(breakdown.Speed, s_format("= %.2f ^8(lower of cast rates)", output.Speed))
+ end
end
if breakdown and calcLib.mod(skillModList, skillCfg, "SkillAttackTime") > 0 then
breakdown.Time = { }
@@ -1218,7 +1259,7 @@ function calcs.offence(env, actor, activeSkill)
})
end
end
- if skillData.hitTimeOverride then
+ if skillData.hitTimeOverride and not skillData.triggeredOnDeath then
output.HitTime = skillData.hitTimeOverride
output.HitSpeed = 1 / output.HitTime
--Brands always have hitTimeOverride
@@ -1277,7 +1318,9 @@ function calcs.offence(env, actor, activeSkill)
end
globalOutput.AncestralCryCastTime = calcWarcryCastTime(value.skillModList, value.skillCfg, actor)
globalOutput.AncestralExertsCount = env.modDB:Sum("BASE", nil, "NumAncestralExerts") or 0
- globalOutput.AncestralUpTimeRatio = m_min((globalOutput.AncestralExertsCount / output.Speed) / (globalOutput.AncestralCryCooldown + globalOutput.AncestralCryCastTime), 1) * 100
+ local baseUptimeRatio = m_min((globalOutput.AncestralExertsCount / output.Speed) / (globalOutput.AncestralCryCooldown + globalOutput.AncestralCryCastTime), 1) * 100
+ local additionalCooldownUses = value.skillModList:Sum("BASE", value.skillCfg, "AdditionalCooldownUses")
+ globalOutput.AncestralUpTimeRatio = m_min(100, baseUptimeRatio * (additionalCooldownUses + 1))
if globalBreakdown then
globalBreakdown.AncestralUpTimeRatio = { }
t_insert(globalBreakdown.AncestralUpTimeRatio, s_format("(%d ^8(number of exerts)", globalOutput.AncestralExertsCount))
@@ -1286,7 +1329,7 @@ function calcs.offence(env, actor, activeSkill)
t_insert(globalBreakdown.AncestralUpTimeRatio, s_format("/ (%.2f ^8(warcry cooldown)", globalOutput.AncestralCryCooldown))
t_insert(globalBreakdown.AncestralUpTimeRatio, s_format("+ %.2f) ^8(warcry casttime)", globalOutput.AncestralCryCastTime))
else
- t_insert(globalBreakdown.AncestralUpTimeRatio, s_format("/ %.2f ^8(average ooldown)", globalOutput.AncestralCryCooldown))
+ t_insert(globalBreakdown.AncestralUpTimeRatio, s_format("/ %.2f ^8(average warcry cooldown)", globalOutput.AncestralCryCooldown))
end
t_insert(globalBreakdown.AncestralUpTimeRatio, s_format("= %d%%", globalOutput.AncestralUpTimeRatio))
end
@@ -1302,7 +1345,9 @@ function calcs.offence(env, actor, activeSkill)
globalOutput.InfernalCryCastTime = calcWarcryCastTime(value.skillModList, value.skillCfg, actor)
if activeSkill.skillTypes[SkillType.Melee] then
globalOutput.InfernalExertsCount = env.modDB:Sum("BASE", nil, "NumInfernalExerts") or 0
- globalOutput.InfernalUpTimeRatio = m_min((globalOutput.InfernalExertsCount / output.Speed) / (globalOutput.InfernalCryCooldown + globalOutput.InfernalCryCastTime), 1) * 100
+ local baseUptimeRatio = m_min((globalOutput.InfernalExertsCount / output.Speed) / (globalOutput.InfernalCryCooldown + globalOutput.InfernalCryCastTime), 1) * 100
+ local additionalCooldownUses = value.skillModList:Sum("BASE", value.skillCfg, "AdditionalCooldownUses")
+ globalOutput.InfernalUpTimeRatio = m_min(100, baseUptimeRatio * (additionalCooldownUses + 1))
if globalBreakdown then
globalBreakdown.InfernalUpTimeRatio = { }
t_insert(globalBreakdown.InfernalUpTimeRatio, s_format("(%d ^8(number of exerts)", globalOutput.InfernalExertsCount))
@@ -1328,7 +1373,9 @@ function calcs.offence(env, actor, activeSkill)
end
globalOutput.IntimidatingCryCastTime = calcWarcryCastTime(value.skillModList, value.skillCfg, actor)
globalOutput.IntimidatingExertsCount = env.modDB:Sum("BASE", nil, "NumIntimidatingExerts") or 0
- globalOutput.IntimidatingUpTimeRatio = m_min((globalOutput.IntimidatingExertsCount / output.Speed) / (globalOutput.IntimidatingCryCooldown + globalOutput.IntimidatingCryCastTime), 1) * 100
+ local baseUptime = m_min((globalOutput.IntimidatingExertsCount / output.Speed) / (globalOutput.IntimidatingCryCooldown + globalOutput.IntimidatingCryCastTime), 1) * 100
+ local additionalCooldownUses = value.skillModList:Sum("BASE", value.skillCfg, "AdditionalCooldownUses")
+ globalOutput.IntimidatingUpTimeRatio = m_min(100, baseUptime * (additionalCooldownUses + 1))
if globalBreakdown then
globalBreakdown.IntimidatingUpTimeRatio = { }
t_insert(globalBreakdown.IntimidatingUpTimeRatio, s_format("(%d ^8(number of exerts)", globalOutput.IntimidatingExertsCount))
@@ -1375,7 +1422,9 @@ function calcs.offence(env, actor, activeSkill)
end
globalOutput.RallyingCryCastTime = calcWarcryCastTime(value.skillModList, value.skillCfg, actor)
globalOutput.RallyingExertsCount = env.modDB:Sum("BASE", nil, "NumRallyingExerts") or 0
- globalOutput.RallyingUpTimeRatio = m_min((globalOutput.RallyingExertsCount / output.Speed) / (globalOutput.RallyingCryCooldown + globalOutput.RallyingCryCastTime), 1) * 100
+ local baseUptimeRatio = m_min((globalOutput.RallyingExertsCount / output.Speed) / (globalOutput.RallyingCryCooldown + globalOutput.RallyingCryCastTime), 1) * 100
+ local additionalCooldownUses = value.skillModList:Sum("BASE", value.skillCfg, "AdditionalCooldownUses")
+ globalOutput.RallyingUpTimeRatio = m_min(100, baseUptimeRatio * (additionalCooldownUses + 1))
if globalBreakdown then
globalBreakdown.RallyingUpTimeRatio = { }
t_insert(globalBreakdown.RallyingUpTimeRatio, s_format("(%d ^8(number of exerts)", globalOutput.RallyingExertsCount))
@@ -1423,7 +1472,9 @@ function calcs.offence(env, actor, activeSkill)
end
globalOutput.SeismicCryCastTime = calcWarcryCastTime(value.skillModList, value.skillCfg, actor)
globalOutput.SeismicExertsCount = env.modDB:Sum("BASE", nil, "NumSeismicExerts") or 0
- globalOutput.SeismicUpTimeRatio = m_min((globalOutput.SeismicExertsCount / output.Speed) / (globalOutput.SeismicCryCooldown + globalOutput.SeismicCryCastTime), 1) * 100
+ local baseUptimeRatio = m_min((globalOutput.SeismicExertsCount / output.Speed) / (globalOutput.SeismicCryCooldown + globalOutput.SeismicCryCastTime), 1) * 100
+ local additionalCooldownUses = value.skillModList:Sum("BASE", value.skillCfg, "AdditionalCooldownUses")
+ globalOutput.SeismicUpTimeRatio = m_min(100, baseUptimeRatio * (additionalCooldownUses + 1))
if globalBreakdown then
globalBreakdown.SeismicUpTimeRatio = { }
t_insert(globalBreakdown.SeismicUpTimeRatio, s_format("(%d ^8(number of exerts)", globalOutput.SeismicExertsCount))
@@ -1716,8 +1767,16 @@ function calcs.offence(env, actor, activeSkill)
output.DoubleDamageChance = m_max(output.DoubleDamageChance - output.TripleDamageChance * output.DoubleDamageChance / 100, 0)
end
output.DoubleDamageEffect = 1 + output.DoubleDamageChance / 100
- output.ScaledDamageEffect = output.ScaledDamageEffect * output.DoubleDamageEffect
-
+ output.ScaledDamageEffect = output.ScaledDamageEffect * output.DoubleDamageEffect
+ -- Calculate culling DPS
+ local criticalCull = skillModList:Max(cfg, "CriticalCullPercent") or 0
+ if criticalCull > 0 then
+ criticalCull = criticalCull * (output.CritChance / 100)
+ end
+ local regularCull = skillModList:Max(cfg, "CullPercent") or 0
+ local maxCullPercent = m_max(criticalCull, regularCull)
+ globalOutput.CullPercent = maxCullPercent
+ globalOutput.CullMultiplier = 100 / (100 - globalOutput.CullPercent)
-- Calculate base hit damage
for _, damageType in ipairs(dmgTypeList) do
local damageTypeMin = damageType.."Min"
@@ -1837,13 +1896,11 @@ function calcs.offence(env, actor, activeSkill)
return not skillModList:Flag(cfg, "Ignore"..damageType.."Resistance", isElemental[damageType] and "IgnoreElementalResistances" or nil) and not enemyDB:Flag(nil, "SelfIgnore"..damageType.."Resistance")
end
if damageType == "Physical" then
- if isAttack then
- -- store pre-armour physical damage from attacks for impale calculations
- if pass == 1 then
- output.impaleStoredHitAvg = output.impaleStoredHitAvg + damageTypeHitAvg * (output.CritChance / 100)
- else
- output.impaleStoredHitAvg = output.impaleStoredHitAvg + damageTypeHitAvg * (1 - output.CritChance / 100)
- end
+ -- store pre-armour physical damage from attacks for impale calculations
+ if pass == 1 then
+ output.impaleStoredHitAvg = output.impaleStoredHitAvg + damageTypeHitAvg * (output.CritChance / 100)
+ else
+ output.impaleStoredHitAvg = output.impaleStoredHitAvg + damageTypeHitAvg * (1 - output.CritChance / 100)
end
local enemyArmour = calcLib.val(enemyDB, "Armour")
local armourReduction = calcs.armourReductionF(enemyArmour, damageTypeHitAvg)
@@ -2328,11 +2385,7 @@ function calcs.offence(env, actor, activeSkill)
else
output.SapChanceOnHit = 0
end
- if not skillFlags.attack then
- output.ImpaleChance = 0
- else
- output.ImpaleChance = m_min(100, skillModList:Sum("BASE", cfg, "ImpaleChance"))
- end
+ output.ImpaleChance = m_min(100, skillModList:Sum("BASE", cfg, "ImpaleChance"))
if skillModList:Sum("BASE", cfg, "FireExposureChance") > 0 then
skillFlags.applyFireExposure = true
end
@@ -3209,7 +3262,7 @@ function calcs.offence(env, actor, activeSkill)
end
end
end
- if (output.ScorchChanceOnHit + output.ScorchChanceOnCrit) > 0 then
+ if (output.ScorchChanceOnHit + output.ScorchChanceOnCrit) > 0 or enemyDB:Flag(nil, "Condition:AlreadyScorched") then
local sourceHitDmg = 0
local sourceCritDmg = 0
if output.ScorchChanceOnCrit == 0 and output.ScorchChanceOnHit > 0 then
@@ -3224,7 +3277,7 @@ function calcs.offence(env, actor, activeSkill)
output.ScorchChanceOnHit = 0
end
local baseVal = calcAilmentDamage("Scorch", sourceHitDmg, sourceCritDmg)
- if baseVal > 0 then
+ if baseVal > 0 or enemyDB:Flag(nil, "Condition:AlreadyScorched") then
skillFlags.scorch = true
output.ScorchEffectMod = skillModList:Sum("INC", cfg, "EnemyScorchEffect")
output.ScorchDurationMod = 1 + skillModList:Sum("INC", cfg, "EnemyScorchDuration") / 100 + enemyDB:Sum("INC", nil, "SelfScorchDuration") / 100
@@ -3457,9 +3510,21 @@ function calcs.offence(env, actor, activeSkill)
skillPart = skillCfg.skillPart,
skillTypes = skillCfg.skillTypes,
slotName = skillCfg.slotName,
- flags = bor(ModFlag.Dot, skillData.dotIsSpell and ModFlag.Spell or 0, skillData.dotIsArea and ModFlag.Area or 0, skillData.dotIsProjectile and ModFlag.Projectile or 0),
+ flags = bor(ModFlag.Dot, skillCfg.flags),
keywordFlags = band(skillCfg.keywordFlags, bnot(KeywordFlag.Hit)),
}
+ if bor(dotCfg.flags, ModFlag.Area) == dotCfg.flags and not skillData.dotIsArea then
+ dotCfg.flags = band(dotCfg.flags, bnot(ModFlag.Area))
+ end
+ if bor(dotCfg.flags, ModFlag.Projectile) == dotCfg.flags and not skillData.dotIsProjectile then
+ dotCfg.flags = band(dotCfg.flags, bnot(ModFlag.Projectile))
+ end
+ if bor(dotCfg.flags, ModFlag.Spell) == dotCfg.flags and not skillData.dotIsSpell then
+ dotCfg.flags = band(dotCfg.flags, bnot(ModFlag.Spell))
+ end
+ if bor(dotCfg.flags, ModFlag.Attack) == dotCfg.flags and not skillData.dotIsAttack then
+ dotCfg.flags = band(dotCfg.flags, bnot(ModFlag.Attack))
+ end
-- spell_damage_modifiers_apply_to_skill_dot does not apply to enemy damage taken
local dotTakenCfg = copyTable(dotCfg, true)
@@ -3522,11 +3587,19 @@ function calcs.offence(env, actor, activeSkill)
end
end
if skillModList:Flag(nil, "DotCanStack") then
- output.TotalDot = output.TotalDotInstance * output.Speed * output.Duration * (skillData.dpsMultiplier or 1)
+ local speed = output.Speed
+ -- Check if skill is being triggered via Mine (e.g., Blastchain Mine Support) or Trap
+ -- if "yes", you cannot use output.Speed but rather should use output.MineLayingSpeed or output.TrapThrowingSpeed
+ if band(dotCfg.keywordFlags, KeywordFlag.Mine) ~= 0 then
+ speed = output.MineLayingSpeed
+ elseif band(dotCfg.keywordFlags, KeywordFlag.Trap) ~= 0 then
+ speed = output.TrapThrowingSpeed
+ end
+ output.TotalDot = output.TotalDotInstance * speed * output.Duration * (skillData.dpsMultiplier or 1)
if breakdown then
breakdown.TotalDot = {
s_format("%.1f ^8(Damage per Instance)", output.TotalDotInstance),
- s_format("x %.2f ^8(hits per second)", output.Speed),
+ s_format("x %.2f ^8(hits per second)", speed),
s_format("x %.2f ^8(skill duration)", output.Duration),
}
if skillData.dpsMultiplier then
@@ -3591,9 +3664,13 @@ function calcs.offence(env, actor, activeSkill)
end
output.TotalDotDPS = (output.TotalDot or 0) + (output.TotalPoisonDPS or 0) + (output.TotalIgniteDPS or output.IgniteDPS or 0) + (output.BleedDPS or 0) + (output.DecayDPS or 0)
if skillFlags.impale then
- output.ImpaleHit = ((output.MainHand.PhysicalHitAverage or output.OffHand.PhysicalHitAverage) + (output.OffHand.PhysicalHitAverage or output.MainHand.PhysicalHitAverage)) / 2 * (1-output.CritChance/100) + ((output.MainHand.PhysicalCritAverage or output.OffHand.PhysicalCritAverage) + (output.OffHand.PhysicalCritAverage or output.MainHand.PhysicalCritAverage)) / 2 * (output.CritChance/100)
- if skillData.doubleHitsWhenDualWielding and skillFlags.bothWeaponAttack then
- output.ImpaleHit = output.ImpaleHit * 2
+ if skillFlags.attack then
+ output.ImpaleHit = ((output.MainHand.PhysicalHitAverage or output.OffHand.PhysicalHitAverage) + (output.OffHand.PhysicalHitAverage or output.MainHand.PhysicalHitAverage)) / 2 * (1-output.CritChance/100) + ((output.MainHand.PhysicalCritAverage or output.OffHand.PhysicalCritAverage) + (output.OffHand.PhysicalCritAverage or output.MainHand.PhysicalCritAverage)) / 2 * (output.CritChance/100)
+ if skillData.doubleHitsWhenDualWielding and skillFlags.bothWeaponAttack then
+ output.ImpaleHit = output.ImpaleHit * 2
+ end
+ else
+ output.ImpaleHit = output.PhysicalHitAverage * (1-output.CritChance/100) + output.PhysicalCritAverage * (output.CritChance/100)
end
output.ImpaleDPS = output.ImpaleHit * ((output.ImpaleModifier or 1) - 1) * output.HitChance / 100 * (skillData.dpsMultiplier or 1)
if skillData.showAverage then
@@ -3609,7 +3686,7 @@ function calcs.offence(env, actor, activeSkill)
t_insert(breakdown.ImpaleDPS, s_format("%.2f ^8(average physical hit)", output.ImpaleHit))
t_insert(breakdown.ImpaleDPS, s_format("x %.2f ^8(chance to hit)", output.HitChance / 100))
if skillFlags.notAverage then
- t_insert(breakdown.ImpaleDPS, output.HitSpeed and s_format("x %.2f ^8(hit rate)", output.HitSpeed) or s_format("x %.2f ^8(attack rate)", output.Speed))
+ t_insert(breakdown.ImpaleDPS, output.HitSpeed and s_format("x %.2f ^8(hit rate)", output.HitSpeed) or s_format("x %.2f ^8(%s rate)", output.Speed, skillFlags.attack and "attack" or "cast"))
end
t_insert(breakdown.ImpaleDPS, s_format("x %.2f ^8(impale damage multiplier)", ((output.ImpaleModifier or 1) - 1)))
if skillData.dpsMultiplier then
@@ -3618,4 +3695,5 @@ function calcs.offence(env, actor, activeSkill)
t_insert(breakdown.ImpaleDPS, s_format("= %.1f", output.ImpaleDPS))
end
end
+ output.CombinedDPS = output.CombinedDPS * output.CullMultiplier
end
diff --git b/Modules/CalcPerform.lua a/Modules/CalcPerform.lua
index 7c2c1e8f..e5e6ec94 100644
--- b/Modules/CalcPerform.lua
+++ a/Modules/CalcPerform.lua
@@ -332,51 +332,77 @@ local function doActorMisc(env, actor)
output.BlitzChargesMax = modDB:Sum("BASE", nil, "BlitzChargesMax")
output.InspirationChargesMax = modDB:Sum("BASE", nil, "InspirationChargesMax")
output.CrabBarriersMax = modDB:Sum("BASE", nil, "CrabBarriersMax")
+ output.BrutalChargesMin = modDB:Flag(nil, "MinimumEnduranceChargesEqualsMinimumBrutalCharges") and output.EnduranceChargesMin or 0
+ output.BrutalChargesMax = modDB:Flag(nil, "MaximumEnduranceChargesEqualsMaximumBrutalCharges") and output.EnduranceChargesMax or 0
+ output.AbsorptionChargesMin = modDB:Flag(nil, "MinimumPowerChargesEqualsMinimumAbsorptionCharges") and output.PowerChargesMin or 0
+ output.AbsorptionChargesMax = modDB:Flag(nil, "MaximumPowerChargesEqualsMaximumAbsorptionCharges") and output.PowerChargesMax or 0
+ output.AfflictionChargesMin = modDB:Flag(nil, "MinimumFrenzyChargesEqualsMinimumAfflictionCharges") and output.FrenzyChargesMin or 0
+ output.AfflictionChargesMax = modDB:Flag(nil, "MaximumFrenzyChargesEqualsMaximumAfflictionCharges") and output.FrenzyChargesMax or 0
+
+ -- Initialize Charges
+ output.PowerCharges = 0
+ output.FrenzyCharges = 0
+ output.EnduranceCharges = 0
+ output.SiphoningCharges = 0
+ output.ChallengerCharges = 0
+ output.BlitzCharges = 0
+ output.InspirationCharges = 0
+ output.GhostShrouds = 0
+ output.BrutalCharges = 0
+ output.AbsorptionCharges = 0
+ output.AfflictionCharges = 0
+
+ -- Conditionally over-write Charge values
if modDB:Flag(nil, "UsePowerCharges") then
output.PowerCharges = modDB:Override(nil, "PowerCharges") or output.PowerChargesMax
- else
+ end
+ if modDB:Flag(nil, "PowerChargesConvertToAbsorptionCharges") then
+ -- we max with possible Power Charge Override from Config since Absorption Charges won't have their own config entry
+ -- and are converted from Power Charges
+ output.AbsorptionCharges = m_max(output.PowerCharges, m_min(output.AbsorptionChargesMax, output.AbsorptionChargesMin))
output.PowerCharges = 0
+ else
+ output.PowerCharges = m_max(output.PowerCharges, m_min(output.PowerChargesMax, output.PowerChargesMin))
end
- output.PowerCharges = m_max(output.PowerCharges, m_min(output.PowerChargesMax, output.PowerChargesMin))
output.RemovablePowerCharges = m_max(output.PowerCharges - output.PowerChargesMin, 0)
if modDB:Flag(nil, "UseFrenzyCharges") then
output.FrenzyCharges = modDB:Override(nil, "FrenzyCharges") or output.FrenzyChargesMax
- else
+ end
+ if modDB:Flag(nil, "FrenzyChargesConvertToAfflictionCharges") then
+ -- we max with possible Power Charge Override from Config since Absorption Charges won't have their own config entry
+ -- and are converted from Power Charges
+ output.AfflictionCharges = m_max(output.FrenzyCharges, m_min(output.AfflictionChargesMax, output.AfflictionChargesMin))
output.FrenzyCharges = 0
+ else
+ output.FrenzyCharges = m_max(output.FrenzyCharges, m_min(output.FrenzyChargesMax, output.FrenzyChargesMin))
end
- output.FrenzyCharges = m_max(output.FrenzyCharges, m_min(output.FrenzyChargesMax, output.FrenzyChargesMin))
output.RemovableFrenzyCharges = m_max(output.FrenzyCharges - output.FrenzyChargesMin, 0)
if modDB:Flag(nil, "UseEnduranceCharges") then
output.EnduranceCharges = modDB:Override(nil, "EnduranceCharges") or output.EnduranceChargesMax
- else
+ end
+ if modDB:Flag(nil, "EnduranceChargesConvertToBrutalCharges") then
+ -- we max with possible Endurance Charge Override from Config since Brutal Charges won't have their own config entry
+ -- and are converted from Endurance Charges
+ output.BrutalCharges = m_max(output.EnduranceCharges, m_min(output.BrutalChargesMax, output.BrutalChargesMin))
output.EnduranceCharges = 0
+ else
+ output.EnduranceCharges = m_max(output.EnduranceCharges, m_min(output.EnduranceChargesMax, output.EnduranceChargesMin))
end
- output.EnduranceCharges = m_max(output.EnduranceCharges, m_min(output.EnduranceChargesMax, output.EnduranceChargesMin))
output.RemovableEnduranceCharges = m_max(output.EnduranceCharges - output.EnduranceChargesMin, 0)
if modDB:Flag(nil, "UseSiphoningCharges") then
output.SiphoningCharges = modDB:Override(nil, "SiphoningCharges") or output.SiphoningChargesMax
- else
- output.SiphoningCharges = 0
end
if modDB:Flag(nil, "UseChallengerCharges") then
output.ChallengerCharges = modDB:Override(nil, "ChallengerCharges") or output.ChallengerChargesMax
- else
- output.ChallengerCharges = 0
end
if modDB:Flag(nil, "UseBlitzCharges") then
output.BlitzCharges = modDB:Override(nil, "BlitzCharges") or output.BlitzChargesMax
- else
- output.BlitzCharges = 0
end
if modDB:Flag(nil, "UseInspirationCharges") then
output.InspirationCharges = modDB:Override(nil, "InspirationCharges") or output.InspirationChargesMax
- else
- output.InspirationCharges = 0
end
if modDB:Flag(nil, "UseGhostShrouds") then
output.GhostShrouds = modDB:Override(nil, "GhostShrouds") or 3
- else
- output.GhostShrouds = 0
end
if modDB:Flag(nil, "CryWolfMinimumPower") and modDB:Sum("BASE", nil, "WarcryPower") < 10 then
modDB:NewMod("WarcryPower", "OVERRIDE", 10, "Minimum Warcry Power from CryWolf")
@@ -399,6 +425,9 @@ local function doActorMisc(env, actor)
modDB.multipliers["InspirationCharge"] = output.InspirationCharges
modDB.multipliers["GhostShroud"] = output.GhostShrouds
modDB.multipliers["CrabBarrier"] = output.CrabBarriers
+ modDB.multipliers["BrutalCharge"] = output.BrutalCharges
+ modDB.multipliers["AbsorptionCharge"] = output.AbsorptionCharges
+ modDB.multipliers["AfflictionCharge"] = output.AfflictionCharges
-- Process enemy modifiers
for _, value in ipairs(modDB:List(nil, "EnemyModifier")) do
@@ -465,6 +494,9 @@ local function doActorMisc(env, actor)
modDB:NewMod("Speed", "INC", 20, "Her Embrace")
modDB:NewMod("MovementSpeed", "INC", 20, "Her Embrace")
end
+ if modDB:Flag(nil, "Condition:PhantasmalMight") then
+ modDB.multipliers["BuffOnSelf"] = (modDB.multipliers["BuffOnSelf"] or 0) + (output.ActivePhantasmLimit or 1) - 1 -- slight hack to not double count the initial buff
+ end
if modDB:Flag(nil, "Elusive") then
local effect = 1 + modDB:Sum("INC", nil, "ElusiveEffect", "BuffEffectOnSelf") / 100
condList["Elusive"] = true
@@ -658,19 +690,6 @@ function calcs.perform(env)
end
for _, activeSkill in ipairs(env.player.activeSkillList) do
- if activeSkill.activeEffect.grantedEffect.name == "Herald of Purity" then
- local limit = activeSkill.skillModList:Sum("BASE", nil, "ActiveSentinelOfPurityLimit")
- output.ActiveSentinelOfPurityLimit = m_max(limit, output.ActiveSentinelOfPurityLimit or 0)
- end
- if activeSkill.skillFlags.golem then
- local limit = activeSkill.skillModList:Sum("BASE", nil, "ActiveGolemLimit")
- output.ActiveGolemLimit = m_max(limit, output.ActiveGolemLimit or 0)
- end
- if activeSkill.skillFlags.totem then
- local limit = env.player.mainSkill.skillModList:Sum("BASE", env.player.mainSkill.skillCfg, "ActiveTotemLimit", "ActiveBallistaLimit" )
- output.ActiveTotemLimit = m_max(limit, output.ActiveTotemLimit or 0)
- output.TotemsSummoned = modDB:Override(nil, "TotemsSummoned") or output.ActiveTotemLimit
- end
if activeSkill.skillFlags.brand then
local attachLimit = env.player.mainSkill.skillModList:Sum("BASE", env.player.mainSkill.skillCfg, "BrandsAttachedLimit")
if activeSkill.activeEffect.grantedEffect.name == "Arcanist Brand" then
@@ -686,9 +705,9 @@ function calcs.perform(env)
modDB.multipliers["BrandsAttachedToEnemy"] = m_max(actual, modDB.multipliers["BrandsAttachedToEnemy"] or 0)
enemyDB.multipliers["BrandsAttached"] = m_max(actual, enemyDB.multipliers["BrandsAttached"] or 0)
end
- -- The actual hexes as opposed to hex related skills all have the curse flag. Type31 is to remove blasphemy
+ -- The actual hexes as opposed to hex related skills all have the curse flag. DamageCannotBeReflected is to remove blasphemy
-- Note that this doesn't work for triggers yet, insufficient support
- if activeSkill.skillFlags.hex and activeSkill.skillFlags.curse and not activeSkill.skillTypes[SkillType.Type31] then
+ if activeSkill.skillFlags.hex and activeSkill.skillFlags.curse and not activeSkill.skillTypes[SkillType.DamageCannotBeReflected] then
local hexDoom = modDB:Sum("BASE", nil, "Multiplier:HexDoomStack")
local maxDoom = activeSkill.skillModList:Sum("BASE", nil, "MaxDoom") or 30
local doomEffect = activeSkill.skillModList:More(nil, "DoomEffect")
@@ -704,13 +723,18 @@ function calcs.perform(env)
end
output.BonechillEffect = m_max(output.BonechillEffect or 0, modDB:Override(nil, "BonechillEffect") or output.BonechillDotEffect or 0)
end
- if (activeSkill.activeEffect.grantedEffect.name == "Vaal Lightning Trap" or activeSkill.activeEffect.grantedEffect.name == "Shock Ground") then
+ if (activeSkill.activeEffect.grantedEffect.name == "Vaal Lightning Trap" or activeSkill.activeEffect.grantedEffect.name == "Shock Ground") then
modDB:NewMod("ShockOverride", "BASE", activeSkill.skillModList:Sum("BASE", nil, "ShockedGroundEffect"), "Shocked Ground", { type = "ActorCondition", actor = "enemy", var = "OnShockedGround" } )
end
- if activeSkill.activeEffect.grantedEffect.name == "Summon Skitterbots" and not activeSkill.skillModList:Flag(nil, "SkitterbotsCannotShock") then
- local effect = activeSkill.skillModList:Sum("INC", { source = "Skill" }, "EnemyShockEffect")
- modDB:NewMod("ShockOverride", "BASE", 15 * (1 + effect / 100), "Summon Skitterbots")
- enemyDB:NewMod("Condition:Shocked", "FLAG", true, "Summon Skitterbots")
+ if activeSkill.activeEffect.grantedEffect.name == "Summon Skitterbots" then
+ if not activeSkill.skillModList:Flag(nil, "SkitterbotsCannotShock") then
+ local effect = activeSkill.skillModList:Sum("INC", { source = "Skill" }, "EnemyShockEffect")
+ modDB:NewMod("ShockOverride", "BASE", 15 * (1 + effect / 100), "Summon Skitterbots")
+ enemyDB:NewMod("Condition:Shocked", "FLAG", true, "Summon Skitterbots")
+ end
+ if not activeSkill.skillModList:Flag(nil, "SkitterbotsCannotChill") then
+ enemyDB:NewMod("Condition:Chilled", "FLAG", true, "Summon Skitterbots")
+ end
end
for _, damageType in ipairs({"Physical", "Lightning", "Cold", "Fire", "Chaos"}) do
if activeSkill.activeEffect.grantedEffect.name == damageType.." Aegis" then
@@ -752,21 +776,9 @@ function calcs.perform(env)
env.player.modDB:NewMod("GlobalWarcryCount", "BASE", numWarcries)
modDB:NewMod("AlreadyGlobalWarcryCooldown", "FLAG", true, "Config") -- Prevents effect from applying multiple times
end
- if activeSkill.activeEffect.grantedEffect.name == "Summon Skeletons" then
- local limit = env.player.mainSkill.skillModList:Sum("BASE", env.player.mainSkill.skillCfg, "ActiveSkeletonLimit")
- output.ActiveSkeletonLimit = m_max(limit, output.ActiveSkeletonLimit or 0)
- elseif activeSkill.activeEffect.grantedEffect.name == "Raise Zombie" then
- local limit = env.player.mainSkill.skillModList:Sum("BASE", env.player.mainSkill.skillCfg, "ActiveZombieLimit")
- output.ActiveZombieLimit = m_max(limit, output.ActiveZombieLimit or 0)
- elseif activeSkill.activeEffect.grantedEffect.name == "Summon Raging Spirit" then
- local limit = env.player.mainSkill.skillModList:Sum("BASE", env.player.mainSkill.skillCfg, "ActiveRagingSpiritLimit")
- output.ActiveRagingSpiritLimit = m_max(limit, output.ActiveRagingSpiritLimit or 0)
- elseif activeSkill.activeEffect.grantedEffect.name == "Summoned Phantasm" then
- local limit = env.player.mainSkill.skillModList:Sum("BASE", env.player.mainSkill.skillCfg, "ActivePhantasmLimit")
- output.ActivePhantasmLimit = m_max(limit, output.ActivePhantasmLimit or 0)
- elseif activeSkill.activeEffect.grantedEffect.name == "Raise Spectre" then
- local limit = env.player.mainSkill.skillModList:Sum("BASE", env.player.mainSkill.skillCfg, "ActiveSpectreLimit")
- output.ActiveSpectreLimit = m_max(limit, output.ActiveSpectreLimit or 0)
+ if activeSkill.minion and activeSkill.minion.minionData and activeSkill.minion.minionData.limit then
+ local limit = activeSkill.skillModList:Sum("BASE", nil, activeSkill.minion.minionData.limit)
+ output[activeSkill.minion.minionData.limit] = m_max(limit, output[activeSkill.minion.minionData.limit] or 0)
end
if env.mode_buffs and activeSkill.skillFlags.warcry then
local extraExertions = activeSkill.skillModList:Sum("BASE", nil, "ExtraExertedAttacks") or 0
@@ -781,9 +793,13 @@ function calcs.perform(env)
if modDB:Flag(nil, "WarcryShareCooldown") then
uptime = m_min(full_duration / (actual_cooldown + (globalCooldown - actual_cooldown) / globalCount), 1)
end
+ if modDB:Flag(nil, "Condition:WarcryMaxHit") then
+ uptime = 1;
+ end
if activeSkill.activeEffect.grantedEffect.name == "Ancestral Cry" and not modDB:Flag(nil, "AncestralActive") then
local ancestralArmour = activeSkill.skillModList:Sum("BASE", env.player.mainSkill.skillCfg, "AncestralArmourPer5MP")
local ancestralArmourMax = activeSkill.skillModList:Sum("BASE", env.player.mainSkill.skillCfg, "AncestralArmourMax")
+ local ancestralArmourIncrease = activeSkill.skillModList:Sum("INC", env.player.mainSkill.skillCfg, "AncestralArmourMax")
local ancestralStrikeRange = activeSkill.skillModList:Sum("BASE", env.player.mainSkill.skillCfg, "AncestralMeleeWeaponRangePer5MP")
local ancestralStrikeRangeMax = m_floor(6 * buff_inc)
env.player.modDB:NewMod("NumAncestralExerts", "BASE", activeSkill.skillModList:Sum("BASE", env.player.mainSkill.skillCfg, "AncestralExertedAttacks") + extraExertions)
@@ -791,8 +807,12 @@ function calcs.perform(env)
if warcryPowerBonus ~= 0 then
ancestralArmour = m_floor(ancestralArmour * warcryPowerBonus * buff_inc) / warcryPowerBonus
ancestralStrikeRange = m_floor(ancestralStrikeRange * warcryPowerBonus * buff_inc) / warcryPowerBonus
+ else
+ -- Since no buff happens, you don't get the divergent increase.
+ ancestralArmourIncrease = 0
end
- env.player.modDB:NewMod("Armour", "BASE", 257 * uptime, "Ancestral Cry", { type = "Multiplier", var = "WarcryPower", div = 5, limit = ancestralArmourMax, limitTotal = true })
+ env.player.modDB:NewMod("Armour", "BASE", ancestralArmour * uptime, "Ancestral Cry", { type = "Multiplier", var = "WarcryPower", div = 5, limit = ancestralArmourMax, limitTotal = true })
+ env.player.modDB:NewMod("Armour", "INC", ancestralArmourIncrease * uptime, "Ancestral Cry")
env.player.modDB:NewMod("MeleeWeaponRange", "BASE", ancestralStrikeRange * uptime, "Ancestral Cry", { type = "Multiplier", var = "WarcryPower", div = 5, limit = ancestralStrikeRangeMax, limitTotal = true })
modDB:NewMod("AncestralActive", "FLAG", true) -- Prevents effect from applying multiple times
elseif activeSkill.activeEffect.grantedEffect.name == "Enduring Cry" and not modDB:Flag(nil, "EnduringActive") then
@@ -820,7 +840,6 @@ function calcs.perform(env)
env.player.modDB:NewMod("EnemyPhysicalDamageReduction", "BASE", -intimidatingOverwhelmEffect * uptime, "Intimidating Cry Buff", { type = "Multiplier", var = "WarcryPower", div = 5, limit = 6 })
modDB:NewMod("IntimidatingActive", "FLAG", true) -- Prevents effect from applying multiple times
elseif activeSkill.activeEffect.grantedEffect.name == "Rallying Cry" and not modDB:Flag(nil, "RallyingActive") then
- local extraExertions = activeSkill.skillModList:Sum("BASE", nil, "ExtraExertedAttacks") or 0
env.player.modDB:NewMod("NumRallyingExerts", "BASE", activeSkill.skillModList:Sum("BASE", env.player.mainSkill.skillCfg, "RallyingExertedAttacks") + extraExertions)
env.player.modDB:NewMod("RallyingExertMoreDamagePerAlly", "BASE", activeSkill.skillModList:Sum("BASE", env.player.mainSkill.skillCfg, "RallyingCryExertDamageBonus"))
local rallyingWeaponEffect = activeSkill.skillModList:Sum("BASE", env.player.mainSkill.skillCfg, "RallyingCryAllyDamageBonusPer5Power")
@@ -892,6 +911,17 @@ function calcs.perform(env)
activeSkill.skillModList:NewMod("CastWhileChannellingSpellsLinked", "BASE", spellCount, "Skill")
activeSkill.skillData.triggerTime = trigTime
end
+ if activeSkill.skillData.triggeredOnDeath and not activeSkill.skillFlags.minion then
+ activeSkill.skillData.triggered = true
+ for _, value in ipairs(activeSkill.skillModList:Tabulate("INC", env.player.mainSkill.skillCfg, "TriggeredDamage")) do
+ activeSkill.skillModList:NewMod("Damage", "INC", value.mod.value, value.mod.source, value.mod.flags, value.mod.keywordFlags, unpack(value.mod))
+ end
+ for _, value in ipairs(activeSkill.skillModList:Tabulate("MORE", env.player.mainSkill.skillCfg, "TriggeredDamage")) do
+ activeSkill.skillModList:NewMod("Damage", "MORE", value.mod.value, value.mod.source, value.mod.flags, value.mod.keywordFlags, unpack(value.mod))
+ end
+ -- Set trigger time to 1 min in ms ( == 6000 ). Technically any large value would do.
+ activeSkill.skillData.triggerTime = 60 * 1000
+ end
end
local breakdown
@@ -1214,7 +1244,7 @@ function calcs.perform(env)
srcList:ScaleAddList(extraAuraModList, (1 + inc / 100) * more)
mergeBuff(srcList, buffs, buff.name)
end
- if env.minion and not modDB:Flag(nil, "SelfAurasCannotAffectAllies") then
+ if env.minion and not (modDB:Flag(nil, "SelfAurasCannotAffectAllies") or modDB:Flag(nil, "SelfAuraSkillsCannotAffectAllies")) then
activeSkill.minionBuffSkill = true
affectedByAura[env.minion] = true
env.minion.modDB.conditions["AffectedBy"..buff.name:gsub(" ","")] = true
@@ -1255,12 +1285,13 @@ function calcs.perform(env)
mergeBuff(srcList, debuffs, buff.name)
end
elseif buff.type == "Curse" or buff.type == "CurseBuff" then
- if env.mode_effective and (not enemyDB:Flag(nil, "Hexproof") or modDB:Flag(nil, "CursesIgnoreHexproof")) then
+ local mark = activeSkill.skillTypes[SkillType.Mark]
+ if env.mode_effective and (not enemyDB:Flag(nil, "Hexproof") or modDB:Flag(nil, "CursesIgnoreHexproof")) or mark then
local curse = {
name = buff.name,
fromPlayer = true,
priority = activeSkill.skillTypes[SkillType.Aura] and 3 or 1,
- isMark = activeSkill.skillTypes[SkillType.Mark],
+ isMark = mark,
}
local inc = skillModList:Sum("INC", skillCfg, "CurseEffect") + enemyDB:Sum("INC", nil, "CurseEffectOnSelf")
local more = skillModList:More(skillCfg, "CurseEffect")
@@ -1335,7 +1366,7 @@ function calcs.perform(env)
end
end
elseif buff.type == "Curse" then
- if env.mode_effective and activeSkill.skillData.enable and not enemyDB:Flag(nil, "Hexproof") then
+ if env.mode_effective and activeSkill.skillData.enable and (not enemyDB:Flag(nil, "Hexproof") or activeSkill.skillTypes[SkillType.Mark]) then
local curse = {
name = buff.name,
priority = 1,
@@ -1568,7 +1599,6 @@ function calcs.perform(env)
-- Calculates maximum Shock, then applies the strongest Shock effect to the enemy
if (enemyDB:Sum("BASE", nil, "ShockVal") > 0 or modDB:Sum(nil, "ShockBase", "ShockOverride")) and not enemyDB:Flag(nil, "Condition:AlreadyShocked") then
- local baseShock = (modDB:Override(nil, "ShockBase") or 0) * (1 + modDB:Sum("INC", nil, "EnemyShockEffect") / 100)
local overrideShock = 0
for i, value in ipairs(modDB:Tabulate("BASE", { }, "ShockBase", "ShockOverride")) do
local mod = value.mod
@@ -1589,6 +1619,28 @@ function calcs.perform(env)
enemyDB:NewMod("Condition:AlreadyShocked", "FLAG", true, { type = "Condition", var = "Shocked"} ) -- Prevents Shock from applying doubly for minions
end
+ -- Calculates maximum Scorch, then applies the strongest Scorch effect to the enemy
+ if (enemyDB:Sum("BASE", nil, "ScorchVal") > 0 or modDB:Sum(nil, "ScorchBase", "ScorchOverride")) and not enemyDB:Flag(nil, "Condition:AlreadyScorched") then
+ local overrideScorch = 0
+ for i, value in ipairs(modDB:Tabulate("BASE", { }, "ScorchBase", "ScorchOverride")) do
+ local mod = value.mod
+ local inc = 1 + modDB:Sum("INC", nil, "EnemyScorchEffect") / 100
+ local effect = mod.value
+ if mod.name == "ScorchOverride" then
+ enemyDB:NewMod("Condition:Scorched", "FLAG", true, mod.source)
+ end
+ if mod.name == "ScorchBase" then
+ effect = effect * inc
+ modDB:NewMod("ScorchOverride", "BASE", effect, mod.source, mod.flags, mod.keywordFlags, unpack(mod))
+ end
+ overrideScorch = m_max(overrideScorch or 0, effect or 0)
+ end
+ output.MaximumScorch = modDB:Override(nil, "ScorchMax") or 50
+ output.CurrentScorch = m_floor(m_min(m_max(overrideScorch, enemyDB:Sum("BASE", nil, "ScorchVal")), output.MaximumScorch))
+ enemyDB:NewMod("ElementalResist", "BASE", -m_floor(output.CurrentScorch), "Scorch", { type = "Condition", var = "Scorched"} )
+ enemyDB:NewMod("Condition:AlreadyScorched", "FLAG", true, { type = "Condition", var = "Scorched"} ) -- Prevents Scorch from applying doubly for minions
+ end
+
-- Check for extra auras
for _, value in ipairs(modDB:List(nil, "ExtraAura")) do
local modList = { value.mod }
@@ -1640,6 +1692,14 @@ function calcs.perform(env)
end
doActorMisc(env, env.enemy)
+ for _, activeSkill in ipairs(env.player.activeSkillList) do
+ if activeSkill.skillFlags.totem then
+ local limit = env.player.mainSkill.skillModList:Sum("BASE", env.player.mainSkill.skillCfg, "ActiveTotemLimit", "ActiveBallistaLimit" )
+ output.ActiveTotemLimit = m_max(limit, output.ActiveTotemLimit or 0)
+ output.TotemsSummoned = modDB:Override(nil, "TotemsSummoned") or output.ActiveTotemLimit
+ end
+ end
+
-- Apply exposures
for _, element in pairs({"Fire", "Cold", "Lightning"}) do
local min = math.huge
diff --git b/Modules/CalcSections-2_6.lua a/Modules/CalcSections-2_6.lua
deleted file mode 100644
index a6076e72..00000000
--- b/Modules/CalcSections-2_6.lua
+++ /dev/null
@@ -1,885 +0,0 @@
--- Path of Building
---
--- Module: Calc Sections
--- List of sections for the Calcs tab
---
-
-return {
-{ 3, "HitDamage", 1, "Skill Hit Damage", colorCodes.OFFENCE, {
- extra = "{output:DisplayDamage}",
- flag = "hit",
- colWidth = 95,
- {
- { format = "All Types:", },
- { format = "Physical:" },
- { format = "Lightning:" },
- { format = "Cold:" },
- { format = "Fire:" },
- { format = "Chaos:" },
- },
- { label = "Added Min",
- { },
- { format = "{0:mod:1}", { modName = "PhysicalMin", modType = "BASE", cfg = "skill" }, },
- { format = "{0:mod:1}", { modName = "LightningMin", modType = "BASE", cfg = "skill" }, },
- { format = "{0:mod:1}", { modName = "ColdMin", modType = "BASE", cfg = "skill" }, },
- { format = "{0:mod:1}", { modName = "FireMin", modType = "BASE", cfg = "skill" }, },
- { format = "{0:mod:1}", { modName = "ChaosMin", modType = "BASE", cfg = "skill" }, },
- },
- { label = "Added Max",
- { },
- { format = "{0:mod:1}", { modName = "PhysicalMax", modType = "BASE", cfg = "skill" }, },
- { format = "{0:mod:1}", { modName = "LightningMax", modType = "BASE", cfg = "skill" }, },
- { format = "{0:mod:1}", { modName = "ColdMax", modType = "BASE", cfg = "skill" }, },
- { format = "{0:mod:1}", { modName = "FireMax", modType = "BASE", cfg = "skill" }, },
- { format = "{0:mod:1}", { modName = "ChaosMax", modType = "BASE", cfg = "skill" }, },
- },
- -- Skill Hit Damage
- { label = "Total Increased", notFlag = "attack",
- { format = "{0:mod:1}%", { modName = "Damage", modType = "INC", cfg = "skill" }, },
- { format = "{0:mod:1}%", { modName = "PhysicalDamage", modType = "INC", cfg = "skill" }, },
- { format = "{0:mod:1}%", { modName = { "LightningDamage", "ElementalDamage" }, modType = "INC", cfg = "skill" }, },
- { format = "{0:mod:1}%", { modName = { "ColdDamage", "ElementalDamage" }, modType = "INC", cfg = "skill" }, },
- { format = "{0:mod:1}%", { modName = { "FireDamage", "ElementalDamage" }, modType = "INC", cfg = "skill" }, },
- { format = "{0:mod:1}%", { modName = "ChaosDamage", modType = "INC", cfg = "skill" }, },
- },
- { label = "Total More", notFlag = "attack",
- { format = "{0:mod:1}%", { modName = "Damage", modType = "MORE", cfg = "skill" }, },
- { format = "{0:mod:1}%", { modName = "PhysicalDamage", modType = "MORE", cfg = "skill" }, },
- { format = "{0:mod:1}%", { modName = { "LightningDamage", "ElementalDamage" }, modType = "MORE", cfg = "skill" }, },
- { format = "{0:mod:1}%", { modName = { "ColdDamage", "ElementalDamage" }, modType = "MORE", cfg = "skill" }, },
- { format = "{0:mod:1}%", { modName = { "FireDamage", "ElementalDamage" }, modType = "MORE", cfg = "skill" }, },
- { format = "{0:mod:1}%", { modName = "ChaosDamage", modType = "MORE", cfg = "skill" }, },
- },
- { label = "Effective DPS Mod", notFlag = "attack", flag = "effective",
- { },
- { format = "x {3:output:PhysicalEffMult}",
- { breakdown = "PhysicalEffMult" },
- { label = "Enemy modifiers", modName = { "DamageTaken", "PhysicalDamageTaken" }, enemy = true },
- },
- { format = "x {3:output:LightningEffMult}",
- { breakdown = "LightningEffMult" },
- { label = "Player modifiers", modName = { "LightningPenetration", "ElementalPenetration" }, cfg = "skill" },
- { label = "Enemy modifiers", modName = { "DamageTaken", "LightningDamageTaken", "ElementalDamageTaken", "LightningResist", "ElementalResist" }, enemy = true },
- },
- { format = "x {3:output:ColdEffMult}",
- { breakdown = "ColdEffMult" },
- { label = "Player modifiers", modName = { "ColdPenetration", "ElementalPenetration" }, cfg = "skill" },
- { label = "Enemy modifiers", modName = { "DamageTaken", "ColdDamageTaken", "ElementalDamageTaken", "ColdResist", "ElementalResist" }, enemy = true },
- },
- { format = "x {3:output:FireEffMult}",
- { breakdown = "FireEffMult" },
- { label = "Player modifiers", modName = { "FirePenetration", "ElementalPenetration" }, cfg = "skill" },
- { label = "Enemy modifiers", modName = { "DamageTaken", "FireDamageTaken", "ElementalDamageTaken", "FireResist", "ElementalResist" }, enemy = true },
- },
- { format = "x {3:output:ChaosEffMult}",
- { breakdown = "ChaosEffMult" },
- { label = "Enemy modifiers", modName = { "DamageTaken", "ChaosDamageTaken", "ChaosResist" }, enemy = true },
- },
- },
- { label = "Skill Hit Damage", textSize = 12, notFlag = "attack",
- { format = "{0:output:TotalMin} to {0:output:TotalMax}", },
- { format = "{0:output:PhysicalMin} to {0:output:PhysicalMax}",
- { breakdown = "Physical" },
- { label = "Conversions", modType = "BASE", cfg = "skill", modName = { "SkillPhysicalDamageConvertToLightning", "SkillPhysicalDamageConvertToCold", "SkillPhysicalDamageConvertToFire", "SkillPhysicalDamageConvertToChaos", "PhysicalDamageConvertToLightning", "PhysicalDamageConvertToCold", "PhysicalDamageConvertToFire", "PhysicalDamageConvertToChaos", "PhysicalDamageGainAsLightning", "PhysicalDamageGainAsCold", "PhysicalDamageGainAsFire", "PhysicalDamageGainAsChaos" } },
- },
- { format = "{0:output:LightningMin} to {0:output:LightningMax}",
- { breakdown = "Lightning" },
- { label = "Conversions", modType = "BASE", cfg = "skill", modName = { "LightningDamageConvertToCold", "LightningDamageConvertToFire", "LightningDamageConvertToChaos", "ElementalDamageConvertToChaos", "LightningDamageGainAsCold", "LightningDamageGainAsFire", "LightningDamageGainAsChaos", "ElementalDamageGainAsChaos" } },
- },
- { format = "{0:output:ColdMin} to {0:output:ColdMax}",
- { breakdown = "Cold" },
- { label = "Conversions", modType = "BASE", cfg = "skill", modName = { "SkillColdDamageConvertToFire", "ColdDamageConvertToFire", "ColdDamageConvertToChaos", "ElementalDamageConvertToChaos", "ColdDamageGainAsFire", "ColdDamageGainAsChaos", "ElementalDamageGainAsChaos" } },
- },
- { format = "{0:output:FireMin} to {0:output:FireMax}",
- { breakdown = "Fire" },
- { label = "Conversions", modType = "BASE", cfg = "skill", modName = { "FireDamageConvertToChaos", "ElementalDamageConvertToChaos", "FireDamageGainAsChaos", "ElementalDamageGainAsChaos" } },
- },
- { format = "{0:output:ChaosMin} to {0:output:ChaosMax}",
- { breakdown = "Chaos" },
- },
- },
- { label = "Skill Average Hit", notFlag = "attack", { format = "{1:output:AverageHit}", { breakdown = "AverageHit" }, }, },
- -- Main Hand Hit Damage
- { label = "MH Total Increased", bgCol = colorCodes.MAINHANDBG, flag = "weapon1Attack",
- { format = "{0:mod:1}%", { modName = "Damage", modType = "INC", cfg = "weapon1" }, },
- { format = "{0:mod:1}%", { modName = "PhysicalDamage", modType = "INC", cfg = "weapon1" }, },
- { format = "{0:mod:1}%", { modName = { "LightningDamage", "ElementalDamage" }, modType = "INC", cfg = "weapon1" }, },
- { format = "{0:mod:1}%", { modName = { "ColdDamage", "ElementalDamage" }, modType = "INC", cfg = "weapon1" }, },
- { format = "{0:mod:1}%", { modName = { "FireDamage", "ElementalDamage" }, modType = "INC", cfg = "weapon1" }, },
- { format = "{0:mod:1}%", { modName = "ChaosDamage", modType = "INC", cfg = "weapon1" }, },
- },
- { label = "MH Total More", bgCol = colorCodes.MAINHANDBG, flag = "weapon1Attack",
- { format = "{0:mod:1}%", { modName = "Damage", modType = "MORE", cfg = "weapon1" }, },
- { format = "{0:mod:1}%", { modName = "PhysicalDamage", modType = "MORE", cfg = "weapon1" }, },
- { format = "{0:mod:1}%", { modName = { "LightningDamage", "ElementalDamage" }, modType = "MORE", cfg = "weapon1" }, },
- { format = "{0:mod:1}%", { modName = { "ColdDamage", "ElementalDamage" }, modType = "MORE", cfg = "weapon1" }, },
- { format = "{0:mod:1}%", { modName = { "FireDamage", "ElementalDamage" }, modType = "MORE", cfg = "weapon1" }, },
- { format = "{0:mod:1}%", { modName = "ChaosDamage", modType = "MORE", cfg = "weapon1" }, },
- },
- { label = "MH Eff. DPS Mod", bgCol = colorCodes.MAINHANDBG, flagList = {"weapon1Attack","effective"},
- { },
- { format = "x {3:output:MainHand.PhysicalEffMult}",
- { breakdown = "MainHand.PhysicalEffMult" },
- { label = "Enemy modifiers", modName = { "DamageTaken", "PhysicalDamageTaken" }, enemy = true },
- },
- { format = "x {3:output:MainHand.LightningEffMult}",
- { breakdown = "MainHand.LightningEffMult" },
- { label = "Player modifiers", modName = { "LightningPenetration", "ElementalPenetration" }, cfg = "weapon1" },
- { label = "Enemy modifiers", modName = { "DamageTaken", "LightningDamageTaken", "ElementalDamageTaken", "LightningResist", "ElementalResist" }, enemy = true },
- },
- { format = "x {3:output:MainHand.ColdEffMult}",
- { breakdown = "MainHand.ColdEffMult" },
- { label = "Player modifiers", modName = { "ColdPenetration", "ElementalPenetration" }, cfg = "weapon1" },
- { label = "Enemy modifiers", modName = { "DamageTaken", "ColdDamageTaken", "ElementalDamageTaken", "ColdResist", "ElementalResist" }, enemy = true },
- },
- { format = "x {3:output:MainHand.FireEffMult}",
- { breakdown = "MainHand.FireEffMult" },
- { label = "Player modifiers", modName = { "FirePenetration", "ElementalPenetration" }, cfg = "weapon1" },
- { label = "Enemy modifiers", modName = { "DamageTaken", "FireDamageTaken", "ElementalDamageTaken", "FireResist", "ElementalResist" }, enemy = true },
- },
- { format = "x {3:output:MainHand.ChaosEffMult}",
- { breakdown = "MainHand.ChaosEffMult" },
- { label = "Enemy modifiers", modName = { "DamageTaken", "ChaosDamageTaken", "ChaosResist" }, enemy = true },
- },
- },
- { label = "MH Hit Damage", bgCol = colorCodes.MAINHANDBG, textSize = 12, flag = "weapon1Attack",
- { format = "{0:output:MainHand.TotalMin} to {0:output:MainHand.TotalMax}", },
- { format = "{0:output:MainHand.PhysicalMin} to {0:output:MainHand.PhysicalMax}",
- { breakdown = "MainHand.Physical" },
- { label = "Conversions", modType = "BASE", cfg = "weapon1", modName = { "SkillPhysicalDamageConvertToLightning", "SkillPhysicalDamageConvertToCold", "SkillPhysicalDamageConvertToFire", "SkillPhysicalDamageConvertToChaos", "PhysicalDamageConvertToLightning", "PhysicalDamageConvertToCold", "PhysicalDamageConvertToFire", "PhysicalDamageConvertToChaos", "PhysicalDamageGainAsLightning", "PhysicalDamageGainAsCold", "PhysicalDamageGainAsFire", "PhysicalDamageGainAsChaos" } },
- },
- { format = "{0:output:MainHand.LightningMin} to {0:output:MainHand.LightningMax}",
- { breakdown = "MainHand.Lightning" },
- { label = "Conversions", modType = "BASE", cfg = "weapon1", modName = { "LightningDamageConvertToCold", "LightningDamageConvertToFire", "LightningDamageConvertToChaos", "ElementalDamageConvertToChaos", "LightningDamageGainAsCold", "LightningDamageGainAsFire", "LightningDamageGainAsChaos", "ElementalDamageGainAsChaos" } },
- },
- { format = "{0:output:MainHand.ColdMin} to {0:output:MainHand.ColdMax}",
- { breakdown = "MainHand.Cold" },
- { label = "Conversions", modType = "BASE", cfg = "weapon1", modName = { "SkillColdDamageConvertToFire", "ColdDamageConvertToFire", "ColdDamageConvertToChaos", "ElementalDamageConvertToChaos", "ColdDamageGainAsFire", "ColdDamageGainAsChaos", "ElementalDamageGainAsChaos" } },
- },
- { format = "{0:output:MainHand.FireMin} to {0:output:MainHand.FireMax}",
- { breakdown = "MainHand.Fire" },
- { label = "Conversions", modType = "BASE", cfg = "weapon1", modName = { "FireDamageConvertToChaos", "ElementalDamageConvertToChaos", "FireDamageGainAsChaos", "ElementalDamageGainAsChaos" } },
- },
- { format = "{0:output:MainHand.ChaosMin} to {0:output:MainHand.ChaosMax}",
- { breakdown = "MainHand.Chaos" },
- },
- },
- { label = "MH Average Hit", bgCol = colorCodes.MAINHANDBG, flag = "weapon1Attack", { format = "{1:output:MainHand.AverageHit}", { breakdown = "MainHand.AverageHit" }, }, },
- -- Off Hand Hit Damage
- { label = "OH Total Increased", bgCol = colorCodes.OFFHANDBG, flag = "weapon2Attack",
- { format = "{0:mod:1}%", { modName = "Damage", modType = "INC", cfg = "weapon2" }, },
- { format = "{0:mod:1}%", { modName = "PhysicalDamage", modType = "INC", cfg = "weapon2" }, },
- { format = "{0:mod:1}%", { modName = { "LightningDamage", "ElementalDamage" }, modType = "INC", cfg = "weapon2" }, },
- { format = "{0:mod:1}%", { modName = { "ColdDamage", "ElementalDamage" }, modType = "INC", cfg = "weapon2" }, },
- { format = "{0:mod:1}%", { modName = { "FireDamage", "ElementalDamage" }, modType = "INC", cfg = "weapon2" }, },
- { format = "{0:mod:1}%", { modName = "ChaosDamage", modType = "INC", cfg = "weapon2" }, },
- },
- { label = "OH Total More", bgCol = colorCodes.OFFHANDBG, flag = "weapon2Attack",
- { format = "{0:mod:1}%", { modName = "Damage", modType = "MORE", cfg = "weapon2" }, },
- { format = "{0:mod:1}%", { modName = "PhysicalDamage", modType = "MORE", cfg = "weapon2" }, },
- { format = "{0:mod:1}%", { modName = { "LightningDamage", "ElementalDamage" }, modType = "MORE", cfg = "weapon2" }, },
- { format = "{0:mod:1}%", { modName = { "ColdDamage", "ElementalDamage" }, modType = "MORE", cfg = "weapon2" }, },
- { format = "{0:mod:1}%", { modName = { "FireDamage", "ElementalDamage" }, modType = "MORE", cfg = "weapon2" }, },
- { format = "{0:mod:1}%", { modName = "ChaosDamage", modType = "MORE", cfg = "weapon2" }, },
- },
- { label = "OH Eff. DPS Mod", bgCol = colorCodes.OFFHANDBG, flagList = {"weapon2Attack","effective"},
- { },
- { format = "x {3:output:OffHand.PhysicalEffMult}",
- { breakdown = "OffHand.PhysicalEffMult" },
- { label = "Enemy modifiers", modName = { "DamageTaken", "PhysicalDamageTaken" }, enemy = true },
- },
- { format = "x {3:output:OffHand.LightningEffMult}",
- { breakdown = "OffHand.LightningEffMult" },
- { label = "Player modifiers", modName = { "LightningPenetration", "ElementalPenetration" }, cfg = "weapon2" },
- { label = "Enemy modifiers", modName = { "DamageTaken", "LightningDamageTaken", "ElementalDamageTaken", "LightningResist", "ElementalResist" }, enemy = true },
- },
- { format = "x {3:output:OffHand.ColdEffMult}",
- { breakdown = "OffHand.ColdEffMult" },
- { label = "Player modifiers", modName = { "ColdPenetration", "ElementalPenetration" }, cfg = "weapon2" },
- { label = "Enemy modifiers", modName = { "DamageTaken", "ColdDamageTaken", "ElementalDamageTaken", "ColdResist", "ElementalResist" }, enemy = true },
- },
- { format = "x {3:output:OffHand.FireEffMult}",
- { breakdown = "OffHand.FireEffMult" },
- { label = "Player modifiers", modName = { "FirePenetration", "ElementalPenetration" }, cfg = "weapon2" },
- { label = "Enemy modifiers", modName = { "DamageTaken", "FireDamageTaken", "ElementalDamageTaken", "FireResist", "ElementalResist" }, enemy = true },
- },
- { format = "x {3:output:OffHand.ChaosEffMult}",
- { breakdown = "OffHand.ChaosEffMult" },
- { label = "Enemy modifiers", modName = { "DamageTaken", "ChaosDamageTaken", "ChaosResist" }, enemy = true },
- },
- },
- { label = "OH Hit Damage", bgCol = colorCodes.OFFHANDBG, textSize = 12, flag = "weapon2Attack",
- { format = "{0:output:OffHand.TotalMin} to {0:output:OffHand.TotalMax}", },
- { format = "{0:output:OffHand.PhysicalMin} to {0:output:OffHand.PhysicalMax}",
- { breakdown = "OffHand.Physical" },
- { label = "Conversions", modType = "BASE", cfg = "weapon2", modName = { "SkillPhysicalDamageConvertToLightning", "SkillPhysicalDamageConvertToCold", "SkillPhysicalDamageConvertToFire", "SkillPhysicalDamageConvertToChaos", "PhysicalDamageConvertToLightning", "PhysicalDamageConvertToCold", "PhysicalDamageConvertToFire", "PhysicalDamageConvertToChaos", "PhysicalDamageGainAsLightning", "PhysicalDamageGainAsCold", "PhysicalDamageGainAsFire", "PhysicalDamageGainAsChaos" } },
- },
- { format = "{0:output:OffHand.LightningMin} to {0:output:OffHand.LightningMax}",
- { breakdown = "OffHand.Lightning" },
- { label = "Conversions", modType = "BASE", cfg = "weapon2", modName = { "LightningDamageConvertToCold", "LightningDamageConvertToFire", "LightningDamageConvertToChaos", "ElementalDamageConvertToChaos", "LightningDamageGainAsCold", "LightningDamageGainAsFire", "LightningDamageGainAsChaos", "ElementalDamageGainAsChaos" } },
- },
- { format = "{0:output:OffHand.ColdMin} to {0:output:OffHand.ColdMax}",
- { breakdown = "OffHand.Cold" },
- { label = "Conversions", modType = "BASE", cfg = "weapon2", modName = { "SkillColdDamageConvertToFire", "ColdDamageConvertToFire", "ColdDamageConvertToChaos", "ElementalDamageConvertToChaos", "ColdDamageGainAsFire", "ColdDamageGainAsChaos", "ElementalDamageGainAsChaos" } },
- },
- { format = "{0:output:OffHand.FireMin} to {0:output:OffHand.FireMax}",
- { breakdown = "OffHand.Fire" },
- { label = "Conversions", modType = "BASE", cfg = "weapon2", modName = { "FireDamageConvertToChaos", "ElementalDamageConvertToChaos", "FireDamageGainAsChaos", "ElementalDamageGainAsChaos" } },
- },
- { format = "{0:output:OffHand.ChaosMin} to {0:output:OffHand.ChaosMax}",
- { breakdown = "OffHand.Chaos" },
- },
- },
- { label = "OH Average Hit", bgCol = colorCodes.OFFHANDBG, flag = "weapon2Attack", { format = "{1:output:OffHand.AverageHit}", { breakdown = "OffHand.AverageHit" }, }, },
- { label = "Average Damage", flag = "attack", { format = "{1:output:AverageDamage}",
- { breakdown = "MainHand.AverageDamage" },
- { breakdown = "OffHand.AverageDamage" },
- { breakdown = "AverageDamage" },
- }, },
- { label = "Skill DPS", flag = "notAverage", { format = "{1:output:TotalDPS}", { breakdown = "TotalDPS" }, }, },
- { label = "Mana Cost", { format = "{0:output:ManaCost}", { breakdown = "ManaCost" }, { modName = "ManaCost", cfg = "skill" }, }, },
-} },
-{ 3, "Dot", 1, "Skill Damage over Time", colorCodes.OFFENCE, {
- extra = "{1:output:TotalDot} total DoT",
- flag = "dot",
- colWidth = 95,
- { { format = "All Types:", }, { format = "Physical:" }, { format = "Lightning:" }, { format = "Cold:" }, { format = "Fire:" }, { format = "Chaos:" }, },
- { label = "Total Increased",
- { format = "{0:mod:1}%", { modName = "Damage", modType = "INC", cfg = "dot" }, },
- { format = "{0:mod:1}%", { modName = "PhysicalDamage", modType = "INC", cfg = "dot" }, },
- { format = "{0:mod:1}%", { modName = { "LightningDamage", "ElementalDamage" }, modType = "INC", cfg = "dot" }, },
- { format = "{0:mod:1}%", { modName = { "ColdDamage", "ElementalDamage" }, modType = "INC", cfg = "dot" }, },
- { format = "{0:mod:1}%", { modName = { "FireDamage", "ElementalDamage" }, modType = "INC", cfg = "dot" }, },
- { format = "{0:mod:1}%", { modName = "ChaosDamage", modType = "INC", cfg = "dot" }, },
- },
- { label = "Total More",
- { format = "{0:mod:1}%", { modName = "Damage", modType = "MORE", cfg = "dot" }, },
- { format = "{0:mod:1}%", { modName = "PhysicalDamage", modType = "MORE", cfg = "dot" }, },
- { format = "{0:mod:1}%", { modName = { "LightningDamage", "ElementalDamage" }, modType = "MORE", cfg = "dot" }, },
- { format = "{0:mod:1}%", { modName = { "ColdDamage", "ElementalDamage" }, modType = "MORE", cfg = "dot" }, },
- { format = "{0:mod:1}%", { modName = { "FireDamage", "ElementalDamage" }, modType = "MORE", cfg = "dot" }, },
- { format = "{0:mod:1}%", { modName = "ChaosDamage", modType = "MORE", cfg = "dot" }, },
- },
- { label = "Effective DPS Mod", flag = "effective",
- { },
- { format = "x {3:output:PhysicalDotEffMult}",
- { breakdown = "PhysicalDotEffMult" },
- { label = "Enemy modifiers", modName = { "DamageTaken", "DamageTakenOverTime", "PhysicalDamageTaken", "PhysicalDamageTakenOverTime", "PhysicalDamageReduction" }, enemy = true },
- },
- { format = "x {3:output:LightningDotEffMult}",
- { breakdown = "LightningDotEffMult" },
- { label = "Enemy modifiers", modName = { "DamageTaken", "DamageTakenOverTime", "LightningDamageTaken", "LightningDamageTakenOverTime", "ElementalDamageTaken", "LightningResist", "ElementalResist" }, enemy = true },
- },
- { format = "x {3:output:ColdDotEffMult}",
- { breakdown = "ColdDotEffMult" },
- { label = "Enemy modifiers", modName = { "DamageTaken", "DamageTakenOverTime", "ColdDamageTaken", "ColdDamageTakenOverTime", "ElementalDamageTaken", "ColdResist", "ElementalResist" }, enemy = true },
- },
- { format = "x {3:output:FireDotEffMult}",
- { breakdown = "FireDotEffMult" },
- { label = "Enemy modifiers", modName = { "DamageTaken", "DamageTakenOverTime", "FireDamageTaken", "FireDamageTakenOverTime", "ElementalDamageTaken", "FireResist", "ElementalResist" }, enemy = true },
- },
- { format = "x {3:output:ChaosDotEffMult}",
- { breakdown = "ChaosDotEffMult" },
- { label = "Enemy modifiers", modName = { "DamageTaken", "DamageTakenOverTime", "ChaosDamageTaken", "ChaosDamageTakenOverTime", "ChaosResist" }, enemy = true },
- },
- },
- { label = "Damage over Time",
- { format = "{1:output:TotalDot}", },
- { format = "{1:output:PhysicalDot}", { breakdown = "PhysicalDot" }, },
- { format = "{1:output:LightningDot}", { breakdown = "LightningDot" }, },
- { format = "{1:output:ColdDot}", { breakdown = "ColdDot" }, },
- { format = "{1:output:FireDot}", { breakdown = "FireDot" }, },
- { format = "{1:output:ChaosDot}", { breakdown = "ChaosDot" }, },
- },
- { label = "Mana Cost", { format = "{0:output:ManaCost}", { breakdown = "ManaCost" }, { modName = "ManaCost", cfg = "skill" }, }, },
-} },
-{ 1, "Speed", 1, "Attack/Cast Rate", colorCodes.OFFENCE, {
- extra = "{2:output:Speed}/s",
- { label = "MH Inc. Att. Speed", bgCol = colorCodes.MAINHANDBG, flag = "weapon1Attack", { format = "{0:mod:1}%", { modName = "Speed", modType = "INC", cfg = "weapon1", }, }, },
- { label = "MH More Att. Speed", bgCol = colorCodes.MAINHANDBG, flag = "weapon1Attack", { format = "{0:mod:1}%", { modName = "Speed", modType = "MORE", cfg = "weapon1", }, }, },
- { label = "MH Att. per second", bgCol = colorCodes.MAINHANDBG, flag = "weapon1Attack", { format = "{2:output:MainHand.Speed}", { breakdown = "MainHand.Speed" }, }, },
- { label = "OH Inc. Att. Speed", bgCol = colorCodes.OFFHANDBG, flag = "weapon2Attack", { format = "{0:mod:1}%", { modName = "Speed", modType = "INC", cfg = "weapon2", }, }, },
- { label = "OH More Att. Speed", bgCol = colorCodes.OFFHANDBG, flag = "weapon2Attack", { format = "{0:mod:1}%", { modName = "Speed", modType = "MORE", cfg = "weapon2", }, }, },
- { label = "OH Att. per second", bgCol = colorCodes.OFFHANDBG, flag = "weapon2Attack", { format = "{2:output:OffHand.Speed}", { breakdown = "OffHand.Speed" }, }, },
- { label = "Attacks per second", flag = "bothWeaponAttack", { format = "{2:output:Speed}", { breakdown = "Speed" }, }, },
- { label = "Attack time", flag = "attack", { format = "{2:output:Time}s", }, },
- { label = "Inc. Cast Speed", flag = "spell", { format = "{0:mod:1}%", { modName = "Speed", modType = "INC", cfg = "skill", }, }, },
- { label = "More Cast Speed", flag = "spell", { format = "{0:mod:1}%", { modName = "Speed", modType = "MORE", cfg = "skill", }, }, },
- { label = "Casts per second", flag = "spell", { format = "{2:output:Speed}", { breakdown = "Speed" }, }, },
- { label = "Cast time", flag = "spell", { format = "{2:output:Time}s", }, },
-} },
-{ 1, "Crit", 1, "Crits", colorCodes.OFFENCE, {
- extra = "{2:output:CritChance}% x{2:output:CritMultiplier}",
- flag = "hit",
- -- Skill
- { label = "Inc. Crit Chance", notFlag = "attack", { format = "{0:mod:1}%", { modName = "CritChance", modType = "INC", cfg = "skill" }, }, },
- { label = "Crit Chance", notFlag = "attack", { format = "{2:output:CritChance}%",
- { breakdown = "CritChance" },
- { label = "Player modifiers", modName = "CritChance", cfg = "skill" },
- { label = "Enemy modifiers", modName = "SelfExtraCritChance", enemy = true },
- }, },
- { label = "Crit Multiplier", notFlag = "attack", { format = "x {2:output:CritMultiplier}",
- { breakdown = "CritMultiplier" },
- { label = "Player modifiers", modName = "CritMultiplier", cfg = "skill" },
- { label = "Enemy modifiers", modName = "SelfCritMultiplier", enemy = true },
- }, },
- { label = "Crit Effect Mod", notFlag = "attack", { format = "x {3:output:CritEffect}", { breakdown = "CritEffect" }, }, },
- -- Main Hand
- { label = "MH Inc. Crit Chance", bgCol = colorCodes.MAINHANDBG, flag = "weapon1Attack", { format = "{0:mod:1}%", { modName = "CritChance", modType = "INC", cfg = "weapon1" }, }, },
- { label = "MH Crit Chance", bgCol = colorCodes.MAINHANDBG, flag = "weapon1Attack", { format = "{2:output:MainHand.CritChance}%",
- { breakdown = "MainHand.CritChance" },
- { label = "Player modifiers", modName = "CritChance", cfg = "weapon1" },
- { label = "Enemy modifiers", modName = "SelfExtraCritChance", enemy = true },
- }, },
- { label = "MH Crit Multiplier", bgCol = colorCodes.MAINHANDBG, flag = "weapon1Attack", { format = "x {2:output:MainHand.CritMultiplier}",
- { breakdown = "MainHand.CritMultiplier" },
- { label = "Player modifiers", modName = "CritMultiplier", cfg = "weapon1" },
- { label = "Enemy modifiers", modName = "SelfCritMultiplier", enemy = true },
- }, },
- { label = "MH Crit Effect Mod", bgCol = colorCodes.MAINHANDBG, flag = "weapon1Attack", { format = "x {3:output:MainHand.CritEffect}", { breakdown = "MainHand.CritEffect" }, }, },
- -- Off Hand
- { label = "OH Inc. Crit Chance", bgCol = colorCodes.OFFHANDBG, flag = "weapon2Attack", { format = "{0:mod:1}%", { modName = "CritChance", modType = "INC", cfg = "weapon2" }, }, },
- { label = "OH Crit Chance", bgCol = colorCodes.OFFHANDBG, flag = "weapon2Attack", { format = "{2:output:OffHand.CritChance}%",
- { breakdown = "OffHand.CritChance" },
- { label = "Player modifiers", modName = "CritChance", cfg = "weapon2" },
- { label = "Enemy modifiers", modName = "SelfExtraCritChance", enemy = true },
- }, },
- { label = "OH Crit Multiplier", bgCol = colorCodes.OFFHANDBG, flag = "weapon2Attack", { format = "x {2:output:OffHand.CritMultiplier}",
- { breakdown = "OffHand.CritMultiplier" },
- { label = "Player modifiers", modName = "CritMultiplier", cfg = "weapon2" },
- { label = "Enemy modifiers", modName = "SelfCritMultiplier", enemy = true },
- }, },
- { label = "OH Crit Effect Mod", bgCol = colorCodes.OFFHANDBG, flag = "weapon2Attack", { format = "x {3:output:OffHand.CritEffect}", { breakdown = "OffHand.CritEffect" }, }, },
-} },
-{ 1, "HitChance", 1, "Accuracy", colorCodes.OFFENCE, {
- extra = "{0:output:HitChance}%",
- flag = "attack",
- { label = "MH Accuracy", bgCol = colorCodes.MAINHANDBG, flag = "weapon1Attack", { format = "{0:output:MainHand.Accuracy}",
- { breakdown = "MainHand.Accuracy" },
- { modName = "Accuracy", cfg = "weapon1" },
- }, },
- { label = "MH Chance to Hit", bgCol = colorCodes.MAINHANDBG, flag = "weapon1Attack", { format = "{0:output:MainHand.HitChance}%",
- { breakdown = "MainHand.HitChance" },
- { label = "Enemy Evasion modifiers", modName = "Evasion", enemy = true },
- }, },
- { label = "OH Accuracy", bgCol = colorCodes.OFFHANDBG, flag = "weapon2Attack", { format = "{0:output:OffHand.Accuracy}",
- { breakdown = "OffHand.Accuracy" },
- { modName = "Accuracy", cfg = "weapon2" },
- }, },
- { label = "OH Chance to Hit", bgCol = colorCodes.OFFHANDBG, flag = "weapon2Attack", { format = "{0:output:OffHand.HitChance}%",
- { breakdown = "OffHand.HitChance" },
- { label = "Enemy Evasion modifiers", modName = "Evasion", enemy = true },
- }, },
-} },
-{ 1, "SkillTypeStats", 1, "Skill type-specific Stats", colorCodes.OFFENCE, {
- { label = "Active Minion Limit", haveOutput = "ActiveMinionLimit", { format = "{0:output:ActiveMinionLimit}" } },
- { label = "Skill Cooldown", haveOutput = "Cooldown", { format = "{2:output:Cooldown}s",
- { breakdown = "Cooldown" },
- { modName = "CooldownRecovery", cfg = "skill" },
- }, },
- { label = "Duration Mod", flag = "duration", { format = "x {2:output:DurationMod}",
- { breakdown = "DurationMod"},
- { modName = "Duration", cfg = "skill" },
- }, },
- { label = "Skill Duration", flag = "duration", haveOutput = "Duration", { format = "{2:output:Duration}s", { breakdown = "Duration" }, }, },
- { label = "Max Chain Count", flag = "chaining", { format = "{0:output:ChainMax}", { modName = "ChainCountMax", cfg = "skill" }, }, },
- { label = "Projectile Count", flag = "projectile", { format = "{0:output:ProjectileCount}", { modName = "ProjectileCount", cfg = "skill" }, }, },
- { label = "Pierce Chance", flag = "projectile", { format = "{0:output:PierceChance}%", { modName = "PierceChance", cfg = "skill" }, }, },
- { label = "Proj. Speed Mod", flag = "projectile", { format = "x {2:output:ProjectileSpeedMod}",
- { breakdown = "ProjectileSpeedMod" },
- { modName = "ProjectileSpeed", cfg = "skill" },
- }, },
- { label = "Area of Effect Mod", flag = "area", { format = "x {2:output:AreaOfEffectMod}",
- { breakdown = "AreaOfEffectMod" },
- { modName = "AreaOfEffect", cfg = "skill" },
- }, },
- { label = "Radius", haveOutput = "AreaOfEffectRadius", { format = "{0:output:AreaOfEffectRadius}", { breakdown = "AreaOfEffectRadius" }, }, },
- { label = "Secondary Radius", haveOutput = "AreaOfEffectRadiusSecondary", { format = "{0:output:AreaOfEffectRadiusSecondary}", { breakdown = "AreaOfEffectRadiusSecondary" }, }, },
- { label = "Weapon Range", haveOutput = "WeaponRange", { format = "{0:output:WeaponRange}", { breakdown = "WeaponRange" }, }, },
- { label = "Trap Throw Time", flag = "trap", { format = "{2:output:TrapThrowingTime}s",
- { breakdown = "TrapThrowingTime" },
- { modName = "TrapThrowingSpeed", cfg = "skill" },
- }, },
- { label = "Active Trap Limit", flag = "trap", { format = "{0:output:ActiveTrapLimit}", { modName = "ActiveTrapLimit", cfg = "skill" }, }, },
- { label = "Trap Cooldown", flag = "trap", { format = "{2:output:TrapCooldown}s",
- { breakdown = "TrapCooldown" },
- { modName = "CooldownRecovery", cfg = "skill" },
- }, },
- { label = "Trap Trigg. Radius", flag = "trap", { format = "{0:output:TrapTriggerRadius}",
- { breakdown = "TrapTriggerRadius" },
- { label = "Area of Effect modifiers", modName = "TrapTriggerAreaOfEffect", cfg = "skill" },
- }, },
- { label = "Mine Laying Time", flag = "mine", { format = "{2:output:MineLayingTime}s",
- { breakdown = "MineLayingTime" },
- { modName = "MineLayingSpeed", cfg = "skill" },
- }, },
- { label = "Active Mine Limit", flag = "mine", { format = "{0:output:ActiveMineLimit}", { modName = "ActiveMineLimit", cfg = "skill" }, }, },
- { label = "Mine Deton. Radius", flag = "mine", { format = "{0:output:MineDetonationRadius}",
- { breakdown = "MineDetonationRadius" },
- { label = "Area of Effect modifiers", modName = "MineDetonationAreaOfEffect", cfg = "skill" },
- }, },
- { label = "Totem Place Time", flag = "totem", { format = "{2:output:TotemPlacementTime}s",
- { breakdown = "TotemPlacementTime" },
- { modName = "TotemPlacementSpeed", cfg = "skill" },
- }, },
- { label = "Active Totem Limit", flag = "totem", { format = "{0:output:ActiveTotemLimit}", { modName = "ActiveTotemLimit", cfg = "skill" }, }, },
- { label = "Totem Life Mod", flag = "totem", { format = "x {2:output:TotemLifeMod}",
- { breakdown = "TotemLifeMod" },
- { modName = "TotemLife", cfg = "skill" },
- }, },
- { label = "Totem Life", flag = "totem", { format = "{0:output:TotemLife}", { breakdown = "TotemLife" }, }, },
-} },
-{ 1, "Bleed", 1, "Bleed", colorCodes.OFFENCE, {
- extra = "{0:output:BleedChance}% {1:output:BleedDPS} {2:output:BleedDuration}s",
- flag = "bleed",
- { label = "Chance to Bleed", { format = "{0:output:BleedChance}%",
- { breakdown = "MainHand.BleedChance" },
- { breakdown = "OffHand.BleedChance" },
- { breakdown = "BleedChance" },
- { label = "Main Hand", flag = "weapon1Attack", modName = "BleedChance", modType = "BASE", cfg = "weapon1" },
- { label = "Off Hand", flag = "weapon2Attack", modName = "BleedChance", modType = "BASE", cfg = "weapon2" },
- }, },
- { label = "Total Increased", { format = "{0:mod:1}%", { modName = { "Damage", "PhysicalDamage" }, modType = "INC", cfg = "bleed" }, }, },
- { label = "Total More", { format = "{0:mod:1}%", { modName = { "Damage", "PhysicalDamage" }, modType = "MORE", cfg = "bleed" }, }, },
- { label = "Effective DPS Mod", flag = "effective", { format = "x {3:output:BleedEffMult}", { breakdown = "BleedEffMult" }, { label = "Enemy modifiers", modName = { "DamageTaken", "DamageTakenOverTime", "PhysicalDamageTaken", "PhysicalDamageTakenOverTime", "PhysicalDamageReduction" }, enemy = true }, }, },
- { label = "Bleed DPS", { format = "{1:output:BleedDPS}", { breakdown = "BleedDPS" }, { breakdown = "MainHand.BleedDPS" }, { breakdown = "OffHand.BleedDPS" }, }, },
- { label = "Bleed Duration", { format = "{2:output:BleedDuration}s",
- { breakdown = "BleedDuration" },
- { label = "Player modifiers", modName = "Duration", cfg = "bleed" },
- { label = "Enemy modifiers", modName = "SelfBleedDuration", enemy = true },
- }, },
-} },
-{ 1, "Poison", 1, "Poison", colorCodes.OFFENCE, {
- extra = "{0:output:PoisonChance}% {1:output:PoisonDPS} {2:output:PoisonDuration}s",
- flag = "poison",
- { label = "Chance to Poison", { format = "{0:output:PoisonChance}%",
- { breakdown = "MainHand.PoisonChance" },
- { breakdown = "OffHand.PoisonChance" },
- { breakdown = "PoisonChance" },
- { notFlag = "attack", modName = "PoisonChance", modType = "BASE", cfg = "skill" },
- { label = "Main Hand", flag = "weapon1Attack", modName = "PoisonChance", modType = "BASE", cfg = "weapon1" },
- { label = "Off Hand", flag = "weapon2Attack", modName = "PoisonChance", modType = "BASE", cfg = "weapon2" },
- }, },
- { label = "Total Increased", { format = "{0:mod:1}%", { modName = { "Damage", "ChaosDamage" }, modType = "INC", cfg = "poison" }, }, },
- { label = "Total More", { format = "{0:mod:1}%", { modName = { "Damage", "ChaosDamage" }, modType = "MORE", cfg = "poison" }, }, },
- { label = "Effective DPS Mod", flag = "effective", { format = "x {3:output:PoisonEffMult}",
- { breakdown = "PoisonEffMult" },
- { label = "Enemy modifiers", modName = { "ChaosResist", "DamageTaken", "DamageTakenOverTime", "ChaosDamageTaken", "ChaosDamageTakenOverTime" }, enemy = true },
- }, },
- { label = "Poison DPS", { format = "{1:output:PoisonDPS}",
- { breakdown = "PoisonDPS" },
- { breakdown = "MainHand.PoisonDPS" },
- { breakdown = "OffHand.PoisonDPS" },
- }, },
- { label = "Poison Duration", { format = "{2:output:PoisonDuration}s",
- { breakdown = "PoisonDuration" },
- { label = "Player modifiers", modName = "Duration", cfg = "poison" },
- { label = "Enemy modifiers", modName = "SelfPoisonDuration", enemy = true },
- }, },
- { label = "Dmg. per Poison", { format = "{1:output:PoisonDamage}",
- { breakdown = "MainHand.PoisonDamage" },
- { breakdown = "OffHand.PoisonDamage" },
- { breakdown = "PoisonDamage" },
- }, },
- { flag = "notAverage", label = "Max Poison Stacks", { format = "{1:output:TotalPoisonStacks}",
- { breakdown = "MainHand.TotalPoisonStacks" },
- { breakdown = "OffHand.TotalPoisonStacks" },
- { breakdown = "TotalPoisonStacks" },
- }, },
-} },
-{ 1, "Ignite", 1, "Ignite", colorCodes.OFFENCE, {
- extra = "{0:output:IgniteChance}% {1:output:IgniteDPS} {2:output:IgniteDuration}s",
- flag = "ignite",
- { label = "Chance to Ignite", { format = "{0:output:IgniteChance}%",
- { breakdown = "MainHand.IgniteChance" },
- { breakdown = "OffHand.IgniteChance" },
- { breakdown = "IgniteChance" },
- { label = "Player modifiers", modName = "EnemyIgniteChance", cfg = "skill" },
- { label = "Enemy modifiers", modName = "SelfIgniteChance", enemy = true },
- }, },
- { label = "Total Increased", { format = "{0:mod:1}%", { modName = { "Damage", "FireDamage", "ElementalDamage" }, modType = "INC", cfg = "ignite" }, }, },
- { label = "Total More", { format = "{0:mod:1}%", { modName = { "Damage", "FireDamage", "ElementalDamage" }, modType = "MORE", cfg = "ignite" }, }, },
- { label = "Effective DPS Mod", flag = "effective", { format = "x {3:output:IgniteEffMult}",
- { breakdown = "IgniteEffMult" },
- { label = "Enemy modifiers", modName = { "FireResist", "ElementalResist", "DamageTaken", "DamageTakenOverTime", "FireDamageTaken", "FireDamageTakenOverTime", "ElementalDamageTaken" }, enemy = true },
- }, },
- { label = "Ignite DPS", { format = "{1:output:IgniteDPS}",
- { breakdown = "IgniteDPS" },
- { breakdown = "MainHand.IgniteDPS" },
- { breakdown = "OffHand.IgniteDPS" },
- { modName = { "IgniteBurnRate" }, cfg = "skill" },
- }, },
- { label = "Ignite Duration", { format = "{2:output:IgniteDuration}s",
- { breakdown = "IgniteDuration" },
- { label = "Player modifiers", modName = "EnemyIgniteDuration", cfg = "skill" },
- { label = "Enemy modifiers", modName = "SelfIgniteDuration", enemy = true },
- }, },
- { label = "Dmg. per Ignite", flag = "igniteCanStack", { format = "{1:output:IgniteDamage}",
- { breakdown = "MainHand.IgniteDamage" },
- { breakdown = "OffHand.IgniteDamage" },
- { breakdown = "IgniteDamage" },
- }, },
- { flagList = { "notAverage", "igniteCanStack" }, label = "Max Ignite Stacks", { format = "{1:output:TotalIgniteStacks}",
- { breakdown = "MainHand.TotalIgniteStacks" },
- { breakdown = "OffHand.TotalIgniteStacks" },
- { breakdown = "TotalIgniteStacks" },
- }, },
-} },
-{ 1, "Decay", 1, "Decay", colorCodes.OFFENCE, {
- extra = "{1:output:DecayDPS} {2:output:DecayDuration}s",
- flag = "decay",
- { label = "Total Increased", { format = "{0:mod:1}%", { modName = { "Damage", "ChaosDamage" }, modType = "INC", cfg = "decay" }, }, },
- { label = "Total More", { format = "{0:mod:1}%", { modName = { "Damage", "ChaosDamage" }, modType = "MORE", cfg = "decay" }, }, },
- { label = "Effective DPS Mod", flag = "effective", { format = "x {3:output:DecayEffMult}",
- { breakdown = "DecayEffMult" },
- { label = "Enemy modifiers", modName = { "ChaosResist", "DamageTaken", "DamageTakenOverTime", "ChaosDamageTaken", "ChaosDamageTakenOverTime" }, enemy = true },
- }, },
- { label = "Decay DPS", { format = "{1:output:DecayDPS}",
- { breakdown = "DecayDPS" },
- }, },
- { label = "Decay Duration", { format = "{2:output:DecayDuration}s",
- { breakdown = "DecayDuration" },
- { modName = "Duration", cfg = "decay" },
- }, },
-} },
-{ 1, "LeechGain", 1, "Leech & Gain on Hit", colorCodes.OFFENCE, {
- { label = "Life Leech Cap", flag = "leechLife", { format = "{1:output:MaxLifeLeechRate}",
- { breakdown = "MaxLifeLeechRate" },
- { modName = "MaxLifeLeechRate" },
- }, },
- { label = "Life Leech Rate", flag = "leechLife", notFlag = "showAverage", { format = "{1:output:LifeLeechRate}",
- { breakdown = "LifeLeech" },
- { label = "Player modifiers", notFlagList = { "totem", "attack" }, modName = { "DamageLeech", "DamageLifeLeech", "PhysicalDamageLifeLeech", "LightningDamageLifeLeech", "ColdDamageLifeLeech", "FireDamageLifeLeech", "ChaosDamageLifeLeech", "ElementalDamageLifeLeech" }, modType = "BASE", cfg = "skill" },
- { label = "Main Hand", notFlag = "totem", flag = "weapon1Attack", modName = { "DamageLeech", "DamageLifeLeech", "PhysicalDamageLifeLeech", "LightningDamageLifeLeech", "ColdDamageLifeLeech", "FireDamageLifeLeech", "ChaosDamageLifeLeech", "ElementalDamageLifeLeech" }, modType = "BASE", cfg = "weapon1" },
- { 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 = "Life Leech per Hit", flagList = { "leechLife", "showAverage" }, { format = "{1:output:LifeLeechPerHit}",
- { breakdown = "LifeLeech" },
- { label = "Player modifiers", notFlagList = { "totem", "attack" }, modName = { "DamageLeech", "DamageLifeLeech", "PhysicalDamageLifeLeech", "LightningDamageLifeLeech", "ColdDamageLifeLeech", "FireDamageLifeLeech", "ChaosDamageLifeLeech", "ElementalDamageLifeLeech" }, modType = "BASE", cfg = "skill" },
- { label = "Main Hand", notFlag = "totem", flag = "weapon1Attack", modName = { "DamageLeech", "DamageLifeLeech", "PhysicalDamageLifeLeech", "LightningDamageLifeLeech", "ColdDamageLifeLeech", "FireDamageLifeLeech", "ChaosDamageLifeLeech", "ElementalDamageLifeLeech" }, modType = "BASE", cfg = "weapon1" },
- { 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 = "Life Gain Rate", notFlag = "showAverage", haveOutput = "LifeOnHitRate", { format = "{1:output:LifeOnHitRate}",
- { label = "Player modifiers", notFlag = "attack", modName = "LifeOnHit", modType = "BASE", cfg = "skill" },
- { label = "Main Hand", flag = "weapon1Attack", modName = "LifeOnHit", modType = "BASE", cfg = "weapon1" },
- { label = "Off Hand", flag = "weapon2Attack", modName = "LifeOnHit", modType = "BASE", cfg = "weapon2" },
- { label = "Enemy modifiers", modName = { "SelfLifeOnHit" }, modType = "BASE", cfg = "skill", enemy = true },
- }, },
- { label = "Life Gain per Hit", flag = "showAverage", haveOutput = "LifeOnHit", { format = "{1:output:LifeOnHit}",
- { label = "Player modifiers", notFlag = "attack", modName = "LifeOnHit", modType = "BASE", cfg = "skill" },
- { label = "Main Hand", flag = "weapon1Attack", modName = "LifeOnHit", modType = "BASE", cfg = "weapon1" },
- { label = "Off Hand", flag = "weapon2Attack", modName = "LifeOnHit", modType = "BASE", cfg = "weapon2" },
- { label = "Enemy modifiers", modName = { "SelfLifeOnHit" }, modType = "BASE", cfg = "skill", enemy = true },
- }, },
- { label = "ES Leech Cap", flag = "leechES", { format = "{1:output:MaxEnergyShieldLeechRate}",
- { breakdown = "MaxEnergyShieldLeechRate" },
- { modName = "MaxLifeLeechRate" },
- }, },
- { label = "ES Leech Rate", flag = "leechES", notFlag = "showAverage", { format = "{1:output:EnergyShieldLeechRate}",
- { breakdown = "EnergyShieldLeech" },
- { label = "Player modifiers", notFlagList = { "totem", "attack" }, modName = { "DamageLeech", "DamageLifeLeech", "PhysicalDamageLifeLeech", "LightningDamageLifeLeech", "ColdDamageLifeLeech", "FireDamageLifeLeech", "ChaosDamageLifeLeech", "ElementalDamageLifeLeech" }, modType = "BASE", cfg = "skill" },
- { label = "Main Hand", notFlag = "totem", flag = "weapon1Attack", modName = { "DamageLeech", "DamageLifeLeech", "PhysicalDamageLifeLeech", "LightningDamageLifeLeech", "ColdDamageLifeLeech", "FireDamageLifeLeech", "ChaosDamageLifeLeech", "ElementalDamageLifeLeech" }, modType = "BASE", cfg = "weapon1" },
- { 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 = "ES Leech per Hit", flagList = { "leechES", "showAverage" }, { format = "{1:output:EnergyShieldLeechPerHit}",
- { breakdown = "EnergyShieldLeech" },
- { label = "Player modifiers", notFlagList = { "totem", "attack" }, modName = { "DamageLeech", "DamageLifeLeech", "PhysicalDamageLifeLeech", "LightningDamageLifeLeech", "ColdDamageLifeLeech", "FireDamageLifeLeech", "ChaosDamageLifeLeech", "ElementalDamageLifeLeech" }, modType = "BASE", cfg = "skill" },
- { label = "Main Hand", notFlag = "totem", flag = "weapon1Attack", modName = { "DamageLeech", "DamageLifeLeech", "PhysicalDamageLifeLeech", "LightningDamageLifeLeech", "ColdDamageLifeLeech", "FireDamageLifeLeech", "ChaosDamageLifeLeech", "ElementalDamageLifeLeech" }, modType = "BASE", cfg = "weapon1" },
- { 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 = "ES Gain Rate", notFlag = "showAverage", haveOutput = "EnergyShieldOnHitRate", { format = "{1:output:EnergyShieldOnHitRate}",
- { label = "Player modifiers", notFlag = "attack", modName = "EnergyShieldOnHit", modType = "BASE", cfg = "skill" },
- { label = "Main Hand", flag = "weapon1Attack", modName = "EnergyShieldOnHit", modType = "BASE", cfg = "weapon1" },
- { label = "Off Hand", flag = "weapon2Attack", modName = "EnergyShieldOnHit", modType = "BASE", cfg = "weapon2" },
- { label = "Enemy modifiers", modName = { "SelfEnergyShieldOnHit" }, modType = "BASE", enemy = true },
- }, },
- { label = "ES Gain per Hit", flag = "showAverage", haveOutput = "EnergyShieldOnHit", { format = "{1:output:EnergyShieldOnHit}",
- { label = "Player modifiers", notFlag = "attack", modName = "EnergyShieldOnHit", modType = "BASE", cfg = "skill" },
- { label = "Main Hand", flag = "weapon1Attack", modName = "EnergyShieldOnHit", modType = "BASE", cfg = "weapon1" },
- { label = "Off Hand", flag = "weapon2Attack", modName = "EnergyShieldOnHit", modType = "BASE", cfg = "weapon2" },
- { label = "Enemy modifiers", modName = { "SelfEnergyShieldOnHit" }, modType = "BASE", cfg = "skill", enemy = true },
- }, },
- { label = "Mana Leech Cap", flag = "leechMana", { format = "{1:output:MaxManaLeechRate}",
- { breakdown = "MaxManaLeechRate" },
- { modName = "MaxManaLeechRate" },
- }, },
- { label = "Mana Leech Rate", flag = "leechMana", notFlag = "showAverage", { format = "{1:output:ManaLeechRate}",
- { breakdown = "ManaLeech" },
- { label = "Player modifiers", notFlag = "attack", modName = { "DamageLeech", "DamageManaLeech", "PhysicalDamageManaLeech", "LightningDamageManaLeech", "ColdDamageManaLeech", "FireDamageManaLeech", "ChaosDamageManaLeech", "ElementalDamageManaLeech" }, modType = "BASE", cfg = "skill" },
- { 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 = "Mana Leech per Hit", flagList = { "leechMana", "showAverage" }, { format = "{1:output:ManaLeechPerHit}",
- { breakdown = "ManaLeech" },
- { label = "Player modifiers", notFlag = "attack", modName = { "DamageLeech", "DamageManaLeech", "PhysicalDamageManaLeech", "LightningDamageManaLeech", "ColdDamageManaLeech", "FireDamageManaLeech", "ChaosDamageManaLeech", "ElementalDamageManaLeech" }, modType = "BASE", cfg = "skill" },
- { 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 = "Mana Gain Rate", notFlag = "showAverage", haveOutput = "ManaOnHitRate", { format = "{1:output:ManaOnHitRate}",
- { label = "Player modifiers", notFlag = "attack", modName = "ManaOnHit", modType = "BASE", cfg = "skill" },
- { label = "Main Hand", flag = "weapon1Attack", modName = "ManaOnHit", modType = "BASE", cfg = "weapon1" },
- { label = "Off Hand", flag = "weapon2Attack", modName = "ManaOnHit", modType = "BASE", cfg = "weapon2" },
- { label = "Enemy modifiers", modName = { "SelfManaOnHit" }, modType = "BASE", cfg = "skill", enemy = true },
- }, },
- { label = "Mana Gain per Hit", flag = "showAverage", haveOutput = "ManaOnHit", { format = "{1:output:ManaOnHit}",
- { label = "Player modifiers", notFlag = "attack", modName = "ManaOnHit", modType = "BASE", cfg = "skill" },
- { label = "Main Hand", flag = "weapon1Attack", modName = "ManaOnHit", modType = "BASE", cfg = "weapon1" },
- { label = "Off Hand", flag = "weapon2Attack", modName = "ManaOnHit", modType = "BASE", cfg = "weapon2" },
- { label = "Enemy modifiers", modName = { "SelfManaOnHit" }, modType = "BASE", cfg = "skill", enemy = true },
- }, },
-} },
-{ 1, "MiscEffects", 1, "Other Effects", colorCodes.OFFENCE, {
- { label = "Chance to Shock", flag = "shock", { format = "{0:output:ShockChance}%",
- { breakdown = "MainHand.ShockChance" },
- { breakdown = "OffHand.ShockChance" },
- { breakdown = "ShockChance" },
- { label = "Player modifiers", modName = "EnemyShockChance", cfg = "skill" },
- { label = "Enemy modifiers", modName = "SelfShockChance", enemy = true },
- }, },
- { label = "Shock Dur. Mod", flag = "shock", { format = "x {2:output:ShockDurationMod}",
- { breakdown = "MainHand.ShockDPS" },
- { breakdown = "OffHand.ShockDPS" },
- { breakdown = "ShockDPS" },
- { label = "Player modifiers", modName = "EnemyShockDuration", cfg = "skill" },
- { label = "Enemy modifiers", modName = "SelfShockDuration", enemy = true },
- }, },
- { label = "Chance to Freeze", flag = "freeze", { format = "{0:output:FreezeChance}%",
- { breakdown = "MainHand.FreezeChance" },
- { breakdown = "OffHand.FreezeChance" },
- { breakdown = "FreezeChance" },
- { label = "Player modifiers", modName = "EnemyFreezeChance", cfg = "skill" },
- { label = "Enemy modifiers", modName = "SelfFreezeChance", enemy = true },
- }, },
- { label = "Freeze Dur. Mod", flag = "freeze", { format = "x {2:output:FreezeDurationMod}",
- { breakdown = "MainHand.FreezeDPS" },
- { breakdown = "OffHand.FreezeDPS" },
- { breakdown = "FreezeDPS" },
- { label = "Player modifiers", modName = "EnemyFreezeDuration", cfg = "skill" },
- { label = "Enemy modifiers", modName = "SelfFreezeDuration", enemy = true },
- }, },
- { label = "Stun Threshold", flag = "hit", notFlag = "attack", { format = "x {2:output:EnemyStunThresholdMod}", { modName = "EnemyStunThreshold", cfg = "skill" }, }, },
- { label = "Stun Duration", flag = "hit", notFlag = "attack", { format = "{2:output:EnemyStunDuration}s",
- { breakdown = "EnemyStunDuration" },
- { label = "Player modifiers", modName = { "EnemyStunDuration" }, cfg = "skill" },
- { label = "Enemy modifiers", modName = { "StunRecovery" }, enemy = true },
- }, },
- { label = "MH Stun Threshold", bgCol = colorCodes.MAINHANDBG, flagList = {"hit","weapon1Attack"}, { format = "x {2:output:MainHand.EnemyStunThresholdMod}", { modName = "EnemyStunThreshold", cfg = "weapon1" }, }, },
- { label = "MH Stun Duration", bgCol = colorCodes.MAINHANDBG, flagList = {"hit","weapon1Attack"}, { format = "{2:output:MainHand.EnemyStunDuration}s",
- { breakdown = "MainHand.EnemyStunDuration" },
- { label = "Player modifiers", modName = { "EnemyStunDuration" }, cfg = "weapon1" },
- { label = "Enemy modifiers", modName = { "StunRecovery" }, enemy = true },
- }, },
- { label = "OH Stun Threshold", bgCol = colorCodes.OFFHANDBG, flagList = {"hit","weapon2Attack"}, { format = "x {2:output:OffHand.EnemyStunThresholdMod}", { modName = "EnemyStunThreshold", cfg = "weapon2" }, }, },
- { label = "OH Stun Duration", bgCol = colorCodes.OFFHANDBG, flagList = {"hit","weapon2Attack"}, { format = "{2:output:OffHand.EnemyStunDuration}s",
- { breakdown = "OffHand.EnemyStunDuration" },
- { label = "Player modifiers", modName = { "EnemyStunDuration" }, cfg = "weapon2" },
- { label = "Enemy modifiers", modName = { "StunRecovery" }, enemy = true },
- }, },
- { label = "Knockback Chance", haveOutput = "KnockbackChance", { format = "{0:output:KnockbackChance}%",
- { label = "Player modifiers", modName = "EnemyKnockbackChance", cfg = "skill" },
- { label = "Enemy modifiers", modName = "SelfKnockbackChance", enemy = true },
- }, },
- { label = "Knockback Dist.", haveOutput = "KnockbackChance", { format = "{0:output:KnockbackDistance}",
- { breakdown = "KnockbackDistance" },
- { modName = "EnemyKnockbackDistance", cfg = "skill" },
- }, },
- { label = "MH K.B. Chance", bgCol = colorCodes.MAINHANDBG, haveOutput = "MainHand.KnockbackChance", { format = "{0:output:MainHand.KnockbackChance}%",
- { label = "Player modifiers", modName = "EnemyKnockbackChance", cfg = "weapon1" },
- { label = "Enemy modifiers", modName = "SelfKnockbackChance", enemy = true },
- }, },
- { label = "MH K.B. Dist.", bgCol = colorCodes.MAINHANDBG, haveOutput = "MainHand.KnockbackChance", { format = "{0:output:MainHand.KnockbackDistance}",
- { breakdown = "MainHand.KnockbackDistance" },
- { modName = "EnemyKnockbackDistance", cfg = "weapon1" },
- }, },
- { label = "OH K.B. Chance", bgCol = colorCodes.OFFHANDBG, haveOutput = "OffHand.KnockbackChance", { format = "{0:output:OffHand.KnockbackChance}%",
- { label = "Player modifiers", modName = "EnemyKnockbackChance", cfg = "weapon2" },
- { label = "Enemy modifiers", modName = "SelfKnockbackChance", enemy = true },
- }, },
- { label = "OH K.B. Dist.", bgCol = colorCodes.OFFHANDBG, haveOutput = "OffHand.KnockbackChance", { format = "{0:output:OffHand.KnockbackDistance}",
- { breakdown = "OffHand.KnockbackDistance" },
- { modName = "EnemyKnockbackDistance", cfg = "weapon2" },
- }, },
- { label = "Inc. Item Quantity", { format = "{0:mod:1}%", { modName = "LootQuantity", modType = "INC", cfg = "skill" }, }, },
- { label = "Inc. Item Rarity", { format = "{0:mod:1}%", { modName = "LootRarity", modType = "INC", cfg = "skill" }, }, },
-} },
-{ 1, "Attributes", 2, "Attributes", colorCodes.NORMAL, {
- extra = colorCodes.STRENGTH.."{0:output:Str}^7, "..colorCodes.DEXTERITY.."{0:output:Dex}^7, "..colorCodes.INTELLIGENCE.."{0:output:Int}",
- { label = "Strength", { format = "{0:output:Str}", { breakdown = "Str" }, { modName = "Str" }, }, },
- { label = "Dexterity", { format = "{0:output:Dex}", { breakdown = "Dex" }, { modName = "Dex" }, }, },
- { label = "Intelligence", { format = "{0:output:Int}", { breakdown = "Int" }, { modName = "Int" }, }, },
- { notFlag = "minionSkill", label = "Str. Required", { format = "{output:ReqStrString}", { breakdown = "ReqStr" }, }, },
- { notFlag = "minionSkill", label = "Dex. Required", { format = "{output:ReqDexString}", { breakdown = "ReqDex" }, }, },
- { notFlag = "minionSkill", label = "Int. Required", { format = "{output:ReqIntString}", { breakdown = "ReqInt" }, }, },
-} },
-{ 1, "Life", 2, "Life", colorCodes.DEFENCE, {
- extra = "{0:output:LifeUnreserved}/{0:output:Life}",
- { label = "Base from Gear", { format = "{0:mod:1}", { modName = "Life", modType = "BASE", modSource = "Item" }, }, },
- { label = "Inc. from Tree", { format = "{0:mod:1}%", { modName = "Life", modType = "INC", modSource = "Tree" }, }, },
- { label = "Total Base", { format = "{0:mod:1}", { modName = "Life", modType = "BASE" }, }, },
- { label = "Total Increased", { format = "{0:mod:1}%", { modName = "Life", modType = "INC", }, }, },
- { label = "Total More", { format = "{0:mod:1}%", { modName = "Life", modType = "MORE", }, }, },
- { label = "Total", { format = "{0:output:Life}", { breakdown = "Life" }, }, },
- { label = "Reserved", { format = "{0:output:LifeReserved} ({0:output:LifeReservedPercent}%)", { breakdown = "LifeReserved" }, }, },
- { label = "Unreserved", { format = "{0:output:LifeUnreserved} ({0:output:LifeUnreservedPercent}%)" }, },
- { label = "Regen", { format = "{1:output:LifeRegen} ({1:output:LifeRegenPercent}%)",
- { label = "Sources", modName = { "LifeRegen", "LifeRegenPercent" }, modType = "BASE" },
- { label = "Recovery modifiers", modName = "LifeRecovery" },
- }, },
-} },
-{ 1, "Mana", 2, "Mana", colorCodes.DEFENCE, {
- extra = "{0:output:ManaUnreserved}/{0:output:Mana}",
- notFlag = "minionSkill",
- { label = "Base from Gear", { format = "{0:mod:1}", { modName = "Mana", modType = "BASE", modSource = "Item" }, }, },
- { label = "Inc. from Tree", { format = "{0:mod:1}%", { modName = "Mana", modType = "INC", modSource = "Tree" }, }, },
- { label = "Total Base", { format = "{0:mod:1}", { modName = "Mana", modType = "BASE" }, }, },
- { label = "Total Increased", { format = "{0:mod:1}%", { modName = "Mana", modType = "INC" }, }, },
- { label = "Total", { format = "{0:output:Mana}", { breakdown = "Mana" }, }, },
- { label = "Reserved", { format = "{0:output:ManaReserved} ({0:output:ManaReservedPercent}%)", { breakdown = "ManaReserved" }, }, },
- { label = "Unreserved", { format = "{0:output:ManaUnreserved} ({0:output:ManaUnreservedPercent}%)" }, },
- { label = "Increased Regen", { format = "{0:mod:1}%", { modName = "ManaRegen", modType = "INC" }, }, },
- { label = "Regen", { format = "{1:output:ManaRegen}",
- { breakdown = "ManaRegen" },
- { label = "Sources", modName = { "ManaRegen", "ManaRegenPercent" }, modType = "BASE" },
- { label = "Recovery modifiers", modName = "ManaRecovery" },
- }, },
-} },
-{ 1, "EnergyShield", 2, "Energy Shield", colorCodes.DEFENCE, {
- extra = "{0:output:EnergyShield}",
- { label = "Base from Armours", { format = "{0:output:Gear:EnergyShield}", }, },
- { label = "Global Base", { format = "{0:mod:1}", { modName = "EnergyShield", modType = "BASE" }, }, },
- { label = "Inc. from Tree", { format = "{0:mod:1}%", { modName = "EnergyShield", modType = "INC", modSource = "Tree" }, }, },
- { label = "Total Increased", { format = "{0:mod:1}%", { modName = { "EnergyShield", "Defences" }, modType = "INC" }, }, },
- { label = "Total More", { format = "{0:mod:1}%", { modName = { "EnergyShield", "Defences" }, modType = "MORE" }, }, },
- { label = "Total", { format = "{0:output:EnergyShield}", { breakdown = "EnergyShield" }, }, },
- { label = "Recharge Rate", { format = "{1:output:EnergyShieldRecharge}",
- { breakdown = "EnergyShieldRecharge" },
- { modName = { "EnergyShieldRecharge", "EnergyShieldRecovery" }, },
- }, },
- { label = "Recharge Delay", { format = "{2:output:EnergyShieldRechargeDelay}s",
- { breakdown = "EnergyShieldRechargeDelay" },
- { modName = "EnergyShieldRechargeFaster" },
- }, },
- { label = "Regen", { format = "{1:output:EnergyShieldRegen} ({1:output:EnergyShieldRegenPercent}%)",
- { label = "Sources", modName = { "EnergyShieldRegen", "EnergyShieldRegenPercent" }, modType = "BASE" },
- { label = "Recovery modifiers", modName = "EnergyShieldRecovery" },
- }, },
-} },
-{ 1, "Armour", 3, "Armour", colorCodes.DEFENCE, {
- extra = "{0:output:Armour}",
- { label = "Base from Armours", { format = "{0:output:Gear:Armour}" }, },
- { label = "Global Base", { format = "{0:mod:1}", { modName = "Armour", modType = "BASE" }, }, },
- { label = "Inc. from Tree", { format = "{0:mod:1}%", { modName = { "Armour", "ArmourAndEvasion" }, modType = "INC", modSource = "Tree", }, }, },
- { label = "Total Increased", { format = "{0:mod:1}%", { modName = { "Armour", "ArmourAndEvasion", "Defences" }, modType = "INC" }, }, },
- { label = "Total More", { format = "{0:mod:1}%", { modName = { "Armour", "ArmourAndEvasion", "Defences" }, modType = "MORE" }, }, },
- { label = "Total", { format = "{0:output:Armour}", { breakdown = "Armour" }, }, },
- { label = "Phys. Dmg. Reduct", { format = "{0:output:PhysicalDamageReduction}%",
- { breakdown = "PhysicalDamageReduction" },
- { modName = { "PhysicalDamageReduction" } },
- }, },
-} },
-{ 1, "Evasion", 3, "Evasion", colorCodes.DEFENCE, {
- extra = "{0:output:Evasion}",
- { label = "Base from Armours", { format = "{0:output:Gear:Evasion}", }, },
- { label = "Global Base", { format = "{0:mod:1}", { modName = "Evasion", modType = "BASE" }, }, },
- { label = "Inc. from Tree", { format = "{0:mod:1}%", { modName = { "Evasion", "ArmourAndEvasion" }, modType = "INC", modSource = "Tree" }, }, },
- { label = "Total Increased", { format = "{0:mod:1}%", { modName = { "Evasion", "ArmourAndEvasion", "Defences" }, modType = "INC" }, }, },
- { label = "Total More", { format = "{0:mod:1}%", { modName = { "Evasion", "ArmourAndEvasion", "Defences" }, modType = "MORE" }, }, },
- { label = "Total", { format = "{0:output:Evasion}", { breakdown = "Evasion" }, }, },
- { label = "Evade Chance", { format = "{0:output:EvadeChance}%",
- { breakdown = "EvadeChance" },
- { modName = { "CannotEvade" } },
- { label = "Enemy modifiers", modName = { "Accuracy", "HitChance" }, enemy = true },
- }, },
-} },
-{ 1, "Resist", 3, "Resists", colorCodes.DEFENCE, {
- extra = colorCodes.FIRE.."{0:output:FireResist}+{0:output:FireResistOverCap}^7/"..colorCodes.COLD.."{0:output:ColdResist}+{0:output:ColdResistOverCap}^7/"..colorCodes.LIGHTNING.."{0:output:LightningResist}+{0:output:LightningResistOverCap}",
- { label = "Fire Resist", { format = "{0:output:FireResist}% (+{0:output:FireResistOverCap}%)",
- { breakdown = "FireResist" },
- { modName = { "FireResistMax", "FireResist", "ElementalResist" }, },
- }, },
- { label = "Cold Resist", { format = "{0:output:ColdResist}% (+{0:output:ColdResistOverCap}%)",
- { breakdown = "ColdResist" },
- { modName = { "ColdResistMax", "ColdResist", "ElementalResist" }, },
- }, },
- { label = "Lightning Resist", { format = "{0:output:LightningResist}% (+{0:output:LightningResistOverCap}%)",
- { breakdown = "LightningResist" },
- { modName = { "LightningResistMax", "LightningResist", "ElementalResist" }, },
- }, },
- { label = "Chaos Resist", { format = "{0:output:ChaosResist}% (+{0:output:ChaosResistOverCap}%)",
- { breakdown = "ChaosResist" },
- { modName = { "ChaosResistMax", "ChaosResist" }, },
- }, },
-} },
-{ 1, "DamageTaken", 3, "Damage Taken", colorCodes.DEFENCE, {
- { label = "Physical Hit/DoT", { format = "x {2:output:PhysicalTakenHitMult} / x {2:output:PhysicalTakenDotMult}",
- { breakdown = "PhysicalTakenHitMult" },
- { breakdown = "PhysicalTakenDotMult" },
- { modName = { "DamageTaken", "DamageTakenWhenHit", "DamageTakenOverTime", "PhysicalDamageTaken", "PhysicalDamageTakenWhenHit", "PhysicalDamageTakenOverTime", "PhysicalDamageTakenAsFire", "PhysicalDamageTakenAsCold", "PhysicalDamageTakenAsLightning", "PhysicalDamageTakenAsChaos" } },
- }, },
- { label = "Fire Hit/DoT", { format = "x {2:output:FireTakenHitMult} / x {2:output:FireTakenDotMult}",
- { breakdown = "FireTakenHitMult" },
- { breakdown = "FireTakenDotMult" },
- { modName = { "DamageTaken", "DamageTakenWhenHit", "DamageTakenOverTime", "FireDamageTaken", "FireDamageTakenWhenHit", "FireDamageTakenOverTime", "ElementalDamageTaken", "FireDamageTakenAsPhysical", "FireDamageDamageTakenAsCold", "FireDamageTakenAsLightning", "FireDamageTakenAsChaos", "ElementalDamageTakenAsPhysical", "ElementalDamageTakenAsChaos" } },
- }, },
- { label = "Cold Hit/DoT", { format = "x {2:output:ColdTakenHitMult} / x {2:output:ColdTakenDotMult}",
- { breakdown = "ColdTakenHitMult" },
- { breakdown = "ColdTakenDotMult" },
- { modName = { "DamageTaken", "DamageTakenWhenHit", "DamageTakenOverTime", "ColdDamageTaken", "ColdDamageTakenWhenHit", "ColdDamageTakenOverTime", "ElementalDamageTaken", "ColdDamageTakenAsPhysical", "ColdDamageTakenAsFire", "ColdDamageTakenAsLightning", "ColdDamageTakenAsChaos", "ElementalDamageTakenAsPhysical", "ElementalDamageTakenAsChaos" } },
- }, },
- { label = "Lightning Hit/DoT", { format = "x {2:output:LightningTakenHitMult} / x {2:output:LightningTakenDotMult}",
- { breakdown = "LightningTakenHitMult" },
- { breakdown = "LightningTakenDotMult" },
- { modName = { "DamageTaken", "DamageTakenWhenHit", "DamageTakenOverTime", "LightningDamageTaken", "LightningDamageTakenWhenHit", "LightningDamageTakenOverTime", "ElementalDamageTaken", "LightningDamageTakenAsPhysical", "LightningDamageTakenAsFire", "LightningDamageTakenAsCold", "LightningDamageTakenAsChaos", "ElementalDamageTakenAsPhysical", "ElementalDamageTakenAsChaos" } },
- }, },
- { label = "Chaos Hit/DoT", { format = "x {2:output:ChaosTakenHitMult} / x {2:output:ChaosTakenDotMult}",
- { breakdown = "ChaosTakenHitMult" },
- { breakdown = "ChaosTakenDotMult" },
- { modName = { "DamageTaken", "DamageTakenWhenHit", "DamageTakenOverTime", "ChaosDamageTaken", "ChaosDamageTakenWhenHit", "ChaosDamageTakenOverTime", "ChaosDamageTakenAsPhysical", "ChaosDamageTakenAsFire", "ChaosDamageTakenAsCold", "ChaosDamageTakenAsLightning" } },
- }, },
- { label = "Mind over Matter", haveOutput = "MindOverMatter", { format = "{0:output:MindOverMatter}%",
- { breakdown = "MindOverMatter" },
- { modName = "DamageTakenFromManaBeforeLife" },
- }, },
- { label = "Total Degen", haveOutput = "TotalDegen", { format = "{1:output:TotalDegen}",
- { breakdown = "TotalDegen" },
- { label = "Sources", modName = { "PhysicalDegen", "FireDegen", "ColdDegen", "LightningDegen", "ChaosDegen" }, modType = "BASE" },
- }, },
- { label = "Net Regen", haveOutput = "NetRegen", { format = "{1:output:NetRegen}", { breakdown = "NetRegen" }, }, },
-} },
-{ 1, "MiscDefences", 3, "Other Defences", colorCodes.DEFENCE, {
- { label = "Movement Speed", { format = "x {2:output:EffectiveMovementSpeedMod}", { breakdown = "EffectiveMovementSpeedMod" }, { modName = "MovementSpeed" }, }, },
- { label = "Dodge Chance", { format = "{0:output:AttackDodgeChance}%", { modName = "AttackDodgeChance" }, }, },
- { label = "Spell Ddg. Chance", { format = "{0:output:SpellDodgeChance}%", { modName = "SpellDodgeChance" }, }, },
- { label = "Block Chance", { format = "{0:output:BlockChance}%",
- { breakdown = "BlockChance" },
- { modName = "BlockChance" },
- }, },
- { label = "Spell Block Chance", { format = "{0:output:SpellBlockChance}%",
- { breakdown = "SpellBlockChance" },
- { modName = { "SpellBlockChance", "BlockChanceConv" }, },
- }, },
- { label = "Melee Avoid Ch.", { format = "{0:output:MeleeAvoidChance}%", { breakdown = "MeleeAvoidChance" }, }, },
- { label = "Projectile Avoid Ch.", { format = "{0:output:ProjectileAvoidChance}%", { breakdown = "ProjectileAvoidChance" }, }, },
- { label = "Spell Avoid Ch.", { format = "{0:output:SpellAvoidChance}%", { breakdown = "SpellAvoidChance" }, }, },
- { label = "Stun Avoid Chance", { format = "{0:output:StunAvoidChance}%", { modName = "AvoidStun" }, }, },
- { label = "Stun Duration", { format = "{2:output:StunDuration}s",
- { breakdown = "StunDuration" },
- { modName = "StunRecovery" },
- }, },
- { label = "Block Duration", { format = "{2:output:BlockDuration}s",
- { breakdown = "BlockDuration" },
- { modName = { "StunRecovery", "BlockRecovery" }, },
- }, },
- { label = "Light Radius Mod", { format = "x {2:output:LightRadiusMod}", { breakdown = "LightRadiusMod" }, { modName = "LightRadius" }, }, },
-} },
-}
\ No newline at end of file
diff --git b/Modules/CalcSections-3_0.lua a/Modules/CalcSections-3_0.lua
deleted file mode 100644
index 3dc52081..00000000
--- b/Modules/CalcSections-3_0.lua
+++ /dev/null
@@ -1,1002 +0,0 @@
--- Path of Building
---
--- Module: Calc Sections
--- List of sections for the Calcs tab
---
-
--- Commonly used modifier lists
-local physicalHitTaken = {
- "DamageTaken", "PhysicalDamageTaken"
-}
-local lightningHitTaken = {
- "DamageTaken", "LightningDamageTaken", "ElementalDamageTaken", "LightningResist", "ElementalResist"
-}
-local coldHitTaken = {
- "DamageTaken", "ColdDamageTaken", "ElementalDamageTaken", "ColdResist", "ElementalResist"
-}
-local fireHitTaken = {
- "DamageTaken", "FireDamageTaken", "ElementalDamageTaken", "FireResist", "ElementalResist"
-}
-local chaosHitTaken = {
- "DamageTaken", "ChaosDamageTaken", "ChaosResist"
-}
-local physicalConvert = {
- "SkillPhysicalDamageConvertToLightning", "SkillPhysicalDamageConvertToCold", "SkillPhysicalDamageConvertToFire", "SkillPhysicalDamageConvertToChaos",
- "PhysicalDamageConvertToLightning", "PhysicalDamageConvertToCold", "PhysicalDamageConvertToFire", "PhysicalDamageConvertToChaos", "NonChaosDamageConvertToChaos",
- "PhysicalDamageGainAsLightning", "PhysicalDamageGainAsCold", "PhysicalDamageGainAsFire", "PhysicalDamageGainAsChaos", "NonChaosDamageGainAsChaos"
-}
-local lightningConvert = {
- "SkillLightningDamageConvertToCold", "SkillLightningDamageConvertToFire", "SkillLightningDamageConvertToChaos",
- "LightningDamageConvertToCold", "LightningDamageConvertToFire", "LightningDamageConvertToChaos", "ElementalDamageConvertToChaos", "NonChaosDamageConvertToChaos",
- "LightningDamageGainAsCold", "LightningDamageGainAsFire", "LightningDamageGainAsChaos", "ElementalDamageGainAsChaos", "NonChaosDamageGainAsChaos"
-}
-local coldConvert = {
- "SkillColdDamageConvertToFire", "SkillColdDamageConvertToChaos",
- "ColdDamageConvertToFire", "ColdDamageConvertToChaos", "ElementalDamageConvertToChaos", "NonChaosDamageConvertToChaos",
- "ColdDamageGainAsFire", "ColdDamageGainAsChaos", "ElementalDamageGainAsChaos", "NonChaosDamageGainAsChaos"
-}
-local fireConvert = {
- "SkillFireDamageConvertToChaos",
- "FireDamageConvertToChaos", "ElementalDamageConvertToChaos", "NonChaosDamageConvertToChaos",
- "FireDamageGainAsChaos", "ElementalDamageGainAsChaos", "NonChaosDamageGainAsChaos"
-}
-
-return {
-{ 3, "HitDamage", 1, "Skill Hit Damage", colorCodes.OFFENCE, {
- extra = "{output:DisplayDamage}",
- flag = "hit",
- colWidth = 95,
- {
- { format = "All Types:", },
- { format = "Physical:" },
- { format = "Lightning:" },
- { format = "Cold:" },
- { format = "Fire:" },
- { format = "Chaos:" },
- },
- { label = "Added Min",
- { },
- { format = "{0:mod:1,2}",
- { label = "Player modifiers", modName = "PhysicalMin", modType = "BASE", cfg = "skill" },
- { label = "Enemy modifiers", modName = "SelfPhysicalMin", modType = "BASE", enemy = true, cfg = "skill" },
- },
- { format = "{0:mod:1,2}",
- { label = "Player modifiers", modName = "LightningMin", modType = "BASE", cfg = "skill" },
- { label = "Enemy modifiers", modName = "SelfLightningMin", modType = "BASE", enemy = true, cfg = "skill" },
- },
- { format = "{0:mod:1,2}",
- { label = "Player modifiers", modName = "ColdMin", modType = "BASE", cfg = "skill" },
- { label = "Enemy modifiers", modName = "SelfColdMin", modType = "BASE", enemy = true, cfg = "skill" },
- },
- { format = "{0:mod:1,2}",
- { label = "Player modifiers", modName = "FireMin", modType = "BASE", cfg = "skill" },
- { label = "Enemy modifiers", modName = "SelfFireMin", modType = "BASE", enemy = true, cfg = "skill" },
- },
- { format = "{0:mod:1,2}",
- { label = "Player modifiers", modName = "ChaosMin", modType = "BASE", cfg = "skill" },
- { label = "Enemy modifiers", modName = "SelfChaosMin", modType = "BASE", enemy = true, cfg = "skill" },
- },
- },
- { label = "Added Max",
- { },
- { format = "{0:mod:1,2}",
- { label = "Player modifiers", modName = "PhysicalMax", modType = "BASE", cfg = "skill" },
- { label = "Enemy modifiers", modName = "SelfPhysicalMax", modType = "BASE", enemy = true, cfg = "skill" },
- },
- { format = "{0:mod:1,2}",
- { label = "Player modifiers", modName = "LightningMax", modType = "BASE", cfg = "skill" },
- { label = "Enemy modifiers", modName = "SelfLightningMax", modType = "BASE", enemy = true, cfg = "skill" },
- },
- { format = "{0:mod:1,2}",
- { label = "Player modifiers", modName = "ColdMax", modType = "BASE", cfg = "skill" },
- { label = "Enemy modifiers", modName = "SelfColdMax", modType = "BASE", enemy = true, cfg = "skill" },
- },
- { format = "{0:mod:1,2}",
- { label = "Player modifiers", modName = "FireMax", modType = "BASE", cfg = "skill" },
- { label = "Enemy modifiers", modName = "SelfFireMax", modType = "BASE", enemy = true, cfg = "skill" },
- },
- { format = "{0:mod:1,2}",
- { label = "Player modifiers", modName = "ChaosMax", modType = "BASE", cfg = "skill" },
- { label = "Enemy modifiers", modName = "SelfChaosMax", modType = "BASE", enemy = true, cfg = "skill" },
- },
- },
- -- Skill Hit Damage
- { label = "Total Increased", notFlag = "attack",
- { format = "{0:mod:1}%", { modName = "Damage", modType = "INC", cfg = "skill" }, },
- { format = "{0:mod:1}%", { modName = "PhysicalDamage", modType = "INC", cfg = "skill" }, },
- { format = "{0:mod:1}%", { modName = { "LightningDamage", "ElementalDamage" }, modType = "INC", cfg = "skill" }, },
- { format = "{0:mod:1}%", { modName = { "ColdDamage", "ElementalDamage" }, modType = "INC", cfg = "skill" }, },
- { format = "{0:mod:1}%", { modName = { "FireDamage", "ElementalDamage" }, modType = "INC", cfg = "skill" }, },
- { format = "{0:mod:1}%", { modName = "ChaosDamage", modType = "INC", cfg = "skill" }, },
- },
- { label = "Total More", notFlag = "attack",
- { format = "{0:mod:1}%", { modName = "Damage", modType = "MORE", cfg = "skill" }, },
- { format = "{0:mod:1}%", { modName = "PhysicalDamage", modType = "MORE", cfg = "skill" }, },
- { format = "{0:mod:1}%", { modName = { "LightningDamage", "ElementalDamage" }, modType = "MORE", cfg = "skill" }, },
- { format = "{0:mod:1}%", { modName = { "ColdDamage", "ElementalDamage" }, modType = "MORE", cfg = "skill" }, },
- { format = "{0:mod:1}%", { modName = { "FireDamage", "ElementalDamage" }, modType = "MORE", cfg = "skill" }, },
- { format = "{0:mod:1}%", { modName = "ChaosDamage", modType = "MORE", cfg = "skill" }, },
- },
- { label = "Effective DPS Mod", notFlag = "attack", flag = "effective",
- { },
- { format = "x {3:output:PhysicalEffMult}",
- { breakdown = "PhysicalEffMult" },
- { label = "Enemy modifiers", modName = physicalHitTaken, enemy = true, cfg = "skill" },
- },
- { format = "x {3:output:LightningEffMult}",
- { breakdown = "LightningEffMult" },
- { label = "Player modifiers", modName = { "LightningPenetration", "ElementalPenetration" }, cfg = "skill" },
- { label = "Enemy modifiers", modName = lightningHitTaken, enemy = true, cfg = "skill" },
- },
- { format = "x {3:output:ColdEffMult}",
- { breakdown = "ColdEffMult" },
- { label = "Player modifiers", modName = { "ColdPenetration", "ElementalPenetration" }, cfg = "skill" },
- { label = "Enemy modifiers", modName = coldHitTaken, enemy = true, cfg = "skill" },
- },
- { format = "x {3:output:FireEffMult}",
- { breakdown = "FireEffMult" },
- { label = "Player modifiers", modName = { "FirePenetration", "ElementalPenetration" }, cfg = "skill" },
- { label = "Enemy modifiers", modName = fireHitTaken, enemy = true, cfg = "skill" },
- },
- { format = "x {3:output:ChaosEffMult}",
- { breakdown = "ChaosEffMult" },
- { label = "Player modifiers", modName = "ChaosPenetration", cfg = "skill" },
- { label = "Enemy modifiers", modName = chaosHitTaken, enemy = true, cfg = "skill" },
- },
- },
- { label = "Skill Hit Damage", textSize = 12, notFlag = "attack",
- { format = "{0:output:TotalMin} to {0:output:TotalMax}", },
- { format = "{0:output:PhysicalMin} to {0:output:PhysicalMax}",
- { breakdown = "Physical" },
- { label = "Conversions", modType = "BASE", cfg = "skill", modName = physicalConvert },
- },
- { format = "{0:output:LightningMin} to {0:output:LightningMax}",
- { breakdown = "Lightning" },
- { label = "Conversions", modType = "BASE", cfg = "skill", modName = lightningConvert },
- },
- { format = "{0:output:ColdMin} to {0:output:ColdMax}",
- { breakdown = "Cold" },
- { label = "Conversions", modType = "BASE", cfg = "skill", modName = coldConvert },
- },
- { format = "{0:output:FireMin} to {0:output:FireMax}",
- { breakdown = "Fire" },
- { label = "Conversions", modType = "BASE", cfg = "skill", modName = fireConvert },
- },
- { format = "{0:output:ChaosMin} to {0:output:ChaosMax}",
- { breakdown = "Chaos" },
- },
- },
- { label = "Skill Average Hit", notFlag = "attack", { format = "{1:output:AverageHit}", { breakdown = "AverageHit" }, }, },
- -- Main Hand Hit Damage
- { label = "MH Total Increased", bgCol = colorCodes.MAINHANDBG, flag = "weapon1Attack",
- { format = "{0:mod:1}%", { modName = "Damage", modType = "INC", cfg = "weapon1" }, },
- { format = "{0:mod:1}%", { modName = "PhysicalDamage", modType = "INC", cfg = "weapon1" }, },
- { format = "{0:mod:1}%", { modName = { "LightningDamage", "ElementalDamage" }, modType = "INC", cfg = "weapon1" }, },
- { format = "{0:mod:1}%", { modName = { "ColdDamage", "ElementalDamage" }, modType = "INC", cfg = "weapon1" }, },
- { format = "{0:mod:1}%", { modName = { "FireDamage", "ElementalDamage" }, modType = "INC", cfg = "weapon1" }, },
- { format = "{0:mod:1}%", { modName = "ChaosDamage", modType = "INC", cfg = "weapon1" }, },
- },
- { label = "MH Total More", bgCol = colorCodes.MAINHANDBG, flag = "weapon1Attack",
- { format = "{0:mod:1}%", { modName = "Damage", modType = "MORE", cfg = "weapon1" }, },
- { format = "{0:mod:1}%", { modName = "PhysicalDamage", modType = "MORE", cfg = "weapon1" }, },
- { format = "{0:mod:1}%", { modName = { "LightningDamage", "ElementalDamage" }, modType = "MORE", cfg = "weapon1" }, },
- { format = "{0:mod:1}%", { modName = { "ColdDamage", "ElementalDamage" }, modType = "MORE", cfg = "weapon1" }, },
- { format = "{0:mod:1}%", { modName = { "FireDamage", "ElementalDamage" }, modType = "MORE", cfg = "weapon1" }, },
- { format = "{0:mod:1}%", { modName = "ChaosDamage", modType = "MORE", cfg = "weapon1" }, },
- },
- { label = "MH Eff. DPS Mod", bgCol = colorCodes.MAINHANDBG, flagList = {"weapon1Attack","effective"},
- { },
- { format = "x {3:output:MainHand.PhysicalEffMult}",
- { breakdown = "MainHand.PhysicalEffMult" },
- { label = "Enemy modifiers", modName = physicalHitTaken, enemy = true, cfg = "weapon1" },
- },
- { format = "x {3:output:MainHand.LightningEffMult}",
- { breakdown = "MainHand.LightningEffMult" },
- { label = "Player modifiers", modName = { "LightningPenetration", "ElementalPenetration" }, cfg = "weapon1" },
- { label = "Enemy modifiers", modName = lightningHitTaken, enemy = true, cfg = "weapon1" },
- },
- { format = "x {3:output:MainHand.ColdEffMult}",
- { breakdown = "MainHand.ColdEffMult" },
- { label = "Player modifiers", modName = { "ColdPenetration", "ElementalPenetration" }, cfg = "weapon1" },
- { label = "Enemy modifiers", modName = coldHitTaken, enemy = true, cfg = "weapon1" },
- },
- { format = "x {3:output:MainHand.FireEffMult}",
- { breakdown = "MainHand.FireEffMult" },
- { label = "Player modifiers", modName = { "FirePenetration", "ElementalPenetration" }, cfg = "weapon1" },
- { label = "Enemy modifiers", modName = fireHitTaken, enemy = true, cfg = "weapon1" },
- },
- { format = "x {3:output:MainHand.ChaosEffMult}",
- { breakdown = "MainHand.ChaosEffMult" },
- { label = "Player modifiers", modName = "ChaosPenetration", cfg = "weapon1" },
- { label = "Enemy modifiers", modName = chaosHitTaken, enemy = true, cfg = "weapon1" },
- },
- },
- { label = "MH Hit Damage", bgCol = colorCodes.MAINHANDBG, textSize = 12, flag = "weapon1Attack",
- { format = "{0:output:MainHand.TotalMin} to {0:output:MainHand.TotalMax}", },
- { format = "{0:output:MainHand.PhysicalMin} to {0:output:MainHand.PhysicalMax}",
- { breakdown = "MainHand.Physical" },
- { label = "Conversions", modType = "BASE", cfg = "weapon1", modName = physicalConvert },
- },
- { format = "{0:output:MainHand.LightningMin} to {0:output:MainHand.LightningMax}",
- { breakdown = "MainHand.Lightning" },
- { label = "Conversions", modType = "BASE", cfg = "weapon1", modName = lightningConvert },
- },
- { format = "{0:output:MainHand.ColdMin} to {0:output:MainHand.ColdMax}",
- { breakdown = "MainHand.Cold" },
- { label = "Conversions", modType = "BASE", cfg = "weapon1", modName = coldConvert },
- },
- { format = "{0:output:MainHand.FireMin} to {0:output:MainHand.FireMax}",
- { breakdown = "MainHand.Fire" },
- { label = "Conversions", modType = "BASE", cfg = "weapon1", modName = fireConvert },
- },
- { format = "{0:output:MainHand.ChaosMin} to {0:output:MainHand.ChaosMax}",
- { breakdown = "MainHand.Chaos" },
- },
- },
- { label = "MH Average Hit", bgCol = colorCodes.MAINHANDBG, flag = "weapon1Attack", { format = "{1:output:MainHand.AverageHit}", { breakdown = "MainHand.AverageHit" }, }, },
- -- Off Hand Hit Damage
- { label = "OH Total Increased", bgCol = colorCodes.OFFHANDBG, flag = "weapon2Attack",
- { format = "{0:mod:1}%", { modName = "Damage", modType = "INC", cfg = "weapon2" }, },
- { format = "{0:mod:1}%", { modName = "PhysicalDamage", modType = "INC", cfg = "weapon2" }, },
- { format = "{0:mod:1}%", { modName = { "LightningDamage", "ElementalDamage" }, modType = "INC", cfg = "weapon2" }, },
- { format = "{0:mod:1}%", { modName = { "ColdDamage", "ElementalDamage" }, modType = "INC", cfg = "weapon2" }, },
- { format = "{0:mod:1}%", { modName = { "FireDamage", "ElementalDamage" }, modType = "INC", cfg = "weapon2" }, },
- { format = "{0:mod:1}%", { modName = "ChaosDamage", modType = "INC", cfg = "weapon2" }, },
- },
- { label = "OH Total More", bgCol = colorCodes.OFFHANDBG, flag = "weapon2Attack",
- { format = "{0:mod:1}%", { modName = "Damage", modType = "MORE", cfg = "weapon2" }, },
- { format = "{0:mod:1}%", { modName = "PhysicalDamage", modType = "MORE", cfg = "weapon2" }, },
- { format = "{0:mod:1}%", { modName = { "LightningDamage", "ElementalDamage" }, modType = "MORE", cfg = "weapon2" }, },
- { format = "{0:mod:1}%", { modName = { "ColdDamage", "ElementalDamage" }, modType = "MORE", cfg = "weapon2" }, },
- { format = "{0:mod:1}%", { modName = { "FireDamage", "ElementalDamage" }, modType = "MORE", cfg = "weapon2" }, },
- { format = "{0:mod:1}%", { modName = "ChaosDamage", modType = "MORE", cfg = "weapon2" }, },
- },
- { label = "OH Eff. DPS Mod", bgCol = colorCodes.OFFHANDBG, flagList = {"weapon2Attack","effective"},
- { },
- { format = "x {3:output:OffHand.PhysicalEffMult}",
- { breakdown = "OffHand.PhysicalEffMult" },
- { label = "Enemy modifiers", modName = physicalHitTaken, enemy = true, cfg = "weapon2" },
- },
- { format = "x {3:output:OffHand.LightningEffMult}",
- { breakdown = "OffHand.LightningEffMult" },
- { label = "Player modifiers", modName = { "LightningPenetration", "ElementalPenetration" }, cfg = "weapon2" },
- { label = "Enemy modifiers", modName = lightningHitTaken, enemy = true, cfg = "weapon2" },
- },
- { format = "x {3:output:OffHand.ColdEffMult}",
- { breakdown = "OffHand.ColdEffMult" },
- { label = "Player modifiers", modName = { "ColdPenetration", "ElementalPenetration" }, cfg = "weapon2" },
- { label = "Enemy modifiers", modName = coldHitTaken, enemy = true, cfg = "weapon2" },
- },
- { format = "x {3:output:OffHand.FireEffMult}",
- { breakdown = "OffHand.FireEffMult" },
- { label = "Player modifiers", modName = { "FirePenetration", "ElementalPenetration" }, cfg = "weapon2" },
- { label = "Enemy modifiers", modName = fireHitTaken, enemy = true, cfg = "weapon2" },
- },
- { format = "x {3:output:OffHand.ChaosEffMult}",
- { breakdown = "OffHand.ChaosEffMult" },
- { label = "Player modifiers", modName = "ChaosPenetration", cfg = "weapon2" },
- { label = "Enemy modifiers", modName = chaosHitTaken, enemy = true, cfg = "weapon2" },
- },
- },
- { label = "OH Hit Damage", bgCol = colorCodes.OFFHANDBG, textSize = 12, flag = "weapon2Attack",
- { format = "{0:output:OffHand.TotalMin} to {0:output:OffHand.TotalMax}", },
- { format = "{0:output:OffHand.PhysicalMin} to {0:output:OffHand.PhysicalMax}",
- { breakdown = "OffHand.Physical" },
- { label = "Conversions", modType = "BASE", cfg = "weapon2", modName = physicalConvert },
- },
- { format = "{0:output:OffHand.LightningMin} to {0:output:OffHand.LightningMax}",
- { breakdown = "OffHand.Lightning" },
- { label = "Conversions", modType = "BASE", cfg = "weapon2", modName = lightningConvert },
- },
- { format = "{0:output:OffHand.ColdMin} to {0:output:OffHand.ColdMax}",
- { breakdown = "OffHand.Cold" },
- { label = "Conversions", modType = "BASE", cfg = "weapon2", modName = coldConvert },
- },
- { format = "{0:output:OffHand.FireMin} to {0:output:OffHand.FireMax}",
- { breakdown = "OffHand.Fire" },
- { label = "Conversions", modType = "BASE", cfg = "weapon2", modName = fireConvert },
- },
- { format = "{0:output:OffHand.ChaosMin} to {0:output:OffHand.ChaosMax}",
- { breakdown = "OffHand.Chaos" },
- },
- },
- { label = "OH Average Hit", bgCol = colorCodes.OFFHANDBG, flag = "weapon2Attack", { format = "{1:output:OffHand.AverageHit}", { breakdown = "OffHand.AverageHit" }, }, },
- { label = "Average Damage", flag = "attack", { format = "{1:output:AverageDamage}",
- { breakdown = "MainHand.AverageDamage" },
- { breakdown = "OffHand.AverageDamage" },
- { breakdown = "AverageDamage" },
- }, },
- { label = "Skill DPS", flag = "notAverage", { format = "{1:output:TotalDPS}", { breakdown = "TotalDPS" }, }, },
- { label = "Mana Cost", { format = "{0:output:ManaCost}", { breakdown = "ManaCost" }, { modName = "ManaCost", cfg = "skill" }, }, },
-} },
-{ 3, "Dot", 1, "Skill Damage over Time", colorCodes.OFFENCE, {
- extra = "{1:output:TotalDot} total DoT",
- flag = "dot",
- colWidth = 95,
- { { format = "All Types:", }, { format = "Physical:" }, { format = "Lightning:" }, { format = "Cold:" }, { format = "Fire:" }, { format = "Chaos:" }, },
- { label = "Total Increased",
- { format = "{0:mod:1}%", { modName = "Damage", modType = "INC", cfg = "dot" }, },
- { format = "{0:mod:1}%", { modName = "PhysicalDamage", modType = "INC", cfg = "dotPhysical" }, },
- { format = "{0:mod:1}%", { modName = { "LightningDamage", "ElementalDamage" }, modType = "INC", cfg = "dotLightning" }, },
- { format = "{0:mod:1}%", { modName = { "ColdDamage", "ElementalDamage" }, modType = "INC", cfg = "dotCold" }, },
- { format = "{0:mod:1}%", { modName = { "FireDamage", "ElementalDamage" }, modType = "INC", cfg = "dotFire" }, },
- { format = "{0:mod:1}%", { modName = "ChaosDamage", modType = "INC", cfg = "dotChaos" }, },
- },
- { label = "Total More",
- { format = "{0:mod:1}%", { modName = "Damage", modType = "MORE", cfg = "dot" }, },
- { format = "{0:mod:1}%", { modName = "PhysicalDamage", modType = "MORE", cfg = "dotPhysical" }, },
- { format = "{0:mod:1}%", { modName = { "LightningDamage", "ElementalDamage" }, modType = "MORE", cfg = "dotLightning" }, },
- { format = "{0:mod:1}%", { modName = { "ColdDamage", "ElementalDamage" }, modType = "MORE", cfg = "dotCold" }, },
- { format = "{0:mod:1}%", { modName = { "FireDamage", "ElementalDamage" }, modType = "MORE", cfg = "dotFire" }, },
- { format = "{0:mod:1}%", { modName = "ChaosDamage", modType = "MORE", cfg = "dotChaos" }, },
- },
- { label = "Effective DPS Mod", flag = "effective",
- { },
- { format = "x {3:output:PhysicalDotEffMult}",
- { breakdown = "PhysicalDotEffMult" },
- { label = "Enemy modifiers", modName = { "DamageTaken", "DamageTakenOverTime", "PhysicalDamageTaken", "PhysicalDamageTakenOverTime", "PhysicalDamageReduction" }, enemy = true },
- },
- { format = "x {3:output:LightningDotEffMult}",
- { breakdown = "LightningDotEffMult" },
- { label = "Enemy modifiers", modName = { "DamageTaken", "DamageTakenOverTime", "LightningDamageTaken", "LightningDamageTakenOverTime", "ElementalDamageTaken", "LightningResist", "ElementalResist" }, enemy = true },
- },
- { format = "x {3:output:ColdDotEffMult}",
- { breakdown = "ColdDotEffMult" },
- { label = "Enemy modifiers", modName = { "DamageTaken", "DamageTakenOverTime", "ColdDamageTaken", "ColdDamageTakenOverTime", "ElementalDamageTaken", "ColdResist", "ElementalResist" }, enemy = true },
- },
- { format = "x {3:output:FireDotEffMult}",
- { breakdown = "FireDotEffMult" },
- { label = "Enemy modifiers", modName = { "DamageTaken", "DamageTakenOverTime", "FireDamageTaken", "FireDamageTakenOverTime", "ElementalDamageTaken", "FireResist", "ElementalResist" }, enemy = true },
- },
- { format = "x {3:output:ChaosDotEffMult}",
- { breakdown = "ChaosDotEffMult" },
- { label = "Enemy modifiers", modName = { "DamageTaken", "DamageTakenOverTime", "ChaosDamageTaken", "ChaosDamageTakenOverTime", "ChaosResist" }, enemy = true },
- },
- },
- { label = "Damage over Time",
- { format = "{1:output:TotalDot}", },
- { format = "{1:output:PhysicalDot}", { breakdown = "PhysicalDot" }, },
- { format = "{1:output:LightningDot}", { breakdown = "LightningDot" }, },
- { format = "{1:output:ColdDot}", { breakdown = "ColdDot" }, },
- { format = "{1:output:FireDot}", { breakdown = "FireDot" }, },
- { format = "{1:output:ChaosDot}", { breakdown = "ChaosDot" }, },
- },
- { label = "Mana Cost", { format = "{0:output:ManaCost}", { breakdown = "ManaCost" }, { modName = "ManaCost", cfg = "skill" }, }, },
-} },
-{ 1, "Speed", 1, "Attack/Cast Rate", colorCodes.OFFENCE, {
- extra = "{2:output:Speed}/s",
- { label = "MH Inc. Att. Speed", bgCol = colorCodes.MAINHANDBG, flag = "weapon1Attack", { format = "{0:mod:1}%", { modName = "Speed", modType = "INC", cfg = "weapon1", }, }, },
- { label = "MH More Att. Speed", bgCol = colorCodes.MAINHANDBG, flag = "weapon1Attack", { format = "{0:mod:1}%", { modName = "Speed", modType = "MORE", cfg = "weapon1", }, }, },
- { label = "MH Att. per second", bgCol = colorCodes.MAINHANDBG, flag = "weapon1Attack", { format = "{2:output:MainHand.Speed}", { breakdown = "MainHand.Speed" }, }, },
- { label = "OH Inc. Att. Speed", bgCol = colorCodes.OFFHANDBG, flag = "weapon2Attack", { format = "{0:mod:1}%", { modName = "Speed", modType = "INC", cfg = "weapon2", }, }, },
- { label = "OH More Att. Speed", bgCol = colorCodes.OFFHANDBG, flag = "weapon2Attack", { format = "{0:mod:1}%", { modName = "Speed", modType = "MORE", cfg = "weapon2", }, }, },
- { label = "OH Att. per second", bgCol = colorCodes.OFFHANDBG, flag = "weapon2Attack", { format = "{2:output:OffHand.Speed}", { breakdown = "OffHand.Speed" }, }, },
- { label = "Attacks per second", flag = "bothWeaponAttack", { format = "{2:output:Speed}", { breakdown = "Speed" }, }, },
- { label = "Attack time", flag = "attack", { format = "{2:output:Time}s", }, },
- { label = "Inc. Cast Speed", flag = "spell", { format = "{0:mod:1}%", { modName = "Speed", modType = "INC", cfg = "skill", }, }, },
- { label = "More Cast Speed", flag = "spell", { format = "{0:mod:1}%", { modName = "Speed", modType = "MORE", cfg = "skill", }, }, },
- { label = "Casts per second", flag = "spell", { format = "{2:output:Speed}", { breakdown = "Speed" }, }, },
- { label = "Cast time", flag = "spell", { format = "{2:output:Time}s", }, },
-} },
-{ 1, "Crit", 1, "Crits", colorCodes.OFFENCE, {
- extra = "{2:output:CritChance}% x{2:output:CritMultiplier}",
- flag = "hit",
- -- Skill
- { label = "Inc. Crit Chance", notFlag = "attack", { format = "{0:mod:1,2}%",
- { label = "Player modifiers", modName = "CritChance", modType = "INC", cfg = "skill" },
- { label = "Enemy modifiers", modName = "SelfCritChance", modType = "INC", enemy = true },
- }, },
- { label = "Crit Chance", notFlag = "attack", { format = "{2:output:CritChance}%",
- { breakdown = "CritChance" },
- { label = "Player modifiers", modName = "CritChance", cfg = "skill" },
- { label = "Enemy modifiers", modName = "SelfCritChance", enemy = true },
- }, },
- { label = "Crit Multiplier", notFlag = "attack", { format = "x {2:output:CritMultiplier}",
- { breakdown = "CritMultiplier" },
- { label = "Player modifiers", modName = "CritMultiplier", cfg = "skill" },
- { label = "Enemy modifiers", modName = "SelfCritMultiplier", enemy = true },
- }, },
- { label = "Crit Effect Mod", notFlag = "attack", { format = "x {3:output:CritEffect}", { breakdown = "CritEffect" }, }, },
- -- Main Hand
- { label = "MH Inc. Crit Chance", bgCol = colorCodes.MAINHANDBG, flag = "weapon1Attack", { format = "{0:mod:1,2}%",
- { label = "Player modifiers", modName = "CritChance", modType = "INC", cfg = "weapon1" },
- { label = "Enemy modifiers", modName = "SelfCritChance", modType = "INC", enemy = true },
- }, },
- { label = "MH Crit Chance", bgCol = colorCodes.MAINHANDBG, flag = "weapon1Attack", { format = "{2:output:MainHand.CritChance}%",
- { breakdown = "MainHand.CritChance" },
- { label = "Player modifiers", modName = "CritChance", cfg = "weapon1" },
- { label = "Enemy modifiers", modName = "SelfCritChance", enemy = true },
- }, },
- { label = "MH Crit Multiplier", bgCol = colorCodes.MAINHANDBG, flag = "weapon1Attack", { format = "x {2:output:MainHand.CritMultiplier}",
- { breakdown = "MainHand.CritMultiplier" },
- { label = "Player modifiers", modName = "CritMultiplier", cfg = "weapon1" },
- { label = "Enemy modifiers", modName = "SelfCritMultiplier", enemy = true },
- }, },
- { label = "MH Crit Effect Mod", bgCol = colorCodes.MAINHANDBG, flag = "weapon1Attack", { format = "x {3:output:MainHand.CritEffect}", { breakdown = "MainHand.CritEffect" }, }, },
- -- Off Hand
- { label = "OH Inc. Crit Chance", bgCol = colorCodes.OFFHANDBG, flag = "weapon2Attack", { format = "{0:mod:1,2}%",
- { label = "Player modifiers", modName = "CritChance", modType = "INC", cfg = "weapon2" },
- { label = "Enemy modifiers", modName = "SelfCritChance", modType = "INC", enemy = true },
- }, },
- { label = "OH Crit Chance", bgCol = colorCodes.OFFHANDBG, flag = "weapon2Attack", { format = "{2:output:OffHand.CritChance}%",
- { breakdown = "OffHand.CritChance" },
- { label = "Player modifiers", modName = "CritChance", cfg = "weapon2" },
- { label = "Enemy modifiers", modName = "SelfCritChance", enemy = true },
- }, },
- { label = "OH Crit Multiplier", bgCol = colorCodes.OFFHANDBG, flag = "weapon2Attack", { format = "x {2:output:OffHand.CritMultiplier}",
- { breakdown = "OffHand.CritMultiplier" },
- { label = "Player modifiers", modName = "CritMultiplier", cfg = "weapon2" },
- { label = "Enemy modifiers", modName = "SelfCritMultiplier", enemy = true },
- }, },
- { label = "OH Crit Effect Mod", bgCol = colorCodes.OFFHANDBG, flag = "weapon2Attack", { format = "x {3:output:OffHand.CritEffect}", { breakdown = "OffHand.CritEffect" }, }, },
-} },
-{ 1, "HitChance", 1, "Accuracy", colorCodes.OFFENCE, {
- extra = "{0:output:HitChance}%",
- flag = "attack",
- { label = "MH Accuracy", bgCol = colorCodes.MAINHANDBG, flag = "weapon1Attack", { format = "{0:output:MainHand.Accuracy}",
- { breakdown = "MainHand.Accuracy" },
- { modName = "Accuracy", cfg = "weapon1" },
- }, },
- { label = "MH Chance to Hit", bgCol = colorCodes.MAINHANDBG, flag = "weapon1Attack", { format = "{0:output:MainHand.HitChance}%",
- { breakdown = "MainHand.HitChance" },
- { label = "Enemy Evasion modifiers", modName = "Evasion", enemy = true },
- }, },
- { label = "OH Accuracy", bgCol = colorCodes.OFFHANDBG, flag = "weapon2Attack", { format = "{0:output:OffHand.Accuracy}",
- { breakdown = "OffHand.Accuracy" },
- { modName = "Accuracy", cfg = "weapon2" },
- }, },
- { label = "OH Chance to Hit", bgCol = colorCodes.OFFHANDBG, flag = "weapon2Attack", { format = "{0:output:OffHand.HitChance}%",
- { breakdown = "OffHand.HitChance" },
- { label = "Enemy Evasion modifiers", modName = "Evasion", enemy = true },
- }, },
-} },
-{ 1, "SkillTypeStats", 1, "Skill type-specific Stats", colorCodes.OFFENCE, {
- { label = "Active Minion Limit", haveOutput = "ActiveMinionLimit", { format = "{0:output:ActiveMinionLimit}" } },
- { label = "Skill Cooldown", haveOutput = "Cooldown", { format = "{2:output:Cooldown}s",
- { breakdown = "Cooldown" },
- { modName = "CooldownRecovery", cfg = "skill" },
- }, },
- { label = "Duration Mod", flag = "duration", { format = "x {2:output:DurationMod}",
- { breakdown = "DurationMod"},
- { modName = { "Duration", "SkillAndDamagingAilmentDuration" }, cfg = "skill" },
- }, },
- { label = "Skill Duration", flag = "duration", haveOutput = "Duration", { format = "{2:output:Duration}s", { breakdown = "Duration" }, }, },
- { label = "Secondary Duration", flag = "duration", haveOutput = "DurationSecondary", { format = "{2:output:DurationSecondary}s", { breakdown = "DurationSecondary" }, }, },
- { label = "Aura Duration", haveOutput = "AuraDuration", { format = "{2:output:AuraDuration}s", { breakdown = "AuraDuration" }, }, },
- { label = "Reserve Duration", haveOutput = "ReserveDuration", { format = "{2:output:ReserveDuration}s", { breakdown = "ReserveDuration" }, }, },
- { label = "Max Chain Count", flag = "chaining", { format = "{output:ChainMaxString}", { modName = { "CannotChain", "ChainCountMax" }, cfg = "skill" }, }, },
- { label = "Projectile Count", flag = "projectile", { format = "{0:output:ProjectileCount}", { modName = "ProjectileCount", cfg = "skill" }, }, },
- { label = "Pierce Count", flag = "projectile", { format = "{output:PierceCountString}", { modName = { "CannotPierce", "PierceCount", "PierceAllTargets" }, cfg = "skill" }, }, },
- { label = "Proj. Speed Mod", flag = "projectile", { format = "x {2:output:ProjectileSpeedMod}",
- { breakdown = "ProjectileSpeedMod" },
- { modName = "ProjectileSpeed", cfg = "skill" },
- }, },
- { label = "Area of Effect Mod", haveOutput = "AreaOfEffectMod", { format = "x {2:output:AreaOfEffectMod}",
- { breakdown = "AreaOfEffectMod" },
- { modName = "AreaOfEffect", cfg = "skill" },
- }, },
- { label = "Radius", haveOutput = "AreaOfEffectRadius", { format = "{0:output:AreaOfEffectRadius}", { breakdown = "AreaOfEffectRadius" }, }, },
- { label = "Secondary Radius", haveOutput = "AreaOfEffectRadiusSecondary", { format = "{0:output:AreaOfEffectRadiusSecondary}", { breakdown = "AreaOfEffectRadiusSecondary" }, }, },
- { label = "Tertiary Radius", haveOutput = "AreaOfEffectRadiusTertiary", { format = "{0:output:AreaOfEffectRadiusTertiary}", { breakdown = "AreaOfEffectRadiusTertiary" }, }, },
- { label = "Weapon Range", haveOutput = "WeaponRange", { format = "{0:output:WeaponRange}", { breakdown = "WeaponRange" }, }, },
- { label = "Trap Throw Time", flag = "trap", { format = "{2:output:TrapThrowingTime}s",
- { breakdown = "TrapThrowingTime" },
- { modName = "TrapThrowingSpeed", cfg = "skill" },
- }, },
- { label = "Active Trap Limit", flag = "trap", { format = "{0:output:ActiveTrapLimit}", { modName = "ActiveTrapLimit", cfg = "skill" }, }, },
- { label = "Trap Cooldown", haveOutput = "TrapCooldown", { format = "{2:output:TrapCooldown}s",
- { breakdown = "TrapCooldown" },
- { modName = "CooldownRecovery", cfg = "skill" },
- }, },
- { label = "Trap Trigg. Radius", flag = "trap", { format = "{0:output:TrapTriggerRadius}",
- { breakdown = "TrapTriggerRadius" },
- { label = "Area of Effect modifiers", modName = "TrapTriggerAreaOfEffect", cfg = "skill" },
- }, },
- { label = "Mine Laying Time", flag = "mine", { format = "{2:output:MineLayingTime}s",
- { breakdown = "MineLayingTime" },
- { modName = "MineLayingSpeed", cfg = "skill" },
- }, },
- { label = "Active Mine Limit", flag = "mine", { format = "{0:output:ActiveMineLimit}", { modName = "ActiveMineLimit", cfg = "skill" }, }, },
- { label = "Mine Deton. Radius", flag = "mine", { format = "{0:output:MineDetonationRadius}",
- { breakdown = "MineDetonationRadius" },
- { label = "Area of Effect modifiers", modName = "MineDetonationAreaOfEffect", cfg = "skill" },
- }, },
- { label = "Mine Aura Radius", haveOutput = "MineAuraRadius", { format = "{0:output:MineAuraRadius}", { breakdown = "MineAuraRadius" }, }, },
- { label = "Totem Place Time", flag = "totem", { format = "{2:output:TotemPlacementTime}s",
- { breakdown = "TotemPlacementTime" },
- { modName = "TotemPlacementSpeed", cfg = "skill" },
- }, },
- { label = "Active Totem Limit", flag = "totem", { format = "{0:output:ActiveTotemLimit}", { modName = "ActiveTotemLimit", cfg = "skill" }, }, },
- { label = "Totem Life Mod", flag = "totem", { format = "x {2:output:TotemLifeMod}",
- { breakdown = "TotemLifeMod" },
- { modName = "TotemLife", cfg = "skill" },
- }, },
- { label = "Totem Life", flag = "totem", { format = "{0:output:TotemLife}", { breakdown = "TotemLife" }, }, },
-} },
-{ 1, "Bleed", 1, "Bleed", colorCodes.OFFENCE, {
- extra = "{0:output:BleedChance}% {1:output:BleedDPS} {2:output:BleedDuration}s",
- flag = "bleed",
- { label = "Chance to Bleed", { format = "{0:output:BleedChance}%",
- { breakdown = "MainHand.BleedChance" },
- { breakdown = "OffHand.BleedChance" },
- { breakdown = "BleedChance" },
- { label = "Main Hand", flag = "weapon1Attack", modName = "BleedChance", modType = "BASE", cfg = "weapon1" },
- { label = "Off Hand", flag = "weapon2Attack", modName = "BleedChance", modType = "BASE", cfg = "weapon2" },
- }, },
- { label = "Total Increased", { format = "{0:mod:1}%", { modName = { "Damage", "PhysicalDamage" }, modType = "INC", cfg = "bleed" }, }, },
- { label = "Total More", { format = "{0:mod:1}%", { modName = { "Damage", "PhysicalDamage" }, modType = "MORE", cfg = "bleed" }, }, },
- { label = "MH Source Physical", bgCol = colorCodes.MAINHANDBG, textSize = 12, flag = "weapon1Attack", { format = "{0:output:MainHand.BleedPhysicalMin} to {0:output:MainHand.BleedPhysicalMax}", { breakdown = "MainHand.BleedPhysical" }, }, },
- { label = "OH Source Physical", bgCol = colorCodes.OFFHANDBG, textSize = 12, flag = "weapon2Attack", { format = "{0:output:OffHand.BleedPhysicalMin} to {0:output:OffHand.BleedPhysicalMax}", { breakdown = "OffHand.BleedPhysical" }, }, },
- { label = "Effective DPS Mod", flag = "effective", { format = "x {3:output:BleedEffMult}", { breakdown = "BleedEffMult" }, { label = "Enemy modifiers", modName = { "DamageTaken", "DamageTakenOverTime", "PhysicalDamageTaken", "PhysicalDamageTakenOverTime", "PhysicalDamageReduction" }, enemy = true }, }, },
- { label = "Bleed DPS", { format = "{1:output:BleedDPS}", { breakdown = "BleedDPS" }, { breakdown = "MainHand.BleedDPS" }, { breakdown = "OffHand.BleedDPS" }, }, },
- { label = "Bleed Duration", { format = "{2:output:BleedDuration}s",
- { breakdown = "BleedDuration" },
- { label = "Player modifiers", modName = { "EnemyBleedDuration", "SkillAndDamagingAilmentDuration" }, cfg = "bleed" },
- { label = "Enemy modifiers", modName = "SelfBleedDuration", enemy = true },
- }, },
-} },
-{ 1, "Poison", 1, "Poison", colorCodes.OFFENCE, {
- extra = "{0:output:PoisonChance}% {1:output:PoisonDPS} {2:output:PoisonDuration}s",
- flag = "poison",
- { label = "Chance to Poison", { format = "{0:output:PoisonChance}%",
- { breakdown = "MainHand.PoisonChance" },
- { breakdown = "OffHand.PoisonChance" },
- { breakdown = "PoisonChance" },
- { notFlag = "attack", modName = "PoisonChance", modType = "BASE", cfg = "skill" },
- { label = "Main Hand", flag = "weapon1Attack", modName = "PoisonChance", modType = "BASE", cfg = "weapon1" },
- { label = "Off Hand", flag = "weapon2Attack", modName = "PoisonChance", modType = "BASE", cfg = "weapon2" },
- }, },
- { label = "Total Increased", { format = "{0:mod:1}%", { modName = { "Damage", "ChaosDamage" }, modType = "INC", cfg = "poison" }, }, },
- { label = "Total More", { format = "{0:mod:1}%", { modName = { "Damage", "ChaosDamage" }, modType = "MORE", cfg = "poison" }, }, },
- { label = "Source Physical", textSize = 12, notFlag = "attack", haveOutput = "PoisonPhysicalMax", { format = "{0:output:PoisonPhysicalMin} to {0:output:PoisonPhysicalMax}", { breakdown = "PoisonPhysical" }, }, },
- { label = "MH Source Physical", bgCol = colorCodes.MAINHANDBG, textSize = 12, flag = "weapon1Attack", haveOutput = "MainHand.PoisonPhysicalMax", { format = "{0:output:MainHand.PoisonPhysicalMin} to {0:output:MainHand.PoisonPhysicalMax}", { breakdown = "MainHand.PoisonPhysical" }, }, },
- { label = "OH Source Physical", bgCol = colorCodes.OFFHANDBG, textSize = 12, flag = "weapon2Attack", haveOutput = "OffHand.PoisonPhysicalMax", { format = "{0:output:OffHand.PoisonPhysicalMin} to {0:output:OffHand.PoisonPhysicalMax}", { breakdown = "OffHand.PoisonPhysical" }, }, },
- { label = "Source Lightning", textSize = 12, notFlag = "attack", haveOutput = "PoisonLightningMax", { format = "{0:output:PoisonLightningMin} to {0:output:PoisonLightningMax}", { breakdown = "PoisonLightning" }, }, },
- { label = "MH Source Lightning", bgCol = colorCodes.MAINHANDBG, textSize = 12, flag = "weapon1Attack", haveOutput = "MainHand.PoisonLightningMax", { format = "{0:output:MainHand.PoisonLightningMin} to {0:output:MainHand.PoisonLightningMax}", { breakdown = "MainHand.PoisonLightning" }, }, },
- { label = "OH Source Lightning", bgCol = colorCodes.OFFHANDBG, textSize = 12, flag = "weapon2Attack", haveOutput = "OffHand.PoisonLightningMax", { format = "{0:output:OffHand.PoisonLightningMin} to {0:output:OffHand.PoisonLightningMax}", { breakdown = "OffHand.PoisonLightning" }, }, },
- { label = "Source Cold", textSize = 12, notFlag = "attack", haveOutput = "PoisonColdMax", { format = "{0:output:PoisonColdMin} to {0:output:PoisonColdMax}", { breakdown = "PoisonCold" }, }, },
- { label = "MH Source Cold", bgCol = colorCodes.MAINHANDBG, textSize = 12, flag = "weapon1Attack", haveOutput = "MainHand.PoisonColdMax", { format = "{0:output:MainHand.PoisonColdMin} to {0:output:MainHand.PoisonColdMax}", { breakdown = "MainHand.PoisonCold" }, }, },
- { label = "OH Source Cold", bgCol = colorCodes.OFFHANDBG, textSize = 12, flag = "weapon2Attack", haveOutput = "OffHand.PoisonColdMax", { format = "{0:output:OffHand.PoisonColdMin} to {0:output:OffHand.PoisonColdMax}", { breakdown = "OffHand.PoisonCold" }, }, },
- { label = "Source Fire", textSize = 12, notFlag = "attack", haveOutput = "PoisonFireMax", { format = "{0:output:PoisonFireMin} to {0:output:PoisonFireMax}", { breakdown = "PoisonFire" }, }, },
- { label = "MH Source Fire", bgCol = colorCodes.MAINHANDBG, textSize = 12, flag = "weapon1Attack", haveOutput = "MainHand.PoisonFireMax", { format = "{0:output:MainHand.PoisonFireMin} to {0:output:MainHand.PoisonFireMax}", { breakdown = "MainHand.PoisonFire" }, }, },
- { label = "OH Source Fire", bgCol = colorCodes.OFFHANDBG, textSize = 12, flag = "weapon2Attack", haveOutput = "OffHand.PoisonFireMax", { format = "{0:output:OffHand.PoisonFireMin} to {0:output:OffHand.PoisonFireMax}", { breakdown = "OffHand.PoisonFire" }, }, },
- { label = "Source Chaos", textSize = 12, notFlag = "attack", haveOutput = "PoisonChaosMax", { format = "{0:output:PoisonChaosMin} to {0:output:PoisonChaosMax}", { breakdown = "PoisonChaos" }, }, },
- { label = "MH Source Chaos", bgCol = colorCodes.MAINHANDBG, textSize = 12, flag = "weapon1Attack", haveOutput = "MainHand.PoisonChaosMax", { format = "{0:output:MainHand.PoisonChaosMin} to {0:output:MainHand.PoisonChaosMax}", { breakdown = "MainHand.PoisonChaos" }, }, },
- { label = "OH Source Chaos", bgCol = colorCodes.OFFHANDBG, textSize = 12, flag = "weapon2Attack", haveOutput = "OffHand.PoisonChaosMax", { format = "{0:output:OffHand.PoisonChaosMin} to {0:output:OffHand.PoisonChaosMax}", { breakdown = "OffHand.PoisonChaos" }, }, },
- { label = "Effective DPS Mod", flag = "effective", { format = "x {3:output:PoisonEffMult}",
- { breakdown = "PoisonEffMult" },
- { label = "Enemy modifiers", modName = { "ChaosResist", "DamageTaken", "DamageTakenOverTime", "ChaosDamageTaken", "ChaosDamageTakenOverTime" }, enemy = true },
- }, },
- { label = "Poison DPS", { format = "{1:output:PoisonDPS}",
- { breakdown = "PoisonDPS" },
- { breakdown = "MainHand.PoisonDPS" },
- { breakdown = "OffHand.PoisonDPS" },
- }, },
- { label = "Poison Duration", { format = "{2:output:PoisonDuration}s",
- { breakdown = "PoisonDuration" },
- { label = "Player modifiers", modName = { "EnemyPoisonDuration", "SkillAndDamagingAilmentDuration" }, cfg = "poison" },
- { label = "Enemy modifiers", modName = "SelfPoisonDuration", enemy = true },
- }, },
- { label = "Dmg. per Poison", { format = "{1:output:PoisonDamage}",
- { breakdown = "MainHand.PoisonDamage" },
- { breakdown = "OffHand.PoisonDamage" },
- { breakdown = "PoisonDamage" },
- }, },
- { flag = "notAverage", label = "Max Poison Stacks", { format = "{1:output:TotalPoisonStacks}",
- { breakdown = "MainHand.TotalPoisonStacks" },
- { breakdown = "OffHand.TotalPoisonStacks" },
- { breakdown = "TotalPoisonStacks" },
- }, },
-} },
-{ 1, "Ignite", 1, "Ignite", colorCodes.OFFENCE, {
- extra = "{0:output:IgniteChance}% {1:output:IgniteDPS} {2:output:IgniteDuration}s",
- flag = "ignite",
- { label = "Chance to Ignite", { format = "{0:output:IgniteChance}%",
- { breakdown = "MainHand.IgniteChance" },
- { breakdown = "OffHand.IgniteChance" },
- { breakdown = "IgniteChance" },
- { label = "Player modifiers", modName = "EnemyIgniteChance", cfg = "skill" },
- { label = "Enemy modifiers", modName = "SelfIgniteChance", enemy = true },
- }, },
- { label = "Total Increased", { format = "{0:mod:1}%", { modName = { "Damage", "FireDamage", "ElementalDamage" }, modType = "INC", cfg = "ignite" }, }, },
- { label = "Total More", { format = "{0:mod:1}%", { modName = { "Damage", "FireDamage", "ElementalDamage" }, modType = "MORE", cfg = "ignite" }, }, },
- { label = "Source Physical", textSize = 12, notFlag = "attack", haveOutput = "IgnitePhysicalMax", { format = "{0:output:IgnitePhysicalMin} to {0:output:IgnitePhysicalMax}", { breakdown = "IgnitePhysical" }, }, },
- { label = "MH Source Physical", bgCol = colorCodes.MAINHANDBG, textSize = 12, flag = "weapon1Attack", haveOutput = "MainHand.IgnitePhysicalMax", { format = "{0:output:MainHand.IgnitePhysicalMin} to {0:output:MainHand.IgnitePhysicalMax}", { breakdown = "MainHand.IgnitePhysical" }, }, },
- { label = "OH Source Physical", bgCol = colorCodes.OFFHANDBG, textSize = 12, flag = "weapon2Attack", haveOutput = "OffHand.IgnitePhysicalMax", { format = "{0:output:OffHand.IgnitePhysicalMin} to {0:output:OffHand.IgnitePhysicalMax}", { breakdown = "OffHand.IgnitePhysical" }, }, },
- { label = "Source Lightning", textSize = 12, notFlag = "attack", haveOutput = "IgniteLightningMax", { format = "{0:output:IgniteLightningMin} to {0:output:IgniteLightningMax}", { breakdown = "IgniteLightning" }, }, },
- { label = "MH Source Lightning", bgCol = colorCodes.MAINHANDBG, textSize = 12, flag = "weapon1Attack", haveOutput = "MainHand.IgniteLightningMax", { format = "{0:output:MainHand.IgniteLightningMin} to {0:output:MainHand.IgniteLightningMax}", { breakdown = "MainHand.IgniteLightning" }, }, },
- { label = "OH Source Lightning", bgCol = colorCodes.OFFHANDBG, textSize = 12, flag = "weapon2Attack", haveOutput = "OffHand.IgniteLightningMax", { format = "{0:output:OffHand.IgniteLightningMin} to {0:output:OffHand.IgniteLightningMax}", { breakdown = "OffHand.IgniteLightning" }, }, },
- { label = "Source Cold", textSize = 12, notFlag = "attack", haveOutput = "IgniteColdMax", { format = "{0:output:IgniteColdMin} to {0:output:IgniteColdMax}", { breakdown = "IgniteCold" }, }, },
- { label = "MH Source Cold", bgCol = colorCodes.MAINHANDBG, textSize = 12, flag = "weapon1Attack", haveOutput = "MainHand.IgniteColdMax", { format = "{0:output:MainHand.IgniteColdMin} to {0:output:MainHand.IgniteColdMax}", { breakdown = "MainHand.IgniteCold" }, }, },
- { label = "OH Source Cold", bgCol = colorCodes.OFFHANDBG, textSize = 12, flag = "weapon2Attack", haveOutput = "OffHand.IgniteColdMax", { format = "{0:output:OffHand.IgniteColdMin} to {0:output:OffHand.IgniteColdMax}", { breakdown = "OffHand.IgniteCold" }, }, },
- { label = "Source Fire", textSize = 12, notFlag = "attack", haveOutput = "IgniteFireMax", { format = "{0:output:IgniteFireMin} to {0:output:IgniteFireMax}", { breakdown = "IgniteFire" }, }, },
- { label = "MH Source Fire", bgCol = colorCodes.MAINHANDBG, textSize = 12, flag = "weapon1Attack", haveOutput = "MainHand.IgniteFireMax", { format = "{0:output:MainHand.IgniteFireMin} to {0:output:MainHand.IgniteFireMax}", { breakdown = "MainHand.IgniteFire" }, }, },
- { label = "OH Source Fire", bgCol = colorCodes.OFFHANDBG, textSize = 12, flag = "weapon2Attack", haveOutput = "OffHand.IgniteFireMax", { format = "{0:output:OffHand.IgniteFireMin} to {0:output:OffHand.IgniteFireMax}", { breakdown = "OffHand.IgniteFire" }, }, },
- { label = "Source Chaos", textSize = 12, notFlag = "attack", haveOutput = "IgniteChaosMax", { format = "{0:output:IgniteChaosMin} to {0:output:IgniteChaosMax}", { breakdown = "IgniteChaos" }, }, },
- { label = "MH Source Chaos", bgCol = colorCodes.MAINHANDBG, textSize = 12, flag = "weapon1Attack", haveOutput = "MainHand.IgniteChaosMax", { format = "{0:output:MainHand.IgniteChaosMin} to {0:output:MainHand.IgniteChaosMax}", { breakdown = "MainHand.IgniteChaos" }, }, },
- { label = "OH Source Chaos", bgCol = colorCodes.OFFHANDBG, textSize = 12, flag = "weapon2Attack", haveOutput = "OffHand.IgniteChaosMax", { format = "{0:output:OffHand.IgniteChaosMin} to {0:output:OffHand.IgniteChaosMax}", { breakdown = "OffHand.IgniteChaos" }, }, },
- { label = "Effective DPS Mod", flag = "effective", { format = "x {3:output:IgniteEffMult}",
- { breakdown = "IgniteEffMult" },
- { label = "Enemy modifiers", modName = { "FireResist", "ElementalResist", "DamageTaken", "DamageTakenOverTime", "FireDamageTaken", "FireDamageTakenOverTime", "ElementalDamageTaken" }, enemy = true },
- }, },
- { label = "Ignite DPS", { format = "{1:output:IgniteDPS}",
- { breakdown = "IgniteDPS" },
- { breakdown = "MainHand.IgniteDPS" },
- { breakdown = "OffHand.IgniteDPS" },
- { modName = { "IgniteBurnRate" }, cfg = "skill" },
- }, },
- { label = "Ignite Duration", { format = "{2:output:IgniteDuration}s",
- { breakdown = "IgniteDuration" },
- { label = "Player modifiers", modName = { "EnemyIgniteDuration", "SkillAndDamagingAilmentDuration" }, cfg = "skill" },
- { label = "Enemy modifiers", modName = "SelfIgniteDuration", enemy = true },
- }, },
- { label = "Dmg. per Ignite", flag = "igniteCanStack", { format = "{1:output:IgniteDamage}",
- { breakdown = "MainHand.IgniteDamage" },
- { breakdown = "OffHand.IgniteDamage" },
- { breakdown = "IgniteDamage" },
- }, },
- { flagList = { "notAverage", "igniteCanStack" }, label = "Max Ignite Stacks", { format = "{1:output:TotalIgniteStacks}",
- { breakdown = "MainHand.TotalIgniteStacks" },
- { breakdown = "OffHand.TotalIgniteStacks" },
- { breakdown = "TotalIgniteStacks" },
- }, },
-} },
-{ 1, "Decay", 1, "Decay", colorCodes.OFFENCE, {
- extra = "{1:output:DecayDPS} {2:output:DecayDuration}s",
- flag = "decay",
- { label = "Total Increased", { format = "{0:mod:1}%", { modName = { "Damage", "ChaosDamage" }, modType = "INC", cfg = "decay" }, }, },
- { label = "Total More", { format = "{0:mod:1}%", { modName = { "Damage", "ChaosDamage" }, modType = "MORE", cfg = "decay" }, }, },
- { label = "Effective DPS Mod", flag = "effective", { format = "x {3:output:DecayEffMult}",
- { breakdown = "DecayEffMult" },
- { label = "Enemy modifiers", modName = { "ChaosResist", "DamageTaken", "DamageTakenOverTime", "ChaosDamageTaken", "ChaosDamageTakenOverTime" }, enemy = true },
- }, },
- { label = "Decay DPS", { format = "{1:output:DecayDPS}",
- { breakdown = "DecayDPS" },
- }, },
- { label = "Decay Duration", { format = "{2:output:DecayDuration}s",
- { breakdown = "DecayDuration" },
- { modName = { "Duration", "SkillAndDamagingAilmentDuration" }, cfg = "decay" },
- }, },
-} },
-{ 1, "LeechGain", 1, "Leech & Gain on Hit", colorCodes.OFFENCE, {
- { label = "Life Leech Cap", flag = "leechLife", { format = "{1:output:MaxLifeLeechRate}",
- { breakdown = "MaxLifeLeechRate" },
- { modName = "MaxLifeLeechRate" },
- }, },
- { label = "Life Leech Rate", flag = "leechLife", notFlag = "showAverage", { format = "{1:output:LifeLeechRate}",
- { breakdown = "LifeLeech" },
- { label = "Player modifiers", notFlagList = { "totem", "attack" }, modName = { "DamageLeech", "DamageLifeLeech", "PhysicalDamageLifeLeech", "LightningDamageLifeLeech", "ColdDamageLifeLeech", "FireDamageLifeLeech", "ChaosDamageLifeLeech", "ElementalDamageLifeLeech" }, modType = "BASE", cfg = "skill" },
- { label = "Main Hand", notFlag = "totem", flag = "weapon1Attack", modName = { "DamageLeech", "DamageLifeLeech", "PhysicalDamageLifeLeech", "LightningDamageLifeLeech", "ColdDamageLifeLeech", "FireDamageLifeLeech", "ChaosDamageLifeLeech", "ElementalDamageLifeLeech" }, modType = "BASE", cfg = "weapon1" },
- { 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 = "Life Leech per Hit", flagList = { "leechLife", "showAverage" }, { format = "{1:output:LifeLeechPerHit}",
- { breakdown = "LifeLeech" },
- { label = "Player modifiers", notFlagList = { "totem", "attack" }, modName = { "DamageLeech", "DamageLifeLeech", "PhysicalDamageLifeLeech", "LightningDamageLifeLeech", "ColdDamageLifeLeech", "FireDamageLifeLeech", "ChaosDamageLifeLeech", "ElementalDamageLifeLeech" }, modType = "BASE", cfg = "skill" },
- { label = "Main Hand", notFlag = "totem", flag = "weapon1Attack", modName = { "DamageLeech", "DamageLifeLeech", "PhysicalDamageLifeLeech", "LightningDamageLifeLeech", "ColdDamageLifeLeech", "FireDamageLifeLeech", "ChaosDamageLifeLeech", "ElementalDamageLifeLeech" }, modType = "BASE", cfg = "weapon1" },
- { 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 = "Life Gain Rate", notFlag = "showAverage", haveOutput = "LifeOnHitRate", { format = "{1:output:LifeOnHitRate}",
- { label = "Player modifiers", notFlag = "attack", modName = "LifeOnHit", modType = "BASE", cfg = "skill" },
- { label = "Main Hand", flag = "weapon1Attack", modName = "LifeOnHit", modType = "BASE", cfg = "weapon1" },
- { label = "Off Hand", flag = "weapon2Attack", modName = "LifeOnHit", modType = "BASE", cfg = "weapon2" },
- { label = "Enemy modifiers", modName = { "SelfLifeOnHit" }, modType = "BASE", cfg = "skill", enemy = true },
- }, },
- { label = "Life Gain per Hit", flag = "showAverage", haveOutput = "LifeOnHit", { format = "{1:output:LifeOnHit}",
- { label = "Player modifiers", notFlag = "attack", modName = "LifeOnHit", modType = "BASE", cfg = "skill" },
- { label = "Main Hand", flag = "weapon1Attack", modName = "LifeOnHit", modType = "BASE", cfg = "weapon1" },
- { label = "Off Hand", flag = "weapon2Attack", modName = "LifeOnHit", modType = "BASE", cfg = "weapon2" },
- { label = "Enemy modifiers", modName = { "SelfLifeOnHit" }, modType = "BASE", cfg = "skill", enemy = true },
- }, },
- { label = "ES Leech Cap", flag = "leechES", { format = "{1:output:MaxEnergyShieldLeechRate}",
- { breakdown = "MaxEnergyShieldLeechRate" },
- { modName = "MaxEnergyShieldLeechRate" },
- }, },
- { label = "ES Leech Rate", flag = "leechES", notFlag = "showAverage", { format = "{1:output:EnergyShieldLeechRate}",
- { breakdown = "EnergyShieldLeech" },
- { label = "Player modifiers", notFlagList = { "totem", "attack" }, modName = { "DamageEnergyShieldLeech", "PhysicalDamageEnergyShieldLeech", "LightningDamageEnergyShieldLeech", "ColdDamageEnergyShieldLeech", "FireDamageEnergyShieldLeech", "ChaosDamageEnergyShieldLeech", "ElementalDamageEnergyShieldLeech" }, modType = "BASE", cfg = "skill" },
- { label = "Main Hand", notFlag = "totem", flag = "weapon1Attack", modName = { "DamageEnergyShieldLeech", "PhysicalDamageEnergyShieldLeech", "LightningDamageEnergyShieldLeech", "ColdDamageEnergyShieldLeech", "FireDamageEnergyShieldLeech", "ChaosDamageEnergyShieldLeech", "ElementalDamageEnergyShieldLeech" }, modType = "BASE", cfg = "weapon1" },
- { 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 = "ES Leech per Hit", flagList = { "leechES", "showAverage" }, { format = "{1:output:EnergyShieldLeechPerHit}",
- { breakdown = "EnergyShieldLeech" },
- { label = "Player modifiers", notFlagList = { "totem", "attack" }, modName = { "DamageLeech", "DamageLifeLeech", "PhysicalDamageLifeLeech", "LightningDamageLifeLeech", "ColdDamageLifeLeech", "FireDamageLifeLeech", "ChaosDamageLifeLeech", "ElementalDamageLifeLeech" }, modType = "BASE", cfg = "skill" },
- { label = "Main Hand", notFlag = "totem", flag = "weapon1Attack", modName = { "DamageLeech", "DamageLifeLeech", "PhysicalDamageLifeLeech", "LightningDamageLifeLeech", "ColdDamageLifeLeech", "FireDamageLifeLeech", "ChaosDamageLifeLeech", "ElementalDamageLifeLeech" }, modType = "BASE", cfg = "weapon1" },
- { 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 = "ES Gain Rate", notFlag = "showAverage", haveOutput = "EnergyShieldOnHitRate", { format = "{1:output:EnergyShieldOnHitRate}",
- { label = "Player modifiers", notFlag = "attack", modName = "EnergyShieldOnHit", modType = "BASE", cfg = "skill" },
- { label = "Main Hand", flag = "weapon1Attack", modName = "EnergyShieldOnHit", modType = "BASE", cfg = "weapon1" },
- { label = "Off Hand", flag = "weapon2Attack", modName = "EnergyShieldOnHit", modType = "BASE", cfg = "weapon2" },
- { label = "Enemy modifiers", modName = { "SelfEnergyShieldOnHit" }, modType = "BASE", enemy = true },
- }, },
- { label = "ES Gain per Hit", flag = "showAverage", haveOutput = "EnergyShieldOnHit", { format = "{1:output:EnergyShieldOnHit}",
- { label = "Player modifiers", notFlag = "attack", modName = "EnergyShieldOnHit", modType = "BASE", cfg = "skill" },
- { label = "Main Hand", flag = "weapon1Attack", modName = "EnergyShieldOnHit", modType = "BASE", cfg = "weapon1" },
- { label = "Off Hand", flag = "weapon2Attack", modName = "EnergyShieldOnHit", modType = "BASE", cfg = "weapon2" },
- { label = "Enemy modifiers", modName = { "SelfEnergyShieldOnHit" }, modType = "BASE", cfg = "skill", enemy = true },
- }, },
- { label = "Mana Leech Cap", flag = "leechMana", { format = "{1:output:MaxManaLeechRate}",
- { breakdown = "MaxManaLeechRate" },
- { modName = "MaxManaLeechRate" },
- }, },
- { label = "Mana Leech Rate", flag = "leechMana", notFlag = "showAverage", { format = "{1:output:ManaLeechRate}",
- { breakdown = "ManaLeech" },
- { label = "Player modifiers", notFlag = "attack", modName = { "DamageLeech", "DamageManaLeech", "PhysicalDamageManaLeech", "LightningDamageManaLeech", "ColdDamageManaLeech", "FireDamageManaLeech", "ChaosDamageManaLeech", "ElementalDamageManaLeech" }, modType = "BASE", cfg = "skill" },
- { 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 = "Mana Leech per Hit", flagList = { "leechMana", "showAverage" }, { format = "{1:output:ManaLeechPerHit}",
- { breakdown = "ManaLeech" },
- { label = "Player modifiers", notFlag = "attack", modName = { "DamageLeech", "DamageManaLeech", "PhysicalDamageManaLeech", "LightningDamageManaLeech", "ColdDamageManaLeech", "FireDamageManaLeech", "ChaosDamageManaLeech", "ElementalDamageManaLeech" }, modType = "BASE", cfg = "skill" },
- { 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 = "Mana Gain Rate", notFlag = "showAverage", haveOutput = "ManaOnHitRate", { format = "{1:output:ManaOnHitRate}",
- { label = "Player modifiers", notFlag = "attack", modName = "ManaOnHit", modType = "BASE", cfg = "skill" },
- { label = "Main Hand", flag = "weapon1Attack", modName = "ManaOnHit", modType = "BASE", cfg = "weapon1" },
- { label = "Off Hand", flag = "weapon2Attack", modName = "ManaOnHit", modType = "BASE", cfg = "weapon2" },
- { label = "Enemy modifiers", modName = { "SelfManaOnHit" }, modType = "BASE", cfg = "skill", enemy = true },
- }, },
- { label = "Mana Gain per Hit", flag = "showAverage", haveOutput = "ManaOnHit", { format = "{1:output:ManaOnHit}",
- { label = "Player modifiers", notFlag = "attack", modName = "ManaOnHit", modType = "BASE", cfg = "skill" },
- { label = "Main Hand", flag = "weapon1Attack", modName = "ManaOnHit", modType = "BASE", cfg = "weapon1" },
- { label = "Off Hand", flag = "weapon2Attack", modName = "ManaOnHit", modType = "BASE", cfg = "weapon2" },
- { label = "Enemy modifiers", modName = { "SelfManaOnHit" }, modType = "BASE", cfg = "skill", enemy = true },
- }, },
-} },
-{ 1, "MiscEffects", 1, "Other Effects", colorCodes.OFFENCE, {
- { label = "Chance to Shock", flag = "shock", { format = "{0:output:ShockChance}%",
- { breakdown = "MainHand.ShockChance" },
- { breakdown = "OffHand.ShockChance" },
- { breakdown = "ShockChance" },
- { label = "Player modifiers", modName = "EnemyShockChance", cfg = "skill" },
- { label = "Enemy modifiers", modName = "SelfShockChance", enemy = true },
- }, },
- { label = "Shock Dur. Mod", flag = "shock", { format = "x {2:output:ShockDurationMod}",
- { breakdown = "MainHand.ShockDPS" },
- { breakdown = "OffHand.ShockDPS" },
- { breakdown = "ShockDPS" },
- { label = "Player modifiers", modName = "EnemyShockDuration", cfg = "skill" },
- { label = "Enemy modifiers", modName = "SelfShockDuration", enemy = true },
- }, },
- { label = "Chance to Freeze", flag = "freeze", { format = "{0:output:FreezeChance}%",
- { breakdown = "MainHand.FreezeChance" },
- { breakdown = "OffHand.FreezeChance" },
- { breakdown = "FreezeChance" },
- { label = "Player modifiers", modName = "EnemyFreezeChance", cfg = "skill" },
- { label = "Enemy modifiers", modName = "SelfFreezeChance", enemy = true },
- }, },
- { label = "Freeze Dur. Mod", flag = "freeze", { format = "x {2:output:FreezeDurationMod}",
- { breakdown = "MainHand.FreezeDPS" },
- { breakdown = "OffHand.FreezeDPS" },
- { breakdown = "FreezeDPS" },
- { label = "Player modifiers", modName = "EnemyFreezeDuration", cfg = "skill" },
- { label = "Enemy modifiers", modName = "SelfFreezeDuration", enemy = true },
- }, },
- { label = "Stun Threshold", flag = "hit", notFlag = "attack", { format = "x {2:output:EnemyStunThresholdMod}", { modName = "EnemyStunThreshold", cfg = "skill" }, }, },
- { label = "Stun Duration", flag = "hit", notFlag = "attack", { format = "{2:output:EnemyStunDuration}s",
- { breakdown = "EnemyStunDuration" },
- { label = "Player modifiers", modName = { "EnemyStunDuration" }, cfg = "skill" },
- { label = "Enemy modifiers", modName = { "StunRecovery" }, enemy = true },
- }, },
- { label = "MH Stun Threshold", bgCol = colorCodes.MAINHANDBG, flagList = {"hit","weapon1Attack"}, { format = "x {2:output:MainHand.EnemyStunThresholdMod}", { modName = "EnemyStunThreshold", cfg = "weapon1" }, }, },
- { label = "MH Stun Duration", bgCol = colorCodes.MAINHANDBG, flagList = {"hit","weapon1Attack"}, { format = "{2:output:MainHand.EnemyStunDuration}s",
- { breakdown = "MainHand.EnemyStunDuration" },
- { label = "Player modifiers", modName = { "EnemyStunDuration" }, cfg = "weapon1" },
- { label = "Enemy modifiers", modName = { "StunRecovery" }, enemy = true },
- }, },
- { label = "OH Stun Threshold", bgCol = colorCodes.OFFHANDBG, flagList = {"hit","weapon2Attack"}, { format = "x {2:output:OffHand.EnemyStunThresholdMod}", { modName = "EnemyStunThreshold", cfg = "weapon2" }, }, },
- { label = "OH Stun Duration", bgCol = colorCodes.OFFHANDBG, flagList = {"hit","weapon2Attack"}, { format = "{2:output:OffHand.EnemyStunDuration}s",
- { breakdown = "OffHand.EnemyStunDuration" },
- { label = "Player modifiers", modName = { "EnemyStunDuration" }, cfg = "weapon2" },
- { label = "Enemy modifiers", modName = { "StunRecovery" }, enemy = true },
- }, },
- { label = "Knockback Chance", haveOutput = "KnockbackChance", { format = "{0:output:KnockbackChance}%",
- { label = "Player modifiers", modName = "EnemyKnockbackChance", cfg = "skill" },
- { label = "Enemy modifiers", modName = "SelfKnockbackChance", enemy = true },
- }, },
- { label = "Knockback Dist.", haveOutput = "KnockbackChance", { format = "{0:output:KnockbackDistance}",
- { breakdown = "KnockbackDistance" },
- { modName = "EnemyKnockbackDistance", cfg = "skill" },
- }, },
- { label = "MH K.B. Chance", bgCol = colorCodes.MAINHANDBG, haveOutput = "MainHand.KnockbackChance", { format = "{0:output:MainHand.KnockbackChance}%",
- { label = "Player modifiers", modName = "EnemyKnockbackChance", cfg = "weapon1" },
- { label = "Enemy modifiers", modName = "SelfKnockbackChance", enemy = true },
- }, },
- { label = "MH K.B. Dist.", bgCol = colorCodes.MAINHANDBG, haveOutput = "MainHand.KnockbackChance", { format = "{0:output:MainHand.KnockbackDistance}",
- { breakdown = "MainHand.KnockbackDistance" },
- { modName = "EnemyKnockbackDistance", cfg = "weapon1" },
- }, },
- { label = "OH K.B. Chance", bgCol = colorCodes.OFFHANDBG, haveOutput = "OffHand.KnockbackChance", { format = "{0:output:OffHand.KnockbackChance}%",
- { label = "Player modifiers", modName = "EnemyKnockbackChance", cfg = "weapon2" },
- { label = "Enemy modifiers", modName = "SelfKnockbackChance", enemy = true },
- }, },
- { label = "OH K.B. Dist.", bgCol = colorCodes.OFFHANDBG, haveOutput = "OffHand.KnockbackChance", { format = "{0:output:OffHand.KnockbackDistance}",
- { breakdown = "OffHand.KnockbackDistance" },
- { modName = "EnemyKnockbackDistance", cfg = "weapon2" },
- }, },
- { label = "Inc. Item Quantity", { format = "{0:mod:1}%", { modName = "LootQuantity", modType = "INC", cfg = "skill" }, }, },
- { label = "Inc. Item Rarity", { format = "{0:mod:1}%", { modName = "LootRarity", modType = "INC", cfg = "skill" }, }, },
-} },
-{ 1, "Attributes", 2, "Attributes", colorCodes.NORMAL, {
- extra = colorCodes.STRENGTH.."{0:output:Str}^7, "..colorCodes.DEXTERITY.."{0:output:Dex}^7, "..colorCodes.INTELLIGENCE.."{0:output:Int}",
- { label = "Strength", { format = "{0:output:Str}", { breakdown = "Str" }, { modName = "Str" }, }, },
- { label = "Dexterity", { format = "{0:output:Dex}", { breakdown = "Dex" }, { modName = "Dex" }, }, },
- { label = "Intelligence", { format = "{0:output:Int}", { breakdown = "Int" }, { modName = "Int" }, }, },
- { notFlag = "minionSkill", label = "Str. Required", { format = "{output:ReqStrString}", { breakdown = "ReqStr" }, }, },
- { notFlag = "minionSkill", label = "Dex. Required", { format = "{output:ReqDexString}", { breakdown = "ReqDex" }, }, },
- { notFlag = "minionSkill", label = "Int. Required", { format = "{output:ReqIntString}", { breakdown = "ReqInt" }, }, },
-} },
-{ 1, "Life", 2, "Life", colorCodes.DEFENCE, {
- extra = "{0:output:LifeUnreserved}/{0:output:Life}",
- { label = "Base from Gear", { format = "{0:mod:1}", { modName = "Life", modType = "BASE", modSource = "Item" }, }, },
- { label = "Inc. from Tree", { format = "{0:mod:1}%", { modName = "Life", modType = "INC", modSource = "Tree" }, }, },
- { label = "Total Base", { format = "{0:mod:1}", { modName = "Life", modType = "BASE" }, }, },
- { label = "Total Increased", { format = "{0:mod:1}%", { modName = "Life", modType = "INC", }, }, },
- { label = "Total More", { format = "{0:mod:1}%", { modName = "Life", modType = "MORE", }, }, },
- { label = "Total", { format = "{0:output:Life}", { breakdown = "Life" }, }, },
- { label = "Reserved", { format = "{0:output:LifeReserved} ({0:output:LifeReservedPercent}%)", { breakdown = "LifeReserved" }, }, },
- { label = "Unreserved", { format = "{0:output:LifeUnreserved} ({0:output:LifeUnreservedPercent}%)" }, },
- { label = "Regen", { format = "{1:output:LifeRegen} ({1:output:LifeRegenPercent}%)",
- { label = "Sources", modName = { "LifeRegen", "LifeRegenPercent", "LifeDegen" }, modType = "BASE" },
- { label = "Recovery modifiers", modName = "LifeRecovery" },
- }, },
-} },
-{ 1, "Mana", 2, "Mana", colorCodes.DEFENCE, {
- extra = "{0:output:ManaUnreserved}/{0:output:Mana}",
- notFlag = "minionSkill",
- { label = "Base from Gear", { format = "{0:mod:1}", { modName = "Mana", modType = "BASE", modSource = "Item" }, }, },
- { label = "Inc. from Tree", { format = "{0:mod:1}%", { modName = "Mana", modType = "INC", modSource = "Tree" }, }, },
- { label = "Total Base", { format = "{0:mod:1}", { modName = "Mana", modType = "BASE" }, }, },
- { label = "Total Increased", { format = "{0:mod:1}%", { modName = "Mana", modType = "INC" }, }, },
- { label = "Total", { format = "{0:output:Mana}", { breakdown = "Mana" }, }, },
- { label = "Reserved", { format = "{0:output:ManaReserved} ({0:output:ManaReservedPercent}%)", { breakdown = "ManaReserved" }, }, },
- { label = "Unreserved", { format = "{0:output:ManaUnreserved} ({0:output:ManaUnreservedPercent}%)" }, },
- { label = "Increased Regen", { format = "{0:mod:1}%", { modName = "ManaRegen", modType = "INC" }, }, },
- { label = "Regen", { format = "{1:output:ManaRegen}",
- { breakdown = "ManaRegen" },
- { label = "Sources", modName = { "ManaRegen", "ManaRegenPercent" }, modType = "BASE" },
- { label = "Recovery modifiers", modName = "ManaRecovery" },
- }, },
-} },
-{ 1, "EnergyShield", 2, "Energy Shield", colorCodes.DEFENCE, {
- extra = "{0:output:EnergyShield}",
- { label = "Base from Armours", { format = "{0:output:Gear:EnergyShield}", }, },
- { label = "Global Base", { format = "{0:mod:1}", { modName = "EnergyShield", modType = "BASE" }, }, },
- { label = "Inc. from Tree", { format = "{0:mod:1}%", { modName = "EnergyShield", modType = "INC", modSource = "Tree" }, }, },
- { label = "Total Increased", { format = "{0:mod:1}%", { modName = { "EnergyShield", "Defences" }, modType = "INC" }, }, },
- { label = "Total More", { format = "{0:mod:1}%", { modName = { "EnergyShield", "Defences" }, modType = "MORE" }, }, },
- { label = "Total", { format = "{0:output:EnergyShield}", { breakdown = "EnergyShield" }, }, },
- { label = "Recharge Rate", { format = "{1:output:EnergyShieldRecharge}",
- { breakdown = "EnergyShieldRecharge" },
- { modName = { "EnergyShieldRecharge", "EnergyShieldRecovery" }, },
- }, },
- { label = "Recharge Delay", { format = "{2:output:EnergyShieldRechargeDelay}s",
- { breakdown = "EnergyShieldRechargeDelay" },
- { modName = "EnergyShieldRechargeFaster" },
- }, },
- { label = "Regen", { format = "{1:output:EnergyShieldRegen} ({1:output:EnergyShieldRegenPercent}%)",
- { label = "Sources", modName = { "EnergyShieldRegen", "EnergyShieldRegenPercent" }, modType = "BASE" },
- { label = "Recovery modifiers", modName = "EnergyShieldRecovery" },
- }, },
-} },
-{ 1, "Armour", 3, "Armour", colorCodes.DEFENCE, {
- extra = "{0:output:Armour}",
- { label = "Base from Armours", { format = "{0:output:Gear:Armour}" }, },
- { label = "Global Base", { format = "{0:mod:1}", { modName = "Armour", modType = "BASE" }, }, },
- { label = "Inc. from Tree", { format = "{0:mod:1}%", { modName = { "Armour", "ArmourAndEvasion" }, modType = "INC", modSource = "Tree", }, }, },
- { label = "Total Increased", { format = "{0:mod:1}%", { modName = { "Armour", "ArmourAndEvasion", "Defences" }, modType = "INC" }, }, },
- { label = "Total More", { format = "{0:mod:1}%", { modName = { "Armour", "ArmourAndEvasion", "Defences" }, modType = "MORE" }, }, },
- { label = "Total", { format = "{0:output:Armour}", { breakdown = "Armour" }, }, },
- { label = "Phys. Dmg. Reduct", { format = "{0:output:PhysicalDamageReduction}%",
- { breakdown = "PhysicalDamageReduction" },
- { modName = { "PhysicalDamageReduction", "PhysicalDamageReductionWhenHit" } },
- }, },
-} },
-{ 1, "Evasion", 3, "Evasion", colorCodes.DEFENCE, {
- extra = "{0:output:Evasion}",
- { label = "Base from Armours", { format = "{0:output:Gear:Evasion}", }, },
- { label = "Global Base", { format = "{0:mod:1}", { modName = "Evasion", modType = "BASE" }, }, },
- { label = "Inc. from Tree", { format = "{0:mod:1}%", { modName = { "Evasion", "ArmourAndEvasion" }, modType = "INC", modSource = "Tree" }, }, },
- { label = "Total Increased", { format = "{0:mod:1}%", { modName = { "Evasion", "ArmourAndEvasion", "Defences" }, modType = "INC" }, }, },
- { label = "Total More", { format = "{0:mod:1}%", { modName = { "Evasion", "ArmourAndEvasion", "Defences" }, modType = "MORE" }, }, },
- { label = "Total", { format = "{0:output:Evasion}", { breakdown = "Evasion" }, }, },
- { label = "Evade Chance", { format = "{0:output:EvadeChance}%",
- { breakdown = "EvadeChance" },
- { modName = { "CannotEvade" } },
- { label = "Enemy modifiers", modName = { "Accuracy", "HitChance" }, enemy = true },
- }, },
-} },
-{ 1, "Resist", 3, "Resists", colorCodes.DEFENCE, {
- extra = colorCodes.FIRE.."{0:output:FireResist}+{0:output:FireResistOverCap}^7/"..colorCodes.COLD.."{0:output:ColdResist}+{0:output:ColdResistOverCap}^7/"..colorCodes.LIGHTNING.."{0:output:LightningResist}+{0:output:LightningResistOverCap}",
- { label = "Fire Resist", { format = "{0:output:FireResist}% (+{0:output:FireResistOverCap}%)",
- { breakdown = "FireResist" },
- { modName = { "FireResistMax", "FireResist", "ElementalResist" }, },
- }, },
- { label = "Cold Resist", { format = "{0:output:ColdResist}% (+{0:output:ColdResistOverCap}%)",
- { breakdown = "ColdResist" },
- { modName = { "ColdResistMax", "ColdResist", "ElementalResist" }, },
- }, },
- { label = "Lightning Resist", { format = "{0:output:LightningResist}% (+{0:output:LightningResistOverCap}%)",
- { breakdown = "LightningResist" },
- { modName = { "LightningResistMax", "LightningResist", "ElementalResist" }, },
- }, },
- { label = "Chaos Resist", { format = "{0:output:ChaosResist}% (+{0:output:ChaosResistOverCap}%)",
- { breakdown = "ChaosResist" },
- { modName = { "ChaosResistMax", "ChaosResist" }, },
- }, },
-} },
-{ 1, "DamageTaken", 3, "Damage Taken", colorCodes.DEFENCE, {
- { label = "Physical Hit/DoT", { format = "x {2:output:PhysicalTakenHitMult} / x {2:output:PhysicalTakenDotMult}",
- { breakdown = "PhysicalTakenHitMult" },
- { breakdown = "PhysicalTakenDotMult" },
- { modName = { "DamageTaken", "DamageTakenWhenHit", "DamageTakenOverTime", "PhysicalDamageTaken", "PhysicalDamageTakenWhenHit", "PhysicalDamageTakenOverTime", "PhysicalDamageTakenAsFire", "PhysicalDamageTakenAsCold", "PhysicalDamageTakenAsLightning", "PhysicalDamageTakenAsChaos" } },
- }, },
- { label = "Fire Hit/DoT", { format = "x {2:output:FireTakenHitMult} / x {2:output:FireTakenDotMult}",
- { breakdown = "FireTakenHitMult" },
- { breakdown = "FireTakenDotMult" },
- { modName = { "DamageTaken", "DamageTakenWhenHit", "DamageTakenOverTime", "FireDamageTaken", "FireDamageTakenWhenHit", "FireDamageTakenOverTime", "ElementalDamageTaken", "ElementalDamageTakenWhenHit", "ElementalDamageTakenOverTime", "FireDamageTakenAsPhysical", "FireDamageDamageTakenAsCold", "FireDamageTakenAsLightning", "FireDamageTakenAsChaos", "ElementalDamageTakenAsPhysical", "ElementalDamageTakenAsChaos" } },
- }, },
- { label = "Cold Hit/DoT", { format = "x {2:output:ColdTakenHitMult} / x {2:output:ColdTakenDotMult}",
- { breakdown = "ColdTakenHitMult" },
- { breakdown = "ColdTakenDotMult" },
- { modName = { "DamageTaken", "DamageTakenWhenHit", "DamageTakenOverTime", "ColdDamageTaken", "ColdDamageTakenWhenHit", "ColdDamageTakenOverTime", "ElementalDamageTaken", "ElementalDamageTakenWhenHit", "ElementalDamageTakenOverTime", "ColdDamageTakenAsPhysical", "ColdDamageTakenAsFire", "ColdDamageTakenAsLightning", "ColdDamageTakenAsChaos", "ElementalDamageTakenAsPhysical", "ElementalDamageTakenAsChaos" } },
- }, },
- { label = "Lightning Hit/DoT", { format = "x {2:output:LightningTakenHitMult} / x {2:output:LightningTakenDotMult}",
- { breakdown = "LightningTakenHitMult" },
- { breakdown = "LightningTakenDotMult" },
- { modName = { "DamageTaken", "DamageTakenWhenHit", "DamageTakenOverTime", "LightningDamageTaken", "LightningDamageTakenWhenHit", "LightningDamageTakenOverTime", "ElementalDamageTaken", "ElementalDamageTakenWhenHit", "ElementalDamageTakenOverTime", "LightningDamageTakenAsPhysical", "LightningDamageTakenAsFire", "LightningDamageTakenAsCold", "LightningDamageTakenAsChaos", "ElementalDamageTakenAsPhysical", "ElementalDamageTakenAsChaos" } },
- }, },
- { label = "Chaos Hit/DoT", { format = "x {2:output:ChaosTakenHitMult} / x {2:output:ChaosTakenDotMult}",
- { breakdown = "ChaosTakenHitMult" },
- { breakdown = "ChaosTakenDotMult" },
- { modName = { "DamageTaken", "DamageTakenWhenHit", "DamageTakenOverTime", "ChaosDamageTaken", "ChaosDamageTakenWhenHit", "ChaosDamageTakenOverTime", "ChaosDamageTakenAsPhysical", "ChaosDamageTakenAsFire", "ChaosDamageTakenAsCold", "ChaosDamageTakenAsLightning" } },
- }, },
- { label = "Mind over Matter", haveOutput = "MindOverMatter", { format = "{0:output:MindOverMatter}%",
- { breakdown = "MindOverMatter" },
- { modName = "DamageTakenFromManaBeforeLife" },
- }, },
- { label = "Total Degen", haveOutput = "TotalDegen", { format = "{1:output:TotalDegen}",
- { breakdown = "TotalDegen" },
- { label = "Sources", modName = { "PhysicalDegen", "FireDegen", "ColdDegen", "LightningDegen", "ChaosDegen" }, modType = "BASE" },
- }, },
- { label = "Net Life Regen", haveOutput = "NetLifeRegen", { format = "{1:output:NetLifeRegen}", { breakdown = "NetLifeRegen" }, }, },
- { label = "Net Mana Regen", haveOutput = "NetManaRegen", { format = "{1:output:NetManaRegen}", { breakdown = "NetManaRegen" }, }, },
-} },
-{ 1, "MiscDefences", 3, "Other Defences", colorCodes.DEFENCE, {
- { label = "Movement Speed", { format = "x {2:output:EffectiveMovementSpeedMod}", { breakdown = "EffectiveMovementSpeedMod" }, { modName = "MovementSpeed" }, }, },
- { label = "Dodge Chance", { format = "{0:output:AttackDodgeChance}%", { modName = "AttackDodgeChance" }, }, },
- { label = "Spell Ddg. Chance", { format = "{0:output:SpellDodgeChance}%", { modName = "SpellDodgeChance" }, }, },
- { label = "Block Chance", { format = "{0:output:BlockChance}%",
- { breakdown = "BlockChance" },
- { modName = "BlockChance" },
- }, },
- { label = "Spell Block Chance", { format = "{0:output:SpellBlockChance}%",
- { breakdown = "SpellBlockChance" },
- { modName = { "SpellBlockChance", "BlockChanceConv" }, },
- }, },
- { label = "Melee Avoid Ch.", { format = "{0:output:MeleeAvoidChance}%", { breakdown = "MeleeAvoidChance" }, }, },
- { label = "Projectile Avoid Ch.", { format = "{0:output:ProjectileAvoidChance}%", { breakdown = "ProjectileAvoidChance" }, }, },
- { label = "Spell Avoid Ch.", { format = "{0:output:SpellAvoidChance}%", { breakdown = "SpellAvoidChance" }, }, },
- { label = "Stun Avoid Chance", { format = "{0:output:StunAvoidChance}%", { modName = "AvoidStun" }, }, },
- { label = "Stun Duration", { format = "{2:output:StunDuration}s",
- { breakdown = "StunDuration" },
- { modName = "StunRecovery" },
- }, },
- { label = "Block Duration", { format = "{2:output:BlockDuration}s",
- { breakdown = "BlockDuration" },
- { modName = { "StunRecovery", "BlockRecovery" }, },
- }, },
- { label = "Light Radius Mod", { format = "x {2:output:LightRadiusMod}", { breakdown = "LightRadiusMod" }, { modName = "LightRadius" }, }, },
-} },
-}
diff --git b/Modules/CalcSections.lua a/Modules/CalcSections.lua
index 5d06d29e..f7774788 100644
--- b/Modules/CalcSections.lua
+++ a/Modules/CalcSections.lua
@@ -511,6 +511,16 @@ return {
extra = "{0:output:ImpaleChance}%",
{ label = "Max Impale Stacks", { format = "{0:output:ImpaleStacksMax}", { modName = "ImpaleStacksMax" } }, },
{ label = "Stacks on Enemy", { format = "{0:output:ImpaleStacks}" }},
+ { label = "Impale Chance", bgCol = colorCodes.MAINHANDBG, flag = "spell", haveOutput = "ImpaleChance", { format = "{0:output:ImpaleChance}%",
+ { flag = "impale", modName = "ImpaleChance", modType = "BASE", cfg = "skill" },
+ }, },
+ { label = "Stored Damage", bgCol = colorCodes.MAINHANDBG, flag = "spell", haveOutput = "ImpaleStoredDamage", { format = "{1:output:ImpaleStoredDamage}%",
+ { breakdown = "ImpaleStoredDamage" },
+ { flag = "spell", modName = "ImpaleEffect", cfg = "skill" },
+ }, },
+ { label = "Damage Mod.", bgCol = colorCodes.MAINHANDBG, flag = "spell", haveOutput = "ImpaleModifier", { format = "{3:output:ImpaleModifier}",
+ { breakdown = "ImpaleModifier" },
+ } },
{ label = "MH Impale Chance", bgCol = colorCodes.MAINHANDBG, flag = "weapon1Attack", haveOutput = "MainHand.ImpaleChance", { format = "{0:output:MainHand.ImpaleChance}%",
{ flag = "weapon1Attack", modName = "ImpaleChance", modType = "BASE", cfg = "weapon1" },
}, },
@@ -911,6 +921,7 @@ return {
{ breakdown = "ScorchChance" },
{ label = "Player modifiers", modName = "EnemyScorchChance", cfg = "skill" },
{ label = "Enemy modifiers", modName = "SelfScorchChance", enemy = true },
+ { label = "Guaranteed Scorches", modName = "ScorchOverride", modType = "BASE" },
}, },
{ label = "Scorch Dur. Mod", haveOutput = "ScorchDurationMod", flag = "scorch", { format = "x {2:output:ScorchDurationMod}",
{ label = "Player modifiers", modName = "EnemyScorchDuration", cfg = "skill" },
@@ -1052,6 +1063,7 @@ return {
}, },
{ label = "Inc. Item Quantity", { format = "{0:mod:1}%", { modName = "LootQuantity", modType = "INC", cfg = "skill" }, }, },
{ label = "Inc. Item Rarity", { format = "{0:mod:1}%", { modName = "LootRarity", modType = "INC", cfg = "skill" }, }, },
+ { label = "Culling Strike", haveOutput = "CullPercent", { format = "{0:output:CullPercent}%", { modName = { "CullPercent", "CriticalCullPercent" }, cfg = "skill" } } },
} }
} },
--misc
diff --git b/Modules/CalcSetup.lua a/Modules/CalcSetup.lua
index 20f9a053..df71e52a 100644
--- b/Modules/CalcSetup.lua
+++ a/Modules/CalcSetup.lua
@@ -25,7 +25,14 @@ function calcs.initModDB(env, modDB)
modDB:NewMod("PowerChargesMax", "BASE", 3, "Base")
modDB:NewMod("FrenzyChargesMax", "BASE", 3, "Base")
modDB:NewMod("EnduranceChargesMax", "BASE", 3, "Base")
+ modDB:NewMod("SiphoningChargesMax", "BASE", 0, "Base")
+ modDB:NewMod("ChallengerChargesMax", "BASE", 0, "Base")
+ modDB:NewMod("BlitzChargesMax", "BASE", 0, "Base")
modDB:NewMod("InspirationChargesMax", "BASE", 5, "Base")
+ modDB:NewMod("CrabBarriersMax", "BASE", 0, "Base")
+ modDB:NewMod("BrutalChargesMax", "BASE", 0, "Base")
+ modDB:NewMod("AbsorptionChargesMax", "BASE", 0, "Base")
+ modDB:NewMod("AfflictionChargesMax", "BASE", 0, "Base")
modDB:NewMod("MaxLifeLeechRate", "BASE", 20, "Base")
modDB:NewMod("MaxManaLeechRate", "BASE", 20, "Base")
modDB:NewMod("ImpaleStacksMax", "BASE", 5, "Base")
@@ -251,6 +258,9 @@ function calcs.initEnv(build, mode, override)
modDB:NewMod("Damage", "MORE", 200, "Base", 0, KeywordFlag.Bleed, { type = "ActorCondition", actor = "enemy", var = "Moving" }, { type = "Condition", var = "NoExtraBleedDamageToMovingEnemy", neg = true })
modDB:NewMod("Condition:BloodStance", "FLAG", true, "Base", { type = "Condition", var = "SandStance", neg = true })
modDB:NewMod("Condition:PrideMinEffect", "FLAG", true, "Base", { type = "Condition", var = "PrideMaxEffect", neg = true })
+ modDB:NewMod("PerBrutalTripleDamageChance", "BASE", 3, "Base")
+ modDB:NewMod("PerAfflictionAilmentDamage", "BASE", 8, "Base")
+ modDB:NewMod("PerAfflictionNonDamageEffect", "BASE", 8, "Base")
-- Add bandit mods
if build.bandit == "Alira" then
diff --git b/Modules/Calcs.lua a/Modules/Calcs.lua
index 8251b2c1..82069155 100644
--- b/Modules/Calcs.lua
+++ a/Modules/Calcs.lua
@@ -256,12 +256,21 @@ function calcs.buildOutput(build, mode)
if output.PowerCharges > 0 then
t_insert(combatList, s_format("%d Power Charges", output.PowerCharges))
end
+ if output.AbsorptionCharges > 0 then
+ t_insert(combatList, s_format("%d Absoprtion Charges", output.AbsorptionCharges))
+ end
if output.FrenzyCharges > 0 then
t_insert(combatList, s_format("%d Frenzy Charges", output.FrenzyCharges))
end
+ if output.AfflictionCharges > 0 then
+ t_insert(combatList, s_format("%d Affliction Charges", output.AfflictionCharges))
+ end
if output.EnduranceCharges > 0 then
t_insert(combatList, s_format("%d Endurance Charges", output.EnduranceCharges))
end
+ if output.BrutalCharges > 0 then
+ t_insert(combatList, s_format("%d Brutal Charges", output.BrutalCharges))
+ end
if output.SiphoningCharges > 0 then
t_insert(combatList, s_format("%d Siphoning Charges", output.SiphoningCharges))
end
diff --git b/Modules/Common.lua a/Modules/Common.lua
index d00f4f05..905c7b8f 100644
--- b/Modules/Common.lua
+++ a/Modules/Common.lua
@@ -484,10 +484,26 @@ end
-- Formats "1234.56" -> "1,234.5"
function formatNumSep(str)
- return str:gsub("(%d*)(%d%.?)", function(s, e)
- return s:reverse():gsub("(%d%d)(%d)","%1,%2"):reverse()..e
- end)
+ return string.gsub(str, "(-?%d+%.?%d+)", function(m)
+ local x, y, minus, integer, fraction = m:find("(-?)(%d+)(%.?%d*)")
+ if main.showThousandsSeparators then
+ integer = integer:reverse():gsub("(%d%d%d)", "%1"..main.thousandsSeparator):reverse()
+ -- There will be leading separators if the number of digits are divisible by 3
+ -- This checks for their presence and removes them
+ -- Don't use patterns here because thousandsSeparator can be a pattern control character, and will crash if used
+ if main.thousandsSeparator ~= "" then
+ local thousandsSeparator = string.find(integer, main.thousandsSeparator, 1, 2)
+ if thousandsSeparator and thousandsSeparator == 1 then
+ integer = integer:sub(2)
+ end
+ end
+ else
+ integer = integer:reverse():gsub("(%d%d%d)", "%1"):reverse()
+ end
+ return minus..integer..fraction:gsub("%.", main.decimalSeparator)
+ end)
end
+
function getFormatNumSep(dec)
return function(val)
return formatNumSep(val, dec)
diff --git b/Modules/ConfigOptions.lua a/Modules/ConfigOptions.lua
index 9bf2e401..caf4c487 100644
--- b/Modules/ConfigOptions.lua
+++ a/Modules/ConfigOptions.lua
@@ -32,6 +32,9 @@ return {
{ var = "conditionMoving", type = "check", label = "Are you always moving?", ifCond = "Moving", apply = function(val, modList, enemyModList)
modList:NewMod("Condition:Moving", "FLAG", true, "Config")
end },
+ { var = "conditionInsane", type = "check", label = "Are you insane?", ifCond = "Insane", apply = function(val, modList, enemyModList)
+ modList:NewMod("Condition:Insane", "FLAG", true, "Config")
+ end },
{ var = "conditionFullLife", type = "check", label = "Are you always on Full Life?", tooltip = "You will automatically be considered to be on Full Life if you have Chaos Inoculation,\nbut you can use this option to force it if necessary.", apply = function(val, modList, enemyModList)
modList:NewMod("Condition:FullLife", "FLAG", true, "Config")
end },
@@ -293,6 +296,10 @@ return {
{ var = "summonLightningGolemEnableWrath", type = "check", label = "Enable Wrath Aura:", ifSkill = "Summon Lightning Golem", apply = function(val, modList, enemyModList)
modList:NewMod("SkillData", "LIST", { key = "enable", value = true }, "Config", { type = "SkillId", skillId = "LightningGolemWrath" })
end },
+ { label = "Thirst for Blood:", ifSkill = "Thirst for Blood" },
+ { var = "nearbyBleedingEnemies", type = "count", label = "# of Nearby Bleeding Enemies:", ifSkill = "Thirst for Blood", apply = function(val, modList, enemyModList)
+ modList:NewMod("Multiplier:NearbyBleedingEnemies", "BASE", val, "Config" )
+ end },
{ label = "Toxic Rain:", ifSkill = "Toxic Rain" },
{ var = "toxicRainPodOverlap", type = "count", label = "# of Overlapping Pods:", tooltip = "Maximum is limited by the number of Projectiles.", ifSkill = "Toxic Rain", apply = function(val, modList, enemyModList)
modList:NewMod("SkillData", "LIST", { key = "podOverlapMultiplier", value = val }, "Config", { type = "SkillName", skillName = "Toxic Rain" })
@@ -553,7 +560,7 @@ return {
{ var = "buffFortify", type = "check", label = "Do you have Fortify?", tooltip = "In addition to allowing any 'while you have Fortify' modifiers to apply,\n this will enable the Fortify buff itself. (Grants 20% less Damage taken from Hits)", ifCond = "Fortify", apply = function(val, modList, enemyModList)
modList:NewMod("Condition:Fortify", "FLAG", true, "Config", { type = "Condition", var = "Combat" })
end },
- { var = "buffTailwind", type = "check", label = "Do you have Tailwind?", tooltip = "In addition to allowing any 'while you have Tailwind' modifiers to apply,\nthis will enable the Tailwind buff itself. (Grants 10% increased Action Speed)", apply = function(val, modList, enemyModList)
+ { var = "buffTailwind", type = "check", label = "Do you have Tailwind?", tooltip = "In addition to allowing any 'while you have Tailwind' modifiers to apply,\nthis will enable the Tailwind buff itself. (Grants 8% increased Action Speed)", apply = function(val, modList, enemyModList)
modList:NewMod("Condition:Tailwind", "FLAG", true, "Config", { type = "Condition", var = "Combat" })
end },
{ var = "buffAdrenaline", type = "check", label = "Do you have Adrenaline?", tooltip = "This will enable the Adrenaline buff, which grants:\n\t100% increased Damage\n\t25% increased Attack, Cast and Movement Speed\n\t10% additional Physical Damage Reduction", apply = function(val, modList, enemyModList)
@@ -599,7 +606,7 @@ return {
{ var = "conditionSummonedTotemRecently", type = "check", label = "Have you Summoned a Totem Recently?", ifCond = "SummonedTotemRecently", tooltip = "You will automatically be considered to have Summoned a Totem Recently if your main skill is a Totem,\nbut you can use this option to force it if necessary.", apply = function(val, modList, enemyModList)
modList:NewMod("Condition:SummonedTotemRecently", "FLAG", true, "Config", { type = "Condition", var = "Combat" })
end },
- { var = "TotemsSummoned", type = "count", label = "# of Summoned Totems (if not maximum):", ifSkillList = { "Spell Totem", "Ballista Totem", "Siege Ballista", "Artillery Ballista", "Shrapnel Ballista", "Ancestral Protector", "Ancestral Warchief", "Vaal Ancestral Warchief" }, tooltip = "This also implies that you have a Totem summoned.\nThis will affect all 'per Summoned Totem' modifiers, even for non-Totem skills.", apply = function(val, modList, enemyModList)
+ { var = "TotemsSummoned", type = "count", label = "# of Summoned Totems (if not maximum):", ifSkillList = { "Spell Totem", "Searing Bond", "Ballista Totem", "Siege Ballista", "Artillery Ballista", "Shrapnel Ballista", "Ancestral Protector", "Ancestral Warchief", "Vaal Ancestral Warchief" }, tooltip = "This also implies that you have a Totem summoned.\nThis will affect all 'per Summoned Totem' modifiers, even for non-Totem skills.", apply = function(val, modList, enemyModList)
modList:NewMod("TotemsSummoned", "OVERRIDE", val, "Config", { type = "Condition", var = "Combat" })
modList:NewMod("Condition:HaveTotem", "FLAG", val >= 1, "Config", { type = "Condition", var = "Combat" })
end },
@@ -782,6 +789,9 @@ return {
{ var = "conditionGainedPowerChargeRecently", type = "check", label = "Gained a Power Charge Recently?", ifCond = "GainedPowerChargeRecently", apply = function(val, modList, enemyModList)
modList:NewMod("Condition:GainedPowerChargeRecently", "FLAG", true, "Config", { type = "Condition", var = "Combat" })
end },
+ { var = "conditionGainedFrenzyChargeRecently", type = "check", label = "Gained a Frenzy Charge Recently?", ifCond = "GainedFrenzyChargeRecently", apply = function(val, modList, enemyModList)
+ modList:NewMod("Condition:GainedFrenzyChargeRecently", "FLAG", true, "Config", { type = "Condition", var = "Combat" })
+ end },
{ var = "conditionBeenSavageHitRecently", type = "check", label = "Have you taken a Savage Hit Recently?", ifCond = "BeenSavageHitRecently", implyCond = "BeenHitRecently", tooltip = "This also implies that you have been Hit Recently.", apply = function(val, modList, enemyModList)
modList:NewMod("Condition:BeenSavageHitRecently", "FLAG", true, "Config", { type = "Condition", var = "Combat" })
modList:NewMod("Condition:BeenHitRecently", "FLAG", true, "Config", { type = "Condition", var = "Combat" })
@@ -944,7 +954,7 @@ return {
{ var = "conditionTauntedEnemyRecently", type = "check", label = "Taunted an enemy Recently?", ifCond = "TauntedEnemyRecently", apply = function(val, modList, enemyModList)
modList:NewMod("Condition:TauntedEnemyRecently", "FLAG", true, "Config", { type = "Condition", var = "Combat" })
end },
- { var = "conditionLostEnduranceChargeInPast8Sec", type = "check", label = "Lost an Endurance Charge in the past 8s?", ifNode = 32249, apply = function(val, modList, enemyModList)
+ { var = "conditionLostEnduranceChargeInPast8Sec", type = "check", label = "Lost an Endurance Charge in the past 8s?", ifCond = "LostEnduranceChargeInPast8Sec", apply = function(val, modList, enemyModList)
modList:NewMod("Condition:LostEnduranceChargeInPast8Sec", "FLAG", true, "Config", { type = "Condition", var = "Combat" })
end },
{ var = "multiplierEnduranceChargesLostRecently", type = "count", label = "# of Endurance Charges lost Recently:", ifMult = "EnduranceChargesLostRecently", implyCond = "LostEnduranceChargeInPast8Sec", apply = function(val, modList, enemyModList)
@@ -1072,6 +1082,10 @@ return {
{ var = "conditionScorchedEffect", type = "count", label = "Effect of Scorched:", ifOption = "conditionEnemyScorched", tooltip = "This effect will only be applied while you can inflict Scorched.", apply = function(val, modList, enemyModList)
enemyModList:NewMod("ElementalResist", "BASE", -m_min(val, 30), "Config", { type = "Condition", var = "Scorched" }, { type = "ActorCondition", actor = "enemy", var = "CanInflictScorch" })
end },
+ { var = "conditionEnemyOnScorchedGround", type = "check", label = "Is the enemy on Scorched Ground?", tooltip = "This also implies that the enemy is Scorched.", ifEnemyCond = "OnScorchedGround", apply = function(val, modList, enemyModList)
+ enemyModList:NewMod("Condition:Scorched", "FLAG", true, "Config", { type = "Condition", var = "Effective" })
+ enemyModList:NewMod("Condition:OnScorchedGround", "FLAG", true, "Config", { type = "Condition", var = "Effective" })
+ end },
{ var = "conditionEnemyChilled", type = "check", label = "Is the enemy Chilled?", apply = function(val, modList, enemyModList)
enemyModList:NewMod("Condition:Chilled", "FLAG", true, "Config", { type = "Condition", var = "Effective" })
end },
@@ -1172,8 +1186,8 @@ return {
end
end },
{ var = "enemyAwakeningLevel", type = "count", label = "Awakening Level:", tooltip = "Each Awakening Level gives Bosses 3% more Life.", apply = function(val, modList, enemyModList)
- enemyModList:NewMod("Life", "MORE", 3 * m_min(val, 8), "Config")
- modList:NewMod("AwakeningLevel", "BASE", m_min(val, 8), "Config")
+ enemyModList:NewMod("Life", "MORE", 3 * m_min(val, 9), "Config")
+ modList:NewMod("AwakeningLevel", "BASE", m_min(val, 9), "Config")
end },
{ var = "enemyPhysicalReduction", type = "integer", label = "Enemy Phys. Damage Reduction:", apply = function(val, modList, enemyModList)
enemyModList:NewMod("PhysicalDamageReduction", "BASE", val, "Config")
@@ -1200,4 +1214,4 @@ return {
enemyModList:NewMod("Condition:HitByLightningDamage", "FLAG", true, "Config")
end },
{ var = "EEIgnoreHitDamage", type = "check", label = "Ignore Skill Hit Damage?", ifFlag = "ElementalEquilibrium", tooltip = "This option prevents EE from being reset by the hit damage of your main skill." },
-}
\ No newline at end of file
+}
diff --git b/Modules/Data.lua a/Modules/Data.lua
index 3e0b07f7..25c315ba 100644
--- b/Modules/Data.lua
+++ a/Modules/Data.lua
@@ -138,6 +138,7 @@ data.jewelRadius = {
}
data.labyrinths = {
+ { name = "HARVEST", label = "Harvest" },
{ name = "ENDGAME", label = "Eternal" },
{ name = "MERCILESS", label = "Merciless" },
{ name = "CRUEL", label = "Cruel" },
@@ -305,6 +306,7 @@ data.enchantments = {
Helmet = LoadModule("Data/EnchantmentHelmet"),
Boots = LoadModule("Data/EnchantmentBoots"),
Gloves = LoadModule("Data/EnchantmentGloves"),
+ Belt = LoadModule("Data/EnchantmentBelt"),
}
data.essences = LoadModule("Data/Essence")
data.pantheons = LoadModule("Data/Pantheons")
diff --git b/Modules/ItemTools.lua a/Modules/ItemTools.lua
index 4b0a10f8..643439dc 100644
--- b/Modules/ItemTools.lua
+++ a/Modules/ItemTools.lua
@@ -97,10 +97,12 @@ function itemLib.sanitiseItemText(text)
{ "\226\128\148", "-" }, -- U+2014 EM DASH
{ "\226\128\149", "-" }, -- U+2015 HORIZONTAL BAR
{ "\226\136\146", "-" }, -- U+2212 MINUS SIGN
+ { "\195\164", "a" }, -- U+00E4 LATIN SMALL LETTER A WITH DIAERESIS
{ "\195\182", "o" }, -- U+00F6 LATIN SMALL LETTER O WITH DIAERESIS
-- single-byte: Windows-1252 and similar
{ "\150", "-" }, -- U+2013 EN DASH
{ "\151", "-" }, -- U+2014 EM DASH
+ { "\228", "a" }, -- U+00E4 LATIN SMALL LETTER A WITH DIAERESIS
{ "\246", "o" }, -- U+00F6 LATIN SMALL LETTER O WITH DIAERESIS
-- unsupported
{ "[\128-\255]", "?" },
diff --git b/Modules/Main.lua a/Modules/Main.lua
index 3e31a57f..57afa720 100644
--- b/Modules/Main.lua
+++ a/Modules/Main.lua
@@ -199,13 +199,16 @@ the "Releases" section of the GitHub page.]])
self.buildSortMode = "NAME"
self.nodePowerTheme = "RED/BLUE"
- self.showThousandsSidebar = true
- self.showThousandsCalcs = true
+ self.showThousandsSeparators = true
+ self.thousandsSeparator = ","
+ self.decimalSeparator = "."
self.showTitlebarName = true
- self:SetMode("BUILD", false, "Unnamed build")
-
- self:LoadSettings()
+ local ignoreBuild = self:LoadPastebinBuild()
+ if not ignoreBuild then
+ self:SetMode("BUILD", false, "Unnamed build")
+ end
+ self:LoadSettings(ignoreBuild)
end
function main:CanExit()
@@ -233,6 +236,10 @@ function main:OnFrame()
self.mode = self.newMode
self.newMode = nil
self:CallMode("Init", unpack(self.newModeArgs))
+ if self.newModeChangeToTree then
+ self.modes[self.mode].viewMode = "TREE"
+ end
+ self.newModeChangeToTree = false
end
self.viewPort = { x = 0, y = 0, width = self.screenW, height = self.screenH }
@@ -371,7 +378,33 @@ function main:CallMode(func, ...)
end
end
-function main:LoadSettings()
+function main:LoadPastebinBuild()
+ local fullUri = arg[1]
+ if not fullUri then
+ return false
+ end
+ arg[1] = nil -- Protect against downloading again this session.
+ local pastebinCode = string.match(fullUri, "^pob:[/\\]*pastebin[/\\]+(%w+)[/\\]*")
+ if pastebinCode then
+ launch:DownloadPage("https://pastebin.com/raw/" .. pastebinCode, function(page, errMsg)
+ if errMsg then
+ self:SetMode("BUILD", false, "Failed Build Import (Download failed " .. pastebinCode .. ")")
+ else
+ local xmlText = Inflate(common.base64.decode(page:gsub("-","+"):gsub("_","/")))
+ if xmlText then
+ self:SetMode("BUILD", false, "Imported Build", xmlText)
+ self.newModeChangeToTree = true
+ else
+ self:SetMode("BUILD", false, "Failed Build Import (Decompress failed)")
+ end
+ end
+ end)
+ return true
+ end
+ return false
+end
+
+function main:LoadSettings(ignoreBuild)
local setXML, errMsg = common.xml.LoadXMLFile(self.userPath.."Settings.xml")
if not setXML then
return true
@@ -381,7 +414,7 @@ function main:LoadSettings()
end
for _, node in ipairs(setXML[1]) do
if type(node) == "table" then
- if node.elem == "Mode" then
+ if not ignoreBuild and node.elem == "Mode" then
if not node.attrib.mode or not self.modes[node.attrib.mode] then
launch:ShowErrMsg("^1Error parsing 'Settings.xml': Invalid mode attribute in 'Mode' element")
return true
@@ -450,12 +483,22 @@ function main:LoadSettings()
if node.attrib.nodePowerTheme then
self.nodePowerTheme = node.attrib.nodePowerTheme
end
- if node.attrib.showThousandsSidebar then
- self.showThousandsSidebar = node.attrib.showThousandsSidebar == "true"
- end -- else leave at default
+ -- In order to preserve users' settings through renameing/merging this variable, we have this if statement to use the first found setting
+ -- Once the user has closed PoB once, they will be using the new `showThousandsSeparator` variable name, so after some time, this statement may be removed
if node.attrib.showThousandsCalcs then
- self.showThousandsCalcs = node.attrib.showThousandsCalcs == "true"
- end -- else leave at default
+ self.showThousandsSeparators = node.attrib.showThousandsCalcs == "true"
+ elseif node.attrib.showThousandsSidebar then
+ self.showThousandsSeparators = node.attrib.showThousandsSidebar == "true"
+ end
+ if node.attrib.showThousandsSeparators then
+ self.showThousandsSeparators = node.attrib.showThousandsSeparators == "true"
+ end
+ if node.attrib.thousandsSeparator then
+ self.thousandsSeparator = node.attrib.thousandsSeparator
+ end
+ if node.attrib.decimalSeparator then
+ self.decimalSeparator = node.attrib.decimalSeparator
+ end
if node.attrib.showTitlebarName then
self.showTitlebarName = node.attrib.showTitlebarName == "true"
end
@@ -501,8 +544,9 @@ function main:SaveSettings()
proxyURL = launch.proxyURL,
buildPath = (self.buildPath ~= self.defaultBuildPath and self.buildPath or nil),
nodePowerTheme = self.nodePowerTheme,
- showThousandsSidebar = tostring(self.showThousandsSidebar),
- showThousandsCalcs = tostring(self.showThousandsCalcs),
+ showThousandsSeparators = tostring(self.showThousandsSeparators),
+ thousandsSeparator = self.thousandsSeparator,
+ decimalSeparator = self.decimalSeparator,
showTitlebarName = tostring(self.showTitlebarName),
} })
local res, errMsg = common.xml.SaveXMLFile(setXML, self.userPath.."Settings.xml")
@@ -542,24 +586,32 @@ function main:OpenOptionsPopup()
controls.nodePowerThemeLabel = new("LabelControl", {"RIGHT",controls.nodePowerTheme,"LEFT"}, -4, 0, 0, 16, "^7Node Power colours:")
controls.nodePowerTheme.tooltipText = "Changes the colour scheme used for the node power display on the passive tree."
controls.nodePowerTheme:SelByValue(self.nodePowerTheme, "theme")
- controls.thousandsLabel = new("LabelControl", {"TOPRIGHT",nil,"TOPLEFT"}, 210, 94, 0, 16, "^7Show thousands separators in:")
- controls.thousandsSidebar = new("CheckBoxControl", {"TOPLEFT",nil,"TOPLEFT"}, 280, 92, 20, "Sidebar:", function(state)
- self.showThousandsSidebar = state
+ controls.separatorLabel = new("LabelControl", {"TOPRIGHT",nil,"TOPLEFT"}, 210, 94, 0, 16, "^7Show thousands separators:")
+ controls.thousandsSeparators = new("CheckBoxControl", {"TOPLEFT",nil,"TOPLEFT"}, 280, 92, 20, nil, function(state)
+ self.showThousandsSeparators = state
end)
- controls.thousandsSidebar.state = self.showThousandsSidebar
- controls.thousandsCalcs = new("CheckBoxControl", {"TOPLEFT",nil,"TOPLEFT"}, 380, 92, 20, "Calcs tab:", function(state)
- self.showThousandsCalcs = state
+ controls.thousandsSeparators.state = self.showThousandsSeparators
+
+ controls.thousandsSeparator = new("EditControl", {"TOPLEFT",nil,"TOPLEFT"}, 280, 116, 20, 20, self.thousandsSeparator, nil, "%%^", 1, function(buf)
+ self.thousandsSeparator = buf
+ end)
+ controls.thousandsSeparatorLabel = new("LabelControl", {"TOPRIGHT",nil,"TOPLEFT"}, 210, 116, 92, 16, "Thousands Separator:")
+
+ controls.decimalSeparator = new("EditControl", {"TOPLEFT",nil,"TOPLEFT"}, 280, 138, 20, 20, self.decimalSeparator, nil, "%%^", 1, function(buf)
+ self.decimalSeparator = buf
end)
- controls.thousandsCalcs.state = self.showThousandsCalcs
- controls.titlebarName = new("CheckBoxControl", {"TOPLEFT",nil,"TOPLEFT"}, 230, 116, 20, "Show build name in window title:", function(state)
+ controls.decimalSeparatorLabel = new("LabelControl", {"TOPRIGHT",nil,"TOPLEFT"}, 210, 138, 92, 16, "Decimal Separator:")
+
+ controls.titlebarName = new("CheckBoxControl", {"TOPLEFT",nil,"TOPLEFT"}, 230, 160, 20, "Show build name in window title:", function(state)
self.showTitlebarName = state
end)
controls.titlebarName.state = self.showTitlebarName
local initialNodePowerTheme = self.nodePowerTheme
- local initialThousandsSidebar = self.showThousandsSidebar
- local initialThousandsCalcs = self.showThousandsCalcs
+ local initialThousandsSeparatorDisplay = self.showThousandsSeparators
local initialTitlebarName = self.showTitlebarName
- controls.save = new("ButtonControl", nil, -45, 144, 80, 20, "Save", function()
+ local initialThousandsSeparator = self.thousandsSeparator
+ local initialDecimalSeparator = self.decimalSeparator
+ controls.save = new("ButtonControl", nil, -45, 182, 80, 20, "Save", function()
if controls.proxyURL.buf:match("%w") then
launch.proxyURL = controls.proxyType.list[controls.proxyType.selIndex].scheme .. "://" .. controls.proxyURL.buf
else
@@ -578,14 +630,15 @@ function main:OpenOptionsPopup()
end
main:ClosePopup()
end)
- controls.cancel = new("ButtonControl", nil, 45, 144, 80, 20, "Cancel", function()
+ controls.cancel = new("ButtonControl", nil, 45, 182, 80, 20, "Cancel", function()
self.nodePowerTheme = initialNodePowerTheme
- self.showThousandsSidebar = initialThousandsSidebar
- self.showThousandsCalcs = initialThousandsCalcs
+ self.showThousandsSeparators = initialThousandsSeparatorDisplay
+ self.thousandsSeparator = initialThousandsSeparator
+ self.decimalSeparator = initialDecimalSeparator
self.showTitlebarName = initialTitlebarName
main:ClosePopup()
end)
- self:OpenPopup(450, 174, "Options", controls, "save", nil, "cancel")
+ self:OpenPopup(450, 218, "Options", controls, "save", nil, "cancel")
end
function main:OpenUpdatePopup()
@@ -920,4 +973,4 @@ do
end
end
-return main
\ No newline at end of file
+return main
diff --git b/Modules/ModParser-2_6.lua a/Modules/ModParser-2_6.lua
deleted file mode 100644
index 0177647d..00000000
--- b/Modules/ModParser-2_6.lua
+++ /dev/null
@@ -1,1557 +0,0 @@
--- Path of Building
---
--- Module: Mod Parser for 2.6
--- Parser function for modifier names
---
-
-local pairs = pairs
-local ipairs = ipairs
-local t_insert = table.insert
-local band = bit.band
-local bor = bit.bor
-local bnot = bit.bnot
-
--- List of modifier forms
-local formList = {
- ["^(%d+)%% increased"] = "INC",
- ["^(%d+)%% faster"] = "INC",
- ["^(%d+)%% reduced"] = "RED",
- ["^(%d+)%% slower"] = "RED",
- ["^(%d+)%% more"] = "MORE",
- ["^(%d+)%% less"] = "LESS",
- ["^([%+%-][%d%.]+)%%?"] = "BASE",
- ["^([%+%-][%d%.]+)%%? to"] = "BASE",
- ["^([%+%-]?[%d%.]+)%%? of"] = "BASE",
- ["^([%+%-][%d%.]+)%%? base"] = "BASE",
- ["^([%+%-]?[%d%.]+)%%? additional"] = "BASE",
- ["^you gain ([%d%.]+)"] = "BASE",
- ["^gain ([%d%.]+)%% of"] = "BASE",
- ["^([%+%-]?%d+)%% chance"] = "CHANCE",
- ["^([%+%-]?%d+)%% additional chance"] = "CHANCE",
- ["penetrates? (%d+)%%"] = "PEN",
- ["penetrates (%d+)%% of"] = "PEN",
- ["penetrates (%d+)%% of enemy"] = "PEN",
- ["^([%d%.]+) (.+) regenerated per second"] = "REGENFLAT",
- ["^([%d%.]+)%% (.+) regenerated per second"] = "REGENPERCENT",
- ["^([%d%.]+)%% of (.+) regenerated per second"] = "REGENPERCENT",
- ["^regenerate ([%d%.]+) (.+) per second"] = "REGENFLAT",
- ["^regenerate ([%d%.]+)%% (.+) per second"] = "REGENPERCENT",
- ["^regenerate ([%d%.]+)%% of (.+) per second"] = "REGENPERCENT",
- ["^regenerate ([%d%.]+)%% of your (.+) per second"] = "REGENPERCENT",
- ["(%d+) to (%d+) additional (%a+) damage"] = "DMG",
- ["adds (%d+) to (%d+) (%a+) damage"] = "DMG",
- ["adds (%d+)%-(%d+) (%a+) damage"] = "DMG",
- ["adds (%d+) to (%d+) (%a+) damage to attacks"] = "DMGATTACKS",
- ["adds (%d+)%-(%d+) (%a+) damage to attacks"] = "DMGATTACKS",
- ["adds (%d+) to (%d+) (%a+) attack damage"] = "DMGATTACKS",
- ["adds (%d+)%-(%d+) (%a+) attack damage"] = "DMGATTACKS",
- ["adds (%d+) to (%d+) (%a+) damage to spells"] = "DMGSPELLS",
- ["adds (%d+)%-(%d+) (%a+) damage to spells"] = "DMGSPELLS",
- ["adds (%d+) to (%d+) (%a+) spell damage"] = "DMGSPELLS",
- ["adds (%d+)%-(%d+) (%a+) spell damage"] = "DMGSPELLS",
- ["adds (%d+) to (%d+) (%a+) damage to attacks and spells"] = "DMGBOTH",
- ["adds (%d+)%-(%d+) (%a+) damage to attacks and spells"] = "DMGBOTH",
- ["adds (%d+) to (%d+) (%a+) damage to spells and attacks"] = "DMGBOTH", -- o_O
- ["adds (%d+)%-(%d+) (%a+) damage to spells and attacks"] = "DMGBOTH", -- o_O
-}
-
--- Map of modifier names
-local modNameList = {
- -- Attributes
- ["strength"] = "Str",
- ["dexterity"] = "Dex",
- ["intelligence"] = "Int",
- ["strength and dexterity"] = { "Str", "Dex" },
- ["strength and intelligence"] = { "Str", "Int" },
- ["dexterity and intelligence"] = { "Dex", "Int" },
- ["attributes"] = { "Str", "Dex", "Int" },
- ["all attributes"] = { "Str", "Dex", "Int" },
- -- Life/mana
- ["life"] = "Life",
- ["maximum life"] = "Life",
- ["mana"] = "Mana",
- ["maximum mana"] = "Mana",
- ["mana regeneration"] = "ManaRegen",
- ["mana regeneration rate"] = "ManaRegen",
- ["mana cost"] = "ManaCost",
- ["mana cost of skills"] = "ManaCost",
- ["mana reserved"] = "ManaReserved",
- ["mana reservation"] = "ManaReserved",
- -- Primary defences
- ["maximum energy shield"] = "EnergyShield",
- ["energy shield recharge rate"] = "EnergyShieldRecharge",
- ["start of energy shield recharge"] = "EnergyShieldRechargeFaster",
- ["armour"] = "Armour",
- ["evasion"] = "Evasion",
- ["evasion rating"] = "Evasion",
- ["energy shield"] = "EnergyShield",
- ["armour and evasion"] = "ArmourAndEvasion",
- ["armour and evasion rating"] = "ArmourAndEvasion",
- ["evasion rating and armour"] = "ArmourAndEvasion",
- ["armour and energy shield"] = "ArmourAndEnergyShield",
- ["evasion and energy shield"] = "EvasionAndEnergyShield",
- ["armour, evasion and energy shield"] = "Defences",
- ["defences"] = "Defences",
- ["chance to evade"] = "EvadeChance",
- ["chance to evade attacks"] = "EvadeChance",
- ["chance to evade projectile attacks"] = "ProjectileEvadeChance",
- ["chance to evade melee attacks"] = "MeleeEvadeChance",
- -- Resistances
- ["physical damage reduction"] = "PhysicalDamageReduction",
- ["fire resistance"] = "FireResist",
- ["maximum fire resistance"] = "FireResistMax",
- ["cold resistance"] = "ColdResist",
- ["maximum cold resistance"] = "ColdResistMax",
- ["lightning resistance"] = "LightningResist",
- ["maximum lightning resistance"] = "LightningResistMax",
- ["chaos resistance"] = "ChaosResist",
- ["fire and cold resistances"] = { "FireResist", "ColdResist" },
- ["fire and lightning resistances"] = { "FireResist", "LightningResist" },
- ["cold and lightning resistances"] = { "ColdResist", "LightningResist" },
- ["elemental resistances"] = "ElementalResist",
- ["all elemental resistances"] = "ElementalResist",
- ["all resistances"] = { "ElementalResist", "ChaosResist" },
- ["all maximum elemental resistances"] = { "FireResistMax", "ColdResistMax", "LightningResistMax" },
- ["all maximum resistances"] = { "FireResistMax", "ColdResistMax", "LightningResistMax", "ChaosResistMax" },
- -- Damage taken
- ["damage taken"] = "DamageTaken",
- ["damage taken when hit"] = "DamageTakenWhenHit",
- ["damage taken from damage over time"] = "DamageTakenOverTime",
- ["physical damage taken"] = "PhysicalDamageTaken",
- ["physical damage from hits taken"] = "PhysicalDamageTaken",
- ["physical damage taken when hit"] = "PhysicalDamageTakenWhenHit",
- ["physical damage taken over time"] = "PhysicalDamageTakenOverTime",
- ["lightning damage taken"] = "LightningDamageTaken",
- ["lightning damage from hits taken"] = "LightningDamageTaken",
- ["lightning damage taken when hit"] = "LightningDamageTakenWhenHit",
- ["lightning damage taken over time"] = "LightningDamageTakenOverTime",
- ["cold damage taken"] = "ColdDamageTaken",
- ["cold damage from hits taken"] = "ColdDamageTaken",
- ["cold damage taken when hit"] = "ColdDamageTakenWhenHit",
- ["cold damage taken over time"] = "ColdDamageTakenOverTime",
- ["fire damage taken"] = "FireDamageTaken",
- ["fire damage from hits taken"] = "FireDamageTaken",
- ["fire damage taken when hit"] = "FireDamageTakenWhenHit",
- ["fire damage taken over time"] = "FireDamageTakenOverTime",
- ["chaos damage taken"] = "ChaosDamageTaken",
- ["chaos damage from hits taken"] = "ChaosDamageTaken",
- ["chaos damage taken when hit"] = "ChaosDamageTakenWhenHit",
- ["chaos damage taken over time"] = "ChaosDamageTakenOverTime",
- ["elemental damage taken"] = "ElementalDamageTaken",
- ["elemental damage taken when hit"] = "ElementalDamageTakenWhenHit",
- ["elemental damage taken over time"] = "ElementalDamageTakenOverTime",
- -- Other defences
- ["to dodge attacks"] = "AttackDodgeChance",
- ["to dodge spells"] = "SpellDodgeChance",
- ["to dodge spell damage"] = "SpellDodgeChance",
- ["to block"] = "BlockChance",
- ["to block attacks"] = "BlockChance",
- ["block chance"] = "BlockChance",
- ["block chance with staves"] = { "BlockChance", tag = { type = "Condition", var = "UsingStaff" } },
- ["to block with staves"] = { "BlockChance", tag = { type = "Condition", var = "UsingStaff" } },
- ["to block spells"] = "SpellBlockChance",
- ["chance to block attacks and spells"] = { "BlockChance", "SpellBlockChance" },
- ["maximum block chance"] = "BlockChanceMax",
- ["block chance applied to spells"] = "BlockChanceConv",
- ["to avoid being stunned"] = "AvoidStun",
- ["to avoid being shocked"] = "AvoidShock",
- ["to avoid being frozen"] = "AvoidFrozen",
- ["to avoid being chilled"] = "AvoidChilled",
- ["to avoid being ignited"] = "AvoidIgnite",
- ["to avoid elemental ailments"] = { "AvoidShock", "AvoidFrozen", "AvoidChilled", "AvoidIgnite" },
- ["to avoid elemental status ailments"] = { "AvoidShock", "AvoidFrozen", "AvoidChilled", "AvoidIgnite" },
- ["damage is taken from mana before life"] = "DamageTakenFromManaBeforeLife",
- ["effect of curses on you"] = "CurseEffectOnSelf",
- ["life recovery rate"] = "LifeRecoveryRate",
- ["mana recovery rate"] = "ManaRecoveryRate",
- ["energy shield recovery rate"] = "EnergyShieldRecoveryRate",
- ["recovery rate of life, mana and energy shield"] = { "LifeRecoveryRate", "ManaRecoveryRate", "EnergyShieldRecoveryRate" },
- -- Stun/knockback modifiers
- ["stun recovery"] = "StunRecovery",
- ["stun and block recovery"] = "StunRecovery",
- ["block and stun recovery"] = "StunRecovery",
- ["stun threshold"] = "StunThreshold",
- ["block recovery"] = "BlockRecovery",
- ["enemy stun threshold"] = "EnemyStunThreshold",
- ["stun duration on enemies"] = "EnemyStunDuration",
- ["stun duration"] = "EnemyStunDuration",
- ["to knock enemies back on hit"] = "EnemyKnockbackChance",
- ["knockback distance"] = "EnemyKnockbackDistance",
- -- Auras/curses/buffs
- ["aura effect"] = "AuraEffect",
- ["effect of non-curse auras you cast"] = "AuraEffect",
- ["effect of your curses"] = "CurseEffect",
- ["effect of auras on you"] = "AuraEffectOnSelf",
- ["effect of auras on your minions"] = { "AuraEffectOnSelf", addToMinion = true },
- ["curse effect"] = "CurseEffect",
- ["curse duration"] = { "Duration", keywordFlags = KeywordFlag.Curse },
- ["radius of auras"] = { "AreaOfEffect", keywordFlags = KeywordFlag.Aura },
- ["radius of curses"] = { "AreaOfEffect", keywordFlags = KeywordFlag.Curse },
- ["buff effect"] = "BuffEffect",
- ["effect of buffs on you"] = "BuffEffectOnSelf",
- ["effect of buffs granted by your golems"] = { "BuffEffect", tag = { type = "SkillType", skillType = SkillType.Golem } },
- ["effect of buffs granted by socketed golem skills"] = { "BuffEffect", addToSkill = { type = "SocketedIn", slotName = "{SlotName}", keyword = "golem" } },
- ["effect of the buff granted by your stone golems"] = { "BuffEffect", tag = { type = "SkillName", skillName = "Summon Stone Golem" } },
- ["effect of the buff granted by your lightning golems"] = { "BuffEffect", tag = { type = "SkillName", skillName = "Summon Lightning Golem" } },
- ["effect of the buff granted by your ice golems"] = { "BuffEffect", tag = { type = "SkillName", skillName = "Summon Ice Golem" } },
- ["effect of the buff granted by your flame golems"] = { "BuffEffect", tag = { type = "SkillName", skillName = "Summon Flame Golem" } },
- ["effect of the buff granted by your chaos golems"] = { "BuffEffect", tag = { type = "SkillName", skillName = "Summon Chaos Golem" } },
- ["effect of offering spells"] = { "BuffEffect", tag = { type = "SkillName", skillNameList = { "Bone Offering", "Flesh Offering", "Spirit Offering" } } },
- ["warcry effect"] = { "BuffEffect", keywordFlags = KeywordFlag.Warcry },
- -- Charges
- ["maximum power charge"] = "PowerChargesMax",
- ["maximum power charges"] = "PowerChargesMax",
- ["power charge duration"] = "PowerChargesDuration",
- ["maximum frenzy charge"] = "FrenzyChargesMax",
- ["maximum frenzy charges"] = "FrenzyChargesMax",
- ["frenzy charge duration"] = "FrenzyChargesDuration",
- ["maximum endurance charge"] = "EnduranceChargesMax",
- ["maximum endurance charges"] = "EnduranceChargesMax",
- ["endurance charge duration"] = "EnduranceChargesDuration",
- ["endurance, frenzy and power charge duration"] = { "PowerChargesDuration", "FrenzyChargesDuration", "EnduranceChargesDuration" },
- -- On hit/kill/leech effects
- ["life gained on kill"] = "LifeOnKill",
- ["mana gained on kill"] = "ManaOnKill",
- ["life gained for each enemy hit by attacks"] = { "LifeOnHit", flags = ModFlag.Attack },
- ["life gained for each enemy hit by your attacks"] = { "LifeOnHit", flags = ModFlag.Attack },
- ["life gained for each enemy hit by spells"] = { "LifeOnHit", flags = ModFlag.Spell },
- ["life gained for each enemy hit by your spells"] = { "LifeOnHit", flags = ModFlag.Spell },
- ["mana gained for each enemy hit by attacks"] = { "ManaOnHit", flags = ModFlag.Attack },
- ["mana gained for each enemy hit by your attacks"] = { "ManaOnHit", flags = ModFlag.Attack },
- ["energy shield gained for each enemy hit by attacks"] = { "EnergyShieldOnHit", flags = ModFlag.Attack },
- ["energy shield gained for each enemy hit by your attacks"] = { "EnergyShieldOnHit", flags = ModFlag.Attack },
- ["life and mana gained for each enemy hit"] = { "LifeOnHit", "ManaOnHit", flags = ModFlag.Attack },
- ["damage as life"] = "DamageLifeLeech",
- ["life leeched per second"] = "LifeLeechRate",
- ["mana leeched per second"] = "ManaLeechRate",
- ["maximum life per second to maximum life leech rate"] = "MaxLifeLeechRate",
- ["maximum mana per second to maximum mana leech rate"] = "MaxManaLeechRate",
- -- Projectile modifiers
- ["projectile"] = "ProjectileCount",
- ["projectiles"] = "ProjectileCount",
- ["pierce chance"] = "PierceChance",
- ["of projectiles piercing"] = "PierceChance",
- ["of arrows piercing"] = { "PierceChance", flags = ModFlag.Bow },
- ["projectile speed"] = "ProjectileSpeed",
- ["arrow speed"] = { "ProjectileSpeed", flags = ModFlag.Bow },
- -- Totem/trap/mine modifiers
- ["totem placement speed"] = "TotemPlacementSpeed",
- ["totem life"] = "TotemLife",
- ["totem duration"] = "TotemDuration",
- ["trap throwing speed"] = "TrapThrowingSpeed",
- ["trap trigger area of effect"] = "TrapTriggerAreaOfEffect",
- ["trap duration"] = "TrapDuration",
- ["cooldown recovery speed for throwing traps"] = { "CooldownRecovery", keywordFlags = KeywordFlag.Trap },
- ["mine laying speed"] = "MineLayingSpeed",
- ["mine detonation area of effect"] = "MineDetonationAreaOfEffect",
- ["mine duration"] = "MineDuration",
- -- Minion modifiers
- ["maximum number of skeletons"] = "ActiveSkeletonLimit",
- ["maximum number of zombies"] = "ActiveZombieLimit",
- ["number of zombies allowed"] = "ActiveZombieLimit",
- ["maximum number of spectres"] = "ActiveSpectreLimit",
- ["maximum number of golems"] = "ActiveGolemLimit",
- ["skeleton duration"] = { "Duration", tag = { type = "SkillName", skillName = "Summon Skeletons" } },
- -- Other skill modifiers
- ["radius"] = "AreaOfEffect",
- ["radius of area skills"] = "AreaOfEffect",
- ["area of effect radius"] = "AreaOfEffect",
- ["area of effect"] = "AreaOfEffect",
- ["area of effect of skills"] = "AreaOfEffect",
- ["area of effect of area skills"] = "AreaOfEffect",
- ["duration"] = "Duration",
- ["skill effect duration"] = "Duration",
- ["chaos skill effect duration"] = { "Duration", keywordFlags = KeywordFlag.Chaos },
- ["cooldown recovery"] = "CooldownRecovery",
- ["cooldown recovery speed"] = "CooldownRecovery",
- ["weapon range"] = "WeaponRange",
- ["melee weapon range"] = "MeleeWeaponRange",
- ["melee weapon and unarmed range"] = { "MeleeWeaponRange", "UnarmedRange" },
- ["to deal double damage"] = "DoubleDamageChance",
- -- Buffs
- ["onslaught effect"] = "OnslaughtEffect",
- ["fortify duration"] = "FortifyDuration",
- ["effect of fortify on you"] = "FortifyEffectOnSelf",
- -- Basic damage types
- ["damage"] = "Damage",
- ["physical damage"] = "PhysicalDamage",
- ["lightning damage"] = "LightningDamage",
- ["cold damage"] = "ColdDamage",
- ["fire damage"] = "FireDamage",
- ["chaos damage"] = "ChaosDamage",
- ["elemental damage"] = "ElementalDamage",
- -- Other damage forms
- ["attack damage"] = { "Damage", flags = ModFlag.Attack },
- ["physical attack damage"] = { "PhysicalDamage", flags = ModFlag.Attack },
- ["physical weapon damage"] = { "PhysicalDamage", flags = ModFlag.Weapon },
- ["physical melee damage"] = { "PhysicalDamage", flags = ModFlag.Melee },
- ["melee physical damage"] = { "PhysicalDamage", flags = ModFlag.Melee },
- ["projectile damage"] = { "Damage", flags = ModFlag.Projectile },
- ["projectile attack damage"] = { "Damage", flags = bor(ModFlag.Projectile, ModFlag.Attack) },
- ["bow damage"] = { "Damage", flags = ModFlag.Bow },
- ["wand damage"] = { "Damage", flags = ModFlag.Wand },
- ["wand physical damage"] = { "PhysicalDamage", flags = ModFlag.Wand },
- ["claw physical damage"] = { "PhysicalDamage", flags = ModFlag.Claw },
- ["sword physical damage"] = { "PhysicalDamage", flags = ModFlag.Sword },
- ["damage over time"] = { "Damage", flags = ModFlag.Dot },
- ["physical damage over time"] = { "PhysicalDamage", flags = ModFlag.Dot },
- ["burning damage"] = { "FireDamage", flags = ModFlag.Dot },
- -- Crit/accuracy/speed modifiers
- ["critical strike chance"] = "CritChance",
- ["critical strike multiplier"] = "CritMultiplier",
- ["accuracy rating"] = "Accuracy",
- ["attack speed"] = { "Speed", flags = ModFlag.Attack },
- ["cast speed"] = { "Speed", flags = ModFlag.Cast },
- ["attack and cast speed"] = "Speed",
- -- Elemental status ailments
- ["to shock"] = "EnemyShockChance",
- ["shock chance"] = "EnemyShockChance",
- ["to freeze"] = "EnemyFreezeChance",
- ["freeze chance"] = "EnemyFreezeChance",
- ["to ignite"] = "EnemyIgniteChance",
- ["ignite chance"] = "EnemyIgniteChance",
- ["to freeze, shock and ignite"] = { "EnemyFreezeChance", "EnemyShockChance", "EnemyIgniteChance" },
- ["shock duration"] = "EnemyShockDuration",
- ["freeze duration"] = "EnemyFreezeDuration",
- ["chill duration"] = "EnemyChillDuration",
- ["ignite duration"] = "EnemyIgniteDuration",
- ["duration of elemental status ailments"] = { "EnemyShockDuration", "EnemyFreezeDuration", "EnemyChillDuration", "EnemyIgniteDuration" },
- -- Other debuffs
- ["to poison"] = "PoisonChance",
- ["to poison on hit"] = "PoisonChance",
- ["poison duration"] = { "Duration", keywordFlags = KeywordFlag.Poison },
- ["to cause bleeding"] = "BleedChance",
- ["to cause bleeding on hit"] = "BleedChance",
- ["bleed duration"] = { "Duration", keywordFlags = KeywordFlag.Bleed },
- -- Misc modifiers
- ["movement speed"] = "MovementSpeed",
- ["attack, cast and movement speed"] = { "Speed", "MovementSpeed" },
- ["light radius"] = "LightRadius",
- ["rarity of items found"] = "LootRarity",
- ["quantity of items found"] = "LootQuantity",
- ["item quantity"] = "LootQuantity",
- ["strength requirement"] = "StrRequirement",
- ["dexterity requirement"] = "DexRequirement",
- ["intelligence requirement"] = "IntRequirement",
- ["attribute requirements"] = { "StrRequirement", "DexRequirement", "IntRequirement" },
- -- Flask modifiers
- ["effect"] = "FlaskEffect",
- ["effect of flasks"] = "FlaskEffect",
- ["effect of flasks on you"] = "FlaskEffect",
- ["amount recovered"] = "FlaskRecovery",
- ["life recovered"] = "FlaskRecovery",
- ["mana recovered"] = "FlaskRecovery",
- ["life recovery from flasks"] = "FlaskLifeRecovery",
- ["mana recovery from flasks"] = "FlaskManaRecovery",
- ["flask effect duration"] = "FlaskDuration",
- ["recovery speed"] = "FlaskRecoveryRate",
- ["flask recovery speed"] = "FlaskRecoveryRate",
- ["flask life recovery rate"] = "FlaskLifeRecoveryRate",
- ["flask mana recovery rate"] = "FlaskManaRecoveryRate",
- ["extra charges"] = "FlaskCharges",
- ["maximum charges"] = "FlaskCharges",
- ["charges used"] = "FlaskChargesUsed",
- ["flask charges used"] = "FlaskChargesUsed",
- ["flask charges gained"] = "FlaskChargesGained",
- ["charge recovery"] = "FlaskChargeRecovery",
-}
-
--- List of modifier flags
-local modFlagList = {
- -- Weapon types
- ["with axes"] = { flags = ModFlag.Axe },
- ["with bows"] = { flags = ModFlag.Bow },
- ["with claws"] = { flags = ModFlag.Claw },
- ["dealt with claws"] = { flags = ModFlag.Claw },
- ["with daggers"] = { flags = ModFlag.Dagger },
- ["with maces"] = { flags = ModFlag.Mace },
- ["with staves"] = { flags = ModFlag.Staff },
- ["with swords"] = { flags = ModFlag.Sword },
- ["with wands"] = { flags = ModFlag.Wand },
- ["unarmed"] = { flags = ModFlag.Unarmed },
- ["to unarmed attacks"] = { flags = ModFlag.Unarmed },
- ["with one handed weapons"] = { flags = ModFlag.Weapon1H },
- ["with one handed melee weapons"] = { flags = bor(ModFlag.Weapon1H, ModFlag.WeaponMelee) },
- ["with two handed weapons"] = { flags = ModFlag.Weapon2H },
- ["with two handed melee weapons"] = { flags = bor(ModFlag.Weapon2H, ModFlag.WeaponMelee) },
- ["with ranged weapons"] = { flags = ModFlag.WeaponRanged },
- -- Skill types
- ["spell"] = { flags = ModFlag.Spell },
- ["with spells"] = { flags = ModFlag.Spell },
- ["for spells"] = { flags = ModFlag.Spell },
- ["with attacks"] = { flags = ModFlag.Attack },
- ["for attacks"] = { flags = ModFlag.Attack },
- ["weapon"] = { flags = ModFlag.Weapon },
- ["with weapons"] = { flags = ModFlag.Weapon },
- ["melee"] = { flags = ModFlag.Melee },
- ["with melee attacks"] = { flags = ModFlag.Melee },
- ["on melee hit"] = { flags = ModFlag.Melee },
- ["with poison"] = { keywordFlags = KeywordFlag.Poison },
- ["area"] = { flags = ModFlag.Area },
- ["mine"] = { keywordFlags = KeywordFlag.Mine },
- ["with mines"] = { keywordFlags = KeywordFlag.Mine },
- ["trap"] = { keywordFlags = KeywordFlag.Trap },
- ["with traps"] = { keywordFlags = KeywordFlag.Trap },
- ["for traps"] = { keywordFlags = KeywordFlag.Trap },
- ["totem"] = { keywordFlags = KeywordFlag.Totem },
- ["with totem skills"] = { keywordFlags = KeywordFlag.Totem },
- ["for skills used by totems"] = { keywordFlags = KeywordFlag.Totem },
- ["of aura skills"] = { tag = { type = "SkillType", skillType = SkillType.Aura } },
- ["of curse skills"] = { keywordFlags = KeywordFlag.Curse },
- ["of minion skills"] = { tag = { type = "SkillType", skillType = SkillType.Minion } },
- ["for curses"] = { keywordFlags = KeywordFlag.Curse },
- ["warcry"] = { keywordFlags = KeywordFlag.Warcry },
- ["vaal"] = { keywordFlags = KeywordFlag.Vaal },
- ["vaal skill"] = { keywordFlags = KeywordFlag.Vaal },
- ["with movement skills"] = { keywordFlags = KeywordFlag.Movement },
- ["with lightning skills"] = { keywordFlags = KeywordFlag.Lightning },
- ["with cold skills"] = { keywordFlags = KeywordFlag.Cold },
- ["with fire skills"] = { keywordFlags = KeywordFlag.Fire },
- ["with elemental skills"] = { keywordFlags = bor(KeywordFlag.Lightning, KeywordFlag.Cold, KeywordFlag.Fire) },
- ["with chaos skills"] = { keywordFlags = KeywordFlag.Chaos },
- ["zombie"] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Raise Zombie" } },
- ["raised zombie"] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Raise Zombie" } },
- ["raised spectre"] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Raise Spectre" } },
- ["golem"] = { },
- ["chaos golem"] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Chaos Golem" } },
- ["flame golem"] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Flame Golem" } },
- ["increased flame golem"] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Flame Golem" } },
- ["ice golem"] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Ice Golem" } },
- ["lightning golem"] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Lightning Golem" } },
- ["stone golem"] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Stone Golem" } },
- ["animated guardian"] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Animate Guardian" } },
- -- Other
- ["global"] = { tag = { type = "Global" } },
- ["from equipped shield"] = { tag = { type = "SlotName", slotName = "Weapon 2" } },
-}
-
--- List of modifier flags/tags that appear at the start of a line
-local preFlagList = {
- ["^hits deal "] = { flags = ModFlag.Hit },
- ["^critical strikes deal "] = { tag = { type = "Condition", var = "CriticalStrike" } },
- ["^minions "] = { addToMinion = true },
- ["^minions [hd][ae][va][el] "] = { addToMinion = true },
- ["^minions leech "] = { addToMinion = true },
- ["^minions' attacks deal "] = { addToMinion = true, flags = ModFlag.Attack },
- ["^golems [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillType", skillType = SkillType.Golem } },
- ["^golem skills have "] = { tag = { type = "SkillType", skillType = SkillType.Golem } },
- ["^zombies [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Raise Zombie" } },
- ["^skeletons [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Skeletons" } },
- ["^raging spirits [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Raging Spirit" } },
- ["^spectres [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Raise Spectre" } },
- ["^chaos golems [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Chaos Golem" } },
- ["^flame golems [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Flame Golem" } },
- ["^ice golems [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Ice Golem" } },
- ["^lightning golems [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Lightning Golem" } },
- ["^stone golems [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Stone Golem" } },
- ["^blink arrow and blink arrow clones [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Blink Arrow" } },
- ["^mirror arrow and mirror arrow clones [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Mirror Arrow" } },
- ["^animated weapons [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Animate Weapon" } },
- ["^animated guardians [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Animate Guardian" } },
- ["^attacks used by totems have "] = { keywordFlags = KeywordFlag.Totem },
- ["^spells cast by totems have "] = { keywordFlags = KeywordFlag.Totem },
- ["^attacks with this weapon "] = { tag = { type = "Condition", var = "{Hand}Attack" } },
- ["^attacks with this weapon have "] = { tag = { type = "Condition", var = "{Hand}Attack" } },
- ["^attacks have "] = { flags = ModFlag.Attack },
- ["^melee attacks have "] = { flags = ModFlag.Melee },
- ["^trap and mine damage "] = { keywordFlags = bor(KeywordFlag.Trap, KeywordFlag.Mine) },
- ["^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 curse gems [hgd][ae][via][enl] "] = { addToSkill = { type = "SocketedIn", slotName = "{SlotName}", keyword = "curse" } },
- ["^socketed melee gems [hgd][ae][via][enl] "] = { addToSkill = { type = "SocketedIn", slotName = "{SlotName}", keyword = "melee" } },
- ["^socketed golem gems [hgd][ae][via][enl] "] = { addToSkill = { type = "SocketedIn", slotName = "{SlotName}", keyword = "golem" } },
- ["^socketed golem skills [hgd][ae][via][enl] "] = { addToSkill = { type = "SocketedIn", slotName = "{SlotName}", keyword = "golem" } },
- ["^your flasks grant "] = { },
- ["^when hit, "] = { },
- ["^you and allies [hgd][ae][via][enl] "] = { },
- ["^auras you cast grant "] = { addToAura = true },
- ["^you and nearby allies [hgd][ae][via][enl] "] = { newAura = true },
- ["^nearby allies [hgd][ae][via][enl] "] = { newAura = true, newAuraOnlyAllies = true },
- ["^you and allies affected by your auras have "] = { affectedByAura = true },
- ["^take "] = { modSuffix = "Taken" },
-}
-
--- List of modifier tags
-local modTagList = {
- ["on enemies"] = { },
- ["while active"] = { },
- [" on critical strike"] = { tag = { type = "Condition", var = "CriticalStrike" } },
- ["while affected by auras you cast"] = { affectedByAura = true },
- -- Multipliers
- ["per power charge"] = { tag = { type = "Multiplier", var = "PowerCharge" } },
- ["per frenzy charge"] = { tag = { type = "Multiplier", var = "FrenzyCharge" } },
- ["per endurance charge"] = { tag = { type = "Multiplier", var = "EnduranceCharge" } },
- ["per level"] = { tag = { type = "Multiplier", var = "Level" } },
- ["for each normal item you have equipped"] = { tag = { type = "Multiplier", var = "NormalItem" } },
- ["for each equipped normal item"] = { tag = { type = "Multiplier", var = "NormalItem" } },
- ["for each magic item you have equipped"] = { tag = { type = "Multiplier", var = "MagicItem" } },
- ["for each equipped magic item"] = { tag = { type = "Multiplier", var = "MagicItem" } },
- ["for each rare item you have equipped"] = { tag = { type = "Multiplier", var = "RareItem" } },
- ["for each equipped rare item"] = { tag = { type = "Multiplier", var = "RareItem" } },
- ["for each unique item you have equipped"] = { tag = { type = "Multiplier", var = "UniqueItem" } },
- ["for each equipped unique item"] = { tag = { type = "Multiplier", var = "UniqueItem" } },
- ["per buff on you"] = { tag = { type = "Multiplier", var = "BuffOnSelf" } },
- ["per curse on enemy"] = { tag = { type = "Multiplier", var = "CurseOnEnemy" } },
- ["per curse on you"] = { tag = { type = "Multiplier", var = "CurseOnSelf" } },
- ["to you and allies"] = { },
- ["per red socket"] = { tag = { type = "Multiplier", var = "RedSocketIn{SlotName}" } },
- ["per green socket"] = { tag = { type = "Multiplier", var = "GreenSocketIn{SlotName}" } },
- ["per blue socket"] = { tag = { type = "Multiplier", var = "BlueSocketIn{SlotName}" } },
- ["per white socket"] = { tag = { type = "Multiplier", var = "WhiteSocketIn{SlotName}" } },
- -- Per stat
- ["per (%d+) strength"] = function(num) return { tag = { type = "PerStat", stat = "Str", div = num } } end,
- ["per (%d+) dexterity"] = function(num) return { tag = { type = "PerStat", stat = "Dex", div = num } } end,
- ["per (%d+) intelligence"] = function(num) return { tag = { type = "PerStat", stat = "Int", div = num } } end,
- ["per (%d+) evasion rating"] = function(num) return { tag = { type = "PerStat", stat = "Evasion", div = num } } end,
- ["per (%d+) accuracy rating"] = function(num) return { tag = { type = "PerStat", stat = "Accuracy", div = num } } end,
- ["per (%d+)%% block chance"] = function(num) return { tag = { type = "PerStat", stat = "BlockChance", div = num } } end,
- ["per (%d+) of the lowest of armour and evasion rating"] = function(num) return { tag = { type = "PerStat", stat = "LowestOfArmourAndEvasion", div = num } } end,
- -- Stat conditions
- ["with (%d+) or more strength"] = function(num) return { tag = { type = "StatThreshold", stat = "Str", threshold = num } } end,
- ["with at least (%d+) strength"] = function(num) return { tag = { type = "StatThreshold", stat = "Str", threshold = num } } end,
- ["w?h?i[lf]e? you have at least (%d+) strength"] = function(num) return { tag = { type = "StatThreshold", stat = "Str", threshold = num } } end,
- ["w?h?i[lf]e? you have at least (%d+) dexterity"] = function(num) return { tag = { type = "StatThreshold", stat = "Dex", threshold = num } } end,
- ["w?h?i[lf]e? you have at least (%d+) intelligence"] = function(num) return { tag = { type = "StatThreshold", stat = "Int", threshold = num } } end,
- -- Slot conditions
- ["when in main hand"] = { tag = { type = "SlotNumber", num = 1 } },
- ["when in off hand"] = { tag = { type = "SlotNumber", num = 2 } },
- ["in main hand"] = { tag = { type = "InSlot", num = 1 } },
- ["in off hand"] = { tag = { type = "InSlot", num = 2 } },
- ["with main hand"] = { tag = { type = "Condition", var = "MainHandAttack" } },
- ["with off hand"] = { tag = { type = "Condition", var = "OffHandAttack" } },
- ["with this weapon"] = { tag = { type = "Condition", var = "{Hand}Attack" } },
- -- Equipment conditions
- ["while holding a shield"] = { tag = { type = "Condition", var = "UsingShield" } },
- ["with shields"] = { tag = { type = "Condition", var = "UsingShield" } },
- ["while dual wielding"] = { tag = { type = "Condition", var = "DualWielding" } },
- ["while dual wielding claws"] = { tag = { type = "Condition", var = "DualWieldingClaws" } },
- ["while dual wielding or holding a shield"] = { tag = { type = "Condition", varList = { "DualWielding", "UsingShield" } } },
- ["while wielding a staff"] = { tag = { type = "Condition", var = "UsingStaff" } },
- ["while unarmed"] = { tag = { type = "Condition", var = "Unarmed" } },
- ["with a normal item equipped"] = { tag = { type = "MultiplierThreshold", var = "NormalItem", threshold = 1 } },
- ["with a magic item equipped"] = { tag = { type = "MultiplierThreshold", var = "MagicItem", threshold = 1 } },
- ["with a rare item equipped"] = { tag = { type = "MultiplierThreshold", var = "RareItem", threshold = 1 } },
- ["with a unique item equipped"] = { tag = { type = "MultiplierThreshold", var = "UniqueItem", threshold = 1 } },
- ["if you wear no corrupted items"] = { tag = { type = "MultiplierThreshold", var = "CorruptedItem", threshold = 0, upper = true } },
- ["if no worn items are corrupted"] = { tag = { type = "MultiplierThreshold", var = "CorruptedItem", threshold = 0, upper = true } },
- ["if no equipped items are corrupted"] = { tag = { type = "MultiplierThreshold", var = "CorruptedItem", threshold = 0, upper = true } },
- ["if all worn items are corrupted"] = { tag = { type = "MultiplierThreshold", var = "NonCorruptedItem", threshold = 0, upper = true } },
- ["if all equipped items are corrupted"] = { tag = { type = "MultiplierThreshold", var = "NonCorruptedItem", threshold = 0, upper = true } },
- -- Player status conditions
- ["wh[ie][ln]e? on low life"] = { tag = { type = "Condition", var = "LowLife" } },
- ["wh[ie][ln]e? not on low life"] = { tag = { type = "Condition", var = "LowLife", neg = true } },
- ["wh[ie][ln]e? on full life"] = { tag = { type = "Condition", var = "FullLife" } },
- ["wh[ie][ln]e? not on full life"] = { tag = { type = "Condition", var = "FullLife", neg = true } },
- ["wh[ie][ln]e? no mana is reserved"] = { tag = { type = "StatThreshold", stat = "ManaReserved", threshold = 0, upper = true } },
- ["wh[ie][ln]e? on full energy shield"] = { tag = { type = "Condition", var = "FullEnergyShield" } },
- ["wh[ie][ln]e? not on full energy shield"] = { tag = { type = "Condition", var = "FullEnergyShield", neg = true } },
- ["while stationary"] = { tag = { type = "Condition", var = "Stationary" } },
- ["while moving"] = { tag = { type = "Condition", var = "Moving" } },
- ["while you have no power charges"] = { tag = { type = "StatThreshold", stat = "PowerCharges", threshold = 0, upper = true } },
- ["while you have no frenzy charges"] = { tag = { type = "StatThreshold", stat = "FrenzyCharges", threshold = 0, upper = true } },
- ["while you have no endurance charges"] = { tag = { type = "StatThreshold", stat = "EnduranceCharges", threshold = 0, upper = true } },
- ["while at maximum power charges"] = { tag = { type = "StatThreshold", stat = "PowerCharges", thresholdStat = "PowerChargesMax" } },
- ["while at maximum frenzy charges"] = { tag = { type = "StatThreshold", stat = "FrenzyCharges", thresholdStat = "FrenzyChargesMax" } },
- ["while at maximum endurance charges"] = { tag = { type = "StatThreshold", stat = "EnduranceCharges", thresholdStat = "EnduranceChargesMax" } },
- ["while you have a totem"] = { tag = { type = "Condition", var = "HaveTotem" } },
- ["while you have fortify"] = { tag = { type = "Condition", var = "Fortify" } },
- ["during onslaught"] = { tag = { type = "Condition", var = "Onslaught" } },
- ["while you have onslaught"] = { tag = { type = "Condition", var = "Onslaught" } },
- ["while phasing"] = { tag = { type = "Condition", var = "Phasing" } },
- ["while leeching"] = { tag = { type = "Condition", var = "Leeching" } },
- ["while using a flask"] = { tag = { type = "Condition", var = "UsingFlask" } },
- ["during effect"] = { tag = { type = "Condition", var = "UsingFlask" } },
- ["during flask effect"] = { tag = { type = "Condition", var = "UsingFlask" } },
- ["during any flask effect"] = { tag = { type = "Condition", var = "UsingFlask" } },
- ["while on consecrated ground"] = { tag = { type = "Condition", var = "OnConsecratedGround" } },
- ["on burning ground"] = { tag = { type = "Condition", var = "OnBurningGround" } },
- ["on chilled ground"] = { tag = { type = "Condition", var = "OnChilledGround" } },
- ["on shocked ground"] = { tag = { type = "Condition", var = "OnShockedGround" } },
- ["while ignited"] = { tag = { type = "Condition", var = "Ignited" } },
- ["while frozen"] = { tag = { type = "Condition", var = "Frozen" } },
- ["while shocked"] = { tag = { type = "Condition", var = "Shocked" } },
- ["while not ignited, frozen or shocked"] = { tag = { type = "Condition", varList = { "Ignited", "Frozen", "Shocked" }, neg = true } },
- ["while bleeding"] = { tag = { type = "Condition", var = "Bleeding" } },
- ["if you[' ]h?a?ve hit recently"] = { tag = { type = "Condition", var = "HitRecently" } },
- ["if you[' ]h?a?ve crit recently"] = { tag = { type = "Condition", var = "CritRecently" } },
- ["if you[' ]h?a?ve dealt a critical strike recently"] = { tag = { type = "Condition", var = "CritRecently" } },
- ["if you haven't crit recently"] = { tag = { type = "Condition", var = "CritRecently", neg = true } },
- ["if you[' ]h?a?ve dealt a non%-critical strike recently"] = { tag = { type = "Condition", var = "NonCritRecently" } },
- ["if you[' ]h?a?ve killed recently"] = { tag = { type = "Condition", var = "KilledRecently" } },
- ["if you haven't killed recently"] = { tag = { type = "Condition", var = "KilledRecently", neg = true } },
- ["if you or your totems have killed recently"] = { tag = { type = "Condition", varList = {"KilledRecently","TotemsKilledRecently"} } },
- ["if you[' ]h?a?ve killed a maimed enemy recently"] = { tagList = { { type = "Condition", var = "KilledRecently" }, { type = "ActorCondition", actor = "enemy", var = "Maimed" } } },
- ["if you[' ]h?a?ve killed a cursed enemy recently"] = { tagList = { { type = "Condition", var = "KilledRecently" }, { type = "ActorCondition", actor = "enemy", var = "Cursed" } } },
- ["if you[' ]h?a?ve killed a bleeding enemy recently"] = { tagList = { { type = "Condition", var = "KilledRecently" }, { type = "ActorCondition", actor = "enemy", var = "Bleeding" } } },
- ["if you[' ]h?a?ve killed an enemy affected by your damage over time recently"] = { tag = { type = "Condition", var = "KilledAffectedByDotRecently" } },
- ["if you[' ]h?a?ve frozen an enemy recently"] = { tag = { type = "Condition", var = "FrozenEnemyRecently" } },
- ["if you[' ]h?a?ve ignited an enemy recently"] = { tag = { type = "Condition", var = "IgnitedEnemyRecently" } },
- ["if you[' ]h?a?ve been hit recently"] = { tag = { type = "Condition", var = "BeenHitRecently" } },
- ["if you were hit recently"] = { tag = { type = "Condition", var = "BeenHitRecently" } },
- ["if you were damaged by a hit recently"] = { tag = { type = "Condition", var = "BeenHitRecently" } },
- ["if you[' ]h?a?ve taken a critical strike recently"] = { tag = { type = "Condition", var = "BeenCritRecently" } },
- ["if you[' ]h?a?ve taken a savage hit recently"] = { tag = { type = "Condition", var = "BeenSavageHitRecently" } },
- ["if you have ?n[o']t been hit recently"] = { tag = { type = "Condition", var = "BeenHitRecently", neg = true } },
- ["if you[' ]h?a?ve taken no damage from hits recently"] = { tag = { type = "Condition", var = "BeenHitRecently", neg = true } },
- ["if you[' ]h?a?ve blocked recently"] = { tag = { type = "Condition", var = "BlockedRecently" } },
- ["if you[' ]h?a?ve blocked an attack recently"] = { tag = { type = "Condition", var = "BlockedAttackRecently" } },
- ["if you[' ]h?a?ve blocked a spell recently"] = { tag = { type = "Condition", var = "BlockedSpellRecently" } },
- ["if you[' ]h?a?ve blocked a hit from a unique enemy recently"] = { tag = { type = "Condition", var = "BlockedHitFromUniqueEnemyRecently" } },
- ["if you[' ]h?a?ve attacked recently"] = { tag = { type = "Condition", var = "AttackedRecently" } },
- ["if you[' ]h?a?ve cast a spell recently"] = { tag = { type = "Condition", var = "CastSpellRecently" } },
- ["if you[' ]h?a?ve consumed a corpse recently"] = { tag = { type = "Condition", var = "ConsumedCorpseRecently" } },
- ["if you[' ]h?a?ve taunted an enemy recently"] = { tag = { type = "Condition", var = "TauntedEnemyRecently" } },
- ["if you[' ]h?a?ve used a warcry recently"] = { tag = { type = "Condition", var = "UsedWarcryRecently" } },
- ["if you[' ]h?a?ve used a fire skill recently"] = { tag = { type = "Condition", var = "UsedFireSkillRecently" } },
- ["if you[' ]h?a?ve used a cold skill recently"] = { tag = { type = "Condition", var = "UsedColdSkillRecently" } },
- ["if you[' ]h?a?ve used a fire skill in the past 10 seconds"] = { tag = { type = "Condition", var = "UsedFireSkillInPast10Sec" } },
- ["if you[' ]h?a?ve used a cold skill in the past 10 seconds"] = { tag = { type = "Condition", var = "UsedColdSkillInPast10Sec" } },
- ["if you[' ]h?a?ve used a lightning skill in the past 10 seconds"] = { tag = { type = "Condition", var = "UsedLightningSkillInPast10Sec" } },
- ["if you[' ]h?a?ve summoned a totem recently"] = { tag = { type = "Condition", var = "SummonedTotemRecently" } },
- ["if you[' ]h?a?ve used a movement skill recently"] = { tag = { type = "Condition", var = "UsedMovementSkillRecently" } },
- ["if you detonated mines recently"] = { tag = { type = "Condition", var = "DetonatedMinesRecently" } },
- ["if you[' ]h?a?ve crit in the past 8 seconds"] = { tag = { type = "Condition", var = "CritInPast8Sec" } },
- ["if energy shield recharge has started recently"] = { tag = { type = "Condition", var = "EnergyShieldRechargeRecently" } },
- -- Enemy status conditions
- ["at close range"] = { tag = { type = "Condition", var = "AtCloseRange" }, flags = ModFlag.Hit },
- ["against rare and unique enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "RareOrUnique" }, flags = ModFlag.Hit },
- ["against enemies on full life"] = { tag = { type = "ActorCondition", actor = "enemy", var = "FullLife" }, flags = ModFlag.Hit },
- ["against enemies that are on full life"] = { tag = { type = "ActorCondition", actor = "enemy", var = "FullLife" }, flags = ModFlag.Hit },
- ["against enemies on low life"] = { tag = { type = "ActorCondition", actor = "enemy", var = "LowLife" }, flags = ModFlag.Hit },
- ["against enemies that are on low life"] = { tag = { type = "ActorCondition", actor = "enemy", var = "LowLife" }, flags = ModFlag.Hit },
- ["against cursed enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Cursed" }, flags = ModFlag.Hit },
- ["against taunted enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Taunted" }, flags = ModFlag.Hit },
- ["against bleeding enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Bleeding" }, flags = ModFlag.Hit },
- ["against poisoned enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Poisoned" }, flags = ModFlag.Hit },
- ["against hindered enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Hindered" }, flags = ModFlag.Hit },
- ["against blinded enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Blinded" }, flags = ModFlag.Hit },
- ["against burning enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Burning" }, flags = ModFlag.Hit },
- ["against ignited enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Ignited" }, flags = ModFlag.Hit },
- ["against shocked enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Shocked" }, flags = ModFlag.Hit },
- ["against frozen enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Frozen" }, flags = ModFlag.Hit },
- ["against chilled enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Chilled" }, flags = ModFlag.Hit },
- ["enemies which are chilled"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Chilled" }, flags = ModFlag.Hit },
- ["against frozen, shocked or ignited enemies"] = { tag = { type = "ActorCondition", actor = "enemy", varList = {"Frozen","Shocked","Ignited"} }, flags = ModFlag.Hit },
- ["against enemies affected by elemental status ailments"] = { tag = { type = "ActorCondition", actor = "enemy", varList = {"Frozen","Chilled","Shocked","Ignited"} }, flags = ModFlag.Hit },
- ["against enemies that are affected by elemental status ailments"] = { tag = { type = "ActorCondition", actor = "enemy", varList = {"Frozen","Chilled","Shocked","Ignited"} }, flags = ModFlag.Hit },
- ["against enemies that are affected by no elemental status ailments"] = { tagList = { { type = "ActorCondition", actor = "enemy", varList = {"Frozen","Chilled","Shocked","Ignited"}, neg = true }, { type = "Condition", var = "Effective" } }, flags = ModFlag.Hit },
- ["per freeze, shock and ignite on enemy"] = { tag = { type = "Multiplier", var = "FreezeShockIgniteOnEnemy" }, flags = ModFlag.Hit },
-}
-
-local mod = modLib.createMod
-local function flag(name, ...)
- return mod(name, "FLAG", true, ...)
-end
-
-local gemIdLookup = { }
-for name, grantedEffect in pairs(data["3_0"].skills) do
- if not grantedEffect.hidden or grantedEffect.fromItem then
- gemIdLookup[grantedEffect.name:lower()] = grantedEffect.id
- end
-end
-local function extraSkill(name, level, noSupports)
- name = name:gsub(" skill","")
- if gemIdLookup[name] then
- return {
- mod("ExtraSkill", "LIST", { skillId = gemIdLookup[name], level = level, noSupports = noSupports })
- }
- end
-end
-
--- List of special modifiers
-local specialModList = {
- -- Keystones
- ["your hits can't be evaded"] = { flag("CannotBeEvaded") },
- ["never deal critical strikes"] = { flag("NeverCrit") },
- ["no critical strike multiplier"] = { flag("NoCritMultiplier") },
- ["the increase to physical damage from strength applies to projectile attacks as well as melee attacks"] = { flag("IronGrip") },
- ["converts all evasion rating to armour%. dexterity provides no bonus to evasion rating"] = { flag("IronReflexes") },
- ["30%% chance to dodge attacks%. 50%% less armour and energy shield, 30%% less chance to block spells and attacks"] = {
- mod("AttackDodgeChance", "BASE", 30),
- mod("Armour", "MORE", -50),
- mod("EnergyShield", "MORE", -50),
- mod("BlockChance", "MORE", -30),
- mod("SpellBlockChance", "MORE", -30)
- },
- ["maximum life becomes 1, immune to chaos damage"] = { flag("ChaosInoculation") },
- ["life regeneration is applied to energy shield instead"] = { flag("ZealotsOath") },
- ["life leech applies instantly%. life regeneration has no effect%."] = { flag("InstantLifeLeech"), flag("NoLifeRegen") },
- ["deal no non%-fire damage"] = { flag("DealNoPhysical"), flag("DealNoLightning"), flag("DealNoCold"), flag("DealNoChaos") },
- ["(%d+)%% of physical, cold and lightning damage converted to fire damage"] = function(num) return {
- mod("PhysicalDamageConvertToFire", "BASE", num),
- mod("LightningDamageConvertToFire", "BASE", num),
- mod("ColdDamageConvertToFire", "BASE", num)
- } end,
- ["removes all mana%. spend life instead of mana for skills"] = { mod("Mana", "MORE", -100), flag("BloodMagic") },
- ["enemies you hit with elemental damage temporarily get (%+%d+)%% resistance to those elements and (%-%d+)%% resistance to other elements"] = function(plus, _, minus)
- minus = tonumber(minus)
- return {
- flag("ElementalEquilibrium"),
- mod("EnemyModifier", "LIST", { mod = mod("FireResist", "BASE", plus, { type = "Condition", var = "HitByFireDamage" }) }),
- mod("EnemyModifier", "LIST", { mod = mod("FireResist", "BASE", minus, { type = "Condition", var = "HitByFireDamage", neg = true }, { type = "Condition", varList={"HitByColdDamage","HitByLightningDamage"} }) }),
- mod("EnemyModifier", "LIST", { mod = mod("ColdResist", "BASE", plus, { type = "Condition", var = "HitByColdDamage" }) }),
- mod("EnemyModifier", "LIST", { mod = mod("ColdResist", "BASE", minus, { type = "Condition", var = "HitByColdDamage", neg = true }, { type = "Condition", varList={"HitByFireDamage","HitByLightningDamage"} }) }),
- mod("EnemyModifier", "LIST", { mod = mod("LightningResist", "BASE", plus, { type = "Condition", var = "HitByLightningDamage" }) }),
- mod("EnemyModifier", "LIST", { mod = mod("LightningResist", "BASE", minus, { type = "Condition", var = "HitByLightningDamage", neg = true }, { type = "Condition", varList={"HitByFireDamage","HitByColdDamage"} }) }),
- }
- end,
- ["projectile attacks deal up to 50%% more damage to targets at the start of their movement, dealing less damage to targets as the projectile travels farther"] = { flag("PointBlank") },
- ["life leech is applied to energy shield instead"] = { flag("GhostReaver") },
- ["minions explode when reduced to low life, dealing 33%% of their maximum life as fire damage to surrounding enemies"] = { mod("ExtraMinionSkill", "LIST", { skillId = "MinionInstability" }) },
- ["all bonuses from an equipped shield apply to your minions instead of you"] = { }, -- The node itself is detected by the code that handles it
- ["spend energy shield before mana for skill costs"] = { },
- ["energy shield protects mana instead of life"] = { flag("EnergyShieldProtectsMana") },
- -- Ascendancy notables
- ["can allocate passives from the %a+'s starting point"] = { },
- ["movement skills cost no mana"] = { mod("ManaCost", "MORE", -100, nil, 0, KeywordFlag.Movement) },
- ["projectiles have (%d+)%% additional chance to pierce targets at the start of their movement, losing this chance as the projectile travels farther"] = function(num) return { mod("PierceChance", "BASE", num, { type = "DistanceRamp", ramp = {{10,1},{120,0}} }) } end,
- ["projectile critical strike chance increased by arrow pierce chance"] = { mod("CritChance", "INC", 1, nil, ModFlag.Projectile, 0, { type = "PerStat", stat = "PierceChance", div = 1 }) },
- ["always poison on hit while using a flask"] = { mod("PoisonChance", "BASE", 100, { type = "Condition", var = "UsingFlask" }) },
- ["armour received from body armour is doubled"] = { flag("Unbreakable") },
- ["you have fortify"] = { flag("Condition:Fortify") },
- ["(%d+)%% increased damage of each damage type for which you have a matching golem"] = function(num) return {
- mod("PhysicalDamage", "INC", num, { type = "Condition", var = "HavePhysicalGolem"}),
- mod("LightningDamage", "INC", num, { type = "Condition", var = "HaveLightningGolem"}),
- mod("ColdDamage", "INC", num, { type = "Condition", var = "HaveColdGolem"}),
- mod("FireDamage", "INC", num, { type = "Condition", var = "HaveFireGolem"}),
- mod("ChaosDamage", "INC", num, { type = "Condition", var = "HaveChaosGolem"})
- } end,
- ["(%d+)%% increased effect of buffs granted by your elemental golems"] = function(num) return {
- mod("BuffEffect", "INC", num, { type = "SkillType", skillType = SkillType.Golem }, { type = "SkillType", skillType = SkillType.FireSkill }),
- mod("BuffEffect", "INC", num, { type = "SkillType", skillType = SkillType.Golem }, { type = "SkillType", skillType = SkillType.ColdSkill }),
- mod("BuffEffect", "INC", num, { type = "SkillType", skillType = SkillType.Golem }, { type = "SkillType", skillType = SkillType.LightningSkill }),
- } end,
- ["every 10 seconds, gain (%d+)%% increased elemental damage for 4 seconds"] = function(num) return { mod("ElementalDamage", "INC", num, { type = "Condition", var = "PendulumOfDestruction" }) } end,
- ["every 10 seconds, gain (%d+)%% increased area of effect of area skills for 4 seconds"] = function(num) return { mod("AreaOfEffect", "INC", num, { type = "Condition", var = "PendulumOfDestruction" }) } end,
- ["enemies you curse take (%d+)%% increased damage"] = function(num) return { mod("AffectedByCurseMod", "LIST", { mod = mod("DamageTaken", "INC", num) }) } end,
- ["enemies you curse have (%-%d+)%% to chaos resistance"] = function(num) return { mod("AffectedByCurseMod", "LIST", { mod = mod("ChaosResist", "BASE", num) }) } end,
- ["nearby enemies have (%-%d+)%% to chaos resistance"] = function(num) return { mod("EnemyModifier", "LIST", { mod = mod("ChaosResist", "BASE", num) }) } end,
- ["nearby enemies take (%d+)%% increased elemental damage"] = function(num) return { mod("EnemyModifier", "LIST", { mod = mod("ElementalDamageTaken", "INC", num) }) } end,
- ["enemies near your totems take (%d+)%% increased physical and fire damage"] = function(num) return {
- mod("EnemyModifier", "LIST", { mod = mod("PhysicalDamageTaken", "INC", num) }),
- mod("EnemyModifier", "LIST", { mod = mod("FireDamageTaken", "INC", num) })
- } end,
- ["grants armour equal to (%d+)%% of your reserved life to you and nearby allies"] = function(num) return { mod("GrantReservedLifeAsAura", "LIST", { mod = mod("Armour", "BASE", num / 100) }) } end,
- ["grants maximum energy shield equal to (%d+)%% of your reserved mana to you and nearby allies"] = function(num) return { mod("GrantReservedManaAsAura", "LIST", { mod = mod("EnergyShield", "BASE", num / 100) }) } end,
- ["skills from your helmet penetrate (%d+)%% elemental resistances"] = function(num) return { mod("ExtraSkillMod", "LIST", { mod = mod("ElementalPenetration", "BASE", num) }, { type = "SocketedIn", slotName = "Helmet" }) } end,
- ["skills from your gloves have (%d+)%% increased area of effect"] = function(num) return { mod("ExtraSkillMod", "LIST", { mod = mod("AreaOfEffect", "INC", num) }, { type = "SocketedIn", slotName = "Gloves" }) } end,
- ["skills from your boots leech (%d+)%% of damage as life"] = function(num) return { mod("ExtraSkillMod", "LIST", { mod = mod("DamageLifeLeech", "BASE", num) }, { type = "SocketedIn", slotName = "Boots" }) } end,
- ["skills in your helm can have up to (%d+) additional totems? summoned at a time"] = function(num) return { mod("ExtraSkillMod", "LIST", { mod = mod("ActiveTotemLimit", "BASE", num) }, { type = "SocketedIn", slotName = "Helmet" }) } end,
- ["(%d+)%% less totem damage per totem"] = function(num) return { mod("Damage", "MORE", -num, nil, 0, KeywordFlag.Totem, { type = "PerStat", stat = "ActiveTotemLimit", div = 1 }) } end,
- ["poison you inflict with critical strikes deals (%d+)%% more damage"] = function(num) return { mod("PoisonDamageOnCrit", "MORE", 100) } end,
- ["bleeding you inflict on maimed enemies deals (%d+)%% more damage"] = function(num) return { mod("Damage", "MORE", num, nil, 0, KeywordFlag.Bleed, { type = "ActorCondition", actor = "enemy", var = "Maimed"}) } end,
- ["critical strikes ignore enemy monster elemental resistances"] = { flag("IgnoreElementalResistances", { type = "Condition", var = "CriticalStrike" }) },
- ["non%-critical strikes penetrate (%d+)%% of enemy elemental resistances"] = function(num) return { mod("ElementalPenetration", "BASE", num, { type = "Condition", var = "CriticalStrike", neg = true }) } end,
- ["movement speed cannot be modified to below base value"] = { flag("MovementSpeedCannotBeBelowBase") },
- ["you cannot be slowed to below base speed"] = { flag("ActionSpeedCannotBeBelowBase") },
- ["cannot be slowed to below base speed"] = { flag("ActionSpeedCannotBeBelowBase") },
- ["your offering skills also affect you"] = { mod("ExtraSkillMod", "LIST", { mod = mod("SkillData", "LIST", { key = "buffNotPlayer", value = false }) }, { type = "SkillName", skillNameList = { "Bone Offering", "Flesh Offering", "Spirit Offering" } }) },
- ["consecrated ground you create grants (%d+)%% increased damage to you and allies"] = function(num) return { mod("Damage", "INC", num, { type = "Condition", var = "OnConsecratedGround" }) } end,
- ["for each element you've been hit by damage of recently, (%d+)%% increased damage of that element"] = function(num) return {
- mod("FireDamage", "INC", num, { type = "Condition", var = "HitByFireDamageRecently" }),
- mod("ColdDamage", "INC", num, { type = "Condition", var = "HitByColdDamageRecently" }),
- mod("LightningDamage", "INC", num, { type = "Condition", var = "HitByLightningDamageRecently" })
- } end,
- ["for each element you've been hit by damage of recently, (%d+)%% reduced damage taken of that element"] = function(num) return {
- mod("FireDamageTaken", "INC", -num, { type = "Condition", var = "HitByFireDamageRecently" }),
- mod("ColdDamageTaken", "INC", -num, { type = "Condition", var = "HitByColdDamageRecently" }),
- mod("LightningDamageTaken", "INC", -num, { type = "Condition", var = "HitByLightningDamageRecently" })
- } end,
- ["when you kill an enemy, for each curse on that enemy, gain (%d+)%% of non%-chaos damage as extra chaos damage for 4 seconds"] = function(num) return {
- mod("PhysicalDamageGainAsChaos", "BASE", num, { type = "Condition", var = "KilledRecently" }, { type = "Multiplier", var = "CurseOnEnemy" }),
- mod("ElementalDamageGainAsChaos", "BASE", num, { type = "Condition", var = "KilledRecently" }, { type = "Multiplier", var = "CurseOnEnemy" }),
- } end,
- ["warcries cost no mana"] = { mod("ManaCost", "MORE", -100, nil, 0, KeywordFlag.Warcry) },
- ["enemies you taunt take (%d+)%% increased damage"] = function(num) return { mod("EnemyModifier", "LIST", { mod = mod("DamageTaken", "INC", num, { type = "Condition", var = "Taunted" }) }) } end,
- ["you have phasing while at maximum frenzy charges"] = { flag("Condition:Phasing", { type = "StatThreshold", stat = "FrenzyCharges", thresholdStat = "FrenzyChargesMax" }) },
- ["you have phasing while you have onslaught"] = { flag("Condition:Phasing", { type = "Condition", var = "Onlaught" }) },
- ["you have onslaught while on full frenzy charges"] = { flag("Condition:Phasing", { type = "StatThreshold", stat = "FrenzyCharges", thresholdStat = "FrenzyChargesMax" }) },
- ["your minions spread caustic cloud on death, dealing 10%% of their maximum life as chaos damage per second"] = { mod("ExtraMinionSkill", "LIST", { skillId = "BeaconCausticCloud" }) },
- ["you and your minions have (%d+)%% physical damage reduction"] = function(num) return { mod("PhysicalDamageReduction", "BASE", num), mod("MinionModifier", "LIST", { mod = mod("PhysicalDamageReduction", "BASE", num) }) } end,
- ["every %d+ seconds:"] = { },
- ["gain chilling conflux for %d seconds"] = {
- flag("PhysicalCanChill", { type = "Condition", var = "ChillingConflux" }),
- flag("LightningCanChill", { type = "Condition", var = "ChillingConflux" }),
- flag("FireCanChill", { type = "Condition", var = "ChillingConflux" }),
- flag("ChaosCanChill", { type = "Condition", var = "ChillingConflux" }),
- },
- ["gain shocking conflux for %d seconds"] = {
- mod("EnemyShockChance", "BASE", 100, { type = "Condition", var = "ShockingConflux" }),
- flag("PhysicalCanShock", { type = "Condition", var = "ShockingConflux" }),
- flag("ColdCanShock", { type = "Condition", var = "ShockingConflux" }),
- flag("FireCanShock", { type = "Condition", var = "ShockingConflux" }),
- flag("ChaosCanShock", { type = "Condition", var = "ShockingConflux" }),
- },
- ["gain igniting conflux for %d seconds"] = {
- mod("EnemyIgniteChance", "BASE", 100, { type = "Condition", var = "IgnitingConflux" }),
- flag("PhysicalCanIgnite", { type = "Condition", var = "IgnitingConflux" }),
- flag("LightningCanIgnite", { type = "Condition", var = "IgnitingConflux" }),
- flag("ColdCanIgnite", { type = "Condition", var = "IgnitingConflux" }),
- flag("ChaosCanIgnite", { type = "Condition", var = "IgnitingConflux" }),
- },
- ["gain chilling, shocking and igniting conflux for %d seconds"] = { },
- ["(%d+)%% additional block chance for %d second every %d seconds"] = function(num) return { mod("BlockChance", "BASE", num, { type = "Condition", var = "BastionOfHopeActive" }) } end,
- ["grants (%d+) passive skill points?"] = function(num) return { mod("ExtraPoints", "BASE", num) } end,
- -- Item local modifiers
- ["has no sockets"] = { flag("NoSockets") },
- ["has (%d+) sockets?"] = function(num) return { mod("SocketCount", "BASE", num) } end,
- ["no physical damage"] = { mod("WeaponData", "LIST", { key = "PhysicalMin" }), mod("WeaponData", "LIST", { key = "PhysicalMax" }), mod("WeaponData", "LIST", { key = "PhysicalDPS" }) },
- ["all attacks with this weapon are critical strikes"] = { mod("WeaponData", "LIST", { key = "CritChance", value = 100 }) },
- ["counts as dual wielding"] = { mod("WeaponData", "LIST", { key = "countsAsDualWielding", value = true}) },
- ["counts as all one handed melee weapon types"] = { mod("WeaponData", "LIST", { key = "countsAsAll1H", value = true }) },
- ["no block chance"] = { mod("ArmourData", "LIST", { key = "BlockChance", value = 0 }) },
- ["hits can't be evaded"] = { flag("CannotBeEvaded", { type = "Condition", var = "{Hand}Attack" }) },
- ["causes bleeding on hit"] = { mod("BleedChance", "BASE", 100, { type = "Condition", var = "{Hand}Attack" }) },
- ["poisonous hit"] = { mod("PoisonChance", "BASE", 100, { type = "Condition", var = "{Hand}Attack" }) },
- ["attacks with this weapon deal double damage to chilled enemies"] = { mod("Damage", "MORE", 100, nil, ModFlag.Hit, { type = "Condition", var = "{Hand}Attack" }, { type = "ActorCondition", actor = "enemy", var = "Chilled" }) },
- ["life leech from hits with this weapon applies instantly"] = { flag("InstantLifeLeech", { type = "Condition", var = "{Hand}Attack" }) },
- ["instant recovery"] = { mod("FlaskInstantRecovery", "BASE", 100) },
- ["(%d+)%% of recovery applied instantly"] = function(num) return { mod("FlaskInstantRecovery", "BASE", num) } end,
- -- Socketed gem modifiers
- ["%+(%d+) to level of socketed gems"] = function(num) return { mod("GemProperty", "LIST", { keyword = "all", key = "level", value = num }, { type = "SocketedIn", slotName = "{SlotName}" }) } end,
- ["%+(%d+) to level of socketed (%a+) gems"] = function(num, _, type) return { mod("GemProperty", "LIST", { keyword = type, key = "level", value = num }, { type = "SocketedIn", slotName = "{SlotName}" }) } end,
- ["%+(%d+)%% to quality of socketed (%a+) gems"] = function(num, _, type) return { mod("GemProperty", "LIST", { keyword = type, key = "quality", value = num }, { type = "SocketedIn", slotName = "{SlotName}" }) } end,
- ["%+(%d+) to level of active socketed skill gems"] = function(num) return { mod("GemProperty", "LIST", { keyword = "active_skill", key = "level", value = num }, { type = "SocketedIn", slotName = "{SlotName}" }) } end,
- ["socketed gems fire an additional projectile"] = { mod("ExtraSkillMod", "LIST", { mod = mod("ProjectileCount", "BASE", 1) }, { type = "SocketedIn", slotName = "{SlotName}" }) },
- ["socketed gems fire (%d+) additional projectiles"] = function(num) return { mod("ExtraSkillMod", "LIST", { mod = mod("ProjectileCount", "BASE", num) }, { type = "SocketedIn", slotName = "{SlotName}" }) } end,
- ["socketed gems reserve no mana"] = { mod("ManaReserved", "MORE", -100, { type = "SocketedIn", slotName = "{SlotName}" }) },
- ["socketed skill gems get a (%d+)%% mana multiplier"] = function(num) return { mod("ExtraSkillMod", "LIST", { mod = mod("ManaCost", "MORE", num - 100) }, { type = "SocketedIn", slotName = "{SlotName}" }) } end,
- ["socketed gems have blood magic"] = { flag("SkillBloodMagic", { type = "SocketedIn", slotName = "{SlotName}" }) },
- ["socketed gems gain (%d+)%% of physical damage as extra lightning damage"] = function(num) return { mod("ExtraSkillMod", "LIST", { mod = mod("PhysicalDamageGainAsLightning", "BASE", num) }, { type = "SocketedIn", slotName = "{SlotName}" }) } end,
- ["socketed red gems get (%d+)%% physical damage as extra fire damage"] = function(num) return { mod("ExtraSkillMod", "LIST", { mod = mod("PhysicalDamageGainAsFire", "BASE", num) }, { type = "SocketedIn", slotName = "{SlotName}", keyword = "strength" }) } end,
- -- Extra skill/support
- ["grants level (%d+) (.+)"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["[ct][ar][si][tg]g?e?r?s? level (%d+) (.+) when equipped"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["[ct][ar][si][tg]g?e?r?s? level (%d+) (.+) on %a+"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["use level (%d+) (.+) on %a+"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["[ct][ar][si][tg]g?e?r?s? level (%d+) (.+) when you deal a critical strike"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["[ct][ar][si][tg]g?e?r?s? level (%d+) (.+) when hit"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["[ct][ar][si][tg]g?e?r?s? level (%d+) (.+) when you kill an enemy"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["[ct][ar][si][tg]g?e?r?s? level (%d+) (.+) when you use a skill"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["%d+%% chance to attack with level (%d+) (.+) on melee hit"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["%d+%% chance to trigger level (%d+) (.+) on melee hit"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["%d+%% chance to [ct][ar][si][tg]g?e?r? level (%d+) (.+) on %a+"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["attack with level (%d+) (.+) when you kill a bleeding enemy"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["triggers? level (%d+) (.+) when you kill a bleeding enemy"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["curse enemies with (%D+) on %a+"] = function(_, skill) return extraSkill(skill, 1, true) end,
- ["curse enemies with level (%d+) (.+) on %a+"] = function(num, _, skill) return extraSkill(skill, num, true) end,
- ["[ct][ar][si][tg]g?e?r?s? (.+) on %a+"] = function(_, skill) return extraSkill(skill, 1, true) end,
- ["attack with (.+) on %a+"] = function(_, skill) return extraSkill(skill, 1, true) end,
- ["[ct][ar][si][tg]g?e?r?s? (.+) when hit"] = function(_, skill) return extraSkill(skill, 1, true) end,
- ["attack with (.+) when hit"] = function(_, skill) return extraSkill(skill, 1, true) end,
- ["[ct][ar][si][tg]g?e?r?s? (.+) when your skills or minions kill"] = function(_, skill) return extraSkill(skill, 1, true) end,
- ["attack with (.+) when you take a critical strike"] = function( _, skill) return extraSkill(skill, 1, true) end,
- ["triggers? (.+) when you take a critical strike"] = function( _, skill) return extraSkill(skill, 1, true) end,
- ["socketed [%a+]* ?gems a?r?e? ?supported by level (%d+) (.+)"] = function(num, _, support) return { mod("ExtraSupport", "LIST", { skillId = gemIdLookup[support] or gemIdLookup[support:gsub("^increased ","")] or "Unknown", level = num }, { type = "SocketedIn", slotName = "{SlotName}" }) } end,
- -- Conversion
- ["increases and reductions to minion damage also affects? you"] = { flag("MinionDamageAppliesToPlayer") },
- ["increases and reductions to spell damage also apply to attacks"] = { flag("SpellDamageAppliesToAttacks") },
- ["modifiers to claw damage also apply to unarmed"] = { flag("ClawDamageAppliesToUnarmed") },
- ["modifiers to claw damage also apply to unarmed attack damage"] = { flag("ClawDamageAppliesToUnarmed") },
- ["modifiers to claw attack speed also apply to unarmed"] = { flag("ClawAttackSpeedAppliesToUnarmed") },
- ["modifiers to claw attack speed also apply to unarmed attack speed"] = { flag("ClawAttackSpeedAppliesToUnarmed") },
- ["modifiers to claw critical strike chance also apply to unarmed"] = { flag("ClawCritChanceAppliesToUnarmed") },
- ["modifiers to claw critical strike chance also apply to unarmed attack critical strike chance"] = { flag("ClawCritChanceAppliesToUnarmed") },
- ["gain (%d+)%% of bow physical damage as extra damage of each element"] = function(num) return { mod("PhysicalDamageGainAsLightning", "BASE", num, nil, ModFlag.Bow), mod("PhysicalDamageGainAsCold", "BASE", num, nil, ModFlag.Bow), mod("PhysicalDamageGainAsFire", "BASE", num, nil, ModFlag.Bow) } end,
- -- Crit
- ["your critical strike chance is lucky"] = { flag("CritChanceLucky") },
- ["your critical strikes do not deal extra damage"] = { flag("NoCritMultiplier") },
- ["critical strikes deal no damage"] = { mod("Damage", "MORE", -100, { type = "Condition", var = "CriticalStrike" }) },
- ["critical strike chance is increased by uncapped lightning resistance"] = { mod("CritChance", "INC", 1, { type = "PerStat", stat = "LightningResistTotal", div = 1 }) },
- ["non%-critical strikes deal (%d+)%% damage"] = function(num) return { mod("Damage", "MORE", -100+num, nil, ModFlag.Hit, { type = "Condition", var = "CriticalStrike", neg = true }) } end,
- -- Status Ailments
- ["your cold damage can ignite"] = { flag("ColdCanIgnite") },
- ["your fire damage can shock but not ignite"] = { flag("FireCanShock"), flag("FireCannotIgnite") },
- ["your cold damage can ignite but not freeze or chill"] = { flag("ColdCanIgnite"), flag("ColdCannotFreeze"), flag("ColdCannotChill") },
- ["your lightning damage can freeze but not shock"] = { flag("LightningCanFreeze"), flag("LightningCannotShock") },
- ["your chaos damage can shock"] = { flag("ChaosCanShock") },
- ["your physical damage can chill"] = { flag("PhysicalCanChill") },
- ["your physical damage can shock"] = { flag("PhysicalCanShock") },
- ["critical strikes do not always freeze"] = { flag("CritsDontAlwaysFreeze") },
- ["you can inflict up to (%d+) ignites on an enemy"] = { flag("IgniteCanStack") },
- ["enemies chilled by you take (%d+)%% increased burning damage"] = function(num) return { mod("EnemyModifier", "LIST", { mod = mod("FireDamageTakenOverTime", "INC", num) }, { type = "ActorCondition", actor = "enemy", var = "Chilled" }) } end,
- ["ignited enemies burn (%d+)%% faster"] = function(num) return { mod("IgniteBurnFaster", "INC", num) } end,
- ["ignited enemies burn (%d+)%% slower"] = function(num) return { mod("IgniteBurnSlower", "INC", num) } end,
- ["enemies ignited by an attack burn (%d+)%% faster"] = function(num) return { mod("IgniteBurnFaster", "INC", num, nil, ModFlag.Attack) } end,
- -- Bleed
- ["melee attacks cause bleeding"] = { mod("BleedChance", "BASE", 100, nil, ModFlag.Melee) },
- ["attacks cause bleeding when hitting cursed enemies"] = { mod("BleedChance", "BASE", 100, { type = "ActorCondition", actor = "enemy", var = "Cursed" }) },
- ["melee critical strikes cause bleeding"] = { mod("BleedChance", "BASE", 100, nil, ModFlag.Melee, { type = "Condition", var = "CriticalStrike" }) },
- ["causes bleeding on melee critical strike"] = { mod("BleedChance", "BASE", 100, nil, ModFlag.Melee, { type = "Condition", var = "CriticalStrike" }) },
- ["melee critical strikes have (%d+)%% chance to cause bleeding"] = function(num) return { mod("BleedChance", "BASE", num, nil, ModFlag.Melee, { type = "Condition", var = "CriticalStrike" }) } end,
- -- Poison
- ["your chaos damage poisons enemies"] = { mod("ChaosPoisonChance", "BASE", 100) },
- ["melee attacks poison on hit"] = { mod("PoisonChance", "BASE", 100, nil, ModFlag.Melee) },
- ["melee critical strikes have (%d+)%% chance to poison the enemy"] = function(num) return { mod("PoisonChance", "BASE", num, nil, ModFlag.Melee, { type = "Condition", var = "CriticalStrike" }) } end,
- ["critical strikes with daggers have a (%d+)%% chance to poison the enemy"] = function(num) return { mod("PoisonChance", "BASE", num, nil, ModFlag.Dagger, { type = "Condition", var = "CriticalStrike" }) } end,
- ["poison cursed enemies on hit"] = { mod("PoisonChance", "BASE", 100, { type = "ActorCondition", actor = "enemy", var = "Cursed" }) },
- ["wh[ie][ln]e? at maximum frenzy charges, attacks poison enemies"] = { mod("PoisonChance", "BASE", 100, nil, ModFlag.Attack, { type = "StatThreshold", stat = "FrenzyCharges", thresholdStat = "FrenzyChargesMax" }) },
- ["traps and mines have a (%d+)%% chance to poison on hit"] = function(num) return { mod("PoisonChance", "BASE", num, nil, 0, bor(KeywordFlag.Trap, KeywordFlag.Mine)) } end,
- -- Buffs/debuffs
- ["phasing"] = { flag("Condition:Phasing") },
- ["onslaught"] = { flag("Condition:Onslaught") },
- ["you have phasing if you've killed recently"] = { flag("Condition:Phasing", { type = "Condition", var = "KilledRecently" }) },
- ["your aura buffs do not affect allies"] = { flag("SelfAurasCannotAffectAllies") },
- ["allies' aura buffs do not affect you"] = { flag("AlliesAurasCannotAffectSelf") },
- ["enemies can have 1 additional curse"] = { mod("EnemyCurseLimit", "BASE", 1) },
- ["nearby enemies have (%d+)%% increased effect of curses on them"] = function(num) return { mod("EnemyModifier", "LIST", { mod = mod("CurseEffectOnSelf", "INC", num) }) } end,
- ["your hits inflict decay, dealing (%d+) chaos damage per second for %d+ seconds"] = function(num) return { mod("SkillData", "LIST", { key = "decay", value = num, merge = "MAX" }) } end,
- ["temporal chains has (%d+)%% reduced effect on you"] = function(num) return { mod("CurseEffectOnSelf", "INC", -num, { type = "SkillName", skillName = "Temporal Chains" }) } end,
- ["unaffected by temporal chains"] = { mod("CurseEffectOnSelf", "MORE", -100, { type = "SkillName", skillName = "Temporal Chains" }) },
- -- Traps, Mines and Totems
- ["traps and mines deal (%d+)%-(%d+) additional physical damage"] = function(_, min, max) return { mod("PhysicalMin", "BASE", tonumber(min), nil, 0, bor(KeywordFlag.Trap, KeywordFlag.Mine)), mod("PhysicalMax", "BASE", tonumber(max), nil, 0, bor(KeywordFlag.Trap, KeywordFlag.Mine)) } end,
- ["traps and mines deal (%d+) to (%d+) additional physical damage"] = function(_, min, max) return { mod("PhysicalMin", "BASE", tonumber(min), nil, 0, bor(KeywordFlag.Trap, KeywordFlag.Mine)), mod("PhysicalMax", "BASE", tonumber(max), nil, 0, bor(KeywordFlag.Trap, KeywordFlag.Mine)) } end,
- ["can have up to (%d+) additional traps? placed at a time"] = function(num) return { mod("ActiveTrapLimit", "BASE", num) } end,
- ["can have up to (%d+) additional remote mines? placed at a time"] = function(num) return { mod("ActiveMineLimit", "BASE", num) } end,
- ["can have up to (%d+) additional totems? summoned at a time"] = function(num) return { mod("ActiveTotemLimit", "BASE", num) } end,
- ["attack skills can have (%d+) additional totems? summoned at a time"] = function(num) return { mod("ActiveTotemLimit", "BASE", num, nil, 0, KeywordFlag.Attack) } end,
- ["can [hs][au][vm][em]o?n? 1 additional siege ballista totem per (%d+) dexterity"] = function(num) return { mod("ActiveTotemLimit", "BASE", 1, { type = "SkillName", skillName = "Siege Ballista" }, { type = "PerStat", stat = "Dex", div = num }) } end,
- ["totems fire (%d+) additional projectiles"] = function(num) return { mod("ProjectileCount", "BASE", num, nil, 0, KeywordFlag.Totem) } end,
- ["([%d%.]+)%% of damage dealt by y?o?u?r? ?totems is leeched to you as life"] = function(num) return { mod("DamageLifeLeechToPlayer", "BASE", num, nil, 0, KeywordFlag.Totem) } end,
- -- Minions
- ["your strength is added to your minions"] = { flag("StrengthAddedToMinions") },
- ["minions poison enemies on hit"] = { mod("MinionModifier", "LIST", { mod = mod("PoisonChance", "BASE", 100) }) },
- ["(%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,
- ["minions deal (%d+)%% increased damage per 10 dexterity"] = function(num) return { mod("MinionModifier", "LIST", { mod = mod("Damage", "INC", num) }, { type = "PerStat", stat = "Dex", div = 10 }) } end,
- ["(%d+)%% increased golem damage for each type of golem you have summoned"] = function(num) return {
- mod("MinionModifier", "LIST", { mod = mod("Damage", "INC", num, { type = "ActorCondition", actor = "parent", var = "HavePhysicalGolem" }) }, { type = "SkillType", skillType = SkillType.Golem }),
- mod("MinionModifier", "LIST", { mod = mod("Damage", "INC", num, { type = "ActorCondition", actor = "parent", var = "HaveLightningGolem" }) }, { type = "SkillType", skillType = SkillType.Golem }),
- mod("MinionModifier", "LIST", { mod = mod("Damage", "INC", num, { type = "ActorCondition", actor = "parent", var = "HaveColdGolem" }) }, { type = "SkillType", skillType = SkillType.Golem }),
- mod("MinionModifier", "LIST", { mod = mod("Damage", "INC", num, { type = "ActorCondition", actor = "parent", var = "HaveFireGolem" }) }, { type = "SkillType", skillType = SkillType.Golem }),
- mod("MinionModifier", "LIST", { mod = mod("Damage", "INC", num, { type = "ActorCondition", actor = "parent", var = "HaveChaosGolem" }) }, { type = "SkillType", skillType = SkillType.Golem }),
- } end,
- ["can summon up to (%d) additional golems? at a time"] = function(num) return { mod("ActiveGolemLimit", "BASE", num) } end,
- ["if you have 3 primordial jewels, can summon up to (%d) additional golems? at a time"] = function(num) return { mod("ActiveGolemLimit", "BASE", num, { type = "MultiplierThreshold", var = "PrimordialItem", threshold = 3 }) } end,
- ["golems regenerate (%d)%% of their maximum life per second"] = function(num) return { mod("MinionModifier", "LIST", { mod = mod("LifeRegenPercent", "BASE", num) }, { type = "SkillType", skillType = SkillType.Golem }) } end,
- -- Projectiles
- ["skills chain %+(%d) times"] = function(num) return { mod("ChainCountMax", "BASE", num) } end,
- ["skills chain an additional time while at maximum frenzy charges"] = { mod("ChainCountMax", "BASE", 1, { type = "StatThreshold", stat = "FrenzyCharges", thresholdStat = "FrenzyChargesMax" }) },
- ["adds an additional arrow"] = { mod("ProjectileCount", "BASE", 1, nil, ModFlag.Attack) },
- ["(%d+) additional arrows"] = function(num) return { mod("ProjectileCount", "BASE", num, nil, ModFlag.Attack) } end,
- ["skills fire an additional projectile"] = { mod("ProjectileCount", "BASE", 1) },
- ["spells have an additional projectile"] = { mod("ProjectileCount", "BASE", 1, nil, ModFlag.Spell) },
- ["arrows always pierce"] = { mod("PierceChance", "BASE", 100, nil, ModFlag.Attack) },
- ["arrows that pierce cause bleeding"] = { flag("ArrowsThatPierceCauseBleeding") },
- ["projectile damage increased by arrow pierce chance"] = { mod("Damage", "INC", 1, nil, ModFlag.Projectile, 0, { type = "PerStat", stat = "PierceChance", div = 1 }) },
- ["projectile damage increased by (%d+)%% of arrow pierce chance"] = function(num) return { mod("Damage", "INC", 1, nil, ModFlag.Projectile, 0, { type = "PerStat", stat = "PierceChance", div = 100/num }) } end,
- ["projectiles pierce while phasing"] = { mod("PierceChance", "BASE", 100, { type = "Condition", var = "Phasing" }) },
- ["attacks chain an additional time when in main hand"] = { mod("ChainCountMax", "BASE", 1, nil, ModFlag.Attack, { type = "SlotNumber", num = 1 }) },
- ["attacks have an additional projectile when in off hand"] = { mod("ProjectileCount", "BASE", 1, nil, ModFlag.Attack, { type = "SlotNumber", num = 2 }) },
- -- Leech
- ["cannot leech life"] = { flag("CannotLeechLife") },
- ["cannot leech mana"] = { flag("CannotLeechMana") },
- ["cannot leech when on low life"] = { flag("CannotLeechLife", { type = "Condition", var = "LowLife" }), flag("CannotLeechMana", { type = "Condition", var = "LowLife" }) },
- ["cannot leech life from critical strikes"] = { flag("CannotLeechLife", { type = "Condition", var = "CriticalStrike" }) },
- ["leech applies instantly on critical strike"] = { flag("InstantLifeLeech", { type = "Condition", var = "CriticalStrike" }), flag("InstantManaLeech", { type = "Condition", var = "CriticalStrike" }) },
- ["leech applies instantly during flask effect"] = { flag("InstantLifeLeech", { type = "Condition", var = "UsingFlask" }), flag("InstantManaLeech", { type = "Condition", var = "UsingFlask" }) },
- ["with 5 corrupted items equipped: life leech recovers based on your chaos damage instead"] = { flag("LifeLeechBasedOnChaosDamage", { type = "MultiplierThreshold", var = "CorruptedItem", threshold = 5 }) },
- -- Defences
- ["cannot evade enemy attacks"] = { flag("CannotEvade") },
- ["cannot block attacks"] = { flag("CannotBlockAttacks") },
- ["you have no life regeneration"] = { flag("NoLifeRegen") },
- ["elemental resistances are zero"] = {
- mod("FireResist", "OVERRIDE", 0),
- mod("ColdResist", "OVERRIDE", 0),
- mod("LightningResist", "OVERRIDE", 0)
- },
- ["armour is increased by uncapped fire resistance"] = { mod("Armour", "INC", 1, { type = "PerStat", stat = "FireResistTotal", div = 1 }) },
- ["evasion rating is increased by uncapped cold resistance"] = { mod("Evasion", "INC", 1, { type = "PerStat", stat = "ColdResistTotal", div = 1 }) },
- ["reflects (%d+) physical damage to melee attackers"] = { },
- ["ignore all movement penalties from armour"] = { flag("Condition:IgnoreMovementPenalties") },
- ["cannot be stunned"] = { mod("AvoidStun", "BASE", 100) },
- ["cannot be shocked"] = { mod("AvoidShock", "BASE", 100) },
- ["cannot be frozen"] = { mod("AvoidFreeze", "BASE", 100) },
- ["cannot be chilled"] = { mod("AvoidChill", "BASE", 100) },
- ["cannot be ignited"] = { mod("AvoidIgnite", "BASE", 100) },
- ["you are immune to bleeding"] = { mod("AvoidBleed", "BASE", 100) },
- ["you cannot be shocked while at maximum endurance charges"] = { mod("AvoidShock", "BASE", 100, { type = "StatThreshold", stat = "EnduranceCharges", thresholdStat = "EnduranceChargesMax" }) },
- ["immunity to shock during flask effect"] = { mod("AvoidShock", "BASE", 100, { type = "Condition", var = "UsingFlask" }) },
- ["immunity to freeze and chill during flask effect"] = { mod("AvoidFreeze", "BASE", 100, { type = "Condition", var = "UsingFlask" }), mod("AvoidChill", "BASE", 100, { type = "Condition", var = "UsingFlask" }) },
- ["immunity to ignite during flask effect"] = { mod("AvoidIgnite", "BASE", 100, { type = "Condition", var = "UsingFlask" }) },
- ["immunity to bleeding during flask effect"] = { mod("AvoidBleed", "BASE", 100, { type = "Condition", var = "UsingFlask" }) },
- ["immune to poison during flask effect"] = { mod("AvoidPoison", "BASE", 100, { type = "Condition", var = "UsingFlask" }) },
- ["immune to curses during flask effect"] = { mod("AvoidCurse", "BASE", 100, { type = "Condition", var = "UsingFlask" }) },
- ["unaffected by curses"] = { mod("CurseEffectOnSelf", "MORE", -100) },
- -- Knockback
- ["cannot knock enemies back"] = { flag("CannotKnockback") },
- ["knocks back enemies if you get a critical strike with a staff"] = { mod("EnemyKnockbackChance", "BASE", 100, nil, ModFlag.Staff, { type = "Condition", var = "CriticalStrike" }) },
- ["knocks back enemies if you get a critical strike with a bow"] = { mod("EnemyKnockbackChance", "BASE", 100, nil, ModFlag.Bow, { type = "Condition", var = "CriticalStrike" }) },
- ["bow knockback at close range"] = { mod("EnemyKnockbackChance", "BASE", 100, nil, ModFlag.Bow, { type = "Condition", var = "AtCloseRange" }) },
- ["adds knockback during flask effect"] = { mod("EnemyKnockbackChance", "BASE", 100, { type = "Condition", var = "UsingFlask" }) },
- ["adds knockback to melee attacks during flask effect"] = { mod("EnemyKnockbackChance", "BASE", 100, nil, ModFlag.Melee, { type = "Condition", var = "UsingFlask" }) },
- -- Flasks
- ["flasks do not apply to you"] = { flag("FlasksDoNotApplyToPlayer") },
- ["flasks apply to your zombies and spectres"] = { flag("FlasksApplyToMinion", { type = "SkillName", skillNameList = { "Raise Zombie", "Raise Spectre" } }) },
- ["creates a smoke cloud on use"] = { },
- ["creates chilled ground on use"] = { },
- ["creates consecrated ground on use"] = { },
- ["gain unholy might during flask effect"] = { flag("Condition:UnholyMight", { type = "Condition", var = "UsingFlask" }) },
- ["zealot's oath during flask effect"] = { mod("ZealotsOath", "FLAG", true, { type = "Condition", var = "UsingFlask" }) },
- ["grants level (%d+) (.+) curse aura during flask effect"] = function(num, _, skill) return { mod("ExtraCurse", "LIST", { skillId = gemIdLookup[skill:gsub(" skill","")] or "Unknown", level = num }, { type = "Condition", var = "UsingFlask" }) } end,
- ["during flask effect, (%d+)%% reduced damage taken of each element for which your uncapped elemental resistance is lowest"] = function(num) return {
- mod("LightningDamageTaken", "INC", -num, { type = "StatThreshold", stat = "LightningResistTotal", thresholdStat = "ColdResistTotal", upper = true }, { type = "StatThreshold", stat = "LightningResistTotal", thresholdStat = "FireResistTotal", upper = true }),
- mod("ColdDamageTaken", "INC", -num, { type = "StatThreshold", stat = "ColdResistTotal", thresholdStat = "LightningResistTotal", upper = true }, { type = "StatThreshold", stat = "ColdResistTotal", thresholdStat = "FireResistTotal", upper = true }),
- mod("FireDamageTaken", "INC", -num, { type = "StatThreshold", stat = "FireResistTotal", thresholdStat = "LightningResistTotal", upper = true }, { type = "StatThreshold", stat = "FireResistTotal", thresholdStat = "ColdResistTotal", upper = true }),
- } end,
- ["during flask effect, damage penetrates (%d+)%% o?f? ?resistance of each element for which your uncapped elemental resistance is highest"] = function(num) return {
- mod("LightningPenetration", "BASE", num, { type = "StatThreshold", stat = "LightningResistTotal", thresholdStat = "ColdResistTotal" }, { type = "StatThreshold", stat = "LightningResistTotal", thresholdStat = "FireResistTotal" }),
- mod("ColdPenetration", "BASE", num, { type = "StatThreshold", stat = "ColdResistTotal", thresholdStat = "LightningResistTotal" }, { type = "StatThreshold", stat = "ColdResistTotal", thresholdStat = "FireResistTotal" }),
- mod("FirePenetration", "BASE", num, { type = "StatThreshold", stat = "FireResistTotal", thresholdStat = "LightningResistTotal" }, { type = "StatThreshold", stat = "FireResistTotal", thresholdStat = "ColdResistTotal" }),
- } end,
- ["(%d+)%% of maximum life taken as chaos damage per second"] = function(num) return { mod("ChaosDegen", "BASE", num/100, { type = "PerStat", stat = "Life", div = 1 }) } end,
- -- Jewels
- ["passives in radius can be allocated without being connected to your tree"] = { mod("JewelData", "LIST", { key = "intuitiveLeap", value = true }) },
- ["(%d+)%% increased elemental damage per grand spectrum"] = function(num) return {
- mod("ElementalDamage", "INC", num, { type = "Multiplier", var = "GrandSpectrum" }),
- mod("Multiplier:GrandSpectrum", "BASE", 1)
- } end,
- ["gain (%d+) armour per grand spectrum"] = function(num) return {
- mod("Armour", "BASE", num, { type = "Multiplier", var = "GrandSpectrum" }),
- mod("Multiplier:GrandSpectrum", "BASE", 1)
- } end,
- ["gain (%d+) mana per grand spectrum"] = function(num) return {
- mod("Mana", "BASE", num, { type = "Multiplier", var = "GrandSpectrum" }),
- mod("Multiplier:GrandSpectrum", "BASE", 1)
- } end,
- ["primordial"] = { mod("Multiplier:PrimordialItem", "BASE", 1) },
- -- Misc
- ["iron will"] = { flag("IronWill") },
- ["deal no physical damage"] = { flag("DealNoPhysical") },
- ["deal no elemental damage"] = { flag("DealNoLightning"), flag("DealNoCold"), flag("DealNoFire") },
- ["attacks have blood magic"] = { flag("SkillBloodMagic", nil, ModFlag.Attack) },
- ["(%d+)%% chance to cast a? ?socketed lightning spells? on hit"] = function(num) return { mod("ExtraSupport", "LIST", { name = "SupportUniqueMjolnerLightningSpellsCastOnHit", level = 1 }, { type = "SocketedIn", slotName = "{SlotName}" }) } end,
- ["cast a socketed lightning spell on hit"] = { mod("ExtraSupport", "LIST", { name = "SupportUniqueMjolnerLightningSpellsCastOnHit", level = 1 }, { type = "SocketedIn", slotName = "{SlotName}" }) },
- ["cast a socketed cold s[pk][ei]ll on melee critical strike"] = { mod("ExtraSupport", "LIST", { name = "SupportUniqueCosprisMaliceColdSpellsCastOnMeleeCriticalStrike", level = 1 }, { type = "SocketedIn", slotName = "{SlotName}" }) },
- ["your curses can apply to hexproof enemies"] = { flag("CursesIgnoreHexproof") },
- ["you have onslaught while you have fortify"] = { flag("Condition:Onslaught", { type = "Condition", var = "Fortify" }) },
- ["reserves (%d+)%% of life"] = function(num) return { mod("ExtraLifeReserved", "BASE", num) } end,
- ["items and gems have (%d+)%% reduced attribute requirements"] = function(num) return { mod("GlobalAttributeRequirements", "INC", -num) } end,
- -- Skill-specific enchantment modifiers
- ["(%d+)%% increased decoy totem life"] = function(num) return { mod("TotemLife", "INC", num, { type = "SkillName", skillName = "Decoy Totem" }) } end,
- ["(%d+)%% increased ice spear critical strike chance in second form"] = function(num) return { mod("CritChance", "INC", num, { type = "SkillName", skillName = "Ice Spear" }, { type = "SkillPart", skillPart = 2 }) } end,
- ["(%d+)%% increased incinerate damage for each stage"] = function(num) return { mod("Damage", "INC", num * 3, { type = "SkillName", skillName = "Incinerate" }, { type = "SkillPart", skillPart = 2 }) } end,
- ["shock nova ring deals (%d+)%% increased damage"] = function(num) return { mod("Damage", "INC", num, { type = "SkillName", skillName = "Shock Nova" }, { type = "SkillPart", skillPart = 1 }) } end,
- -- Display-only modifiers
- ["prefixes:"] = { },
- ["suffixes:"] = { },
- ["socketed lightning spells have (%d+)%% increased spell damage if triggered"] = { },
- ["manifeste?d? dancing dervish disables both weapon slots"] = { },
- ["manifeste?d? dancing dervish dies when rampage ends"] = { },
-}
-local keystoneList = {
- -- List of keystones that can be found on uniques
- "Acrobatics",
- "Ancestral Bond",
- "Arrow Dancing",
- "Avatar of Fire",
- "Blood Magic",
- "Conduit",
- "Eldritch Battery",
- "Elemental Equilibrium",
- "Elemental Overload",
- "Ghost Reaver",
- "Iron Grip",
- "Iron Reflexes",
- "Mind Over Matter",
- "Minion Instability",
- "Pain Attunement",
- "Phase Acrobatics",
- "Point Blank",
- "Resolute Technique",
- "Unwavering Stance",
- "Vaal Pact",
- "Zealot's Oath",
-}
-for _, name in pairs(keystoneList) do
- specialModList[name:lower()] = { mod("Keystone", "LIST", name) }
-end
-local oldList = specialModList
-specialModList = { }
-for k, v in pairs(oldList) do
- specialModList["^"..k.."$"] = v
-end
-
--- Modifiers that are recognised but unsupported
-local unsupportedModList = {
- ["culling strike"] = true,
- ["properties are doubled while in a breach"] = true,
-}
-
--- Special lookups used for various modifier forms
-local suffixTypes = {
- ["as extra lightning damage"] = "GainAsLightning",
- ["added as lightning damage"] = "GainAsLightning",
- ["gained as extra lightning damage"] = "GainAsLightning",
- ["as extra cold damage"] = "GainAsCold",
- ["added as cold damage"] = "GainAsCold",
- ["gained as extra cold damage"] = "GainAsCold",
- ["as extra fire damage"] = "GainAsFire",
- ["added as fire damage"] = "GainAsFire",
- ["gained as extra fire damage"] = "GainAsFire",
- ["as extra chaos damage"] = "GainAsChaos",
- ["added as chaos damage"] = "GainAsChaos",
- ["gained as extra chaos damage"] = "GainAsChaos",
- ["converted to lightning"] = "ConvertToLightning",
- ["converted to lightning damage"] = "ConvertToLightning",
- ["converted to cold damage"] = "ConvertToCold",
- ["converted to fire damage"] = "ConvertToFire",
- ["converted to chaos damage"] = "ConvertToChaos",
- ["added as energy shield"] = "GainAsEnergyShield",
- ["as extra maximum energy shield"] = "GainAsEnergyShield",
- ["converted to energy shield"] = "ConvertToEnergyShield",
- ["as physical damage"] = "AsPhysical",
- ["as lightning damage"] = "AsLightning",
- ["as cold damage"] = "AsCold",
- ["as fire damage"] = "AsFire",
- ["as chaos damage"] = "AsChaos",
- ["leeched as life and mana"] = "Leech",
- ["leeched as life"] = "LifeLeech",
- ["leeched as mana"] = "ManaLeech",
-}
-local dmgTypes = {
- ["physical"] = "Physical",
- ["lightning"] = "Lightning",
- ["cold"] = "Cold",
- ["fire"] = "Fire",
- ["chaos"] = "Chaos",
-}
-local penTypes = {
- ["lightning resistance"] = "LightningPenetration",
- ["cold resistance"] = "ColdPenetration",
- ["fire resistance"] = "FirePenetration",
- ["elemental resistance"] = "ElementalPenetration",
- ["elemental resistances"] = "ElementalPenetration",
-}
-local regenTypes = {
- ["life"] = "LifeRegen",
- ["maximum life"] = "LifeRegen",
- ["life and mana"] = { "LifeRegen", "ManaRegen" },
- ["mana"] = "ManaRegen",
- ["energy shield"] = "EnergyShieldRegen",
- ["maximum mana and energy shield"] = { "ManaRegen", "EnergyShieldRegen" },
-}
-
--- Build active skill name lookup
-local skillNameList = { }
-local preSkillNameList = { }
-for gemId, gemData in pairs(data["2_6"].gems) do
- local grantedEffect = gemData.grantedEffect
- if not grantedEffect.hidden and not grantedEffect.support then
- local skillName = grantedEffect.name
- skillNameList[" "..skillName:lower().." "] = { tag = { type = "SkillName", skillName = skillName } }
- preSkillNameList["^"..skillName:lower().." has ?a? "] = { tag = { type = "SkillName", skillName = skillName } }
- if gemData.tags.totem then
- preSkillNameList["^"..skillName:lower().." totem deals "] = { tag = { type = "SkillName", skillName = skillName } }
- preSkillNameList["^"..skillName:lower().." totem grants "] = { addToSkill = { type = "SkillName", skillName = skillName }, tag = { type = "GlobalEffect", effectType = "Buff" } }
- end
- if grantedEffect.skillTypes[SkillType.Buff] or grantedEffect.baseFlags.buff then
- preSkillNameList["^"..skillName:lower().." grants "] = { addToSkill = { type = "SkillName", skillName = skillName }, tag = { type = "GlobalEffect", effectType = "Buff" } }
- preSkillNameList["^"..skillName:lower().." grants a?n? ?additional "] = { addToSkill = { type = "SkillName", skillName = skillName }, tag = { type = "GlobalEffect", effectType = "Buff" } }
- end
- if gemData.tags.chaining then
- specialModList["^"..skillName:lower().." chains an additional time"] = { mod("ExtraSkillMod", "LIST", { mod = mod("ChainCountMax", "BASE", 1) }, { type = "SkillName", skillName = skillName }) }
- specialModList["^"..skillName:lower().." chains an additional (%d+) times"] = function(num) return { mod("ExtraSkillMod", "LIST", { mod = mod("ChainCountMax", "BASE", num) }, { type = "SkillName", skillName = skillName }) } end
- end
- end
-end
-
--- Radius jewels that modify other nodes
-local function getSimpleConv(srcList, dst, type, remove, factor)
- return function(node, out, data)
- if node then
- for _, src in pairs(srcList) do
- for _, mod in ipairs(node.modList) do
- if mod.name == src and mod.type == type then
- if remove then
- out:MergeNewMod(src, type, -mod.value, mod.source, mod.flags, mod.keywordFlags, unpack(mod))
- end
- if factor then
- out:MergeNewMod(dst, type, math.floor(mod.value * factor), mod.source, mod.flags, mod.keywordFlags, unpack(mod))
- else
- out:MergeNewMod(dst, type, mod.value, mod.source, mod.flags, mod.keywordFlags, unpack(mod))
- end
- end
- end
- end
- end
- end
-end
-local jewelOtherFuncs = {
- ["Strength from Passives in Radius is Transformed to Dexterity"] = getSimpleConv({"Str"}, "Dex", "BASE", true),
- ["Dexterity from Passives in Radius is Transformed to Strength"] = getSimpleConv({"Dex"}, "Str", "BASE", true),
- ["Strength from Passives in Radius is Transformed to Intelligence"] = getSimpleConv({"Str"}, "Int", "BASE", true),
- ["Intelligence from Passives in Radius is Transformed to Strength"] = getSimpleConv({"Int"}, "Str", "BASE", true),
- ["Dexterity from Passives in Radius is Transformed to Intelligence"] = getSimpleConv({"Dex"}, "Int", "BASE", true),
- ["Intelligence from Passives in Radius is Transformed to Dexterity"] = getSimpleConv({"Int"}, "Dex", "BASE", true),
- ["Increases and Reductions to Life in Radius are Transformed to apply to Energy Shield"] = getSimpleConv({"Life"}, "EnergyShield", "INC", true),
- ["Increases and Reductions to Energy Shield in Radius are Transformed to apply to Armour at 200% of their value"] = getSimpleConv({"EnergyShield"}, "Armour", "INC", true, 2),
- ["Increases and Reductions to Life in Radius are Transformed to apply to Mana at 200% of their value"] = getSimpleConv({"Life"}, "Mana", "INC", true, 2),
- ["Increases and Reductions to Physical Damage in Radius are Transformed to apply to Cold Damage"] = getSimpleConv({"PhysicalDamage"}, "ColdDamage", "INC", true),
- ["Increases and Reductions to Cold Damage in Radius are Transformed to apply to Physical Damage"] = getSimpleConv({"ColdDamage"}, "PhysicalDamage", "INC", true),
- ["Increases and Reductions to other Damage Types in Radius are Transformed to apply to Fire Damage"] = getSimpleConv({"PhysicalDamage","ColdDamage","LightningDamage","ChaosDamage"}, "FireDamage", "INC", true),
- ["Passives granting Lightning Resistance or all Elemental Resistances in Radius also grant Chance to Block Spells at 35% of its value"] = getSimpleConv({"LightningResist","ElementalResist"}, "SpellBlockChance", "BASE", false, 0.35),
- ["Passives granting Cold Resistance or all Elemental Resistances in Radius also grant Chance to Dodge Attacks at 35% of its value"] = getSimpleConv({"ColdResist","ElementalResist"}, "AttackDodgeChance", "BASE", false, 0.35),
- ["Passives granting Fire Resistance or all Elemental Resistances in Radius also grant Chance to Block at 35% of its value"] = getSimpleConv({"FireResist","ElementalResist"}, "BlockChance", "BASE", false, 0.35),
- ["Melee and Melee Weapon Type modifiers in Radius are Transformed to Bow Modifiers"] = function(node, out, data)
- if node then
- local mask1 = bor(ModFlag.Axe, ModFlag.Claw, ModFlag.Dagger, ModFlag.Mace, ModFlag.Staff, ModFlag.Sword, ModFlag.Melee)
- local mask2 = bor(ModFlag.Weapon1H, ModFlag.WeaponMelee)
- local mask3 = bor(ModFlag.Weapon2H, ModFlag.WeaponMelee)
- for _, mod in ipairs(node.modList) do
- if band(mod.flags, mask1) ~= 0 or band(mod.flags, mask2) == mask2 or band(mod.flags, mask3) == mask3 then
- out:MergeNewMod(mod.name, mod.type, -mod.value, mod.source, mod.flags, mod.keywordFlags, unpack(mod))
- out:MergeNewMod(mod.name, mod.type, mod.value, mod.source, bor(band(mod.flags, bnot(bor(mask1, mask2, mask3))), ModFlag.Bow), mod.keywordFlags, unpack(mod))
- elseif mod[1] then
- local using = { UsingAxe = true, UsingClaw = true, UsingDagger = true, UsingMace = true, UsingStaff = true, UsingSword = true, UsingMeleeWeapon = true }
- for _, tag in ipairs(mod) do
- if tag.type == "Condition" and using[tag.var] then
- local newTagList = copyTable(mod)
- for _, tag in ipairs(newTagList) do
- if tag.type == "Condition" and using[tag.var] then
- tag.var = "UsingBow"
- break
- end
- end
- out:MergeNewMod(mod.name, mod.type, -mod.value, mod.source, mod.flags, mod.keywordFlags, unpack(mod))
- out:MergeNewMod(mod.name, mod.type, mod.value, mod.source, mod.flags, mod.keywordFlags, unpack(newTagList))
- break
- end
- end
- end
- end
- end
- end,
-}
-
--- Radius jewels that modify the jewel itself based on nearby allocated nodes
-local function getPerStat(dst, modType, flags, stat, factor)
- return function(node, out, data)
- if node then
- data[stat] = (data[stat] or 0) + out:Sum("BASE", nil, stat)
- else
- out:NewMod(dst, modType, math.floor((data[stat] or 0) * factor + 0.5), data.modSource, flags)
- end
- end
-end
-local jewelSelfFuncs = {
- ["Adds 1 to maximum Life per 3 Intelligence in Radius"] = getPerStat("Life", "BASE", 0, "Int", 1 / 3),
- ["Adds 1 to Maximum Life per 3 Intelligence Allocated in Radius"] = getPerStat("Life", "BASE", 0, "Int", 1 / 3),
- ["1% increased Evasion Rating per 3 Dexterity Allocated in Radius"] = getPerStat("Evasion", "INC", 0, "Dex", 1 / 3),
- ["1% increased Claw Physical Damage per 3 Dexterity Allocated in Radius"] = getPerStat("PhysicalDamage", "INC", ModFlag.Claw, "Dex", 1 / 3),
- ["1% increased Melee Physical Damage while Unarmed per 3 Dexterity Allocated in Radius"] = getPerStat("PhysicalDamage", "INC", ModFlag.Unarmed, "Dex", 1 / 3),
- ["3% increased Totem Life per 10 Strength in Radius"] = getPerStat("TotemLife", "INC", 0, "Str", 3 / 10),
- ["3% increased Totem Life per 10 Strength Allocated in Radius"] = getPerStat("TotemLife", "INC", 0, "Str", 3 / 10),
- ["Adds 1 maximum Lightning Damage to Attacks per 1 Dexterity Allocated in Radius"] = getPerStat("LightningMax", "BASE", ModFlag.Attack, "Dex", 1),
- ["5% increased Chaos damage per 10 Intelligence from Allocated Passives in Radius"] = getPerStat("ChaosDamage", "INC", 0, "Int", 5 / 10),
- ["Dexterity and Intelligence from passives in Radius count towards Strength Melee Damage bonus"] = function(node, out, data)
- if node then
- data.Dex = (data.Dex or 0) + node.modList:Sum("BASE", nil, "Dex")
- data.Int = (data.Int or 0) + node.modList:Sum("BASE", nil, "Int")
- else
- out:NewMod("DexIntToMeleeBonus", "BASE", data.Dex + data.Int, data.modSource)
- end
- end,
-}
-
--- Radius jewels with bonuses conditional upon attributes of nearby nodes
-local function getThreshold(attrib, name, modType, value, ...)
- local baseMod = mod(name, modType, value, "", ...)
- return function(node, out, data)
- if node then
- data[attrib] = (data[attrib] or 0) + out:Sum("BASE", nil, attrib)
- elseif (data[attrib] or 0) >= 40 then
- local mod = copyTable(baseMod)
- mod.source = data.modSource
- if type(value) == "table" and value.mod then
- value.mod.source = data.modSource
- end
- out:AddMod(mod)
- end
- end
-end
-local jewelThresholdFuncs = {
- ["With at least 40 Dexterity in Radius, Frost Blades Melee Damage Penetrates 15% Cold Resistance"] = getThreshold("Dex", "ColdPenetration", "BASE", 15, ModFlag.Melee, { type = "SkillName", skillName = "Frost Blades" }),
- ["With at least 40 Dexterity in Radius, Melee Damage dealt by Frost Blades Penetrates 15% Cold Resistance"] = getThreshold("Dex", "ColdPenetration", "BASE", 15, ModFlag.Melee, { type = "SkillName", skillName = "Frost Blades" }),
- ["With at least 40 Dexterity in Radius, Frost Blades has 25% increased Projectile Speed"] = getThreshold("Dex", "ProjectileSpeed", "INC", 25, { type = "SkillName", skillName = "Frost Blades" }),
- ["With at least 40 Dexterity in Radius, Ice Shot has 25% increased Area of Effect"] = getThreshold("Dex", "AreaOfEffect", "INC", 25, { type = "SkillName", skillName = "Ice Shot" }),
- ["With at least 40 Dexterity in Radius, Ice Shot has 50% chance of Projectiles Piercing"] = getThreshold("Dex", "PierceChance", "BASE", 50, { type = "SkillName", skillName = "Ice Shot" }),
- ["With at least 40 Intelligence in Radius, Frostbolt fires 2 additional Projectiles"] = getThreshold("Int", "ProjectileCount", "BASE", 2, { type = "SkillName", skillName = "Frostbolt" }),
- ["With at least 40 Intelligence in Radius, Magma Orb fires an additional Projectile"] = getThreshold("Int", "ProjectileCount", "BASE", 1, { type = "SkillName", skillName = "Magma Orb" }),
- ["With at least 40 Intelligence in Radius, Magma Orb has 10% increased Area of Effect per Chain"] = getThreshold("Int", "AreaOfEffect", "INC", 10, { type = "SkillName", skillName = "Magma Orb" }, { type = "PerStat", stat = "Chain" }),
- ["With at least 40 Dexterity in Radius, Shrapnel Shot has 25% increased Area of Effect"] = getThreshold("Dex", "AreaOfEffect", "INC", 25, { type = "SkillName", skillName = "Shrapnel Shot" }),
- ["With at least 40 Dexterity in Radius, Shrapnel Shot's cone has a 50% chance to deal Double Damage"] = getThreshold("Dex", "DoubleDamageChance", "BASE", 50, { type = "SkillName", skillName = "Shrapnel Shot" }, { type = "SkillPart", skillPart = 2 }),
- ["With at least 40 Intelligence in Radius, Freezing Pulse fires 2 additional Projectiles"] = getThreshold("Int", "ProjectileCount", "BASE", 2, { type = "SkillName", skillName = "Freezing Pulse" }),
- ["With at least 40 Intelligence in Radius, 25% increased Freezing Pulse Damage if you've Shattered an Enemy Recently"] = getThreshold("Int", "Damage", "INC", 25, { type = "SkillName", skillName = "Freezing Pulse" }, { type = "Condition", var = "ShatteredEnemyRecently" }),
- ["With at least 40 Dexterity in Radius, Ethereal Knives fires 10 additional Projectiles"] = getThreshold("Dex", "ProjectileCount", "BASE", 10, { type = "SkillName", skillName = "Ethereal Knives" }),
- ["With at least 40 Strength in Radius, Molten Strike fires 2 additional Projectiles"] = getThreshold("Str", "ProjectileCount", "BASE", 2, { type = "SkillName", skillName = "Molten Strike" }),
- ["With at least 40 Strength in Radius, Molten Strike has 25% increased Area of Effect"] = getThreshold("Str", "AreaOfEffect", "INC", 25, { type = "SkillName", skillName = "Molten Strike" }),
- ["With at least 40 Strength in Radius, 25% of Glacial Hammer Physical Damage converted to Cold Damage"] = getThreshold("Str", "PhysicalDamageConvertToCold", "BASE", 25, { type = "SkillName", skillName = "Glacial Hammer" }),
- ["With at least 40 Strength in Radius, Heavy Strike has a 20% chance to deal Double Damage"] = getThreshold("Str", "DoubleDamageChance", "BASE", 20, { type = "SkillName", skillName = "Heavy Strike" }),
- ["With at least 40 Strength in Radius, Heavy Strike has a 20% chance to deal Double Damage."] = getThreshold("Str", "DoubleDamageChance", "BASE", 20, { type = "SkillName", skillName = "Heavy Strike" }),
- ["With at least 40 Dexterity in Radius, Dual Strike has a 20% chance to deal Double Damage with the Main-Hand Weapon"] = getThreshold("Dex", "DoubleDamageChance", "BASE", 20, { type = "SkillName", skillName = "Dual Strike" }, { type = "Condition", var = "MainHandAttack" }),
- ["With at least 40 Intelligence in Radius, Raised Zombies' Slam Attack has 100% increased Cooldown Recovery Speed"] = getThreshold("Int", "MinionModifier", "LIST", { mod = mod("CooldownRecovery", "INC", 100, { type = "SkillId", skillId = "ZombieSlam" }) }),
- ["With at least 40 Intelligence in Radius, Raised Zombies' Slam Attack deals 30% increased Damage"] = getThreshold("Int", "MinionModifier", "LIST", { mod = mod("Damage", "INC", 30, { type = "SkillId", skillId = "ZombieSlam" }) }),
- ["With at least 40 Dexterity in Radius, Viper Strike deals 2% increased Attack Damage for each Poison on the Enemy"] = getThreshold("Dex", "Damage", "INC", 2, ModFlag.Attack, { type = "SkillName", skillName = "Viper Strike" }, { type = "Multiplier", actor = "enemy", var = "PoisonStack" }),
- --[""] = getThreshold("", "", "", , { type = "SkillName", skillName = "" }),
-}
-
--- Unified list of jewel functions
-local jewelFuncList = { }
-for k, v in pairs(jewelOtherFuncs) do
- jewelFuncList[k:lower()] = { func = v, type = "Other" }
-end
-for k, v in pairs(jewelSelfFuncs) do
- jewelFuncList[k:lower()] = { func = v, type = "Self" }
-end
-for k, v in pairs(jewelThresholdFuncs) do
- jewelFuncList[k:lower()] = { func = v, type = "Threshold" }
-end
-
--- Scan a line for the earliest and longest match from the pattern list
--- If a match is found, returns the corresponding value from the pattern list, plus the remainder of the line and a table of captures
-local function scan(line, patternList, plain)
- local bestIndex, bestEndIndex
- local bestPattern = ""
- local bestVal, bestStart, bestEnd, bestCaps
- local lineLower = line:lower()
- for pattern, patternVal in pairs(patternList) do
- local index, endIndex, cap1, cap2, cap3, cap4, cap5 = lineLower:find(pattern, 1, plain)
- if index and (not bestIndex or index < bestIndex or (index == bestIndex and (endIndex > bestEndIndex or (endIndex == bestEndIndex and #pattern > #bestPattern)))) then
- bestIndex = index
- bestEndIndex = endIndex
- bestPattern = pattern
- bestVal = patternVal
- bestStart = index
- bestEnd = endIndex
- bestCaps = { cap1, cap2, cap3, cap4, cap5 }
- end
- end
- if bestVal then
- return bestVal, line:sub(1, bestStart - 1) .. line:sub(bestEnd + 1, -1), bestCaps
- else
- return nil, line
- end
-end
-
-local function parseMod(line, order)
- -- Check if this is a special modifier
- local specialMod, specialLine, cap = scan(line, specialModList)
- if specialMod and #specialLine == 0 then
- if type(specialMod) == "function" then
- return specialMod(tonumber(cap[1]), unpack(cap))
- else
- return copyTable(specialMod)
- end
- end
- local lineLower = line:lower()
- local jewelFunc = jewelFuncList[lineLower]
- if jewelFunc then
- return { mod("JewelFunc", "LIST", jewelFunc) }
- end
- if unsupportedModList[lineLower] then
- return { }, line
- end
-
- -- Check for a flag/tag specification at the start of the line
- local preFlag
- preFlag, line = scan(line, preFlagList)
-
- -- Check for skill name at the start of the line
- local skillTag
- skillTag, line = scan(line, preSkillNameList)
-
- -- Scan for modifier form
- local modForm, formCap
- modForm, line, formCap = scan(line, formList)
- if not modForm then
- return nil, line
- end
- local num = tonumber(formCap[1])
-
- -- Check for tags (per-charge, conditionals)
- local modTag, modTag2, tagCap
- modTag, line, tagCap = scan(line, modTagList)
- if type(modTag) == "function" then
- modTag = modTag(tonumber(tagCap[1]), unpack(tagCap))
- end
- if modTag then
- modTag2, line, tagCap = scan(line, modTagList)
- if type(modTag2) == "function" then
- modTag2 = modTag2(tonumber(tagCap[1]), unpack(tagCap))
- end
- end
-
- -- Scan for modifier name and skill name
- local modName
- if order == 2 and not skillTag then
- skillTag, line = scan(line, skillNameList, true)
- end
- if modForm == "PEN" then
- modName, line = scan(line, penTypes, true)
- if not modName then
- return { }, line
- end
- local _
- _, line = scan(line, modNameList, true)
- else
- modName, line = scan(line, modNameList, true)
- end
- if order == 1 and not skillTag then
- skillTag, line = scan(line, skillNameList, true)
- end
-
- -- Scan for flags
- local modFlag
- modFlag, line = scan(line, modFlagList, true)
-
- -- Find modifier value and type according to form
- local modValue = num
- local modType = "BASE"
- local modSuffix
- if modForm == "INC" then
- modType = "INC"
- elseif modForm == "RED" then
- modValue = -num
- modType = "INC"
- elseif modForm == "MORE" then
- modType = "MORE"
- elseif modForm == "LESS" then
- modValue = -num
- modType = "MORE"
- elseif modForm == "BASE" then
- modSuffix, line = scan(line, suffixTypes, true)
- elseif modForm == "CHANCE" then
- elseif modForm == "REGENPERCENT" then
- modName = regenTypes[formCap[2]]
- modSuffix = "Percent"
- elseif modForm == "REGENFLAT" then
- modName = regenTypes[formCap[2]]
- elseif modForm == "DMG" then
- local damageType = dmgTypes[formCap[3]]
- if not damageType then
- return { }, line
- end
- modValue = { tonumber(formCap[1]), tonumber(formCap[2]) }
- modName = { damageType.."Min", damageType.."Max" }
- elseif modForm == "DMGATTACKS" then
- local damageType = dmgTypes[formCap[3]]
- if not damageType then
- return { }, line
- end
- modValue = { tonumber(formCap[1]), tonumber(formCap[2]) }
- modName = { damageType.."Min", damageType.."Max" }
- modFlag = modFlag or { flags = ModFlag.Attack }
- elseif modForm == "DMGSPELLS" then
- local damageType = dmgTypes[formCap[3]]
- if not damageType then
- return { }, line
- end
- modValue = { tonumber(formCap[1]), tonumber(formCap[2]) }
- modName = { damageType.."Min", damageType.."Max" }
- modFlag = modFlag or { flags = ModFlag.Spell }
- elseif modForm == "DMGBOTH" then
- local damageType = dmgTypes[formCap[3]]
- if not damageType then
- return { }, line
- end
- modValue = { tonumber(formCap[1]), tonumber(formCap[2]) }
- modName = { damageType.."Min", damageType.."Max" }
- end
- if not modName then
- return { }, line
- end
-
- -- Combine flags and tags
- local flags = 0
- local keywordFlags = 0
- local tagList = { }
- local misc = { }
- for _, data in pairs({ modName, preFlag, modFlag, modTag, modTag2, skillTag }) do
- if type(data) == "table" then
- flags = bor(flags, data.flags or 0)
- keywordFlags = bor(keywordFlags, data.keywordFlags or 0)
- if data.tag then
- t_insert(tagList, copyTable(data.tag))
- elseif data.tagList then
- for _, tag in ipairs(data.tagList) do
- t_insert(tagList, copyTable(tag))
- end
- end
- for k, v in pairs(data) do
- misc[k] = v
- end
- end
- end
-
- -- Generate modifier list
- local nameList = modName
- local modList = { }
- for i, name in ipairs(type(nameList) == "table" and nameList or { nameList }) do
- modList[i] = {
- name = name .. (modSuffix or misc.modSuffix or ""),
- type = modType,
- value = type(modValue) == "table" and modValue[i] or modValue,
- flags = flags,
- keywordFlags = keywordFlags,
- unpack(tagList)
- }
- end
- if modList[1] then
- -- Special handling for various modifier types
- if misc.addToAura then
- -- Modifiers that add effects to your auras
- for i, effectMod in ipairs(modList) do
- modList[i] = mod("ExtraAuraEffect", "LIST", { mod = effectMod })
- end
- elseif misc.newAura then
- -- Modifiers that add extra auras
- for i, effectMod in ipairs(modList) do
- local tagList = { }
- for i, tag in ipairs(effectMod) do
- tagList[i] = tag
- effectMod[i] = nil
- end
- modList[i] = mod("ExtraAura", "LIST", { mod = effectMod, onlyAllies = misc.newAuraOnlyAllies }, unpack(tagList))
- end
- elseif misc.affectedByAura then
- -- Modifiers that apply to actors affected by your auras
- for i, effectMod in ipairs(modList) do
- modList[i] = mod("AffectedByAuraMod", "LIST", { mod = effectMod })
- end
- elseif misc.addToMinion then
- -- Minion modifiers
- for i, effectMod in ipairs(modList) do
- modList[i] = mod("MinionModifier", "LIST", { mod = effectMod }, misc.addToMinionTag)
- end
- elseif misc.addToSkill then
- -- Skill enchants or socketed gem modifiers that add additional effects
- for i, effectMod in ipairs(modList) do
- modList[i] = mod("ExtraSkillMod", "LIST", { mod = effectMod }, misc.addToSkill)
- end
- end
- end
- return modList, line:match("%S") and line
-end
-
-local cache = { }
-local unsupported = { }
-local count = 0
-return function(line)
- if not cache[line] then
- local modList, extra = parseMod(line, 1)
- if modList and extra then
- modList, extra = parseMod(line, 2)
- end
- cache[line] = { modList, extra }
- --[[if not cache[line][1] then
- local form = line:gsub("[%+%-]?%d+%.?%d*","{num}")
- if not unsupported[form] then
- unsupported[form] = true
- count = count + 1
- ConPrintf("%d %s", count, form)
- end
- end]]
- end
- return unpack(copyTable(cache[line]))
-end, cache
\ No newline at end of file
diff --git b/Modules/ModParser-3_0.lua a/Modules/ModParser-3_0.lua
deleted file mode 100644
index 3966f222..00000000
--- b/Modules/ModParser-3_0.lua
+++ /dev/null
@@ -1,2386 +0,0 @@
--- Path of Building
---
--- Module: Mod Parser for 3.0
--- Parser function for modifier names
---
-local pairs = pairs
-local ipairs = ipairs
-local t_insert = table.insert
-local band = bit.band
-local bor = bit.bor
-local bnot = bit.bnot
-
--- List of modifier forms
-local formList = {
- ["^(%d+)%% increased"] = "INC",
- ["^(%d+)%% faster"] = "INC",
- ["^(%d+)%% reduced"] = "RED",
- ["^(%d+)%% slower"] = "RED",
- ["^(%d+)%% more"] = "MORE",
- ["^(%d+)%% less"] = "LESS",
- ["^([%+%-][%d%.]+)%%?"] = "BASE",
- ["^([%+%-][%d%.]+)%%? to"] = "BASE",
- ["^([%+%-]?[%d%.]+)%%? of"] = "BASE",
- ["^([%+%-][%d%.]+)%%? base"] = "BASE",
- ["^([%+%-]?[%d%.]+)%%? additional"] = "BASE",
- ["^you gain ([%d%.]+)"] = "BASE",
- ["^gains? ([%d%.]+)%% of"] = "BASE",
- ["^([%+%-]?%d+)%% chance"] = "CHANCE",
- ["^([%+%-]?%d+)%% additional chance"] = "CHANCE",
- ["penetrates? (%d+)%%"] = "PEN",
- ["penetrates (%d+)%% of"] = "PEN",
- ["penetrates (%d+)%% of enemy"] = "PEN",
- ["^([%d%.]+) (.+) regenerated per second"] = "REGENFLAT",
- ["^([%d%.]+)%% (.+) regenerated per second"] = "REGENPERCENT",
- ["^([%d%.]+)%% of (.+) regenerated per second"] = "REGENPERCENT",
- ["^regenerate ([%d%.]+) (.+) per second"] = "REGENFLAT",
- ["^regenerate ([%d%.]+)%% (.-) per second"] = "REGENPERCENT",
- ["^regenerate ([%d%.]+)%% of (.-) per second"] = "REGENPERCENT",
- ["^regenerate ([%d%.]+)%% of your (.-) per second"] = "REGENPERCENT",
- ["^([%d%.]+) (%a+) damage taken per second"] = "DEGEN",
- ["^([%d%.]+) (%a+) damage per second"] = "DEGEN",
- ["(%d+) to (%d+) added (%a+) damage"] = "DMG",
- ["(%d+)%-(%d+) added (%a+) damage"] = "DMG",
- ["(%d+) to (%d+) additional (%a+) damage"] = "DMG",
- ["(%d+)%-(%d+) additional (%a+) damage"] = "DMG",
- ["^(%d+) to (%d+) (%a+) damage"] = "DMG",
- ["adds (%d+) to (%d+) (%a+) damage"] = "DMG",
- ["adds (%d+)%-(%d+) (%a+) damage"] = "DMG",
- ["adds (%d+) to (%d+) (%a+) damage to attacks"] = "DMGATTACKS",
- ["adds (%d+)%-(%d+) (%a+) damage to attacks"] = "DMGATTACKS",
- ["adds (%d+) to (%d+) (%a+) attack damage"] = "DMGATTACKS",
- ["adds (%d+)%-(%d+) (%a+) attack damage"] = "DMGATTACKS",
- ["adds (%d+) to (%d+) (%a+) damage to spells"] = "DMGSPELLS",
- ["adds (%d+)%-(%d+) (%a+) damage to spells"] = "DMGSPELLS",
- ["adds (%d+) to (%d+) (%a+) spell damage"] = "DMGSPELLS",
- ["adds (%d+)%-(%d+) (%a+) spell damage"] = "DMGSPELLS",
- ["adds (%d+) to (%d+) (%a+) damage to attacks and spells"] = "DMGBOTH",
- ["adds (%d+)%-(%d+) (%a+) damage to attacks and spells"] = "DMGBOTH",
- ["adds (%d+) to (%d+) (%a+) damage to spells and attacks"] = "DMGBOTH", -- o_O
- ["adds (%d+)%-(%d+) (%a+) damage to spells and attacks"] = "DMGBOTH", -- o_O
- ["adds (%d+) to (%d+) (%a+) damage to hits"] = "DMGBOTH",
- ["adds (%d+)%-(%d+) (%a+) damage to hits"] = "DMGBOTH",
-}
-
--- Map of modifier names
-local modNameList = {
- -- Attributes
- ["strength"] = "Str",
- ["dexterity"] = "Dex",
- ["intelligence"] = "Int",
- ["strength and dexterity"] = { "Str", "Dex" },
- ["strength and intelligence"] = { "Str", "Int" },
- ["dexterity and intelligence"] = { "Dex", "Int" },
- ["attributes"] = { "Str", "Dex", "Int" },
- ["all attributes"] = { "Str", "Dex", "Int" },
- -- Life/mana
- ["life"] = "Life",
- ["maximum life"] = "Life",
- ["mana"] = "Mana",
- ["maximum mana"] = "Mana",
- ["mana regeneration"] = "ManaRegen",
- ["mana regeneration rate"] = "ManaRegen",
- ["mana cost"] = "ManaCost",
- ["mana cost of"] = "ManaCost",
- ["mana cost of skills"] = "ManaCost",
- ["total mana cost"] = "ManaCost",
- ["total mana cost of skills"] = "ManaCost",
- ["mana reserved"] = "ManaReserved",
- ["mana reservation"] = "ManaReserved",
- ["mana reservation of skills"] = "ManaReserved",
- ["maximum life, mana and global energy shield"] = { "Life", "Mana", "EnergyShield", tag = { type = "Global" } },
- -- Primary defences
- ["maximum energy shield"] = "EnergyShield",
- ["energy shield recharge rate"] = "EnergyShieldRecharge",
- ["start of energy shield recharge"] = "EnergyShieldRechargeFaster",
- ["armour"] = "Armour",
- ["evasion"] = "Evasion",
- ["evasion rating"] = "Evasion",
- ["energy shield"] = "EnergyShield",
- ["armour and evasion"] = "ArmourAndEvasion",
- ["armour and evasion rating"] = "ArmourAndEvasion",
- ["evasion rating and armour"] = "ArmourAndEvasion",
- ["armour and energy shield"] = "ArmourAndEnergyShield",
- ["evasion rating and energy shield"] = "EvasionAndEnergyShield",
- ["evasion and energy shield"] = "EvasionAndEnergyShield",
- ["armour, evasion and energy shield"] = "Defences",
- ["defences"] = "Defences",
- ["to evade"] = "EvadeChance",
- ["chance to evade"] = "EvadeChance",
- ["to evade attacks"] = "EvadeChance",
- ["to evade attack hits"] = "EvadeChance",
- ["chance to evade attacks"] = "EvadeChance",
- ["chance to evade attack hits"] = "EvadeChance",
- ["chance to evade projectile attacks"] = "ProjectileEvadeChance",
- ["chance to evade melee attacks"] = "MeleeEvadeChance",
- -- Resistances
- ["physical damage reduction"] = "PhysicalDamageReduction",
- ["physical damage reduction from hits"] = "PhysicalDamageReductionWhenHit",
- ["fire resistance"] = "FireResist",
- ["maximum fire resistance"] = "FireResistMax",
- ["cold resistance"] = "ColdResist",
- ["maximum cold resistance"] = "ColdResistMax",
- ["lightning resistance"] = "LightningResist",
- ["maximum lightning resistance"] = "LightningResistMax",
- ["chaos resistance"] = "ChaosResist",
- ["fire and cold resistances"] = { "FireResist", "ColdResist" },
- ["fire and lightning resistances"] = { "FireResist", "LightningResist" },
- ["cold and lightning resistances"] = { "ColdResist", "LightningResist" },
- ["elemental resistance"] = "ElementalResist",
- ["elemental resistances"] = "ElementalResist",
- ["all elemental resistances"] = "ElementalResist",
- ["all resistances"] = { "ElementalResist", "ChaosResist" },
- ["all maximum elemental resistances"] = "ElementalResistMax",
- ["all maximum resistances"] = { "ElementalResistMax", "ChaosResistMax" },
- ["fire and chaos resistances"] = { "FireResist", "ChaosResist" },
- ["cold and chaos resistances"] = { "ColdResist", "ChaosResist" },
- ["lightning and chaos resistances"] = { "LightningResist", "ChaosResist" },
- -- Damage taken
- ["damage taken"] = "DamageTaken",
- ["damage taken when hit"] = "DamageTakenWhenHit",
- ["damage taken from damage over time"] = "DamageTakenOverTime",
- ["physical damage taken"] = "PhysicalDamageTaken",
- ["physical damage from hits taken"] = "PhysicalDamageTaken",
- ["physical damage taken when hit"] = "PhysicalDamageTakenWhenHit",
- ["physical damage taken over time"] = "PhysicalDamageTakenOverTime",
- ["lightning damage taken"] = "LightningDamageTaken",
- ["lightning damage from hits taken"] = "LightningDamageTaken",
- ["lightning damage taken when hit"] = "LightningDamageTakenWhenHit",
- ["lightning damage taken over time"] = "LightningDamageTakenOverTime",
- ["cold damage taken"] = "ColdDamageTaken",
- ["cold damage from hits taken"] = "ColdDamageTaken",
- ["cold damage taken when hit"] = "ColdDamageTakenWhenHit",
- ["cold damage taken over time"] = "ColdDamageTakenOverTime",
- ["fire damage taken"] = "FireDamageTaken",
- ["fire damage from hits taken"] = "FireDamageTaken",
- ["fire damage taken when hit"] = "FireDamageTakenWhenHit",
- ["fire damage taken over time"] = "FireDamageTakenOverTime",
- ["chaos damage taken"] = "ChaosDamageTaken",
- ["chaos damage from hits taken"] = "ChaosDamageTaken",
- ["chaos damage taken when hit"] = "ChaosDamageTakenWhenHit",
- ["chaos damage taken over time"] = "ChaosDamageTakenOverTime",
- ["elemental damage taken"] = "ElementalDamageTaken",
- ["elemental damage taken when hit"] = "ElementalDamageTakenWhenHit",
- ["elemental damage taken over time"] = "ElementalDamageTakenOverTime",
- -- Other defences
- ["to dodge attacks"] = "AttackDodgeChance",
- ["to dodge attack hits"] = "AttackDodgeChance",
- ["to dodge spells"] = "SpellDodgeChance",
- ["to dodge spell hits"] = "SpellDodgeChance",
- ["to dodge spell damage"] = "SpellDodgeChance",
- ["to dodge attacks and spells"] = { "AttackDodgeChance", "SpellDodgeChance" },
- ["to dodge attacks and spell damage"] = { "AttackDodgeChance", "SpellDodgeChance" },
- ["to dodge attack and spell hits"] = { "AttackDodgeChance", "SpellDodgeChance" },
- ["to block"] = "BlockChance",
- ["to block attacks"] = "BlockChance",
- ["to block attack damage"] = "BlockChance",
- ["block chance"] = "BlockChance",
- ["block chance with staves"] = { "BlockChance", tag = { type = "Condition", var = "UsingStaff" } },
- ["to block with staves"] = { "BlockChance", tag = { type = "Condition", var = "UsingStaff" } },
- ["spell block chance"] = "SpellBlockChance",
- ["to block spells"] = "SpellBlockChance",
- ["to block spell damage"] = "SpellBlockChance",
- ["chance to block attacks and spells"] = { "BlockChance", "SpellBlockChance" },
- ["maximum block chance"] = "BlockChanceMax",
- ["maximum chance to block attack damage"] = "BlockChanceMax",
- ["maximum chance to block spell damage"] = "SpellBlockChanceMax",
- ["to avoid being stunned"] = "AvoidStun",
- ["to avoid being shocked"] = "AvoidShock",
- ["to avoid being frozen"] = "AvoidFrozen",
- ["to avoid being chilled"] = "AvoidChilled",
- ["to avoid being ignited"] = "AvoidIgnite",
- ["to avoid elemental ailments"] = { "AvoidShock", "AvoidFrozen", "AvoidChilled", "AvoidIgnite" },
- ["to avoid elemental status ailments"] = { "AvoidShock", "AvoidFrozen", "AvoidChilled", "AvoidIgnite" },
- ["to avoid bleeding"] = "AvoidBleed",
- ["damage is taken from mana before life"] = "DamageTakenFromManaBeforeLife",
- ["damage taken from mana before life"] = "DamageTakenFromManaBeforeLife",
- ["effect of curses on you"] = "CurseEffectOnSelf",
- ["life recovery rate"] = "LifeRecoveryRate",
- ["mana recovery rate"] = "ManaRecoveryRate",
- ["energy shield recovery rate"] = "EnergyShieldRecoveryRate",
- ["energy shield regeneration rate"] = "EnergyShieldRegen",
- ["recovery rate of life, mana and energy shield"] = { "LifeRecoveryRate", "ManaRecoveryRate", "EnergyShieldRecoveryRate" },
- -- Stun/knockback modifiers
- ["stun recovery"] = "StunRecovery",
- ["stun and block recovery"] = "StunRecovery",
- ["block and stun recovery"] = "StunRecovery",
- ["stun threshold"] = "StunThreshold",
- ["block recovery"] = "BlockRecovery",
- ["enemy stun threshold"] = "EnemyStunThreshold",
- ["stun duration on enemies"] = "EnemyStunDuration",
- ["stun duration"] = "EnemyStunDuration",
- ["to knock enemies back on hit"] = "EnemyKnockbackChance",
- ["knockback distance"] = "EnemyKnockbackDistance",
- -- Auras/curses/buffs
- ["aura effect"] = "AuraEffect",
- ["effect of non-curse auras you cast"] = "AuraEffect",
- ["effect of non-curse auras from your skills"] = "AuraEffect",
- ["effect of your curses"] = "CurseEffect",
- ["effect of auras on you"] = "AuraEffectOnSelf",
- ["effect of auras on your minions"] = { "AuraEffectOnSelf", addToMinion = true },
- ["effect of auras from mines"] = { "AuraEffect", keywordFlags = KeywordFlag.Mine },
- ["curse effect"] = "CurseEffect",
- ["effect of curses applied by bane"] = { "CurseEffect", tag = { type = "Condition", var = "AppliedByBane" } },
- ["curse duration"] = { "Duration", keywordFlags = KeywordFlag.Curse },
- ["radius of auras"] = { "AreaOfEffect", keywordFlags = KeywordFlag.Aura },
- ["radius of curses"] = { "AreaOfEffect", keywordFlags = KeywordFlag.Curse },
- ["buff effect"] = "BuffEffect",
- ["effect of buffs on you"] = "BuffEffectOnSelf",
- ["effect of buffs granted by your golems"] = { "BuffEffect", tag = { type = "SkillType", skillType = SkillType.Golem } },
- ["effect of buffs granted by socketed golem skills"] = { "BuffEffect", addToSkill = { type = "SocketedIn", slotName = "{SlotName}", keyword = "golem" } },
- ["effect of the buff granted by your stone golems"] = { "BuffEffect", tag = { type = "SkillName", skillName = "Summon Stone Golem" } },
- ["effect of the buff granted by your lightning golems"] = { "BuffEffect", tag = { type = "SkillName", skillName = "Summon Lightning Golem" } },
- ["effect of the buff granted by your ice golems"] = { "BuffEffect", tag = { type = "SkillName", skillName = "Summon Ice Golem" } },
- ["effect of the buff granted by your flame golems"] = { "BuffEffect", tag = { type = "SkillName", skillName = "Summon Flame Golem" } },
- ["effect of the buff granted by your chaos golems"] = { "BuffEffect", tag = { type = "SkillName", skillName = "Summon Chaos Golem" } },
- ["effect of the buff granted by your carrion golems"] = { "BuffEffect", tag = { type = "SkillName", skillName = "Summon Carrion Golem" } },
- ["effect of offering spells"] = { "BuffEffect", tag = { type = "SkillName", skillNameList = { "Bone Offering", "Flesh Offering", "Spirit Offering" } } },
- ["effect of heralds on you"] = { "BuffEffect", tag = { type = "SkillType", skillType = SkillType.Herald } },
- ["effect of herald buffs on you"] = { "BuffEffect", tag = { type = "SkillType", skillType = SkillType.Herald } },
- ["effect of buffs granted by your active ancestor totems"] = { "BuffEffect", tag = { type = "SkillName", skillNameList = { "Ancestral Warchief", "Ancestral Protector" } } },
- ["warcry effect"] = { "BuffEffect", keywordFlags = KeywordFlag.Warcry },
- ["aspect of the avian buff effect"] = { "BuffEffect", tag = { type = "SkillName", skillName = "Aspect of the Avian" } },
- -- Charges
- ["maximum power charge"] = "PowerChargesMax",
- ["maximum power charges"] = "PowerChargesMax",
- ["minimum power charge"] = "PowerChargesMin",
- ["minimum power charges"] = "PowerChargesMin",
- ["power charge duration"] = "PowerChargesDuration",
- ["maximum frenzy charge"] = "FrenzyChargesMax",
- ["maximum frenzy charges"] = "FrenzyChargesMax",
- ["minimum frenzy charge"] = "FrenzyChargesMin",
- ["minimum frenzy charges"] = "FrenzyChargesMin",
- ["frenzy charge duration"] = "FrenzyChargesDuration",
- ["maximum endurance charge"] = "EnduranceChargesMax",
- ["maximum endurance charges"] = "EnduranceChargesMax",
- ["minimum endurance charge"] = "EnduranceChargesMin",
- ["minimum endurance charges"] = "EnduranceChargesMin",
- ["endurance charge duration"] = "EnduranceChargesDuration",
- ["maximum frenzy charges and maximum power charges"] = { "FrenzyChargesMax", "PowerChargesMax" },
- ["endurance, frenzy and power charge duration"] = { "PowerChargesDuration", "FrenzyChargesDuration", "EnduranceChargesDuration" },
- ["maximum siphoning charge"] = "SiphoningChargesMax",
- ["maximum siphoning charges"] = "SiphoningChargesMax",
- ["maximum challenger charges"] = "ChallengerChargesMax",
- ["maximum blitz charges"] = "BlitzChargesMax",
- ["maximum number of crab barriers"] = "CrabBarriersMax",
- -- On hit/kill/leech effects
- ["life gained on kill"] = "LifeOnKill",
- ["mana gained on kill"] = "ManaOnKill",
- ["life gained for each enemy hit"] = { "LifeOnHit" },
- ["life gained for each enemy hit by attacks"] = { "LifeOnHit", flags = ModFlag.Attack },
- ["life gained for each enemy hit by your attacks"] = { "LifeOnHit", flags = ModFlag.Attack },
- ["life gained for each enemy hit by spells"] = { "LifeOnHit", flags = ModFlag.Spell },
- ["life gained for each enemy hit by your spells"] = { "LifeOnHit", flags = ModFlag.Spell },
- ["mana gained for each enemy hit by attacks"] = { "ManaOnHit", flags = ModFlag.Attack },
- ["mana gained for each enemy hit by your attacks"] = { "ManaOnHit", flags = ModFlag.Attack },
- ["energy shield gained for each enemy hit"] = { "EnergyShieldOnHit" },
- ["energy shield gained for each enemy hit by attacks"] = { "EnergyShieldOnHit", flags = ModFlag.Attack },
- ["energy shield gained for each enemy hit by your attacks"] = { "EnergyShieldOnHit", flags = ModFlag.Attack },
- ["life and mana gained for each enemy hit"] = { "LifeOnHit", "ManaOnHit", flags = ModFlag.Attack },
- ["damage as life"] = "DamageLifeLeech",
- ["life leeched per second"] = "LifeLeechRate",
- ["mana leeched per second"] = "ManaLeechRate",
- ["total recovery per second from life leech"] = "LifeLeechRate",
- ["total recovery per second from energy shield leech"] = "EnergyShieldLeechRate",
- ["total recovery per second from mana leech"] = "ManaLeechRate",
- ["maximum recovery per life leech"] = "MaxLifeLeechInstance",
- ["maximum recovery per energy shield leech"] = "MaxEnergyShieldLeechInstance",
- ["maximum recovery per mana leech"] = "MaxManaLeechInstance",
- ["maximum total recovery per second from life leech"] = "MaxLifeLeechRate",
- ["maximum total recovery per second from energy shield leech"] = "MaxEnergyShieldLeechRate",
- ["maximum total recovery per second from mana leech"] = "MaxManaLeechRate",
- -- Projectile modifiers
- ["projectile"] = "ProjectileCount",
- ["projectiles"] = "ProjectileCount",
- ["projectile speed"] = "ProjectileSpeed",
- ["arrow speed"] = { "ProjectileSpeed", flags = ModFlag.Bow },
- -- Totem/trap/mine modifiers
- ["totem placement speed"] = "TotemPlacementSpeed",
- ["totem life"] = "TotemLife",
- ["totem duration"] = "TotemDuration",
- ["maximum number of summoned totems"] = "ActiveTotemLimit",
- ["maximum number of summoned totems."] = "ActiveTotemLimit", -- Mark plz
- ["maximum number of summoned ballista totems"] = "ActiveTotemLimit", -- Mark plz
- ["trap throwing speed"] = "TrapThrowingSpeed",
- ["trap trigger area of effect"] = "TrapTriggerAreaOfEffect",
- ["trap duration"] = "TrapDuration",
- ["cooldown recovery speed for throwing traps"] = { "CooldownRecovery", keywordFlags = KeywordFlag.Trap },
- ["mine laying speed"] = "MineLayingSpeed",
- ["mine throwing speed"] = "MineLayingSpeed",
- ["mine detonation area of effect"] = "MineDetonationAreaOfEffect",
- ["mine duration"] = "MineDuration",
- -- Minion modifiers
- ["maximum number of skeletons"] = "ActiveSkeletonLimit",
- ["maximum number of zombies"] = "ActiveZombieLimit",
- ["maximum number of raised zombies"] = "ActiveZombieLimit",
- ["number of zombies allowed"] = "ActiveZombieLimit",
- ["maximum number of spectres"] = "ActiveSpectreLimit",
- ["maximum number of golems"] = "ActiveGolemLimit",
- ["maximum number of summoned golems"] = "ActiveGolemLimit",
- ["maximum number of summoned raging spirits"] = "ActiveRagingSpiritLimit",
- ["maximum number of summoned holy relics"] = "ActiveHolyRelicLimit",
- ["minion duration"] = { "Duration", tag = { type = "SkillType", skillType = SkillType.CreateMinion } },
- ["skeleton duration"] = { "Duration", tag = { type = "SkillName", skillName = "Summon Skeleton" } },
- ["sentinel of dominance duration"] = { "Duration", tag = { type = "SkillName", skillName = "Dominating Blow" } },
- -- Other skill modifiers
- ["radius"] = "AreaOfEffect",
- ["radius of area skills"] = "AreaOfEffect",
- ["area of effect radius"] = "AreaOfEffect",
- ["area of effect"] = "AreaOfEffect",
- ["area of effect of skills"] = "AreaOfEffect",
- ["area of effect of area skills"] = "AreaOfEffect",
- ["aspect of the spider area of effect"] = { "AreaOfEffect", tag = { type = "SkillName", skillName = "Aspect of the Spider" } },
- ["firestorm explosion area of effect"] = { "AreaOfEffectSecondary", tag = { type = "SkillName", skillName = "Firestorm" } },
- ["duration"] = "Duration",
- ["skill effect duration"] = "Duration",
- ["chaos skill effect duration"] = { "Duration", keywordFlags = KeywordFlag.Chaos },
- ["aspect of the spider debuff duration"] = { "Duration", tag = { type = "SkillName", skillName = "Aspect of the Spider" } },
- ["fire trap burning ground duration"] = { "Duration", tag = { type = "SkillName", skillName = "Fire Trap" } },
- ["cooldown recovery"] = "CooldownRecovery",
- ["cooldown recovery speed"] = "CooldownRecovery",
- ["weapon range"] = "WeaponRange",
- ["melee range"] = "MeleeWeaponRange",
- ["melee weapon range"] = "MeleeWeaponRange",
- ["melee strike range"] = { "MeleeWeaponRange", "UnarmedRange" },
- ["melee weapon and unarmed range"] = { "MeleeWeaponRange", "UnarmedRange" },
- ["melee weapon and unarmed attack range"] = { "MeleeWeaponRange", "UnarmedRange" },
- ["to deal double damage"] = "DoubleDamageChance",
- ["activation frequency"] = "BrandActivationFrequency",
- ["brand activation frequency"] = "BrandActivationFrequency",
- -- Buffs
- ["onslaught effect"] = "OnslaughtEffect",
- ["fortify duration"] = "FortifyDuration",
- ["effect of fortify on you"] = "FortifyEffectOnSelf",
- ["effect of tailwind on you"] = "TailwindEffectOnSelf",
- -- Basic damage types
- ["damage"] = "Damage",
- ["physical damage"] = "PhysicalDamage",
- ["lightning damage"] = "LightningDamage",
- ["cold damage"] = "ColdDamage",
- ["fire damage"] = "FireDamage",
- ["chaos damage"] = "ChaosDamage",
- ["non-chaos damage"] = "NonChaosDamage",
- ["elemental damage"] = "ElementalDamage",
- -- Other damage forms
- ["attack damage"] = { "Damage", flags = ModFlag.Attack },
- ["attack physical damage"] = { "PhysicalDamage", flags = ModFlag.Attack },
- ["physical attack damage"] = { "PhysicalDamage", flags = ModFlag.Attack },
- ["physical weapon damage"] = { "PhysicalDamage", flags = ModFlag.Weapon },
- ["physical damage with weapons"] = { "PhysicalDamage", flags = ModFlag.Weapon },
- ["physical melee damage"] = { "PhysicalDamage", flags = ModFlag.Melee },
- ["melee physical damage"] = { "PhysicalDamage", flags = ModFlag.Melee },
- ["projectile damage"] = { "Damage", flags = ModFlag.Projectile },
- ["projectile attack damage"] = { "Damage", flags = bor(ModFlag.Projectile, ModFlag.Attack) },
- ["bow damage"] = { "Damage", flags = bor(ModFlag.Bow, ModFlag.Hit) },
- ["damage with arrow hits"] = { "Damage", flags = bor(ModFlag.Bow, ModFlag.Hit) },
- ["wand damage"] = { "Damage", flags = bor(ModFlag.Wand, ModFlag.Hit) },
- ["wand physical damage"] = { "PhysicalDamage", flags = bor(ModFlag.Wand, ModFlag.Hit) },
- ["claw physical damage"] = { "PhysicalDamage", flags = bor(ModFlag.Claw, ModFlag.Hit) },
- ["sword physical damage"] = { "PhysicalDamage", flags = bor(ModFlag.Sword, ModFlag.Hit) },
- ["damage over time"] = { "Damage", flags = ModFlag.Dot },
- ["physical damage over time"] = { "PhysicalDamage", keywordFlags = KeywordFlag.PhysicalDot },
- ["burning damage"] = { "FireDamage", keywordFlags = KeywordFlag.FireDot },
- ["damage with ignite"] = { "Damage", keywordFlags = KeywordFlag.Ignite },
- ["damage with ignites"] = { "Damage", keywordFlags = KeywordFlag.Ignite },
- ["incinerate damage for each stage"] = { "Damage", tagList = { { type = "Multiplier", var = "IncinerateStage" }, { type = "SkillName", skillName = "Incinerate" } } },
- ["physical damage over time multiplier"] = "PhysicalDotMultiplier",
- ["fire damage over time multiplier"] = "FireDotMultiplier",
- ["cold damage over time multiplier"] = "ColdDotMultiplier",
- ["chaos damage over time multiplier"] = "ChaosDotMultiplier",
- ["damage over time multiplier"] = "DotMultiplier",
- -- Crit/accuracy/speed modifiers
- ["critical strike chance"] = "CritChance",
- ["attack critical strike chance"] = { "CritChance", flags = ModFlag.Attack },
- ["critical strike multiplier"] = "CritMultiplier",
- ["accuracy"] = "Accuracy",
- ["accuracy rating"] = "Accuracy",
- ["minion accuracy rating"] = { "Accuracy", addToMinion = true },
- ["attack speed"] = { "Speed", flags = ModFlag.Attack },
- ["cast speed"] = { "Speed", flags = ModFlag.Cast },
- ["attack and cast speed"] = "Speed",
- ["attack and movement speed"] = { "Speed", "MovementSpeed" },
- -- Elemental ailments
- ["to shock"] = "EnemyShockChance",
- ["shock chance"] = "EnemyShockChance",
- ["to freeze"] = "EnemyFreezeChance",
- ["freeze chance"] = "EnemyFreezeChance",
- ["to ignite"] = "EnemyIgniteChance",
- ["ignite chance"] = "EnemyIgniteChance",
- ["to freeze, shock and ignite"] = { "EnemyFreezeChance", "EnemyShockChance", "EnemyIgniteChance" },
- ["effect of shock"] = "EnemyShockEffect",
- ["effect of chill"] = "EnemyChillEffect",
- ["effect of chill on you"] = "SelfChillEffect",
- ["effect of non-damaging ailments"] = { "EnemyShockEffect", "EnemyChillEffect", "EnemyFreezeEffech" },
- ["shock duration"] = "EnemyShockDuration",
- ["freeze duration"] = "EnemyFreezeDuration",
- ["chill duration"] = "EnemyChillDuration",
- ["ignite duration"] = "EnemyIgniteDuration",
- ["duration of elemental ailments"] = { "EnemyShockDuration", "EnemyFreezeDuration", "EnemyChillDuration", "EnemyIgniteDuration" },
- ["duration of elemental status ailments"] = { "EnemyShockDuration", "EnemyFreezeDuration", "EnemyChillDuration", "EnemyIgniteDuration" },
- ["duration of ailments"] = { "EnemyShockDuration", "EnemyFreezeDuration", "EnemyChillDuration", "EnemyIgniteDuration", "EnemyPoisonDuration", "EnemyBleedDuration" },
- ["duration of ailments you inflict"] = { "EnemyShockDuration", "EnemyFreezeDuration", "EnemyChillDuration", "EnemyIgniteDuration", "EnemyPoisonDuration", "EnemyBleedDuration" },
- -- Other ailments
- ["to poison"] = "PoisonChance",
- ["to cause poison"] = "PoisonChance",
- ["to poison on hit"] = "PoisonChance",
- ["poison duration"] = { "EnemyPoisonDuration" },
- ["duration of poisons you inflict"] = { "EnemyPoisonDuration" },
- ["to cause bleeding"] = "BleedChance",
- ["to cause bleeding on hit"] = "BleedChance",
- ["to inflict bleeding"] = "BleedChance",
- ["to inflict bleeding on hit"] = "BleedChance",
- ["bleed duration"] = { "EnemyBleedDuration" },
- ["bleeding duration"] = { "EnemyBleedDuration" },
- -- Misc modifiers
- ["movement speed"] = "MovementSpeed",
- ["attack, cast and movement speed"] = { "Speed", "MovementSpeed" },
- ["light radius"] = "LightRadius",
- ["rarity of items found"] = "LootRarity",
- ["quantity of items found"] = "LootQuantity",
- ["item quantity"] = "LootQuantity",
- ["strength requirement"] = "StrRequirement",
- ["dexterity requirement"] = "DexRequirement",
- ["intelligence requirement"] = "IntRequirement",
- ["strength and intelligence requirement"] = { "StrRequirement", "IntRequirement" },
- ["attribute requirements"] = { "StrRequirement", "DexRequirement", "IntRequirement" },
- ["effect of socketed jewels"] = "SocketedJewelEffect",
- -- Flask modifiers
- ["effect"] = "FlaskEffect",
- ["effect of flasks"] = "FlaskEffect",
- ["effect of flasks on you"] = "FlaskEffect",
- ["amount recovered"] = "FlaskRecovery",
- ["life recovered"] = "FlaskRecovery",
- ["mana recovered"] = "FlaskRecovery",
- ["life recovery from flasks"] = "FlaskLifeRecovery",
- ["mana recovery from flasks"] = "FlaskManaRecovery",
- ["flask effect duration"] = "FlaskDuration",
- ["recovery speed"] = "FlaskRecoveryRate",
- ["recovery rate"] = "FlaskRecoveryRate",
- ["flask recovery rate"] = "FlaskRecoveryRate",
- ["flask recovery speed"] = "FlaskRecoveryRate",
- ["flask life recovery rate"] = "FlaskLifeRecoveryRate",
- ["flask mana recovery rate"] = "FlaskManaRecoveryRate",
- ["extra charges"] = "FlaskCharges",
- ["maximum charges"] = "FlaskCharges",
- ["charges used"] = "FlaskChargesUsed",
- ["flask charges used"] = "FlaskChargesUsed",
- ["flask charges gained"] = "FlaskChargesGained",
- ["charge recovery"] = "FlaskChargeRecovery",
-}
-
--- List of modifier flags
-local modFlagList = {
- -- Weapon types
- ["with axes"] = { flags = bor(ModFlag.Axe, ModFlag.Hit) },
- ["to axe attacks"] = { flags = bor(ModFlag.Axe, ModFlag.Hit) },
- ["with axes or swords"] = { flags = ModFlag.Hit, tag = { type = "ModFlagOr", modFlags = bor(ModFlag.Axe, ModFlag.Sword) } },
- ["with bows"] = { flags = bor(ModFlag.Bow, ModFlag.Hit) },
- ["to bow attacks"] = { flags = bor(ModFlag.Bow, ModFlag.Hit) },
- ["with claws"] = { flags = bor(ModFlag.Claw, ModFlag.Hit) },
- ["with claws or daggers"] = { flags = ModFlag.Hit, tag = { type = "ModFlagOr", modFlags = bor(ModFlag.Claw, ModFlag.Dagger) } },
- ["to claw attacks"] = { flags = bor(ModFlag.Claw, ModFlag.Hit) },
- ["dealt with claws"] = { flags = bor(ModFlag.Claw, ModFlag.Hit) },
- ["with daggers"] = { flags = bor(ModFlag.Dagger, ModFlag.Hit) },
- ["to dagger attacks"] = { flags = bor(ModFlag.Dagger, ModFlag.Hit) },
- ["with maces"] = { flags = bor(ModFlag.Mace, ModFlag.Hit) },
- ["to mace attacks"] = { flags = bor(ModFlag.Mace, ModFlag.Hit) },
- ["with maces and sceptres"] = { flags = bor(ModFlag.Mace, ModFlag.Hit) },
- ["with maces or sceptres"] = { flags = bor(ModFlag.Mace, ModFlag.Hit) },
- ["with maces, sceptres or staves"] = { flags = ModFlag.Hit, tag = { type = "ModFlagOr", modFlags = bor(ModFlag.Mace, ModFlag.Staff) } },
- ["to mace and sceptre attacks"] = { flags = bor(ModFlag.Mace, ModFlag.Hit) },
- ["to mace or sceptre attacks"] = { flags = bor(ModFlag.Mace, ModFlag.Hit) },
- ["with staves"] = { flags = bor(ModFlag.Staff, ModFlag.Hit) },
- ["to staff attacks"] = { flags = bor(ModFlag.Staff, ModFlag.Hit) },
- ["with swords"] = { flags = bor(ModFlag.Sword, ModFlag.Hit) },
- ["to sword attacks"] = { flags = bor(ModFlag.Sword, ModFlag.Hit) },
- ["with wands"] = { flags = bor(ModFlag.Wand, ModFlag.Hit) },
- ["to wand attacks"] = { flags = bor(ModFlag.Wand, ModFlag.Hit) },
- ["unarmed"] = { flags = bor(ModFlag.Unarmed, ModFlag.Hit) },
- ["with unarmed attacks"] = { flags = bor(ModFlag.Unarmed, ModFlag.Hit) },
- ["to unarmed attacks"] = { flags = bor(ModFlag.Unarmed, ModFlag.Hit) },
- ["with one handed weapons"] = { flags = bor(ModFlag.Weapon1H, ModFlag.Hit) },
- ["with one handed melee weapons"] = { flags = bor(ModFlag.Weapon1H, ModFlag.WeaponMelee, ModFlag.Hit) },
- ["with two handed weapons"] = { flags = bor(ModFlag.Weapon2H, ModFlag.Hit) },
- ["with two handed melee weapons"] = { flags = bor(ModFlag.Weapon2H, ModFlag.WeaponMelee, ModFlag.Hit) },
- ["with ranged weapons"] = { flags = bor(ModFlag.WeaponRanged, ModFlag.Hit) },
- -- Skill types
- ["spell"] = { flags = ModFlag.Spell },
- ["with spells"] = { flags = ModFlag.Spell },
- ["for spells"] = { flags = ModFlag.Spell },
- ["with attacks"] = { keywordFlags = KeywordFlag.Attack },
- ["with attack skills"] = { keywordFlags = KeywordFlag.Attack },
- ["for attacks"] = { flags = ModFlag.Attack },
- ["weapon"] = { flags = ModFlag.Weapon },
- ["with weapons"] = { flags = ModFlag.Weapon },
- ["melee"] = { flags = ModFlag.Melee },
- ["with melee attacks"] = { flags = ModFlag.Melee },
- ["with melee critical strikes"] = { flags = ModFlag.Melee, tag = { type = "Condition", var = "CriticalStrike" } },
- ["with bow skills"] = { keywordFlags = KeywordFlag.Bow },
- ["on melee hit"] = { flags = ModFlag.Melee },
- ["with hits"] = { keywordFlags = KeywordFlag.Hit },
- ["with hits and ailments"] = { keywordFlags = bor(KeywordFlag.Hit, KeywordFlag.Ailment) },
- ["with ailments"] = { flags = ModFlag.Ailment },
- ["with ailments from attack skills"] = { flags = ModFlag.Ailment, keywordFlags = KeywordFlag.Attack },
- ["with poison"] = { keywordFlags = KeywordFlag.Poison },
- ["with bleeding"] = { keywordFlags = KeywordFlag.Bleed },
- ["for ailments"] = { flags = ModFlag.Ailment },
- ["for poison"] = { keywordFlags = KeywordFlag.Poison },
- ["for bleeding"] = { keywordFlags = KeywordFlag.Bleed },
- ["for ignite"] = { keywordFlags = KeywordFlag.Ignite },
- ["area"] = { flags = ModFlag.Area },
- ["mine"] = { keywordFlags = KeywordFlag.Mine },
- ["with mines"] = { keywordFlags = KeywordFlag.Mine },
- ["trap"] = { keywordFlags = KeywordFlag.Trap },
- ["with traps"] = { keywordFlags = KeywordFlag.Trap },
- ["for traps"] = { keywordFlags = KeywordFlag.Trap },
- ["that place mines or throw traps"] = { keywordFlags = bor(KeywordFlag.Mine, KeywordFlag.Trap) },
- ["that throw mines"] = { keywordFlags = KeywordFlag.Mine },
- ["that throw traps"] = { keywordFlags = KeywordFlag.Trap },
- ["totem"] = { keywordFlags = KeywordFlag.Totem },
- ["with totem skills"] = { keywordFlags = KeywordFlag.Totem },
- ["for skills used by totems"] = { keywordFlags = KeywordFlag.Totem },
- ["of aura skills"] = { tag = { type = "SkillType", skillType = SkillType.Aura } },
- ["of curse skills"] = { keywordFlags = KeywordFlag.Curse },
- ["with curse skills"] = { keywordFlags = KeywordFlag.Curse },
- ["of herald skills"] = { tag = { type = "SkillType", skillType = SkillType.Herald } },
- ["minion skills"] = { tag = { type = "SkillType", skillType = SkillType.Minion } },
- ["of minion skills"] = { tag = { type = "SkillType", skillType = SkillType.Minion } },
- ["for curses"] = { keywordFlags = KeywordFlag.Curse },
- ["warcry"] = { keywordFlags = KeywordFlag.Warcry },
- ["vaal"] = { keywordFlags = KeywordFlag.Vaal },
- ["vaal skill"] = { keywordFlags = KeywordFlag.Vaal },
- ["with movement skills"] = { keywordFlags = KeywordFlag.Movement },
- ["of movement skills"] = { keywordFlags = KeywordFlag.Movement },
- ["with lightning skills"] = { keywordFlags = KeywordFlag.Lightning },
- ["with cold skills"] = { keywordFlags = KeywordFlag.Cold },
- ["with fire skills"] = { keywordFlags = KeywordFlag.Fire },
- ["with elemental skills"] = { keywordFlags = bor(KeywordFlag.Lightning, KeywordFlag.Cold, KeywordFlag.Fire) },
- ["with chaos skills"] = { keywordFlags = KeywordFlag.Chaos },
- ["with channelling skills"] = { tag = { type = "SkillType", skillType = SkillType.Channelled } },
- ["with brand skills"] = { tag = { type = "SkillType", skillType = SkillType.Brand } },
- ["brand"] = { tag = { type = "SkillType", skillType = SkillType.Brand } },
- ["zombie"] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Raise Zombie" } },
- ["raised zombie"] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Raise Zombie" } },
- ["skeleton"] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Skeleton" } },
- ["spectre"] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Raise Spectre" } },
- ["raised spectre"] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Raise Spectre" } },
- ["golem"] = { },
- ["chaos golem"] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Chaos Golem" } },
- ["flame golem"] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Flame Golem" } },
- ["increased flame golem"] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Flame Golem" } },
- ["ice golem"] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Ice Golem" } },
- ["lightning golem"] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Lightning Golem" } },
- ["stone golem"] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Stone Golem" } },
- ["animated guardian"] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Animate Guardian" } },
- -- Other
- ["global"] = { tag = { type = "Global" } },
- ["from equipped shield"] = { tag = { type = "SlotName", slotName = "Weapon 2" } },
- ["from body armour"] = { tag = { type = "SlotName", slotName = "Body Armour" } },
-}
-
--- List of modifier flags/tags that appear at the start of a line
-local preFlagList = {
- -- Weapon types
- ["^axe attacks [hd][ae][va][el] "] = { flags = ModFlag.Axe },
- ["^axe or sword attacks [hd][ae][va][el] "] = { tag = { type = "ModFlagOr", modFlags = bor(ModFlag.Axe, ModFlag.Sword) } },
- ["^bow attacks [hd][ae][va][el] "] = { flags = ModFlag.Bow },
- ["^claw attacks [hd][ae][va][el] "] = { flags = ModFlag.Claw },
- ["^claw or dagger attacks [hd][ae][va][el] "] = { tag = { type = "ModFlagOr", modFlags = bor(ModFlag.Claw, ModFlag.Dagger) } },
- ["^dagger attacks [hd][ae][va][el] "] = { flags = ModFlag.Dagger },
- ["^mace or sceptre attacks [hd][ae][va][el] "] = { flags = ModFlag.Mace },
- ["^mace, sceptre or staff attacks [hd][ae][va][el] "] = { tag = { type = "ModFlagOr", modFlags = bor(ModFlag.Mace, ModFlag.Staff) } },
- ["^staff attacks [hd][ae][va][el] "] = { flags = ModFlag.Staff },
- ["^sword attacks [hd][ae][va][el] "] = { flags = ModFlag.Sword },
- ["^wand attacks [hd][ae][va][el] "] = { flags = ModFlag.Wand },
- ["^unarmed attacks [hd][ae][va][el] "] = { flags = ModFlag.Unarmed },
- ["^attacks with one handed weapons [hd][ae][va][el] "] = { flags = ModFlag.Weapon1H },
- ["^attacks with two handed weapons [hd][ae][va][el] "] = { flags = ModFlag.Weapon2H },
- ["^attacks with melee weapons [hd][ae][va][el] "] = { flags = ModFlag.WeaponMelee },
- ["^attacks with one handed melee weapons [hd][ae][va][el] "] = { flags = bor(ModFlag.Weapon1H, ModFlag.WeaponMelee) },
- ["^attacks with two handed melee weapons [hd][ae][va][el] "] = { flags = bor(ModFlag.Weapon2H, ModFlag.WeaponMelee) },
- ["^attacks with ranged weapons [hd][ae][va][el] "] = { flags = ModFlag.WeaponRanged },
- -- Damage types
- ["^hits deal "] = { keywordFlags = KeywordFlag.Hit },
- ["^critical strikes deal "] = { tag = { type = "Condition", var = "CriticalStrike" } },
- -- Add to minion
- ["^minions "] = { addToMinion = true },
- ["^minions [hd][ae][va][el] "] = { addToMinion = true },
- ["^minions leech "] = { addToMinion = true },
- ["^minions' attacks deal "] = { addToMinion = true, flags = ModFlag.Attack },
- ["^golems [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillType", skillType = SkillType.Golem } },
- ["^golem skills have "] = { tag = { type = "SkillType", skillType = SkillType.Golem } },
- ["^zombies [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Raise Zombie" } },
- ["^raised zombies [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Raise Zombie" } },
- ["^skeletons [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Skeleton" } },
- ["^raging spirits [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Raging Spirit" } },
- ["^summoned raging spirits [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Raging Spirit" } },
- ["^spectres [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Raise Spectre" } },
- ["^chaos golems [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Chaos Golem" } },
- ["^summoned chaos golems [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Chaos Golem" } },
- ["^flame golems [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Flame Golem" } },
- ["^summoned flame golems [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Flame Golem" } },
- ["^ice golems [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Ice Golem" } },
- ["^summoned ice golems [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Ice Golem" } },
- ["^lightning golems [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Lightning Golem" } },
- ["^summoned lightning golems [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Lightning Golem" } },
- ["^stone golems [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Stone Golem" } },
- ["^summoned stone golems [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Stone Golem" } },
- ["^summoned carrion golems [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Carrion Golem" } },
- ["^summoned skitterbots [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Carrion Golem" } },
- ["^blink arrow and blink arrow clones [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Blink Arrow" } },
- ["^mirror arrow and mirror arrow clones [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Mirror Arrow" } },
- ["^animated weapons [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Animate Weapon" } },
- ["^animated guardians [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Animate Guardian" } },
- ["^summoned holy relics [hd][ae][va][el] "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Summon Holy Relic" } },
- ["^agony crawler deals "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Herald of Agony" } },
- ["^sentinels of purity deal "] = { addToMinion = true, addToMinionTag = { type = "SkillName", skillName = "Herald of Purity" } },
- ["^raised zombies' slam attack has "] = { addToMinion = true, tag = { type = "SkillId", skillId = "ZombieSlam" } },
- -- Totem/trap/mine
- ["^attacks used by totems have "] = { keywordFlags = KeywordFlag.Totem },
- ["^spells cast by totems have "] = { keywordFlags = KeywordFlag.Totem },
- ["^trap and mine damage "] = { keywordFlags = bor(KeywordFlag.Trap, KeywordFlag.Mine) },
- ["^skills used by traps [hd][ae][va][el] "] = { keywordFlags = KeywordFlag.Trap },
- ["^skills used by mines [hd][ae][va][el] "] = { keywordFlags = KeywordFlag.Mine },
- -- Local damage
- ["^attacks with this weapon "] = { tag = { type = "Condition", var = "{Hand}Attack" } },
- ["^attacks with this weapon [hd][ae][va][el] "] = { tag = { type = "Condition", var = "{Hand}Attack" } },
- ["^hits with this weapon [hd][ae][va][el] "] = { flags = ModFlag.Hit, tag = { type = "Condition", var = "{Hand}Attack" } },
- -- Skill types
- ["^attacks [hd][ae][va][el] "] = { flags = ModFlag.Attack },
- ["^attack skills [hd][ae][va][el] "] = { keywordFlags = KeywordFlag.Attack },
- ["^spells [hd][ae][va][el] "] = { flags = ModFlag.Spell },
- ["^spell skills [hd][ae][va][el] "] = { flags = ModFlag.Spell },
- ["^projectile attack skills [hd][ae][va][el] "] = { tagList = { { type = "SkillType", skillType = SkillType.Attack }, { type = "SkillType", skillType = SkillType.Projectile } } },
- ["^projectiles from attacks [hd][ae][va][el] "] = { tagList = { { type = "SkillType", skillType = SkillType.Attack }, { type = "SkillType", skillType = SkillType.Projectile } } },
- ["^arrows [hd][ae][va][el] "] = { keywordFlags = KeywordFlag.Bow },
- ["^projectiles [hdf][aei][var][el] "] = { flags = ModFlag.Projectile },
- ["^melee attacks have "] = { flags = ModFlag.Melee },
- ["^movement attack skills have "] = { flags = ModFlag.Attack, keywordFlags = KeywordFlag.Movement },
- ["^lightning skills [hd][ae][va][el] "] = { keywordFlags = KeywordFlag.Lightning },
- ["^lightning spells [hd][ae][va][el] "] = { keywordFlags = KeywordFlag.Lightning, flags = ModFlag.Spell },
- ["^cold skills [hd][ae][va][el] "] = { keywordFlags = KeywordFlag.Cold },
- ["^cold spells [hd][ae][va][el] "] = { keywordFlags = KeywordFlag.Cold, flags = ModFlag.Spell },
- ["^fire skills [hd][ae][va][el] "] = { keywordFlags = KeywordFlag.Fire },
- ["^fire spells [hd][ae][va][el] "] = { keywordFlags = KeywordFlag.Fire, flags = ModFlag.Spell },
- ["^chaos skills [hd][ae][va][el] "] = { keywordFlags = KeywordFlag.Chaos },
- ["^vaal skills [hd][ae][va][el] "] = { keywordFlags = KeywordFlag.Vaal },
- ["^brand skills [hd][ae][va][el] "] = { tag = { type = "SkillType", skillType = SkillType.Brand } },
- ["^channelling skills [hd][ae][va][el] "] = { tag = { type = "SkillType", skillType = SkillType.Channelled } },
- ["^curse skills [hd][ae][va][el] "] = { tag = { type = "SkillType", skillType = SkillType.Curse } },
- ["^melee skills [hd][ae][va][el] "] = { tag = { type = "SkillType", skillType = SkillType.Melee } },
- ["^guard skills [hd][ae][va][el] "] = { tag = { type = "SkillType", skillType = SkillType.Guard } },
- ["^herald skills [hd][ae][va][el] "] = { tag = { type = "SkillType", skillType = SkillType.Herald } },
- ["^skills [hdfg][aei][vari][eln] "] = { },
- -- 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 skills [hgd][ae][via][enl] "] = { addToSkill = { type = "SocketedIn", slotName = "{SlotName}" } },
- ["^socketed attacks [hgd][ae][via][enl] "] = { addToSkill = { type = "SocketedIn", slotName = "{SlotName}", keyword = "attack" } },
- ["^socketed spells [hgd][ae][via][enl] "] = { addToSkill = { type = "SocketedIn", slotName = "{SlotName}", keyword = "spell" } },
- ["^socketed curse gems [hgd][ae][via][enl] "] = { addToSkill = { type = "SocketedIn", slotName = "{SlotName}", keyword = "curse" } },
- ["^socketed melee gems [hgd][ae][via][enl] "] = { addToSkill = { type = "SocketedIn", slotName = "{SlotName}", keyword = "melee" } },
- ["^socketed golem gems [hgd][ae][via][enl] "] = { addToSkill = { type = "SocketedIn", slotName = "{SlotName}", keyword = "golem" } },
- ["^socketed golem skills [hgd][ae][via][enl] "] = { addToSkill = { type = "SocketedIn", slotName = "{SlotName}", keyword = "golem" } },
- -- Other
- ["^your flasks grant "] = { },
- ["^when hit, "] = { },
- ["^you and allies [hgd][ae][via][enl] "] = { },
- ["^auras from your skills grant "] = { addToAura = true },
- ["^you and nearby allies [hgd][ae][via][enl] "] = { newAura = true },
- ["^nearby allies [hgd][ae][via][enl] "] = { newAura = true, newAuraOnlyAllies = true },
- ["^you and allies affected by auras from your skills [hgd][ae][via][enl] "] = { affectedByAura = true },
- ["^take "] = { modSuffix = "Taken" },
- ["^marauder: melee skills have "] = { flags = ModFlag.Melee, tag = { type = "Condition", var = "ConnectedToMarauderStart" } },
- ["^duelist: "] = { tag = { type = "Condition", var = "ConnectedToDuelistStart" } },
- ["^ranger: "] = { tag = { type = "Condition", var = "ConnectedToRangerStart" } },
- ["^shadow: "] = { tag = { type = "Condition", var = "ConnectedToShadowStart" } },
- ["^witch: "] = { tag = { type = "Condition", var = "ConnectedToWitchStart" } },
- ["^templar: "] = { tag = { type = "Condition", var = "ConnectedToTemplarStart" } },
- ["^scion: "] = { tag = { type = "Condition", var = "ConnectedToScionStart" } },
- ["^skills supported by spellslinger have "] = { tag = { type = "Condition", var = "SupportedBySpellslinger" } },
-}
-
--- List of modifier tags
-local modTagList = {
- ["on enemies"] = { },
- ["while active"] = { },
- [" on critical strike"] = { tag = { type = "Condition", var = "CriticalStrike" } },
- ["from critical strikes"] = { tag = { type = "Condition", var = "CriticalStrike" } },
- ["while affected by auras you cast"] = { affectedByAura = true },
- ["for you and nearby allies"] = { newAura = true },
- -- Multipliers
- ["per power charge"] = { tag = { type = "Multiplier", var = "PowerCharge" } },
- ["per frenzy charge"] = { tag = { type = "Multiplier", var = "FrenzyCharge" } },
- ["per endurance charge"] = { tag = { type = "Multiplier", var = "EnduranceCharge" } },
- ["per siphoning charge"] = { tag = { type = "Multiplier", var = "SiphoningCharge" } },
- ["per challenger charge"] = { tag = { type = "Multiplier", var = "ChallengerCharge" } },
- ["per blitz charge"] = { tag = { type = "Multiplier", var = "BlitzCharge" } },
- ["per crab barrier"] = { tag = { type = "Multiplier", var = "CrabBarrier" } },
- ["per (%d+) rage"] = function(num) return { tag = { type = "Multiplier", var = "Rage", div = num } } end,
- ["per level"] = { tag = { type = "Multiplier", var = "Level" } },
- ["per (%d+) player levels"] = function(num) return { tag = { type = "Multiplier", var = "Level", div = num } } end,
- ["for each equipped normal item"] = { tag = { type = "Multiplier", var = "NormalItem" } },
- ["for each normal item equipped"] = { tag = { type = "Multiplier", var = "NormalItem" } },
- ["for each normal item you have equipped"] = { tag = { type = "Multiplier", var = "NormalItem" } },
- ["for each equipped magic item"] = { tag = { type = "Multiplier", var = "MagicItem" } },
- ["for each magic item equipped"] = { tag = { type = "Multiplier", var = "MagicItem" } },
- ["for each magic item you have equipped"] = { tag = { type = "Multiplier", var = "MagicItem" } },
- ["for each equipped rare item"] = { tag = { type = "Multiplier", var = "RareItem" } },
- ["for each rare item equipped"] = { tag = { type = "Multiplier", var = "RareItem" } },
- ["for each rare item you have equipped"] = { tag = { type = "Multiplier", var = "RareItem" } },
- ["for each equipped unique item"] = { tag = { type = "Multiplier", var = "UniqueItem" } },
- ["for each unique item equipped"] = { tag = { type = "Multiplier", var = "UniqueItem" } },
- ["for each unique item you have equipped"] = { tag = { type = "Multiplier", var = "UniqueItem" } },
- ["per elder item equipped"] = { tag = { type = "Multiplier", var = "ElderItem" } },
- ["per shaper item equipped"] = { tag = { type = "Multiplier", var = "ShaperItem" } },
- ["per elder or shaper item equipped"] = { tag = { type = "Multiplier", varList = { "ElderItem", "ShaperItem" } } },
- ["for each equipped corrupted item"] = { tag = { type = "Multiplier", var = "CorruptedItem" } },
- ["for each corrupted item equipped"] = { tag = { type = "Multiplier", var = "CorruptedItem" } },
- ["for each uncorrupted item equipped"] = { tag = { type = "Multiplier", var = "NonCorruptedItem" } },
- ["per abyssa?l? jewel affecting you"] = { tag = { type = "Multiplier", var = "AbyssJewel" } },
- ["for each type of abyssa?l? jewel affecting you"] = { tag = { type = "Multiplier", var = "AbyssJewelType" } },
- ["per sextant affecting the area"] = { tag = { type = "Multiplier", var = "Sextant" } },
- ["per buff on you"] = { tag = { type = "Multiplier", var = "BuffOnSelf" } },
- ["per curse on enemy"] = { tag = { type = "Multiplier", var = "CurseOnEnemy" } },
- ["for each curse on enemy"] = { tag = { type = "Multiplier", var = "CurseOnEnemy" } },
- ["per curse on you"] = { tag = { type = "Multiplier", var = "CurseOnSelf" } },
- ["per poison on you"] = { tag = { type = "Multiplier", var = "PoisonStack" } },
- ["per poison on you, up to (%d+) per second"] = function(num) return { tag = { type = "Multiplier", var = "PoisonStack", limit = tonumber(num), limitTotal = true } } end,
- ["for each poison you have inflicted recently"] = { tag = { type = "Multiplier", var = "PoisonAppliedRecently" } },
- ["for each shocked enemy you've killed recently"] = { tag = { type = "Multiplier", var = "ShockedEnemyKilledRecently" } },
- ["per enemy killed recently, up to (%d+)%%"] = function(num) return { tag = { type = "Multiplier", var = "EnemyKilledRecently", limit = tonumber(num), limitTotal = true } } end,
- ["for each enemy you or your minions have killed recently, up to (%d+)%%"] = function(num) return { tag = { type = "Multiplier", varList = {"EnemyKilledRecently","EnemyKilledByMinionsRecently"}, limit = tonumber(num), limitTotal = true } } end,
- ["for each enemy you or your minions have killed recently, up to (%d+)%% per second"] = function(num) return { tag = { type = "Multiplier", varList = {"EnemyKilledRecently","EnemyKilledByMinionsRecently"}, limit = tonumber(num), limitTotal = true } } end,
- ["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 = "NearbyEnemy", limit = num, limitTotal = true } } end,
- ["to you and allies"] = { },
- ["per red socket"] = { tag = { type = "Multiplier", var = "RedSocketIn{SlotName}" } },
- ["per green socket"] = { tag = { type = "Multiplier", var = "GreenSocketIn{SlotName}" } },
- ["per blue socket"] = { tag = { type = "Multiplier", var = "BlueSocketIn{SlotName}" } },
- ["per white socket"] = { tag = { type = "Multiplier", var = "WhiteSocketIn{SlotName}" } },
- -- Per stat
- ["per (%d+) strength"] = function(num) return { tag = { type = "PerStat", stat = "Str", div = num } } end,
- ["per (%d+) dexterity"] = function(num) return { tag = { type = "PerStat", stat = "Dex", div = num } } end,
- ["per (%d+) intelligence"] = function(num) return { tag = { type = "PerStat", stat = "Int", div = num } } end,
- ["per (%d+) total attributes"] = function(num) return { tag = { type = "PerStat", statList = { "Str", "Dex", "Int" }, div = num } } end,
- ["per (%d+) of your lowest attribute"] = function(num) return { tag = { type = "PerStat", stat = "LowestAttribute", div = num } } end,
- ["per (%d+) reserved life"] = function(num) return { tag = { type = "PerStat", stat = "LifeReserved", div = num } } end,
- ["per (%d+) unreserved maximum mana, up to (%d+)%%"] = function(num, _, limit) return { tag = { type = "PerStat", stat = "ManaUnreserved", div = num, limit = tonumber(limit), limitTotal = true } } end,
- ["per (%d+) evasion rating"] = function(num) return { tag = { type = "PerStat", stat = "Evasion", div = num } } end,
- ["per (%d+) evasion rating, up to (%d+)%%"] = function(num, _, limit) return { tag = { type = "PerStat", stat = "Evasion", div = num, limit = tonumber(limit), limitTotal = true } } end,
- ["per (%d+) maximum energy shield"] = function(num) return { tag = { type = "PerStat", stat = "EnergyShield", div = num } } end,
- ["per (%d+) maximum life"] = function(num) return { tag = { type = "PerStat", stat = "Life", div = num } } end,
- ["per (%d+) maximum mana, up to (%d+)%%"] = function(num, _, limit) return { tag = { type = "PerStat", stat = "Mana", div = num, limit = tonumber(limit), limitTotal = true } } end,
- ["per (%d+) accuracy rating"] = function(num) return { tag = { type = "PerStat", stat = "Accuracy", div = num } } end,
- ["per (%d+)%% block chance"] = function(num) return { tag = { type = "PerStat", stat = "BlockChance", div = num } } end,
- ["per (%d+)%% chance to block attack damage"] = function(num) return { tag = { type = "PerStat", stat = "BlockChance", div = num } } end,
- ["per (%d+)%% chance to block spell damage"] = function(num) return { tag = { type = "PerStat", stat = "SpellBlockChance", div = num } } end,
- ["per (%d+) of the lowest of armour and evasion rating"] = function(num) return { tag = { type = "PerStat", stat = "LowestOfArmourAndEvasion", div = num } } end,
- ["per (%d+) maximum energy shield on helmet"] = function(num) return { tag = { type = "PerStat", stat = "EnergyShieldOnHelmet", div = num } } end,
- ["per (%d+) evasion rating on body armour"] = function(num) return { tag = { type = "PerStat", stat = "EvasionOnBody Armour", div = num } } end,
- ["per (%d+) armour on equipped shield"] = function(num) return { tag = { type = "PerStat", stat = "ArmourOnWeapon 2", div = num } } end,
- ["per (%d+) evasion rating on equipped shield"] = function(num) return { tag = { type = "PerStat", stat = "EvasionOnWeapon 2", div = num } } end,
- ["per (%d+) maximum energy shield on equipped shield"] = function(num) return { tag = { type = "PerStat", stat = "EnergyShieldOnWeapon 2", div = num } } end,
- ["per (%d+)%% cold resistance above 75%%"] = function(num) return { tag = { type = "PerStat", stat = "ColdResistOver75", div = num } } end,
- ["per (%d+)%% lightning resistance above 75%%"] = function(num) return { tag = { type = "PerStat", stat = "LightningResistOver75", div = num } } end,
- ["per totem"] = { tag = { type = "PerStat", stat = "ActiveTotemLimit" } },
- ["per summoned totem"] = { tag = { type = "PerStat", stat = "ActiveTotemLimit" } },
- ["for each summoned totem"] = { tag = { type = "PerStat", stat = "ActiveTotemLimit" } },
- ["for each time they have chained"] = { tag = { type = "PerStat", stat = "Chain" } },
- ["for each time it has chained"] = { tag = { type = "PerStat", stat = "Chain" } },
- -- Stat conditions
- ["with (%d+) or more strength"] = function(num) return { tag = { type = "StatThreshold", stat = "Str", threshold = num } } end,
- ["with at least (%d+) strength"] = function(num) return { tag = { type = "StatThreshold", stat = "Str", threshold = num } } end,
- ["w?h?i[lf]e? you have at least (%d+) strength"] = function(num) return { tag = { type = "StatThreshold", stat = "Str", threshold = num } } end,
- ["w?h?i[lf]e? you have at least (%d+) dexterity"] = function(num) return { tag = { type = "StatThreshold", stat = "Dex", threshold = num } } end,
- ["w?h?i[lf]e? you have at least (%d+) intelligence"] = function(num) return { tag = { type = "StatThreshold", stat = "Int", threshold = num } } end,
- ["at least (%d+) intelligence"] = function(num) return { tag = { type = "StatThreshold", stat = "Int", threshold = num } } end, -- lol
- ["if dexterity is higher than intelligence"] = { tag = { type = "Condition", var = "DexHigherThanInt" } },
- ["if strength is higher than intelligence"] = { tag = { type = "Condition", var = "StrHigherThanInt" } },
- ["w?h?i[lf]e? you have at least (%d+) maximum energy shield"] = function(num) return { tag = { type = "StatThreshold", stat = "EnergyShield", threshold = num } } end,
- ["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 } },
- -- Slot conditions
- ["when in main hand"] = { tag = { type = "SlotNumber", num = 1 } },
- ["when in off hand"] = { tag = { type = "SlotNumber", num = 2 } },
- ["in main hand"] = { tag = { type = "InSlot", num = 1 } },
- ["in off hand"] = { tag = { type = "InSlot", num = 2 } },
- ["with main hand"] = { tag = { type = "Condition", var = "MainHandAttack" } },
- ["with off hand"] = { tag = { type = "Condition", var = "OffHandAttack" } },
- ["with this weapon"] = { tag = { type = "Condition", var = "{Hand}Attack" } },
- ["if your other ring is a shaper item"] = { tag = { type = "Condition", var = "ShaperItemInRing {OtherSlotNum}" } },
- ["if your other ring is an elder item"] = { tag = { type = "Condition", var = "ElderItemInRing {OtherSlotNum}" } },
- -- Equipment conditions
- ["while holding a shield"] = { tag = { type = "Condition", var = "UsingShield" } },
- ["while your off hand is empty"] = { tag = { type = "Condition", var = "OffHandIsEmpty" } },
- ["with shields"] = { tag = { type = "Condition", var = "UsingShield" } },
- ["while dual wielding"] = { tag = { type = "Condition", var = "DualWielding" } },
- ["while dual wielding claws"] = { tag = { type = "Condition", var = "DualWieldingClaws" } },
- ["while dual wielding or holding a shield"] = { tag = { type = "Condition", varList = { "DualWielding", "UsingShield" } } },
- ["while wielding an axe"] = { tag = { type = "Condition", var = "UsingAxe" } },
- ["while wielding a bow"] = { tag = { type = "Condition", var = "UsingBow" } },
- ["while wielding a claw"] = { tag = { type = "Condition", var = "UsingClaw" } },
- ["while wielding a dagger"] = { tag = { type = "Condition", var = "UsingDagger" } },
- ["while wielding a mace"] = { tag = { type = "Condition", var = "UsingMace" } },
- ["while wielding a mace or sceptre"] = { tag = { type = "Condition", var = "UsingMace" } },
- ["while wielding a mace, sceptre or staff"] = { tag = { type = "Condition", varList = { "UsingMace", "UsingStaff" } } },
- ["while wielding a staff"] = { tag = { type = "Condition", var = "UsingStaff" } },
- ["while wielding a sword"] = { tag = { type = "Condition", var = "UsingSword" } },
- ["while wielding a melee weapon"] = { tag = { type = "Condition", var = "UsingMeleeWeapon" } },
- ["while wielding a one handed weapon"] = { tag = { type = "Condition", var = "UsingOneHandedWeapon" } },
- ["while wielding a two handed weapon"] = { tag = { type = "Condition", var = "UsingTwoHandedWeapon" } },
- ["while wielding a wand"] = { tag = { type = "Condition", var = "UsingWand" } },
- ["while unarmed"] = { tag = { type = "Condition", var = "Unarmed" } },
- ["with a normal item equipped"] = { tag = { type = "MultiplierThreshold", var = "NormalItem", threshold = 1 } },
- ["with a magic item equipped"] = { tag = { type = "MultiplierThreshold", var = "MagicItem", threshold = 1 } },
- ["with a rare item equipped"] = { tag = { type = "MultiplierThreshold", var = "RareItem", threshold = 1 } },
- ["with a unique item equipped"] = { tag = { type = "MultiplierThreshold", var = "UniqueItem", threshold = 1 } },
- ["if you wear no corrupted items"] = { tag = { type = "MultiplierThreshold", var = "CorruptedItem", threshold = 0, upper = true } },
- ["if no worn items are corrupted"] = { tag = { type = "MultiplierThreshold", var = "CorruptedItem", threshold = 0, upper = true } },
- ["if no equipped items are corrupted"] = { tag = { type = "MultiplierThreshold", var = "CorruptedItem", threshold = 0, upper = true } },
- ["if all worn items are corrupted"] = { tag = { type = "MultiplierThreshold", var = "NonCorruptedItem", threshold = 0, upper = true } },
- ["if all equipped items are corrupted"] = { tag = { type = "MultiplierThreshold", var = "NonCorruptedItem", threshold = 0, upper = true } },
- ["if equipped shield has at least (%d+)%% chance to block"] = function(num) return { tag = { type = "StatThreshold", stat = "ShieldBlockChance", threshold = num } } end,
- ["if you have (%d+) primordial items socketed or equipped"] = function(num) return { tag = { type = "MultiplierThreshold", var = "PrimordialItem", threshold = num } } end,
- -- Player status conditions
- ["wh[ie][ln]e? on low life"] = { tag = { type = "Condition", var = "LowLife" } },
- ["wh[ie][ln]e? not on low life"] = { tag = { type = "Condition", var = "LowLife", neg = true } },
- ["wh[ie][ln]e? on full life"] = { tag = { type = "Condition", var = "FullLife" } },
- ["wh[ie][ln]e? not on full life"] = { tag = { type = "Condition", var = "FullLife", neg = true } },
- ["wh[ie][ln]e? no life is reserved"] = { tag = { type = "StatThreshold", stat = "LifeReserved", threshold = 0, upper = true } },
- ["wh[ie][ln]e? no mana is reserved"] = { tag = { type = "StatThreshold", stat = "ManaReserved", threshold = 0, upper = true } },
- ["wh[ie][ln]e? on full energy shield"] = { tag = { type = "Condition", var = "FullEnergyShield" } },
- ["wh[ie][ln]e? not on full energy shield"] = { tag = { type = "Condition", var = "FullEnergyShield", neg = true } },
- ["wh[ie][ln]e? you have energy shield"] = { tag = { type = "Condition", var = "HaveEnergyShield" } },
- ["if you have energy shield"] = { tag = { type = "Condition", var = "HaveEnergyShield" } },
- ["while stationary"] = { tag = { type = "Condition", var = "Stationary" } },
- ["while moving"] = { tag = { type = "Condition", var = "Moving" } },
- ["while channelling"] = { tag = { type = "Condition", var = "Channelling" } },
- ["while you have no power charges"] = { tag = { type = "StatThreshold", stat = "PowerCharges", threshold = 0, upper = true } },
- ["while you have no frenzy charges"] = { tag = { type = "StatThreshold", stat = "FrenzyCharges", threshold = 0, upper = true } },
- ["while you have no endurance charges"] = { tag = { type = "StatThreshold", stat = "EnduranceCharges", threshold = 0, upper = true } },
- ["while you have a power charge"] = { tag = { type = "StatThreshold", stat = "PowerCharges", threshold = 1 } },
- ["while you have a frenzy charge"] = { tag = { type = "StatThreshold", stat = "FrenzyCharges", threshold = 1 } },
- ["while you have an endurance charge"] = { tag = { type = "StatThreshold", stat = "EnduranceCharges", threshold = 1 } },
- ["while at maximum power charges"] = { tag = { type = "StatThreshold", stat = "PowerCharges", thresholdStat = "PowerChargesMax" } },
- ["while at maximum frenzy charges"] = { tag = { type = "StatThreshold", stat = "FrenzyCharges", thresholdStat = "FrenzyChargesMax" } },
- ["while at maximum endurance charges"] = { tag = { type = "StatThreshold", stat = "EnduranceCharges", thresholdStat = "EnduranceChargesMax" } },
- ["while you have at least (%d+) crab barriers"] = function(num) return { tag = { type = "StatThreshold", stat = "CrabBarriers", threshold = num } } end,
- ["while you have a totem"] = { tag = { type = "Condition", var = "HaveTotem" } },
- ["while you have at least one nearby ally"] = { tag = { type = "MultiplierThreshold", var = "NearbyAlly", threshold = 1 } },
- ["while you have fortify"] = { tag = { type = "Condition", var = "Fortify" } },
- ["during onslaught"] = { tag = { type = "Condition", var = "Onslaught" } },
- ["while you have onslaught"] = { tag = { type = "Condition", var = "Onslaught" } },
- ["while phasing"] = { tag = { type = "Condition", var = "Phasing" } },
- ["while you have tailwind"] = { tag = { type = "Condition", var = "Tailwind" } },
- ["while you have arcane surge"] = { tag = { type = "Condition", var = "AffectedByArcaneSurge" } },
- ["while you have cat's stealth"] = { tag = { type = "Condition", var = "AffectedByCat'sStealth" } },
- ["while you have avian's might"] = { tag = { type = "Condition", var = "AffectedByAvian'sMight" } },
- ["while you have avian's flight"] = { tag = { type = "Condition", var = "AffectedByAvian'sFlight" } },
- ["while affected by aspect of the cat"] = { tag = { type = "Condition", varList = { "AffectedByCat'sStealth", "AffectedByCat'sAgility" } } },
- ["while you have a bestial minion"] = { tag = { type = "Condition", var = "HaveBestialMinion" } },
- ["while focussed"] = { tag = { type = "Condition", var = "Focused" } },
- ["while leeching"] = { tag = { type = "Condition", var = "Leeching" } },
- ["while leeching energy shield"] = { tag = { type = "Condition", var = "LeechingEnergyShield" } },
- ["while using a flask"] = { tag = { type = "Condition", var = "UsingFlask" } },
- ["during effect"] = { tag = { type = "Condition", var = "UsingFlask" } },
- ["during flask effect"] = { tag = { type = "Condition", var = "UsingFlask" } },
- ["during any flask effect"] = { tag = { type = "Condition", var = "UsingFlask" } },
- ["while on consecrated ground"] = { tag = { type = "Condition", var = "OnConsecratedGround" } },
- ["on burning ground"] = { tag = { type = "Condition", var = "OnBurningGround" } },
- ["on chilled ground"] = { tag = { type = "Condition", var = "OnChilledGround" } },
- ["on shocked ground"] = { tag = { type = "Condition", var = "OnShockedGround" } },
- ["while ignited"] = { tag = { type = "Condition", var = "Ignited" } },
- ["while frozen"] = { tag = { type = "Condition", var = "Frozen" } },
- ["while shocked"] = { tag = { type = "Condition", var = "Shocked" } },
- ["while not ignited, frozen or shocked"] = { tag = { type = "Condition", varList = { "Ignited", "Frozen", "Shocked" }, neg = true } },
- ["while bleeding"] = { tag = { type = "Condition", var = "Bleeding" } },
- ["while poisoned"] = { tag = { type = "Condition", var = "Poisoned" } },
- ["while cursed"] = { tag = { type = "Condition", var = "Cursed" } },
- ["while not cursed"] = { tag = { type = "Condition", var = "Cursed", neg = true } },
- ["while there is only one nearby enemy"] = { tag = { type = "Condition", var = "OnlyOneNearbyEnemy" } },
- ["while t?h?e?r?e? ?i?s? ?a rare or unique enemy i?s? ?nearby"] = { tag = { type = "ActorCondition", actor = "enemy", var = "RareOrUnique" } },
- ["if you[' ]h?a?ve hit recently"] = { tag = { type = "Condition", var = "HitRecently" } },
- ["if you[' ]h?a?ve hit an enemy recently"] = { tag = { type = "Condition", var = "HitRecently" } },
- ["if you[' ]h?a?ve hit a cursed enemy recently"] = { tagList = { { type = "Condition", var = "HitRecently" }, { type = "ActorCondition", actor = "enemy", var = "Cursed" } } },
- ["if you[' ]h?a?ve crit recently"] = { tag = { type = "Condition", var = "CritRecently" } },
- ["if you[' ]h?a?ve dealt a critical strike recently"] = { tag = { type = "Condition", var = "CritRecently" } },
- ["if you[' ]h?a?ve crit in the past 8 seconds"] = { tag = { type = "Condition", var = "CritInPast8Sec" } },
- ["if you[' ]h?a?ve dealt a crit in the past 8 seconds"] = { tag = { type = "Condition", var = "CritInPast8Sec" } },
- ["if you[' ]h?a?ve dealt a critical strike in the past 8 seconds"] = { tag = { type = "Condition", var = "CritInPast8Sec" } },
- ["if you haven't crit recently"] = { tag = { type = "Condition", var = "CritRecently", neg = true } },
- ["if you haven't dealt a critical strike recently"] = { tag = { type = "Condition", var = "CritRecently", neg = true } },
- ["if you[' ]h?a?ve dealt a non%-critical strike recently"] = { tag = { type = "Condition", var = "NonCritRecently" } },
- ["if your skills have dealt a critical strike recently"] = { tag = { type = "Condition", var = "SkillCritRecently" } },
- ["if you[' ]h?a?ve killed recently"] = { tag = { type = "Condition", var = "KilledRecently" } },
- ["if you haven't killed recently"] = { tag = { type = "Condition", var = "KilledRecently", neg = true } },
- ["if you or your totems have killed recently"] = { tag = { type = "Condition", varList = {"KilledRecently","TotemsKilledRecently"} } },
- ["if you[' ]h?a?ve killed a maimed enemy recently"] = { tagList = { { type = "Condition", var = "KilledRecently" }, { type = "ActorCondition", actor = "enemy", var = "Maimed" } } },
- ["if you[' ]h?a?ve killed a cursed enemy recently"] = { tagList = { { type = "Condition", var = "KilledRecently" }, { type = "ActorCondition", actor = "enemy", var = "Cursed" } } },
- ["if you[' ]h?a?ve killed a bleeding enemy recently"] = { tagList = { { type = "Condition", var = "KilledRecently" }, { type = "ActorCondition", actor = "enemy", var = "Bleeding" } } },
- ["if you[' ]h?a?ve killed an enemy affected by your damage over time recently"] = { tag = { type = "Condition", var = "KilledAffectedByDotRecently" } },
- ["if you[' ]h?a?ve frozen an enemy recently"] = { tag = { type = "Condition", var = "FrozenEnemyRecently" } },
- ["if you[' ]h?a?ve ignited an enemy recently"] = { tag = { type = "Condition", var = "IgnitedEnemyRecently" } },
- ["if you[' ]h?a?ve shocked an enemy recently"] = { tag = { type = "Condition", var = "ShockedEnemyRecently" } },
- ["if you[' ]h?a?ve been hit recently"] = { tag = { type = "Condition", var = "BeenHitRecently" } },
- ["if you were hit recently"] = { tag = { type = "Condition", var = "BeenHitRecently" } },
- ["if you were damaged by a hit recently"] = { tag = { type = "Condition", var = "BeenHitRecently" } },
- ["if you[' ]h?a?ve taken a critical strike recently"] = { tag = { type = "Condition", var = "BeenCritRecently" } },
- ["if you[' ]h?a?ve taken a savage hit recently"] = { tag = { type = "Condition", var = "BeenSavageHitRecently" } },
- ["if you have ?n[o']t been hit recently"] = { tag = { type = "Condition", var = "BeenHitRecently", neg = true } },
- ["if you[' ]h?a?ve taken no damage from hits recently"] = { tag = { type = "Condition", var = "BeenHitRecently", neg = true } },
- ["if you[' ]h?a?ve taken fire damage from a hit recently"] = { tag = { type = "Condition", var = "HitByFireDamageRecently" } },
- ["if you[' ]h?a?ve blocked recently"] = { tag = { type = "Condition", var = "BlockedRecently" } },
- ["if you[' ]h?a?ve blocked an attack recently"] = { tag = { type = "Condition", var = "BlockedAttackRecently" } },
- ["if you[' ]h?a?ve blocked a spell recently"] = { tag = { type = "Condition", var = "BlockedSpellRecently" } },
- ["if you[' ]h?a?ve blocked damage from a unique enemy in the past 10 seconds"] = { tag = { type = "Condition", var = "BlockedHitFromUniqueEnemyInPast10Sec" } },
- ["if you[' ]h?a?ve attacked recently"] = { tag = { type = "Condition", var = "AttackedRecently" } },
- ["if you[' ]h?a?ve cast a spell recently"] = { tag = { type = "Condition", var = "CastSpellRecently" } },
- ["if you[' ]h?a?ve consumed a corpse recently"] = { tag = { type = "Condition", var = "ConsumedCorpseRecently" } },
- ["for each corpse consumed recently"] = { tag = { type = "Multiplier", var = "CorpseConsumedRecently" } },
- ["if you[' ]h?a?ve taunted an enemy recently"] = { tag = { type = "Condition", var = "TauntedEnemyRecently" } },
- ["if you[' ]h?a?ve used a skill recently"] = { tag = { type = "Condition", var = "UsedSkillRecently" } },
- ["for each skill you've used recently, up to (%d+)%%"] = function(num) return { tag = { type = "Multiplier", var = "SkillUsedRecently", limit = num, limitTotal = true } } end,
- ["if you[' ]h?a?ve used a warcry recently"] = { tag = { type = "Condition", var = "UsedWarcryRecently" } },
- ["if you[' ]h?a?ve warcried recently"] = { tag = { type = "Condition", var = "UsedWarcryRecently" } },
- ["for each of your mines detonated recently, up to (%d+)%%"] = function(num) return { tag = { type = "Multiplier", var = "MineDetonatedRecently", limit = num, limitTotal = true } } end,
- ["for each mine detonated recently, up to (%d+)%%"] = function(num) return { tag = { type = "Multiplier", var = "MineDetonatedRecently", limit = num, limitTotal = true } } end,
- ["for each mine detonated recently, up to (%d+)%% per second"] = function(num) return { tag = { type = "Multiplier", var = "MineDetonatedRecently", limit = num, limitTotal = true } } end,
- ["for each of your traps triggered recently, up to (%d+)%%"] = function(num) return { tag = { type = "Multiplier", var = "TrapTriggeredRecently", limit = num, limitTotal = true } } end,
- ["for each trap triggered recently, up to (%d+)%%"] = function(num) return { tag = { type = "Multiplier", var = "TrapTriggeredRecently", limit = num, limitTotal = true } } end,
- ["for each trap triggered recently, up to (%d+)%% per second"] = function(num) return { tag = { type = "Multiplier", var = "TrapTriggeredRecently", limit = num, limitTotal = true } } end,
- ["if you[' ]h?a?ve used a fire skill recently"] = { tag = { type = "Condition", var = "UsedFireSkillRecently" } },
- ["if you[' ]h?a?ve used a cold skill recently"] = { tag = { type = "Condition", var = "UsedColdSkillRecently" } },
- ["if you[' ]h?a?ve used a fire skill in the past 10 seconds"] = { tag = { type = "Condition", var = "UsedFireSkillInPast10Sec" } },
- ["if you[' ]h?a?ve used a cold skill in the past 10 seconds"] = { tag = { type = "Condition", var = "UsedColdSkillInPast10Sec" } },
- ["if you[' ]h?a?ve used a lightning skill in the past 10 seconds"] = { tag = { type = "Condition", var = "UsedLightningSkillInPast10Sec" } },
- ["if you[' ]h?a?ve summoned a totem recently"] = { tag = { type = "Condition", var = "SummonedTotemRecently" } },
- ["if you[' ]h?a?ve used a minion skill recently"] = { tag = { type = "Condition", var = "UsedMinionSkillRecently" } },
- ["if you[' ]h?a?ve used a movement skill recently"] = { tag = { type = "Condition", var = "UsedMovementSkillRecently" } },
- ["if you[' ]h?a?ve used a vaal skill recently"] = { tag = { type = "Condition", var = "UsedVaalSkillRecently" } },
- ["during soul gain prevention"] = { tag = { type = "Condition", var = "SoulGainPrevention" } },
- ["if you detonated mines recently"] = { tag = { type = "Condition", var = "DetonatedMinesRecently" } },
- ["if you detonated a mine recently"] = { tag = { type = "Condition", var = "DetonatedMinesRecently" } },
- ["if energy shield recharge has started recently"] = { tag = { type = "Condition", var = "EnergyShieldRechargeRecently" } },
- ["when cast on frostbolt"] = { tag = { type = "Condition", var = "CastOnFrostbolt" } },
- ["branded enemy's"] = { tag = { type = "Condition", var = "BrandAttachedToEnemy" } },
- ["to enemies they're attached to"] = { tag = { type = "Condition", var = "BrandAttachedToEnemy" } },
- -- Enemy status conditions
- ["at close range"] = { tag = { type = "Condition", var = "AtCloseRange" }, flags = ModFlag.Hit },
- ["against rare and unique enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "RareOrUnique" }, keywordFlags = KeywordFlag.Hit },
- ["against unique enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "RareOrUnique" }, keywordFlags = KeywordFlag.Hit },
- ["against enemies on full life"] = { tag = { type = "ActorCondition", actor = "enemy", var = "FullLife" }, keywordFlags = KeywordFlag.Hit },
- ["against enemies that are on full life"] = { tag = { type = "ActorCondition", actor = "enemy", var = "FullLife" }, keywordFlags = KeywordFlag.Hit },
- ["against enemies on low life"] = { tag = { type = "ActorCondition", actor = "enemy", var = "LowLife" }, keywordFlags = KeywordFlag.Hit },
- ["against enemies that are on low life"] = { tag = { type = "ActorCondition", actor = "enemy", var = "LowLife" }, keywordFlags = KeywordFlag.Hit },
- ["against cursed enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Cursed" }, keywordFlags = KeywordFlag.Hit },
- ["when hitting cursed enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Cursed" }, keywordFlags = KeywordFlag.Hit },
- ["against taunted enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Taunted" }, keywordFlags = KeywordFlag.Hit },
- ["against bleeding enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Bleeding" }, keywordFlags = KeywordFlag.Hit },
- ["to bleeding enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Bleeding" }, keywordFlags = KeywordFlag.Hit },
- ["from bleeding enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Bleeding" } },
- ["against poisoned enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Poisoned" }, keywordFlags = KeywordFlag.Hit },
- ["to poisoned enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Poisoned" }, keywordFlags = KeywordFlag.Hit },
- ["against enemies affected by (%d+) or more poisons"] = function(num) return { tag = { type = "MultiplierThreshold", actor = "enemy", var = "PoisonStack", threshold = num } } end,
- ["against enemies affected by at least (%d+) poisons"] = function(num) return { tag = { type = "MultiplierThreshold", actor = "enemy", var = "PoisonStack", threshold = num } } end,
- ["against hindered enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Hindered" }, keywordFlags = KeywordFlag.Hit },
- ["against maimed enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Maimed" }, keywordFlags = KeywordFlag.Hit },
- ["against blinded enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Blinded" }, keywordFlags = KeywordFlag.Hit },
- ["from blinded enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Blinded" } },
- ["against burning enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Burning" }, keywordFlags = KeywordFlag.Hit },
- ["against ignited enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Ignited" }, keywordFlags = KeywordFlag.Hit },
- ["to ignited enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Ignited" }, keywordFlags = KeywordFlag.Hit },
- ["against shocked enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Shocked" }, keywordFlags = KeywordFlag.Hit },
- ["to shocked enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Shocked" }, keywordFlags = KeywordFlag.Hit },
- ["against frozen enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Frozen" }, keywordFlags = KeywordFlag.Hit },
- ["to frozen enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Frozen" }, keywordFlags = KeywordFlag.Hit },
- ["against chilled enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Chilled" }, keywordFlags = KeywordFlag.Hit },
- ["to chilled enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Chilled" }, keywordFlags = KeywordFlag.Hit },
- ["inflicted on chilled enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Chilled" } },
- ["enemies which are chilled"] = { tag = { type = "ActorCondition", actor = "enemy", var = "Chilled" }, keywordFlags = KeywordFlag.Hit },
- ["against frozen, shocked or ignited enemies"] = { tag = { type = "ActorCondition", actor = "enemy", varList = {"Frozen","Shocked","Ignited"} }, keywordFlags = KeywordFlag.Hit },
- ["against enemies affected by elemental ailments"] = { tag = { type = "ActorCondition", actor = "enemy", varList = {"Frozen","Chilled","Shocked","Ignited"} }, keywordFlags = KeywordFlag.Hit },
- ["against enemies that are affected by elemental ailments"] = { tag = { type = "ActorCondition", actor = "enemy", varList = {"Frozen","Chilled","Shocked","Ignited"} }, fkeywordFlags = KeywordFlag.Hit },
- ["against enemies that are affected by no elemental ailments"] = { tagList = { { type = "ActorCondition", actor = "enemy", varList = {"Frozen","Chilled","Shocked","Ignited"}, neg = true }, { type = "Condition", var = "Effective" } }, keywordFlags = KeywordFlag.Hit },
- ["against enemies affected by (%d+) spider's webs"] = function(num) return { tag = { type = "MultiplierThreshold", actor = "enemy", var = "Spider's WebStack", threshold = num } } end,
- ["against enemies on consecrated ground"] = { tag = { type = "ActorCondition", actor = "enemy", var = "OnConsecratedGround" } },
- -- Enemy multipliers
- ["per freeze, shock and ignite on enemy"] = { tag = { type = "Multiplier", var = "FreezeShockIgniteOnEnemy" }, keywordFlags = KeywordFlag.Hit },
- ["per poison affecting enemy"] = { tag = { type = "Multiplier", actor = "enemy", var = "PoisonStack" } },
- ["per poison affecting enemy, up to %+([%d%.]+)%%"] = function(num) return { tag = { type = "Multiplier", actor = "enemy", var = "PoisonStack", limit = num, limitTotal = true } } end,
- ["for each spider's web on the enemy"] = { tag = { type = "Multiplier", actor = "enemy", var = "Spider's WebStack" } },
-}
-
-local mod = modLib.createMod
-local function flag(name, ...)
- return mod(name, "FLAG", true, ...)
-end
-
-local gemIdLookup = {
- ["power charge on critical strike"] = "SupportPowerChargeOnCrit",
-}
-for name, grantedEffect in pairs(data["3_0"].skills) do
- if not grantedEffect.hidden or grantedEffect.fromItem then
- gemIdLookup[grantedEffect.name:lower()] = grantedEffect.id
- end
-end
-local function extraSkill(name, level, noSupports)
- name = name:gsub(" skill","")
- if gemIdLookup[name] then
- return {
- mod("ExtraSkill", "LIST", { skillId = gemIdLookup[name], level = level, noSupports = noSupports })
- }
- end
-end
-
--- List of special modifiers
-local specialModList = {
- -- Keystones
- ["your hits can't be evaded"] = { flag("CannotBeEvaded") },
- ["never deal critical strikes"] = { flag("NeverCrit") },
- ["no critical strike multiplier"] = { flag("NoCritMultiplier") },
- ["ailments never count as being from critical strikes"] = { flag("AilmentsAreNeverFromCrit") },
- ["the increase to physical damage from strength applies to projectile attacks as well as melee attacks"] = { flag("IronGrip") },
- ["converts all evasion rating to armour%. dexterity provides no bonus to evasion rating"] = { flag("IronReflexes") },
- ["30%% chance to dodge attack hits%. 50%% less armour, 30%% less energy shield, 30%% less chance to block spell and attack damage"] = {
- mod("AttackDodgeChance", "BASE", 30),
- mod("Armour", "MORE", -50),
- mod("EnergyShield", "MORE", -30),
- mod("BlockChance", "MORE", -30),
- mod("SpellBlockChance", "MORE", -30)
- },
- ["maximum life becomes 1, immune to chaos damage"] = { flag("ChaosInoculation") },
- ["life regeneration is applied to energy shield instead"] = { flag("ZealotsOath") },
- ["life leeched per second is doubled"] = { mod("LifeLeechRate", "MORE", 100) },
- ["maximum total recovery per second from life leech is doubled"] = { mod("MaxLifeLeechRate", "MORE", 100) },
- ["maximum total recovery per second from energy shield leech is doubled"] = { mod("MaxEnergyShieldLeechRate", "MORE", 100) },
- ["life regeneration has no effect"] = { flag("NoLifeRegen") },
- ["deal no non%-fire damage"] = { flag("DealNoPhysical"), flag("DealNoLightning"), flag("DealNoCold"), flag("DealNoChaos") },
- ["(%d+)%% of physical, cold and lightning damage converted to fire damage"] = function(num) return {
- mod("PhysicalDamageConvertToFire", "BASE", num),
- mod("LightningDamageConvertToFire", "BASE", num),
- mod("ColdDamageConvertToFire", "BASE", num)
- } end,
- ["removes all mana%. spend life instead of mana for skills"] = { mod("Mana", "MORE", -100), flag("BloodMagic") },
- ["enemies you hit with elemental damage temporarily get (%+%d+)%% resistance to those elements and (%-%d+)%% resistance to other elements"] = function(plus, _, minus)
- minus = tonumber(minus)
- return {
- flag("ElementalEquilibrium"),
- mod("EnemyModifier", "LIST", { mod = mod("FireResist", "BASE", plus, { type = "Condition", var = "HitByFireDamage" }) }),
- mod("EnemyModifier", "LIST", { mod = mod("FireResist", "BASE", minus, { type = "Condition", var = "HitByFireDamage", neg = true }, { type = "Condition", varList={"HitByColdDamage","HitByLightningDamage"} }) }),
- mod("EnemyModifier", "LIST", { mod = mod("ColdResist", "BASE", plus, { type = "Condition", var = "HitByColdDamage" }) }),
- mod("EnemyModifier", "LIST", { mod = mod("ColdResist", "BASE", minus, { type = "Condition", var = "HitByColdDamage", neg = true }, { type = "Condition", varList={"HitByFireDamage","HitByLightningDamage"} }) }),
- mod("EnemyModifier", "LIST", { mod = mod("LightningResist", "BASE", plus, { type = "Condition", var = "HitByLightningDamage" }) }),
- mod("EnemyModifier", "LIST", { mod = mod("LightningResist", "BASE", minus, { type = "Condition", var = "HitByLightningDamage", neg = true }, { type = "Condition", varList={"HitByFireDamage","HitByColdDamage"} }) }),
- }
- end,
- ["projectile attack hits deal up to 30%% more damage to targets at the start of their movement, dealing less damage to targets as the projectile travels farther"] = { flag("PointBlank") },
- ["leech energy shield instead of life"] = { flag("GhostReaver") },
- ["minions explode when reduced to low life, dealing 33%% of their maximum life as fire damage to surrounding enemies"] = { mod("ExtraMinionSkill", "LIST", { skillId = "MinionInstability" }) },
- ["minions explode when reduced to low life, dealing 33%% of their life as fire damage to surrounding enemies"] = { mod("ExtraMinionSkill", "LIST", { skillId = "MinionInstability" }) },
- ["all bonuses from an equipped shield apply to your minions instead of you"] = { }, -- The node itself is detected by the code that handles it
- ["spend energy shield before mana for skill costs"] = { },
- ["energy shield protects mana instead of life"] = { flag("EnergyShieldProtectsMana") },
- ["modifiers to critical strike multiplier also apply to damage over time multiplier for ailments from critical strikes at (%d+)%% of their value"] = function(num) return { mod("CritMultiplierAppliesToDegen", "BASE", num) } end,
- ["your bleeding does not deal extra damage while the enemy is moving"] = { flag("Condition:NoExtraBleedDamageToMovingEnemy") },
- -- Ascendant
- ["grants (%d+) passive skill points?"] = function(num) return { mod("ExtraPoints", "BASE", num) } end,
- ["can allocate passives from the %a+'s starting point"] = { },
- ["projectiles gain damage as they travel farther, dealing up to (%d+)%% increased damage with hits to targets"] = function(num) return { mod("Damage", "INC", num, nil, bor(ModFlag.Attack, ModFlag.Projectile), { type = "DistanceRamp", ramp = {{35,0},{70,1}} }) } end,
- -- Assassin
- ["poison you inflict with critical strikes deals (%d+)%% more damage"] = function(num) return { mod("Damage", "MORE", num, nil, 0, KeywordFlag.Poison, { type = "Condition", var = "CriticalStrike" }) } end,
- -- Berserker
- ["gain %d+ rage when you kill an enemy"] = {
- flag("Condition:CanGainRage"),
- mod("Dummy", "DUMMY", 1, { type = "Condition", var = "CanGainRage" }) -- Make the Configuration option appear
- },
- ["gain %d+ rage when you use a warcry"] = {
- flag("Condition:CanGainRage"),
- mod("Dummy", "DUMMY", 1, { type = "Condition", var = "CanGainRage" }) -- Make the Configuration option appear
- },
- ["gain %d+ rage on hit with attacks, no more than once every [%d%.]+ seconds"] = {
- flag("Condition:CanGainRage"),
- mod("Dummy", "DUMMY", 1, { type = "Condition", var = "CanGainRage" }) -- Make the Configuration option appear
- },
- ["inherent effects from having rage are tripled"] = { mod("Multiplier:RageEffect", "BASE", 2) },
- ["cannot be stunned while you have at least (%d+) rage"] = function(num) return { mod("AvoidStun", "BASE", 100, { type = "MultiplierThreshold", var = "Rage", threshold = 25 }) } end,
- ["lose ([%d%.]+)%% of life per second per rage while you are not losing rage"] = function(num) return { mod("LifeDegen", "BASE", num / 100, { type = "PerStat", stat = "Life" }, { type = "Multiplier", var = "Rage", limit = 50 }) } end,
- ["if you've warcried recently, you and nearby allies have (%d+)%% increased attack speed"] = function(num) return { mod("ExtraAura", "LIST", { mod = mod("Speed", "INC", num, nil, ModFlag.Attack) }, { type = "Condition", var = "UsedWarcryRecently" }) } end,
- -- Champion
- ["you have fortify"] = { flag("Condition:Fortify") },
- ["cannot be stunned while you have fortify"] = { mod("AvoidStun", "BASE", 100, { type = "Condition", var = "Fortify" }) },
- ["enemies taunted by you take (%d+)%% increased damage"] = function(num) return { mod("EnemyModifier", "LIST", { mod = mod("DamageTaken", "INC", num, { type = "Condition", var = "Taunted" }) }) } end,
- ["enemies taunted by you cannot evade attacks"] = { mod("EnemyModifier", "LIST", { mod = flag("CannotEvade", { type = "Condition", var = "Taunted" }) }) },
- -- Chieftain
- ["enemies near your totems take (%d+)%% increased physical and fire damage"] = function(num) return {
- mod("EnemyModifier", "LIST", { mod = mod("PhysicalDamageTaken", "INC", num) }),
- mod("EnemyModifier", "LIST", { mod = mod("FireDamageTaken", "INC", num) })
- } end,
- -- Deadeye
- ["projectiles pierce all nearby targets"] = { flag("PierceAllTargets") },
- ["gain %+(%d+) life when you hit a bleeding enemy"] = function(num) return { mod("LifeOnHit", "BASE", num, { type = "ActorCondition", actor = "enemy", var = "Bleeding" }) } end,
- ["accuracy rating is doubled"] = { mod("Accuracy", "MORE", 100) },
- ["(%d+)%% increased blink arrow and mirror arrow cooldown recovery speed"] = function(num) return {
- mod("CooldownRecovery", "INC", num, { type = "SkillName", skillNameList = { "Blink Arrow", "Mirror Arrow" } }),
- } end,
- ["if you've used a skill recently, you and nearby allies have tailwind"] = { mod("ExtraAura", "LIST", { mod = flag("Condition:Tailwind") }, { type = "Condition", var = "UsedSkillRecently" }) },
- ["projectiles deal (%d+)%% more damage for each remaining chain"] = function(num) return { mod("Damage", "MORE", num, nil, ModFlag.Projectile, { type = "PerStat", stat = "ChainRemaining" }) } end,
- ["far shot"] = { flag("FarShot") },
- -- Elementalist
- ["gain (%d+)%% increased area of effect for %d+ seconds"] = function(num) return { mod("AreaOfEffect", "INC", num, { type = "Condition", var = "PendulumOfDestructionAreaOfEffect" }) } end,
- ["gain (%d+)%% increased elemental damage for %d+ seconds"] = function(num) return { mod("ElementalDamage", "INC", num, { type = "Condition", var = "PendulumOfDestructionElementalDamage" }) } end,
- ["for each element you've been hit by damage of recently, (%d+)%% increased damage of that element"] = function(num) return {
- mod("FireDamage", "INC", num, { type = "Condition", var = "HitByFireDamageRecently" }),
- mod("ColdDamage", "INC", num, { type = "Condition", var = "HitByColdDamageRecently" }),
- mod("LightningDamage", "INC", num, { type = "Condition", var = "HitByLightningDamageRecently" })
- } end,
- ["for each element you've been hit by damage of recently, (%d+)%% reduced damage taken of that element"] = function(num) return {
- mod("FireDamageTaken", "INC", -num, { type = "Condition", var = "HitByFireDamageRecently" }),
- mod("ColdDamageTaken", "INC", -num, { type = "Condition", var = "HitByColdDamageRecently" }),
- mod("LightningDamageTaken", "INC", -num, { type = "Condition", var = "HitByLightningDamageRecently" })
- } end,
- ["every %d+ seconds:"] = { },
- ["gain chilling conflux for %d seconds"] = {
- flag("PhysicalCanChill", { type = "Condition", var = "ChillingConflux" }),
- flag("LightningCanChill", { type = "Condition", var = "ChillingConflux" }),
- flag("FireCanChill", { type = "Condition", var = "ChillingConflux" }),
- flag("ChaosCanChill", { type = "Condition", var = "ChillingConflux" }),
- },
- ["gain shocking conflux for %d seconds"] = {
- mod("EnemyShockChance", "BASE", 100, { type = "Condition", var = "ShockingConflux" }),
- flag("PhysicalCanShock", { type = "Condition", var = "ShockingConflux" }),
- flag("ColdCanShock", { type = "Condition", var = "ShockingConflux" }),
- flag("FireCanShock", { type = "Condition", var = "ShockingConflux" }),
- flag("ChaosCanShock", { type = "Condition", var = "ShockingConflux" }),
- },
- ["gain igniting conflux for %d seconds"] = {
- mod("EnemyIgniteChance", "BASE", 100, { type = "Condition", var = "IgnitingConflux" }),
- flag("PhysicalCanIgnite", { type = "Condition", var = "IgnitingConflux" }),
- flag("LightningCanIgnite", { type = "Condition", var = "IgnitingConflux" }),
- flag("ColdCanIgnite", { type = "Condition", var = "IgnitingConflux" }),
- flag("ChaosCanIgnite", { type = "Condition", var = "IgnitingConflux" }),
- },
- ["gain chilling, shocking and igniting conflux for %d seconds"] = { },
- -- Gladiator
- ["enemies maimed by you take (%d+)%% increased physical damage"] = function(num) return { mod("EnemyModifier", "LIST", { mod = mod("PhysicalDamageTaken", "INC", num, { type = "Condition", var = "Maimed" }) }) } end,
- ["chance to block spell damage is equal to chance to block attack damage"] = { flag("SpellBlockChanceIsBlockChance") },
- ["maximum chance to block spell damage is equal to maximum chance to block attack damage"] = { flag("SpellBlockChanceMaxIsBlockChanceMax") },
- -- Guardian
- ["grants armour equal to (%d+)%% of your reserved life to you and nearby allies"] = function(num) return { mod("GrantReservedLifeAsAura", "LIST", { mod = mod("Armour", "BASE", num / 100) }) } end,
- ["grants maximum energy shield equal to (%d+)%% of your reserved mana to you and nearby allies"] = function(num) return { mod("GrantReservedManaAsAura", "LIST", { mod = mod("EnergyShield", "BASE", num / 100) }) } end,
- ["warcries cost no mana"] = { mod("ManaCost", "MORE", -100, nil, 0, KeywordFlag.Warcry) },
- ["%+(%d+)%% chance to block attack damage for %d seconds? every %d seconds"] = function(num) return { mod("BlockChance", "BASE", num, { type = "Condition", var = "BastionOfHopeActive" }) } end,
- ["if you've attacked recently, you and nearby allies have %+(%d+)%% chance to block attack damage"] = function(num) return { mod("ExtraAura", "LIST", { mod = mod("BlockChance", "BASE", num) }, { type = "Condition", var = "AttackedRecently" }) } end,
- ["if you've cast a spell recently, you and nearby allies have %+(%d+)%% chance to block spell damage"] = function(num) return { mod("ExtraAura", "LIST", { mod = mod("SpellBlockChance", "BASE", num) }, { type = "Condition", var = "CastSpellRecently" }) } end,
- ["while there is at least one nearby ally, you and nearby allies deal (%d+)%% more damage"] = function(num) return { mod("ExtraAura", "LIST", { mod = mod("Damage", "MORE", num) }, { type = "MultiplierThreshold", var = "NearbyAlly", threshold = 1 }) } end,
- ["while there are at least five nearby allies, you and nearby allies have onslaught"] = { mod("ExtraAura", "LIST", { mod = flag("Onslaught") }, { type = "MultiplierThreshold", var = "NearbyAlly", threshold = 5 }) },
- -- Hierophant
- ["you and your totems regenerate (%d+)%% of life per second for each summoned totem"] = function (num) return {
- mod("LifeRegenPercent", "BASE", num, {type = "PerStat", stat = "ActiveTotemLimit"}),
- mod("LifeRegenPercent", "BASE", num, {type = "PerStat", stat = "ActiveTotemLimit"}, 0, KeywordFlag.Totem),
- } end,
- -- Inquisitor
- ["critical strikes ignore enemy monster elemental resistances"] = { flag("IgnoreElementalResistances", { type = "Condition", var = "CriticalStrike" }) },
- ["non%-critical strikes penetrate (%d+)%% of enemy elemental resistances"] = function(num) return { mod("ElementalPenetration", "BASE", num, { type = "Condition", var = "CriticalStrike", neg = true }) } end,
- ["consecrated ground you create grants (%d+)%% increased damage to you and allies"] = function(num) return { mod("Damage", "INC", num, { type = "Condition", var = "OnConsecratedGround" }) } end,
- ["nearby enemies take (%d+)%% increased elemental damage"] = function(num) return { mod("EnemyModifier", "LIST", { mod = mod("ElementalDamageTaken", "INC", num) }) } end,
- -- Juggernaut
- ["armour received from body armour is doubled"] = { flag("Unbreakable") },
- ["movement speed cannot be modified to below base value"] = { flag("MovementSpeedCannotBeBelowBase") },
- ["you cannot be slowed to below base speed"] = { flag("ActionSpeedCannotBeBelowBase") },
- ["cannot be slowed to below base speed"] = { flag("ActionSpeedCannotBeBelowBase") },
- ["gain accuracy rating equal to your strength"] = { mod("Accuracy", "BASE", 1, { type = "PerStat", stat = "Str" }) },
- -- Necromancer
- ["your offering skills also affect you"] = { mod("ExtraSkillMod", "LIST", { mod = mod("SkillData", "LIST", { key = "buffNotPlayer", value = false }) }, { type = "SkillName", skillNameList = { "Bone Offering", "Flesh Offering", "Spirit Offering" } }) },
- ["your offerings have (%d+)%% reduced effect on you"] = function(num) return { mod("ExtraSkillMod", "LIST", { mod = mod("BuffEffectOnPlayer", "INC", -num) }, { type = "SkillName", skillNameList = { "Bone Offering", "Flesh Offering", "Spirit Offering" } }) } end,
- ["if you've consumed a corpse recently, you and your minions have (%d+)%% increased area of effect"] = function(num) return { mod("AreaOfEffect", "INC", num, { type = "Condition", var = "ConsumedCorpseRecently" }), mod("MinionModifier", "LIST", { mod = mod("AreaOfEffect", "INC", num) }, { type = "Condition", var = "ConsumedCorpseRecently" }) } end,
- ["with at least one nearby corpse, you and nearby allies deal (%d+)%% more damage"] = function(num) return { mod("ExtraAura", "LIST", { mod = mod("Damage", "MORE", num) }, { type = "MultiplierThreshold", var = "NearbyCorpse", threshold = 1 }) } end,
- ["for each nearby corpse, you and nearby allies regenerate ([%d%.]+)%% of energy shield per second, up to ([%d%.]+)%% per second"] = function(num, _, limit) return { mod("ExtraAura", "LIST", { mod = mod("EnergyShieldRegenPercent", "BASE", num) }, { type = "Multiplier", var = "NearbyCorpse", limit = tonumber(limit), limitTotal = true }) } end,
- ["for each nearby corpse, you and nearby allies regenerate (%d+) mana per second, up to (%d+) per second"] = function(num, _, limit) return { mod("ExtraAura", "LIST", { mod = mod("ManaRegen", "BASE", num) }, { type = "Multiplier", var = "NearbyCorpse", limit = tonumber(limit), limitTotal = true }) } end,
- -- Occultist
- ["enemies you curse have malediction"] = { mod("AffectedByCurseMod", "LIST", { mod = mod("DamageTaken", "INC", 10) }) },
- ["nearby enemies have (%-%d+)%% to chaos resistance"] = function(num) return { mod("EnemyModifier", "LIST", { mod = mod("ChaosResist", "BASE", num) }) } end,
- ["nearby enemies have (%-%d+)%% to cold resistance"] = function(num) return { mod("EnemyModifier", "LIST", { mod = mod("ColdResist", "BASE", num) }) } end,
- ["when you kill an enemy, for each curse on that enemy, gain (%d+)%% of non%-chaos damage as extra chaos damage for 4 seconds"] = function(num) return {
- mod("NonChaosDamageGainAsChaos", "BASE", num, { type = "Condition", var = "KilledRecently" }, { type = "Multiplier", var = "CurseOnEnemy" }),
- } end,
- ["cannot be stunned while you have energy shield"] = { mod("AvoidStun", "BASE", 100, { type = "Condition", var = "HaveEnergyShield" }) },
- -- Pathfinder
- ["always poison on hit while using a flask"] = { mod("PoisonChance", "BASE", 100, { type = "Condition", var = "UsingFlask" }) },
- ["poisons you inflict during any flask effect have (%d+)%% chance to deal (%d+)%% more damage"] = function(num, _, more) return { mod("Damage", "MORE", tonumber(more) * num / 100, nil, 0, KeywordFlag.Poison, { type = "Condition", var = "UsingFlask" }) } end,
- -- Raider
- ["you have phasing while at maximum frenzy charges"] = { flag("Condition:Phasing", { type = "StatThreshold", stat = "FrenzyCharges", thresholdStat = "FrenzyChargesMax" }) },
- ["you have phasing during onslaught"] = { flag("Condition:Phasing", { type = "Condition", var = "Onslaught" }) },
- ["you have onslaught while on full frenzy charges"] = { flag("Condition:Onslaught", { type = "StatThreshold", stat = "FrenzyCharges", thresholdStat = "FrenzyChargesMax" }) },
- ["you have onslaught while at maximum endurance charges"] = { flag("Condition:Onslaught", { type = "StatThreshold", stat = "EnduranceCharges", thresholdStat = "EnduranceChargesMax" }) },
- -- Sabotuer
- -- Slayer
- -- Trickster
- ["(%d+)%% chance to gain (%d+)%% of non%-chaos damage with hits as extra chaos damage"] = function(num, _, perc) return { mod("NonChaosDamageGainAsChaos", "BASE", num / 100 * tonumber(perc)) } end,
- ["movement skills cost no mana"] = { mod("ManaCost", "MORE", -100, nil, 0, KeywordFlag.Movement) },
- -- Item local modifiers
- ["has no sockets"] = { flag("NoSockets") },
- ["has (%d+) sockets?"] = function(num) return { mod("SocketCount", "BASE", num) } end,
- ["has (%d+) abyssal sockets?"] = function(num) return { mod("AbyssalSocketCount", "BASE", num) } end,
- ["no physical damage"] = { mod("WeaponData", "LIST", { key = "PhysicalMin" }), mod("WeaponData", "LIST", { key = "PhysicalMax" }), mod("WeaponData", "LIST", { key = "PhysicalDPS" }) },
- ["all attacks with this weapon are critical strikes"] = { mod("WeaponData", "LIST", { key = "CritChance", value = 100 }) },
- ["counts as dual wielding"] = { mod("WeaponData", "LIST", { key = "countsAsDualWielding", value = true}) },
- ["counts as all one handed melee weapon types"] = { mod("WeaponData", "LIST", { key = "countsAsAll1H", value = true }) },
- ["no block chance"] = { mod("ArmourData", "LIST", { key = "BlockChance", value = 0 }) },
- ["hits can't be evaded"] = { flag("CannotBeEvaded", { type = "Condition", var = "{Hand}Attack" }) },
- ["causes bleeding on hit"] = { mod("BleedChance", "BASE", 100, { type = "Condition", var = "{Hand}Attack" }) },
- ["poisonous hit"] = { mod("PoisonChance", "BASE", 100, { type = "Condition", var = "{Hand}Attack" }) },
- ["attacks with this weapon deal double damage"] = { mod("Damage", "MORE", 100, nil, ModFlag.Hit, { type = "Condition", var = "{Hand}Attack" }) },
- ["attacks with this weapon deal double damage to chilled enemies"] = { mod("Damage", "MORE", 100, nil, ModFlag.Hit, { type = "Condition", var = "{Hand}Attack" }, { type = "ActorCondition", actor = "enemy", var = "Chilled" }) },
- ["life leech from hits with this weapon applies instantly"] = { flag("InstantLifeLeech", { type = "Condition", var = "{Hand}Attack" }) },
- ["gain life from leech instantly from hits with this weapon"] = { flag("InstantLifeLeech", { type = "Condition", var = "{Hand}Attack" }) },
- ["instant recovery"] = { mod("FlaskInstantRecovery", "BASE", 100) },
- ["(%d+)%% of recovery applied instantly"] = function(num) return { mod("FlaskInstantRecovery", "BASE", num) } end,
- ["has no attribute requirements"] = { flag("NoAttributeRequirements") },
- -- Socketed gem modifiers
- ["%+(%d+) to level of socketed gems"] = function(num) return { mod("GemProperty", "LIST", { keyword = "all", key = "level", value = num }, { type = "SocketedIn", slotName = "{SlotName}" }) } end,
- ["%+(%d+) to level of socketed ([%a ]+) gems"] = function(num, _, type) return { mod("GemProperty", "LIST", { keyword = type, key = "level", value = num }, { type = "SocketedIn", slotName = "{SlotName}" }) } end,
- ["%+(%d+)%% to quality of socketed ([%a ]+) gems"] = function(num, _, type) return { mod("GemProperty", "LIST", { keyword = type, key = "quality", value = num }, { type = "SocketedIn", slotName = "{SlotName}" }) } end,
- ["%+(%d+) to level of active socketed skill gems"] = function(num) return { mod("GemProperty", "LIST", { keyword = "active_skill", key = "level", value = num }, { type = "SocketedIn", slotName = "{SlotName}" }) } end,
- ["%+(%d+) to level of socketed active skill gems"] = function(num) return { mod("GemProperty", "LIST", { keyword = "active_skill", key = "level", value = num }, { type = "SocketedIn", slotName = "{SlotName}" }) } end,
- ["%+(%d+) to level of socketed active skill gems per (%d+) player levels"] = function(num, _, div) return { mod("GemProperty", "LIST", { keyword = "active_skill", key = "level", value = num }, { type = "SocketedIn", slotName = "{SlotName}" }, { type = "Multiplier", var = "Level", div = tonumber(div) }) } end,
- ["socketed gems fire an additional projectile"] = { mod("ExtraSkillMod", "LIST", { mod = mod("ProjectileCount", "BASE", 1) }, { type = "SocketedIn", slotName = "{SlotName}" }) },
- ["socketed gems fire (%d+) additional projectiles"] = function(num) return { mod("ExtraSkillMod", "LIST", { mod = mod("ProjectileCount", "BASE", num) }, { type = "SocketedIn", slotName = "{SlotName}" }) } end,
- ["socketed gems reserve no mana"] = { mod("ManaReserved", "MORE", -100, { type = "SocketedIn", slotName = "{SlotName}" }) },
- ["socketed skill gems get a (%d+)%% mana multiplier"] = function(num) return { mod("ExtraSkillMod", "LIST", { mod = mod("ManaCost", "MORE", num - 100) }, { type = "SocketedIn", slotName = "{SlotName}" }) } end,
- ["socketed gems have blood magic"] = { flag("SkillBloodMagic", { type = "SocketedIn", slotName = "{SlotName}" }) },
- ["socketed gems gain (%d+)%% of physical damage as extra lightning damage"] = function(num) return { mod("ExtraSkillMod", "LIST", { mod = mod("PhysicalDamageGainAsLightning", "BASE", num) }, { type = "SocketedIn", slotName = "{SlotName}" }) } end,
- ["socketed red gems get (%d+)%% physical damage as extra fire damage"] = function(num) return { mod("ExtraSkillMod", "LIST", { mod = mod("PhysicalDamageGainAsFire", "BASE", num) }, { type = "SocketedIn", slotName = "{SlotName}", keyword = "strength" }) } end,
- -- Global gem modifiers
- ["%+(%d+) to level of all minion skill gems"] = function(num) return { mod("GemProperty", "LIST", { keywordList = { "minion", "active_skill" }, key = "level", value = num }) } end,
- ["%+(%d+) to level of all spell skill gems"] = function(num) return { mod("GemProperty", "LIST", { keywordList = { "spell", "active_skill" }, key = "level", value = num }) } end,
- ["%+(%d+) to level of all physical spell skill gems"] = function(num) return { mod("GemProperty", "LIST", { keywordList = { "spell", "physical", "active_skill" }, key = "level", value = num }) } end,
- ["%+(%d+) to level of all lightning spell skill gems"] = function(num) return { mod("GemProperty", "LIST", { keywordList = { "spell", "lightning", "active_skill" }, key = "level", value = num }) } end,
- ["%+(%d+) to level of all cold spell skill gems"] = function(num) return { mod("GemProperty", "LIST", { keywordList = { "spell", "cold", "active_skill" }, key = "level", value = num }) } end,
- ["%+(%d+) to level of all fire spell skill gems"] = function(num) return { mod("GemProperty", "LIST", { keywordList = { "spell", "fire", "active_skill" }, key = "level", value = num }) } end,
- ["%+(%d+) to level of all chaos spell skill gems"] = function(num) return { mod("GemProperty", "LIST", { keywordList = { "spell", "chaos", "active_skill" }, key = "level", value = num }) } end,
- ["%+(%d+) to level of all (.+) gems"] = function(num, _, skill) return { mod("GemProperty", "LIST", {keyword = skill, key = "level", value = num }) } end,
- -- Extra skill/support
- ["grants (%D+)"] = function(_, skill) return extraSkill(skill, 1) end,
- ["grants level (%d+) (.+)"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["[ct][ar][si][tg]g?e?r?s? level (%d+) (.+) when equipped"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["[ct][ar][si][tg]g?e?r?s? level (%d+) (.+) on %a+"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["use level (%d+) (.+) on %a+"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["[ct][ar][si][tg]g?e?r?s? level (%d+) (.+) when you attack"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["[ct][ar][si][tg]g?e?r?s? level (%d+) (.+) when you deal a critical strike"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["[ct][ar][si][tg]g?e?r?s? level (%d+) (.+) when hit"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["[ct][ar][si][tg]g?e?r?s? level (%d+) (.+) when you kill an enemy"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["[ct][ar][si][tg]g?e?r?s? level (%d+) (.+) when you use a skill"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["trigger level (%d+) (.+) when you use a skill while you have a spirit charge"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["trigger level (%d+) (.+) when you hit an enemy while cursed"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["trigger level (%d+) (.+) when you kill a frozen enemy"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["trigger level (%d+) (.+) when you consume a corpse"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["trigger level (%d+) (.+) when you attack with a bow"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["%d+%% chance to attack with level (%d+) (.+) on melee hit"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["%d+%% chance to trigger level (%d+) (.+) on melee hit"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["%d+%% chance to trigger level (%d+) (.+) [ow][nh]e?n? ?y?o?u? kill ?a?n? ?e?n?e?m?y?"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["%d+%% chance to trigger level (%d+) (.+) when you use a socketed skill"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["%d+%% chance to trigger level (%d+) (.+) when you gain avian's might or avian's flight"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["%d+%% chance to [ct][ar][si][tg]g?e?r? level (%d+) (.+) on %a+"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["attack with level (%d+) (.+) when you kill a bleeding enemy"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["triggers? level (%d+) (.+) when you kill a bleeding enemy"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["curse enemies with (%D+) on %a+"] = function(_, skill) return extraSkill(skill, 1, true) end,
- ["curse enemies with level (%d+) (%D+) on %a+, which can apply to hexproof enemies"] = function(num, _, skill) return extraSkill(skill, num, true) end,
- ["curse enemies with level (%d+) (.+) on %a+"] = function(num, _, skill) return extraSkill(skill, num, true) end,
- ["[ct][ar][si][tg]g?e?r?s? (.+) on %a+"] = function(_, skill) return extraSkill(skill, 1, true) end,
- ["[at][tr][ti][ag][cg][ke]r? (.+) on %a+"] = function(_, skill) return extraSkill(skill, 1, true) end,
- ["[at][tr][ti][ag][cg][ke]r? with (.+) on %a+"] = function(_, skill) return extraSkill(skill, 1, true) end,
- ["[ct][ar][si][tg]g?e?r?s? (.+) when hit"] = function(_, skill) return extraSkill(skill, 1, true) end,
- ["[at][tr][ti][ag][cg][ke]r? (.+) when hit"] = function(_, skill) return extraSkill(skill, 1, true) end,
- ["[at][tr][ti][ag][cg][ke]r? with (.+) when hit"] = function(_, skill) return extraSkill(skill, 1, true) end,
- ["[ct][ar][si][tg]g?e?r?s? (.+) when your skills or minions kill"] = function(_, skill) return extraSkill(skill, 1, true) end,
- ["[at][tr][ti][ag][cg][ke]r? (.+) when you take a critical strike"] = function( _, skill) return extraSkill(skill, 1, true) end,
- ["[at][tr][ti][ag][cg][ke]r? with (.+) when you take a critical strike"] = function( _, skill) return extraSkill(skill, 1, true) end,
- ["trigger (.+) on critical strike"] = function( _, skill) return extraSkill(skill, 1, true) end,
- ["triggers? (.+) when you take a critical strike"] = function( _, skill) return extraSkill(skill, 1, true) end,
- ["socketed [%a+]* ?gems a?r?e? ?supported by level (%d+) (.+)"] = function(num, _, support) return { mod("ExtraSupport", "LIST", { skillId = gemIdLookup[support] or gemIdLookup[support:gsub("^increased ","")] or "Unknown", level = num }, { type = "SocketedIn", slotName = "{SlotName}" }) } end,
- ["trigger level (%d+) (.+) every (%d+) seconds"] = function(num, _, skill) return extraSkill(skill, num) end,
- ["trigger level (%d+) (.+), (.+) or (.+) every (%d+) seconds"] = function(num, _, skill1, skill2, skill3) return {
- mod("ExtraSkill", "LIST", { skillId = gemIdLookup[skill1], level = num }),
- mod("ExtraSkill", "LIST", { skillId = gemIdLookup[skill2], level = num }),
- mod("ExtraSkill", "LIST", { skillId = gemIdLookup[skill3], level = num })
- } end,
- ["offering skills triggered this way also affect you"] = { mod("ExtraSkillMod", "LIST", { mod = mod("SkillData", "LIST", { key = "buffNotPlayer", value = false }) }, { type = "SkillName", skillNameList = { "Bone Offering", "Flesh Offering", "Spirit Offering" } }, { type = "SocketedIn", slotName = "{SlotName}" }) },
- ["trigger level (%d+) (.+) after spending a total of (%d+) mana"] = function(num, _, skill) return extraSkill(skill, num) end,
- -- Conversion
- ["increases and reductions to minion damage also affects? you"] = { flag("MinionDamageAppliesToPlayer") },
- ["increases and reductions to minion attack speed also affects? you"] = { flag("MinionAttackSpeedAppliesToPlayer") },
- ["increases and reductions to spell damage also apply to attacks"] = { flag("SpellDamageAppliesToAttacks") },
- ["increases and reductions to spell damage also apply to attacks at 150%% of their value"] = { flag("SpellDamageAppliesToAttacksAt150Percent") },
- ["increases and reductions to spell damage also apply to attacks while wielding a wand"] = { flag("SpellDamageAppliesToAttacks", { type = "Condition", var = "UsingWand" }) },
- ["modifiers to claw damage also apply to unarmed"] = { flag("ClawDamageAppliesToUnarmed") },
- ["modifiers to claw damage also apply to unarmed attack damage"] = { flag("ClawDamageAppliesToUnarmed") },
- ["modifiers to claw attack speed also apply to unarmed"] = { flag("ClawAttackSpeedAppliesToUnarmed") },
- ["modifiers to claw attack speed also apply to unarmed attack speed"] = { flag("ClawAttackSpeedAppliesToUnarmed") },
- ["modifiers to claw critical strike chance also apply to unarmed"] = { flag("ClawCritChanceAppliesToUnarmed") },
- ["modifiers to claw critical strike chance also apply to unarmed attack critical strike chance"] = { flag("ClawCritChanceAppliesToUnarmed") },
- ["increases and reductions to light radius also apply to accuracy"] = { flag("LightRadiusAppliesToAccuracy") },
- ["increases and reductions to light radius also apply to area of effect at 50%% of their value"] = { flag("LightRadiusAppliesToAreaOfEffect") },
- ["increases and reductions to light radius also apply to damage"] = { flag("LightRadiusAppliesToDamage") },
- ["increases and reductions to cast speed also apply to trap throwing speed"] = { flag("CastSpeedAppliesToTrapThrowingSpeed") },
- ["gain (%d+)%% of bow physical damage as extra damage of each element"] = function(num) return {
- mod("PhysicalDamageGainAsLightning", "BASE", num, nil, ModFlag.Bow),
- mod("PhysicalDamageGainAsCold", "BASE", num, nil, ModFlag.Bow),
- mod("PhysicalDamageGainAsFire", "BASE", num, nil, ModFlag.Bow)
- } end,
- ["gain (%d+)%% of weapon physical damage as extra damage of each element"] = function(num) return {
- mod("PhysicalDamageGainAsLightning", "BASE", num, nil, ModFlag.Weapon),
- mod("PhysicalDamageGainAsCold", "BASE", num, nil, ModFlag.Weapon),
- mod("PhysicalDamageGainAsFire", "BASE", num, nil, ModFlag.Weapon)
- } end,
- -- Crit
- ["your critical strike chance is lucky"] = { flag("CritChanceLucky") },
- ["your critical strike chance is lucky while focussed"] = { flag("CritChanceLucky", { type = "Condition", var = "Focused"}) },
- ["your critical strikes do not deal extra damage"] = { flag("NoCritMultiplier") },
- ["critical strikes deal no damage"] = { mod("Damage", "MORE", -100, { type = "Condition", var = "CriticalStrike" }) },
- ["critical strike chance is increased by uncapped lightning resistance"] = { mod("CritChance", "INC", 1, { type = "PerStat", stat = "LightningResistTotal", div = 1 }) },
- ["critical strike chance is increased by lightning resistance"] = { mod("CritChance", "INC", 1, { type = "PerStat", stat = "LightningResist", div = 1 }) },
- ["non%-critical strikes deal (%d+)%% damage"] = function(num) return { mod("Damage", "MORE", -100+num, nil, ModFlag.Hit, { type = "Condition", var = "CriticalStrike", neg = true }) } end,
- -- Generic Ailments
- ["enemies take (%d+)%% increased damage for each type of ailment you have inflicted on them"] = function(num) return {
- mod("EnemyModifier", "LIST", { mod = mod("DamageTaken", "INC", num) }, { type = "ActorCondition", actor = "enemy", var = "Frozen"}),
- mod("EnemyModifier", "LIST", { mod = mod("DamageTaken", "INC", num) }, { type = "ActorCondition", actor = "enemy", var = "Chilled"}),
- mod("EnemyModifier", "LIST", { mod = mod("DamageTaken", "INC", num) }, { type = "ActorCondition", actor = "enemy", var = "Ignited"}),
- mod("EnemyModifier", "LIST", { mod = mod("DamageTaken", "INC", num) }, { type = "ActorCondition", actor = "enemy", var = "Shocked"}),
- mod("EnemyModifier", "LIST", { mod = mod("DamageTaken", "INC", num) }, { type = "ActorCondition", actor = "enemy", var = "Bleeding"}),
- mod("EnemyModifier", "LIST", { mod = mod("DamageTaken", "INC", num) }, { type = "ActorCondition", actor = "enemy", var = "Poisoned"})
- } end,
- -- Elemental Ailments
- ["your elemental damage can shock"] = { flag("ColdCanShock"), flag("FireCanShock") },
- ["your cold damage can ignite"] = { flag("ColdCanIgnite") },
- ["your lightning damage can ignite"] = { flag("LightningCanIgnite") },
- ["your fire damage can shock but not ignite"] = { flag("FireCanShock"), flag("FireCannotIgnite") },
- ["your cold damage can ignite but not freeze or chill"] = { flag("ColdCanIgnite"), flag("ColdCannotFreeze"), flag("ColdCannotChill") },
- ["your lightning damage can freeze but not shock"] = { flag("LightningCanFreeze"), flag("LightningCannotShock") },
- ["your chaos damage can shock"] = { flag("ChaosCanShock") },
- ["chaos damage can ignite, chill and shock"] = { flag("ChaosCanIgnite"), flag("ChaosCanChill"), flag("ChaosCanShock") },
- ["your physical damage can chill"] = { flag("PhysicalCanChill") },
- ["your physical damage can shock"] = { flag("PhysicalCanShock") },
- ["you always ignite while burning"] = { mod("EnemyIgniteChance", "BASE", 100, { type = "Condition", var = "Burning" }) },
- ["critical strikes do not always freeze"] = { flag("CritsDontAlwaysFreeze") },
- ["you can inflict up to (%d+) ignites on an enemy"] = { flag("IgniteCanStack") },
- ["enemies chilled by you take (%d+)%% increased burning damage"] = function(num) return { mod("EnemyModifier", "LIST", { mod = mod("FireDamageTakenOverTime", "INC", num) }, { type = "ActorCondition", actor = "enemy", var = "Chilled" }) } end,
- ["ignited enemies burn (%d+)%% faster"] = function(num) return { mod("IgniteBurnFaster", "INC", num) } end,
- ["ignited enemies burn (%d+)%% slower"] = function(num) return { mod("IgniteBurnSlower", "INC", num) } end,
- ["enemies ignited by an attack burn (%d+)%% faster"] = function(num) return { mod("IgniteBurnFaster", "INC", num, nil, ModFlag.Attack) } end,
- ["ignites you inflict with attacks deal damage (%d+)%% faster"] = function(num) return { mod("IgniteBurnFaster", "INC", num, nil, ModFlag.Attack) } end,
- ["enemies ignited by you during flask effect take (%d+)%% increased damage"] = function(num) return { mod("EnemyModifier", "LIST", { mod = mod("DamageTaken", "INC", num) }, { type = "ActorCondition", actor = "enemy", var = "Ignited" }) } end,
- ["cannot inflict ignite"] = { flag("CannotIgnite") },
- ["cannot inflict freeze or chill"] = { flag("CannotFreeze"), flag("CannotChill") },
- ["cannot inflict shock"] = { flag("CannotShock") },
- -- Bleed
- ["melee attacks cause bleeding"] = { mod("BleedChance", "BASE", 100, nil, ModFlag.Melee) },
- ["attacks cause bleeding when hitting cursed enemies"] = { mod("BleedChance", "BASE", 100, nil, ModFlag.Attack, { type = "ActorCondition", actor = "enemy", var = "Cursed" }) },
- ["melee critical strikes cause bleeding"] = { mod("BleedChance", "BASE", 100, nil, ModFlag.Melee, { type = "Condition", var = "CriticalStrike" }) },
- ["causes bleeding on melee critical strike"] = { mod("BleedChance", "BASE", 100, nil, ModFlag.Melee, { type = "Condition", var = "CriticalStrike" }) },
- ["melee critical strikes have (%d+)%% chance to cause bleeding"] = function(num) return { mod("BleedChance", "BASE", num, nil, ModFlag.Melee, { type = "Condition", var = "CriticalStrike" }) } end,
- ["attacks always inflict bleeding while you have cat's stealth"] = { mod("BleedChance", "BASE", 100, nil, ModFlag.Attack, { type = "Condition", var = "AffectedByCat'sStealth" }) },
- ["you have crimson dance while you have cat's stealth"] = { mod("Keystone", "LIST", "Crimson Dance", { type = "Condition", var = "AffectedByCat'sStealth" }) },
- ["you have crimson dance if you have dealt a critical strike recently"] = { mod("Keystone", "LIST", "Crimson Dance", { type = "Condition", var = "CritRecently" }) },
- ["bleeding you inflict deals damage (%d+)%% faster"] = function(num) return { mod("BleedFaster", "INC", num) } end,
- -- Poison
- ["y?o?u?r? ?fire damage can poison"] = { flag("FireCanPoison") },
- ["y?o?u?r? ?cold damage can poison"] = { flag("ColdCanPoison") },
- ["y?o?u?r? ?lightning damage can poison"] = { flag("LightningCanPoison") },
- ["your chaos damage poisons enemies"] = { mod("ChaosPoisonChance", "BASE", 100) },
- ["your chaos damage has (%d+)%% chance to poison enemies"] = function(num) return { mod("ChaosPoisonChance", "BASE", num) } end,
- ["melee attacks poison on hit"] = { mod("PoisonChance", "BASE", 100, nil, ModFlag.Melee) },
- ["melee critical strikes have (%d+)%% chance to poison the enemy"] = function(num) return { mod("PoisonChance", "BASE", num, nil, ModFlag.Melee, { type = "Condition", var = "CriticalStrike" }) } end,
- ["critical strikes with daggers have a (%d+)%% chance to poison the enemy"] = function(num) return { mod("PoisonChance", "BASE", num, nil, ModFlag.Dagger, { type = "Condition", var = "CriticalStrike" }) } end,
- ["poison cursed enemies on hit"] = { mod("PoisonChance", "BASE", 100, { type = "ActorCondition", actor = "enemy", var = "Cursed" }) },
- ["wh[ie][ln]e? at maximum frenzy charges, attacks poison enemies"] = { mod("PoisonChance", "BASE", 100, nil, ModFlag.Attack, { type = "StatThreshold", stat = "FrenzyCharges", thresholdStat = "FrenzyChargesMax" }) },
- ["traps and mines have a (%d+)%% chance to poison on hit"] = function(num) return { mod("PoisonChance", "BASE", num, nil, 0, bor(KeywordFlag.Trap, KeywordFlag.Mine)) } end,
- ["poisons you inflict deal damage (%d+)%% faster"] = function(num) return { mod("PoisonFaster", "INC", num) } end,
- -- Buffs/debuffs
- ["phasing"] = { flag("Condition:Phasing") },
- ["onslaught"] = { flag("Condition:Onslaught") },
- ["you have phasing if you've killed recently"] = { flag("Condition:Phasing", { type = "Condition", var = "KilledRecently" }) },
- ["you have phasing while affected by haste"] = { flag("Condition:Phasing", { type = "Condition", var = "AffectedByHaste" }) },
- ["you have phasing while you have cat's stealth"] = { flag("Condition:Phasing", { type = "Condition", var = "AffectedByCat'sStealth" }) },
- ["you have onslaught while on low life"] = { flag("Condition:Onslaught", { type = "Condition", var = "LowLife" }) },
- ["you have onslaught while not on low mana"] = { flag("Condition:Onslaught", { type = "Condition", var = "LowMana", neg = true }) },
- ["your aura buffs do not affect allies"] = { flag("SelfAurasCannotAffectAllies") },
- ["allies' aura buffs do not affect you"] = { flag("AlliesAurasCannotAffectSelf") },
- ["enemies can have 1 additional curse"] = { mod("EnemyCurseLimit", "BASE", 1) },
- ["you can apply an additional curse"] = { mod("EnemyCurseLimit", "BASE", 1) },
- ["nearby enemies have (%d+)%% increased effect of curses on them"] = function(num) return { mod("EnemyModifier", "LIST", { mod = mod("CurseEffectOnSelf", "INC", num) }) } end,
- ["nearby enemies have an additional (%d+)%% chance to receive a critical strike"] = function(num) return { mod("EnemyModifier", "LIST", { mod = mod("SelfExtraCritChance", "BASE", num) }) } end,
- ["nearby enemies have (%-%d+)%% to all resistances"] = function(num) return {
- mod("EnemyModifier", "LIST", { mod = mod("ElementalResist", "BASE", num) }),
- mod("EnemyModifier", "LIST", { mod = mod("ChaosResist", "BASE", num) })
- } end,
- ["your hits inflict decay, dealing (%d+) chaos damage per second for %d+ seconds"] = function(num) return { mod("SkillData", "LIST", { key = "decay", value = num, merge = "MAX" }) } end,
- ["temporal chains has (%d+)%% reduced effect on you"] = function(num) return { mod("CurseEffectOnSelf", "INC", -num, { type = "SkillName", skillName = "Temporal Chains" }) } end,
- ["unaffected by temporal chains"] = { mod("CurseEffectOnSelf", "MORE", -100, { type = "SkillName", skillName = "Temporal Chains" }) },
- ["([%+%-]%d+) seconds to cat's stealth duration"] = function(num) return { mod("PrimaryDuration", "BASE", num, { type = "SkillName", skillName = "Aspect of the Cat" }) } end,
- ["([%+%-]%d+) seconds to avian's might duration"] = function(num) return { mod("PrimaryDuration", "BASE", num, { type = "SkillName", skillName = "Aspect of the Avian" }) } end,
- ["([%+%-]%d+) seconds to avian's flight duration"] = function(num) return { mod("SecondaryDuration", "BASE", num, { type = "SkillName", skillName = "Aspect of the Avian" }) } end,
- ["aspect of the spider can inflict spider's web on enemies an additional time"] = { mod("ExtraSkillMod", "LIST", { mod = mod("Multiplier:SpiderWebApplyStackMax", "BASE", 1) }, { type = "SkillName", skillName = "Aspect of the Spider" }) },
- ["enemies affected by your spider's webs have (%-%d+)%% to all resistances"] = function(num) return {
- mod("EnemyModifier", "LIST", { mod = mod("ElementalResist", "BASE", num, { type = "MultiplierThreshold", var = "Spider's WebStack", threshold = 1 }) }),
- mod("EnemyModifier", "LIST", { mod = mod("ChaosResist", "BASE", num, { type = "MultiplierThreshold", var = "Spider's WebStack", threshold = 1 }) }),
- } end,
- ["you are cursed with level (%d+) (%D+)"] = function(num, _, name) return { mod("ExtraCurse", "LIST", { skillId = gemIdLookup[name], level = num, applyToPlayer = true }) } end,
- ["you count as on low life while you are cursed with vulnerability"] = { flag("Condition:LowLife", { type = "Condition", var = "AffectedByVulnerability" }) },
- ["if you consumed a corpse recently, you and nearby allies regenerate (%d+)%% of life per second"] = function (num) return { mod("ExtraAura", "LIST", { mod = mod("LifeRegenPercent", "BASE", num) }, { type = "Condition", var = "ConsumedCorpseRecently" }) } end,
- ["if you have blocked recently, you and nearby allies regenerate (%d+)%% of life per second"] = function (num) return { mod("ExtraAura", "LIST", { mod = mod("LifeRegenPercent", "BASE", num) }, { type = "Condition", var = "BlockedRecently" }) } end,
- ["(%d+)%% of evasion rating is regenerated as life per second while focussed"] = function(num) return { mod("LifeRegen", "BASE", num / 100, { type = "PerStat", stat = "Evasion"}, { type = "Condition", var = "Focused" }) } end,
- ["nearby allies have (%d+)%% increased defences per (%d+) strength you have"] = function(num, _, div) return { mod("ExtraAura", "LIST", { onlyAllies = true, mod = mod("Defences", "INC", num) }, { type = "PerStat", stat = "Str", div = tonumber(div) }) } end,
- ["nearby allies have %+(%d+)%% to critical strike multiplier per (%d+) dexterity you have"] = function(num, _, div) return { mod("ExtraAura", "LIST", { onlyAllies = true, mod = mod("CritMultiplier", "BASE", num) }, { type = "PerStat", stat = "Dex", div = tonumber(div) }) } end,
- ["nearby allies have (%d+)%% increased cast speed per (%d+) intelligence you have"] = function(num, _, div) return { mod("ExtraAura", "LIST", { onlyAllies = true, mod = mod("Speed", "INC", num, nil, ModFlag.Cast ) }, { type = "PerStat", stat = "Int", div = tonumber(div) }) } end,
- ["you gain divinity for %d+ seconds on reaching maximum divine charges"] = {
- mod("ElementalDamage", "MORE", 50, { type = "Condition", var = "Divinity" }),
- mod("ElementalDamageTaken", "MORE", -20, { type = "Condition", var = "Divinity" }),
- },
- ["your maximum endurance charges is equal to your maximum frenzy charges"] = { flag("MaximumEnduranceChargesIsMaximumFrenzyCharges") },
- ["if you've warcried recently, you and nearby allies have (%d+)%% increased attack, cast and movement speed"] = function(num) return {
- mod("ExtraAura", "LIST", { mod = mod("Speed", "INC", num) }, { type = "Condition", var = "UsedWarcryRecently" }),
- mod("ExtraAura", "LIST", { mod = mod("MovementSpeed", "INC", num) }, { type = "Condition", var = "UsedWarcryRecently" }),
- } end,
- ["enemies you curse take (%d+)%% increased damage"] = function(num) return { mod("AffectedByCurseMod", "LIST", { mod = mod("DamageTaken", "INC", num) }) } end,
- -- Traps, Mines and Totems
- ["traps and mines deal (%d+)%-(%d+) additional physical damage"] = function(_, min, max) return { mod("PhysicalMin", "BASE", tonumber(min), nil, 0, bor(KeywordFlag.Trap, KeywordFlag.Mine)), mod("PhysicalMax", "BASE", tonumber(max), nil, 0, bor(KeywordFlag.Trap, KeywordFlag.Mine)) } end,
- ["traps and mines deal (%d+) to (%d+) additional physical damage"] = function(_, min, max) return { mod("PhysicalMin", "BASE", tonumber(min), nil, 0, bor(KeywordFlag.Trap, KeywordFlag.Mine)), mod("PhysicalMax", "BASE", tonumber(max), nil, 0, bor(KeywordFlag.Trap, KeywordFlag.Mine)) } end,
- ["can have up to (%d+) additional traps? placed at a time"] = function(num) return { mod("ActiveTrapLimit", "BASE", num) } end,
- ["can have up to (%d+) additional remote mines? placed at a time"] = function(num) return { mod("ActiveMineLimit", "BASE", num) } end,
- ["can have up to (%d+) additional totems? summoned at a time"] = function(num) return { mod("ActiveTotemLimit", "BASE", num) } end,
- ["attack skills can have (%d+) additional totems? summoned at a time"] = function(num) return { mod("ActiveTotemLimit", "BASE", num, nil, 0, KeywordFlag.Attack) } end,
- ["can [hs][au][vm][em]o?n? 1 additional siege ballista totem per (%d+) dexterity"] = function(num) return { mod("ActiveTotemLimit", "BASE", 1, { type = "SkillName", skillName = "Siege Ballista" }, { type = "PerStat", stat = "Dex", div = num }) } end,
- ["totems fire (%d+) additional projectiles"] = function(num) return { mod("ProjectileCount", "BASE", num, nil, 0, KeywordFlag.Totem) } end,
- ["([%d%.]+)%% of damage dealt by y?o?u?r? ?totems is leeched to you as life"] = function(num) return { mod("DamageLifeLeechToPlayer", "BASE", num, nil, 0, KeywordFlag.Totem) } end,
- -- Minions
- ["your strength is added to your minions"] = { flag("HalfStrengthAddedToMinions") },
- ["half of your strength is added to your minions"] = { flag("HalfStrengthAddedToMinions") },
- ["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,
- ["(%d+)%% increased minion damage if you've used a minion skill recently"] = function(num) return { mod("MinionModifier", "LIST", { mod = mod("Damage", "INC", num) }, { type = "Condition", var = "UsedMinionSkillRecently" }) } end,
- ["(%d+)%% increased minion attack speed per (%d+) dexterity"] = function(num, _, div) return { mod("MinionModifier", "LIST", { mod = mod("Speed", "INC", num, nil, ModFlag.Attack) }, { type = "PerStat", stat = "Dex", div = tonumber(div) }) } end,
- ["(%d+)%% increased minion movement speed per (%d+) dexterity"] = function(num, _, div) return { mod("MinionModifier", "LIST", { mod = mod("MovementSpeed", "INC", num) }, { type = "PerStat", stat = "Dex", div = tonumber(div) }) } end,
- ["minions deal (%d+)%% increased damage per (%d+) dexterity"] = function(num, _, div) return { mod("MinionModifier", "LIST", { mod = mod("Damage", "INC", num) }, { type = "PerStat", stat = "Dex", div = tonumber(div) }) } end,
- ["(%d+)%% increased golem damage for each type of golem you have summoned"] = function(num) return {
- mod("MinionModifier", "LIST", { mod = mod("Damage", "INC", num, { type = "ActorCondition", actor = "parent", var = "HavePhysicalGolem" }) }, { type = "SkillType", skillType = SkillType.Golem }),
- mod("MinionModifier", "LIST", { mod = mod("Damage", "INC", num, { type = "ActorCondition", actor = "parent", var = "HaveLightningGolem" }) }, { type = "SkillType", skillType = SkillType.Golem }),
- mod("MinionModifier", "LIST", { mod = mod("Damage", "INC", num, { type = "ActorCondition", actor = "parent", var = "HaveColdGolem" }) }, { type = "SkillType", skillType = SkillType.Golem }),
- mod("MinionModifier", "LIST", { mod = mod("Damage", "INC", num, { type = "ActorCondition", actor = "parent", var = "HaveFireGolem" }) }, { type = "SkillType", skillType = SkillType.Golem }),
- mod("MinionModifier", "LIST", { mod = mod("Damage", "INC", num, { type = "ActorCondition", actor = "parent", var = "HaveChaosGolem" }) }, { type = "SkillType", skillType = SkillType.Golem }),
- } end,
- ["can summon up to (%d) additional golems? at a time"] = function(num) return { mod("ActiveGolemLimit", "BASE", num) } end,
- ["if you have 3 primordial jewels, can summon up to (%d) additional golems? at a time"] = function(num) return { mod("ActiveGolemLimit", "BASE", num, { type = "MultiplierThreshold", var = "PrimordialItem", threshold = 3 }) } end,
- ["golems regenerate (%d)%% of their maximum life per second"] = function(num) return { mod("MinionModifier", "LIST", { mod = mod("LifeRegenPercent", "BASE", num) }, { type = "SkillType", skillType = SkillType.Golem }) } end,
- ["raging spirits' hits always ignite"] = { mod("MinionModifier", "LIST", { mod = mod("EnemyIgniteChance", "BASE", 100) }, { type = "SkillName", skillName = "Summon Raging Spirit" }) },
- ["summoned skeletons have avatar of fire"] = { mod("MinionModifier", "LIST", { mod = mod("Keystone", "LIST", "Avatar of Fire") }, { type = "SkillName", skillName = "Summon Skeleton" }) },
- ["summoned skeletons take ([%d%.]+)%% of their maximum life per second as fire damage"] = function(num) return { mod("MinionModifier", "LIST", { mod = mod("FireDegen", "BASE", num/100, { type = "PerStat", stat = "Life", div = 1 }) }, { type = "SkillName", skillName = "Summon Skeleton" }) } end,
- -- Projectiles
- ["skills chain %+(%d) times"] = function(num) return { mod("ChainCountMax", "BASE", num) } end,
- ["skills chain an additional time while at maximum frenzy charges"] = { mod("ChainCountMax", "BASE", 1, { type = "StatThreshold", stat = "FrenzyCharges", thresholdStat = "FrenzyChargesMax" }) },
- ["attacks chain an additional time when in main hand"] = { mod("ChainCountMax", "BASE", 1, nil, ModFlag.Attack, { type = "SlotNumber", num = 1 }) },
- ["adds an additional arrow"] = { mod("ProjectileCount", "BASE", 1, nil, ModFlag.Attack) },
- ["(%d+) additional arrows"] = function(num) return { mod("ProjectileCount", "BASE", num, nil, ModFlag.Attack) } end,
- ["bow attacks fire an additional arrow"] = { mod("ProjectileCount", "BASE", 1, nil, ModFlag.Bow) },
- ["bow attacks fire (%d+) additional arrows"] = function(num) return { mod("ProjectileCount", "BASE", num, nil, ModFlag.Bow) } end,
- ["skills fire an additional projectile"] = { mod("ProjectileCount", "BASE", 1) },
- ["spells have an additional projectile"] = { mod("ProjectileCount", "BASE", 1, nil, ModFlag.Spell) },
- ["attacks have an additional projectile when in off hand"] = { mod("ProjectileCount", "BASE", 1, nil, ModFlag.Attack, { type = "SlotNumber", num = 2 }) },
- ["projectiles pierce an additional target"] = { mod("PierceCount", "BASE", 1) },
- ["projectiles pierce (%d+) targets?"] = function(num) return { mod("PierceCount", "BASE", num) } end,
- ["projectiles pierce (%d+) additional targets?"] = function(num) return { mod("PierceCount", "BASE", num) } end,
- ["projectiles pierce (%d+) additional targets while you have phasing"] = function(num) return { mod("PierceCount", "BASE", num, { type = "Condition", var = "Phasing" }) } end,
- ["arrows pierce an additional target"] = { mod("PierceCount", "BASE", 1, nil, ModFlag.Attack) },
- ["arrows pierce one target"] = { mod("PierceCount", "BASE", 1, nil, ModFlag.Attack) },
- ["arrows pierce (%d+) targets?"] = function(num) return { mod("PierceCount", "BASE", num, nil, ModFlag.Attack) } end,
- ["always pierce with arrows"] = { flag("PierceAllTargets", nil, ModFlag.Attack) },
- ["arrows always pierce"] = { flag("PierceAllTargets", nil, ModFlag.Attack) },
- ["arrows pierce all targets"] = { flag("PierceAllTargets", nil, ModFlag.Attack) },
- ["arrows that pierce cause bleeding"] = { mod("BleedChance", "BASE", 100, nil, bor(ModFlag.Attack, ModFlag.Projectile), { type = "StatThreshold", stat = "PierceCount", threshold = 1 }) },
- ["arrows that pierce have (%d+)%% chance to cause bleeding"] = function(num) return { mod("BleedChance", "BASE", num, nil, bor(ModFlag.Attack, ModFlag.Projectile), { type = "StatThreshold", stat = "PierceCount", threshold = 1 }) } end,
- ["arrows that pierce deal (%d+)%% increased damage"] = function(num) return { mod("Damage", "INC", num, nil, bor(ModFlag.Attack, ModFlag.Projectile), { type = "StatThreshold", stat = "PierceCount", threshold = 1 }) } end,
- ["projectiles gain (%d+)%% of non%-chaos damage as extra chaos damage per chain"] = function(num) return { mod("NonChaosDamageGainAsChaos", "BASE", num, nil, ModFlag.Projectile, { type = "PerStat", stat = "Chain" }) } end,
- ["left ring slot: projectiles from spells cannot chain"] = { flag("CannotChain", nil, bor(ModFlag.Spell, ModFlag.Projectile), { type = "SlotNumber", num = 1 }) },
- ["right ring slot: projectiles from spells chain %+1 times"] = { mod("ChainCountMax", "BASE", 1, nil, bor(ModFlag.Spell, ModFlag.Projectile), { type = "SlotNumber", num = 2 }) },
- ["projectiles from spells cannot pierce"] = { flag("CannotPierce", nil, ModFlag.Spell) },
- -- Leech/Gain on Hit
- ["cannot leech life"] = { flag("CannotLeechLife") },
- ["cannot leech mana"] = { flag("CannotLeechMana") },
- ["cannot leech when on low life"] = { flag("CannotLeechLife", { type = "Condition", var = "LowLife" }), flag("CannotLeechMana", { type = "Condition", var = "LowLife" }) },
- ["cannot leech life from critical strikes"] = { flag("CannotLeechLife", { type = "Condition", var = "CriticalStrike" }) },
- ["leech applies instantly on critical strike"] = { flag("InstantLifeLeech", { type = "Condition", var = "CriticalStrike" }), flag("InstantManaLeech", { type = "Condition", var = "CriticalStrike" }) },
- ["gain life and mana from leech instantly on critical strike"] = { flag("InstantLifeLeech", { type = "Condition", var = "CriticalStrike" }), flag("InstantManaLeech", { type = "Condition", var = "CriticalStrike" }) },
- ["leech applies instantly during flask effect"] = { flag("InstantLifeLeech", { type = "Condition", var = "UsingFlask" }), flag("InstantManaLeech", { type = "Condition", var = "UsingFlask" }) },
- ["gain life and mana from leech instantly during flask effect"] = { flag("InstantLifeLeech", { type = "Condition", var = "UsingFlask" }), flag("InstantManaLeech", { type = "Condition", var = "UsingFlask" }) },
- ["with 5 corrupted items equipped: life leech recovers based on your chaos damage instead"] = { flag("LifeLeechBasedOnChaosDamage", { type = "MultiplierThreshold", var = "CorruptedItem", threshold = 5 }) },
- ["you have vaal pact if you've dealt a critical strike recently"] = { mod("Keystone", "LIST", "Vaal Pact", { type = "Condition", var = "CritRecently" }) },
- ["gain (%d+) energy shield for each enemy you hit which is affected by a spider's web"] = function(num) return { mod("EnergyShieldOnHit", "BASE", num, { type = "MultiplierThreshold", actor = "enemy", var = "Spider's WebStack", threshold = 1 }) } end,
- ["(%d+) life gained for each enemy hit if you have used a vaal skill recently"] = function(num) return { mod("LifeOnHit", "BASE", num, { type = "Condition", var = "UsedVaalSkillRecently"}) } end,
- -- Defences
- ["cannot evade enemy attacks"] = { flag("CannotEvade") },
- ["cannot block"] = { flag("CannotBlockAttacks"), flag("CannotBlockSpells") },
- ["cannot block attacks"] = { flag("CannotBlockAttacks") },
- ["cannot block spells"] = { flag("CannotBlockSpells") },
- ["you have no life regeneration"] = { flag("NoLifeRegen") },
- ["you have no armour or energy shield"] = {
- mod("Armour", "MORE", -100),
- mod("EnergyShield", "MORE", -100),
- },
- ["elemental resistances are zero"] = {
- mod("FireResist", "OVERRIDE", 0),
- mod("ColdResist", "OVERRIDE", 0),
- mod("LightningResist", "OVERRIDE", 0)
- },
- ["your maximum resistances are (%d+)%%"] = function(num) return {
- mod("FireResistMax", "OVERRIDE", num),
- mod("ColdResistMax", "OVERRIDE", num),
- mod("LightningResistMax", "OVERRIDE", num),
- mod("ChaosResistMax", "OVERRIDE", num)
- } end,
- ["your fire resistance is (%d+)%%"] = function(num) return { mod("FireResist", "OVERRIDE", num) } end,
- ["your cold resistance is (%d+)%%"] = function(num) return { mod("ColdResist", "OVERRIDE", num) } end,
- ["your lightning resistance is (%d+)%%"] = function(num) return { mod("LightningResist", "OVERRIDE", num) } end,
- ["chaos resistance is doubled"] = { mod("ChaosResist", "MORE", 100) },
- ["armour is increased by uncapped fire resistance"] = { mod("Armour", "INC", 1, { type = "PerStat", stat = "FireResistTotal", div = 1 }) },
- ["evasion rating is increased by uncapped cold resistance"] = { mod("Evasion", "INC", 1, { type = "PerStat", stat = "ColdResistTotal", div = 1 }) },
- ["reflects (%d+) physical damage to melee attackers"] = { },
- ["ignore all movement penalties from armour"] = { flag("Condition:IgnoreMovementPenalties") },
- ["gain armour equal to your reserved mana"] = { mod("Armour", "BASE", 1, { type = "PerStat", stat = "ManaReserved", div = 1 }) },
- ["cannot be stunned"] = { mod("AvoidStun", "BASE", 100) },
- ["cannot be stunned if you haven't been hit recently"] = { mod("AvoidStun", "BASE", 100, { type = "Condition", var = "BeenHitRecently", neg = true }) },
- ["cannot be stunned if you have at least (%d+) crab barriers"] = function(num) return { mod("AvoidStun", "BASE", 100, { type = "StatThreshold", stat = "CrabBarriers", threshold = num }) } end,
- ["cannot be shocked"] = { mod("AvoidShock", "BASE", 100) },
- ["immune to shock"] = { mod("AvoidShock", "BASE", 100) },
- ["cannot be frozen"] = { mod("AvoidFreeze", "BASE", 100) },
- ["immune to freeze"] = { mod("AvoidFreeze", "BASE", 100) },
- ["cannot be chilled"] = { mod("AvoidChill", "BASE", 100) },
- ["immune to chill"] = { mod("AvoidChill", "BASE", 100) },
- ["cannot be ignited"] = { mod("AvoidIgnite", "BASE", 100) },
- ["immune to ignite"] = { mod("AvoidIgnite", "BASE", 100) },
- ["you cannot be shocked while at maximum endurance charges"] = { mod("AvoidShock", "BASE", 100, { type = "StatThreshold", stat = "EnduranceCharges", thresholdStat = "EnduranceChargesMax" }) },
- ["cannot be shocked if intelligence is higher than strength"] = { mod("AvoidShock", "BASE", 100, { type = "Condition", var = "IntHigherThanStr" }) },
- ["cannot be frozen if dexterity is higher than intelligence"] = { mod("AvoidFreeze", "BASE", 100, { type = "Condition", var = "DexHigherThanInt" }) },
- ["cannot be ignited if strength is higher than dexterity"] = { mod("AvoidIgnite", "BASE", 100, { type = "Condition", var = "StrHigherThanDex" }) },
- ["cannot be inflicted with bleeding"] = { mod("AvoidBleed", "BASE", 100) },
- ["you are immune to bleeding"] = { mod("AvoidBleed", "BASE", 100) },
- ["immunity to shock during flask effect"] = { mod("AvoidShock", "BASE", 100, { type = "Condition", var = "UsingFlask" }) },
- ["immunity to freeze and chill during flask effect"] = {
- mod("AvoidFreeze", "BASE", 100, { type = "Condition", var = "UsingFlask" }),
- mod("AvoidChill", "BASE", 100, { type = "Condition", var = "UsingFlask" })
- },
- ["immunity to ignite during flask effect"] = { mod("AvoidIgnite", "BASE", 100, { type = "Condition", var = "UsingFlask" }) },
- ["immunity to bleeding during flask effect"] = { mod("AvoidBleed", "BASE", 100, { type = "Condition", var = "UsingFlask" }) },
- ["immune to poison during flask effect"] = { mod("AvoidPoison", "BASE", 100, { type = "Condition", var = "UsingFlask" }) },
- ["immune to curses during flask effect"] = { mod("AvoidCurse", "BASE", 100, { type = "Condition", var = "UsingFlask" }) },
- ["immune to freeze, chill, curses and stuns during flask effect"] = {
- mod("AvoidFreeze", "BASE", 100, { type = "Condition", var = "UsingFlask" }),
- mod("AvoidChill", "BASE", 100, { type = "Condition", var = "UsingFlask" }),
- mod("AvoidCurse", "BASE", 100, { type = "Condition", var = "UsingFlask" }),
- mod("AvoidStun", "BASE", 100, { type = "Condition", var = "UsingFlask" }),
- },
- ["unaffected by curses"] = { mod("CurseEffectOnSelf", "MORE", -100) },
- ["the effect of chill on you is reversed"] = { flag("SelfChillEffectIsReversed") },
- -- Knockback
- ["cannot knock enemies back"] = { flag("CannotKnockback") },
- ["knocks back enemies if you get a critical strike with a staff"] = { mod("EnemyKnockbackChance", "BASE", 100, nil, ModFlag.Staff, { type = "Condition", var = "CriticalStrike" }) },
- ["knocks back enemies if you get a critical strike with a bow"] = { mod("EnemyKnockbackChance", "BASE", 100, nil, ModFlag.Bow, { type = "Condition", var = "CriticalStrike" }) },
- ["bow knockback at close range"] = { mod("EnemyKnockbackChance", "BASE", 100, nil, ModFlag.Bow, { type = "Condition", var = "AtCloseRange" }) },
- ["adds knockback during flask effect"] = { mod("EnemyKnockbackChance", "BASE", 100, { type = "Condition", var = "UsingFlask" }) },
- ["adds knockback to melee attacks during flask effect"] = { mod("EnemyKnockbackChance", "BASE", 100, nil, ModFlag.Melee, { type = "Condition", var = "UsingFlask" }) },
- -- Flasks
- ["flasks do not apply to you"] = { flag("FlasksDoNotApplyToPlayer") },
- ["flasks apply to your zombies and spectres"] = { flag("FlasksApplyToMinion", { type = "SkillName", skillNameList = { "Raise Zombie", "Raise Spectre" } }) },
- ["creates a smoke cloud on use"] = { },
- ["creates chilled ground on use"] = { },
- ["creates consecrated ground on use"] = { },
- ["gain unholy might during flask effect"] = { flag("Condition:UnholyMight", { type = "Condition", var = "UsingFlask" }) },
- ["zealot's oath during flask effect"] = { mod("ZealotsOath", "FLAG", true, { type = "Condition", var = "UsingFlask" }) },
- ["grants level (%d+) (.+) curse aura during flask effect"] = function(num, _, skill) return { mod("ExtraCurse", "LIST", { skillId = gemIdLookup[skill:gsub(" skill","")] or "Unknown", level = num }, { type = "Condition", var = "UsingFlask" }) } end,
- ["during flask effect, (%d+)%% reduced damage taken of each element for which your uncapped elemental resistance is lowest"] = function(num) return {
- mod("LightningDamageTaken", "INC", -num, { type = "StatThreshold", stat = "LightningResistTotal", thresholdStat = "ColdResistTotal", upper = true }, { type = "StatThreshold", stat = "LightningResistTotal", thresholdStat = "FireResistTotal", upper = true }),
- mod("ColdDamageTaken", "INC", -num, { type = "StatThreshold", stat = "ColdResistTotal", thresholdStat = "LightningResistTotal", upper = true }, { type = "StatThreshold", stat = "ColdResistTotal", thresholdStat = "FireResistTotal", upper = true }),
- mod("FireDamageTaken", "INC", -num, { type = "StatThreshold", stat = "FireResistTotal", thresholdStat = "LightningResistTotal", upper = true }, { type = "StatThreshold", stat = "FireResistTotal", thresholdStat = "ColdResistTotal", upper = true }),
- } end,
- ["during flask effect, damage penetrates (%d+)%% o?f? ?resistance of each element for which your uncapped elemental resistance is highest"] = function(num) return {
- mod("LightningPenetration", "BASE", num, { type = "StatThreshold", stat = "LightningResistTotal", thresholdStat = "ColdResistTotal" }, { type = "StatThreshold", stat = "LightningResistTotal", thresholdStat = "FireResistTotal" }),
- mod("ColdPenetration", "BASE", num, { type = "StatThreshold", stat = "ColdResistTotal", thresholdStat = "LightningResistTotal" }, { type = "StatThreshold", stat = "ColdResistTotal", thresholdStat = "FireResistTotal" }),
- mod("FirePenetration", "BASE", num, { type = "StatThreshold", stat = "FireResistTotal", thresholdStat = "LightningResistTotal" }, { type = "StatThreshold", stat = "FireResistTotal", thresholdStat = "ColdResistTotal" }),
- } end,
- ["(%d+)%% of maximum life taken as chaos damage per second"] = function(num) return { mod("ChaosDegen", "BASE", num/100, { type = "PerStat", stat = "Life", div = 1 }) } end,
- ["your critical strikes do not deal extra damage during flask effect"] = { flag("NoCritMultiplier", { type = "Condition", var = "UsingFlask" }) },
- ["grants perfect agony during flask effect"] = { mod("Keystone", "LIST", "Perfect Agony", { type = "Condition", var = "UsingFlask" }) },
- ["consecrated ground created during effect applies (%d+)%% increased damage taken to enemies"] = function(num) return { mod("EnemyModifier", "LIST", { mod = mod("DamageTaken", "INC", num, { type = "Condition", var = "OnConsecratedGround" }) }, { type = "Condition", var = "UsingFlask" }) } end,
- -- Jewels
- ["passives in radius can be allocated without being connected to your tree"] = { mod("JewelData", "LIST", { key = "intuitiveLeap", value = true }) },
- ["(%d+)%% increased elemental damage per grand spectrum"] = function(num) return {
- mod("ElementalDamage", "INC", num, { type = "Multiplier", var = "GrandSpectrum" }),
- mod("Multiplier:GrandSpectrum", "BASE", 1)
- } end,
- ["gain (%d+) armour per grand spectrum"] = function(num) return {
- mod("Armour", "BASE", num, { type = "Multiplier", var = "GrandSpectrum" }),
- mod("Multiplier:GrandSpectrum", "BASE", 1)
- } end,
- ["gain (%d+) mana per grand spectrum"] = function(num) return {
- mod("Mana", "BASE", num, { type = "Multiplier", var = "GrandSpectrum" }),
- mod("Multiplier:GrandSpectrum", "BASE", 1)
- } end,
- ["primordial"] = { mod("Multiplier:PrimordialItem", "BASE", 1) },
- ["spectres have a base duration of (%d+) seconds"] = function(num) return { mod("SkillData", "LIST", { key = "duration", value = 6 }, { type = "SkillName", skillName = "Raise Spectre" }) } end,
- ["flasks applied to you have (%d+)%% increased effect"] = function(num) return { mod("FlaskEffect", "INC", num) } end,
- ["adds (%d+) passive skills"] = function(num) return { mod("JewelData", "LIST", { key = "clusterJewelNodeCount", value = num }) } end,
- ["1 added passive skill is a jewel socket"] = { mod("JewelData", "LIST", { key = "clusterJewelSocketCount", value = 1 }) },
- ["(%d+) added passive skills are jewel sockets"] = function(num) return { mod("JewelData", "LIST", { key = "clusterJewelSocketCount", value = num }) } end,
- ["adds (%d+) jewel socket passive skills"] = function(num) return { mod("JewelData", "LIST", { key = "clusterJewelSocketCountOverride", value = num }) } end,
- ["adds (%d+) small passive skills? which grants? nothing"] = function(num) return { mod("JewelData", "LIST", { key = "clusterJewelNothingnessCount", value = num }) } end,
- ["added small passive skills grant nothing"] = { mod("JewelData", "LIST", { key = "clusterJewelSmallsAreNothingness", value = true }) },
- ["added small passive skills have (%d+)%% increased effect"] = function(num) return { mod("JewelData", "LIST", { key = "clusterJewelIncEffect", value = num }) } end,
- -- Misc
- ["iron will"] = { flag("IronWill") },
- ["iron reflexes while stationary"] = { mod("Keystone", "LIST", "Iron Reflexes", { type = "Condition", var = "Stationary" }) },
- ["you have zealot's oath if you haven't been hit recently"] = { mod("Keystone", "LIST", "Zealot's Oath", { type = "Condition", var = "BeenHitRecently", neg = true }) },
- ["deal no physical damage"] = { flag("DealNoPhysical") },
- ["deal no elemental damage"] = { flag("DealNoLightning"), flag("DealNoCold"), flag("DealNoFire") },
- ["deal no non%-elemental damage"] = { flag("DealNoPhysical"), flag("DealNoChaos") },
- ["deal no chaos damage"] = { flag("DealNoChaos") },
- ["attacks have blood magic"] = { flag("SkillBloodMagic", nil, ModFlag.Attack) },
- ["(%d+)%% chance to cast a? ?socketed lightning spells? on hit"] = function(num) return { mod("ExtraSupport", "LIST", { name = "SupportUniqueMjolnerLightningSpellsCastOnHit", level = 1 }, { type = "SocketedIn", slotName = "{SlotName}" }) } end,
- ["cast a socketed lightning spell on hit"] = { mod("ExtraSupport", "LIST", { name = "SupportUniqueMjolnerLightningSpellsCastOnHit", level = 1 }, { type = "SocketedIn", slotName = "{SlotName}" }) },
- ["trigger a socketed lightning spell on hit"] = { mod("ExtraSupport", "LIST", { name = "SupportUniqueMjolnerLightningSpellsCastOnHit", level = 1 }, { type = "SocketedIn", slotName = "{SlotName}" }) },
- ["cast a socketed cold s[pk][ei]ll on melee critical strike"] = { mod("ExtraSupport", "LIST", { name = "SupportUniqueCosprisMaliceColdSpellsCastOnMeleeCriticalStrike", level = 1 }, { type = "SocketedIn", slotName = "{SlotName}" }) },
- ["your curses can apply to hexproof enemies"] = { flag("CursesIgnoreHexproof") },
- ["you have onslaught while you have fortify"] = { flag("Condition:Onslaught", { type = "Condition", var = "Fortify" }) },
- ["reserves (%d+)%% of life"] = function(num) return { mod("ExtraLifeReserved", "BASE", num) } end,
- ["items and gems have (%d+)%% reduced attribute requirements"] = function(num) return { mod("GlobalAttributeRequirements", "INC", -num) } end,
- ["items and gems have (%d+)%% increased attribute requirements"] = function(num) return { mod("GlobalAttributeRequirements", "INC", num) } end,
- ["mana reservation of herald skills is always (%d+)%%"] = function(num) return { mod("SkillData", "LIST", { key = "manaCostForced", value = num }, { type = "SkillType", skillType = SkillType.Herald }) } end,
- ["([%a%s]+) reserves no mana"] = function(_, name) return { mod("SkillData", "LIST", { key = "manaCostForced", value = 0 }, { type = "SkillId", skillId = gemIdLookup[name] }) } end,
- ["banner skills reserve no mana"] = { mod("SkillData", "LIST", { key = "manaCostForced", value = 0 }, { type = "SkillName", skillNameList = { "Dread Banner", "War Banner" } }) },
- ["your spells are disabled"] = { flag("DisableSkill", { type = "SkillType", skillType = SkillType.Spell }) },
- ["strength's damage bonus instead grants (%d+)%% increased melee physical damage per (%d+) strength"] = function(num, _, perStr) return { mod("StrDmgBonusRatioOverride", "BASE", num / tonumber(perStr)) } end,
- ["while in her embrace, take ([%d%.]+)%% of your total maximum life and energy shield as fire damage per second per level"] = function(num) return {
- mod("FireDegen", "BASE", 0.005, { type = "PerStat", stat = "Life" }, { type = "Multiplier", var = "Level" }, { type = "Condition", var = "HerEmbrace" }),
- mod("FireDegen", "BASE", 0.005, { type = "PerStat", stat = "EnergyShield" }, { type = "Multiplier", var = "Level" }, { type = "Condition", var = "HerEmbrace" }),
- } end,
- ["gain her embrace for %d+ seconds when you ignite an enemy"] = { flag("Condition:CanGainHerEmbrace") },
- ["hits ignore enemy monster fire resistance while you are ignited"] = { flag("IgnoreFireResistance", { type = "Condition", var = "Ignited" }) },
- ["your hits can't be evaded by blinded enemies"] = { flag("CannotBeEvaded", { type = "ActorCondition", actor = "enemy", var = "Blinded" }) },
- ["skills which throw traps have blood magic"] = { flag("BloodMagic", { type = "SkillType", skillType = SkillType.Trap }) },
- ["lose ([%d%.]+) mana per second"] = function(num) return { mod("ManaDegen", "BASE", num) } end,
- ["lose ([%d%.]+)%% of maximum mana per second"] = function(num) return { mod("ManaDegen", "BASE", num/100, { type = "PerStat", stat = "Mana" }) } end,
- ["strength provides no bonus to maximum life"] = { flag("NoStrBonusToLife") },
- ["intelligence provides no bonus to maximum mana"] = { flag("NoIntBonusToMana") },
- ["with a ghastly eye jewel socketed, minions have %+(%d+) to accuracy rating"] = function(num) return { mod("MinionModifier", "LIST", { mod = mod("Accuracy", "BASE", num) }, { type = "Condition", var = "HaveGhastlyEyeJewelIn{SlotName}" }) } end,
- ["hits ignore enemy monster chaos resistance if all equipped items are shaper items"] = { flag("IgnoreChaosResistance", { type = "MultiplierThreshold", var = "NonShaperItem", upper = true, threshold = 0 }) },
- ["base critical strike chance for attacks with weapons is (%d)%%"] = function(num) return { mod("WeaponBaseCritChance", "OVERRIDE", num) } end,
- ["gain %d+ rage on critical hit with attacks, no more than once every [%d%.]+ seconds"] = {
- flag("Condition:CanGainRage"),
- mod("Dummy", "DUMMY", 1, { type = "Condition", var = "CanGainRage" }) -- Make the Configuration option appear
- },
- ["warcry skills' cooldown time is (%d+) seconds"] = function(num) return { mod("CooldownRecovery", "OVERRIDE", num, nil, 0, KeywordFlag.Warcry) } end,
- ["your critical strike multiplier is (%d+)%%"] = function(num) return { mod("CritMultiplier", "OVERRIDE", num) } end,
- ["base critical strike chance for attacks with weapons is ([%d%.]+)%%"] = function(num) return { mod("WeaponBaseCritChance", "OVERRIDE", num) } end,
- ["allocates (.+)"] = function(_, passive) return { mod("GrantedPassive", "LIST", passive) } end,
- ["transfiguration of body"] = { flag("TransfigurationOfBody") },
- ["transfiguration of mind"] = { flag("TransfigurationOfMind") },
- ["transfiguration of soul"] = { flag("TransfigurationOfSoul") },
- ["enemies have %-(%d+)%% to total physical damage reduction against your hits"] = function(num) return {
- mod("EnemyPhysicalDamageReduction", "BASE", -num)
- } end,
- -- Skill-specific enchantment modifiers
- ["(%d+)%% increased decoy totem life"] = function(num) return { mod("TotemLife", "INC", num, { type = "SkillName", skillName = "Decoy Totem" }) } end,
- ["(%d+)%% increased ice spear critical strike chance in second form"] = function(num) return { mod("CritChance", "INC", num, { type = "SkillName", skillName = "Ice Spear" }, { type = "SkillPart", skillPart = 2 }) } end,
- ["shock nova ring deals (%d+)%% increased damage"] = function(num) return { mod("Damage", "INC", num, { type = "SkillName", skillName = "Shock Nova" }, { type = "SkillPart", skillPart = 1 }) } end,
- ["lightning strike pierces (%d) additional targets?"] = function(num) return { mod("PierceCount", "BASE", num, { type = "SkillName", skillName = "Lightning Strike" }) } end,
- ["lightning trap pierces (%d) additional targets?"] = function(num) return { mod("PierceCount", "BASE", num, { type = "SkillName", skillName = "Lightning Trap" }) } end,
- ["enemies affected by bear trap take (%d+)%% increased damage from trap or mine hits"] = function(num) return { mod("ExtraSkillMod", "LIST", { mod = mod("TrapMineDamageTaken", "INC", num, { type = "GlobalEffect", effectType = "Debuff" }) }, { type = "SkillName", skillName = "Bear Trap" }) } end,
- ["blade vortex has %+(%d+)%% to critical strike multiplier for each blade"] = function(num) return { mod("CritMultiplier", "BASE", num, { type = "Multiplier", var = "BladeVortexBlade" }, { type = "SkillName", skillName = "Blade Vortex" }) } end,
- ["double strike has a (%d+)%% chance to deal double damage to bleeding enemies"] = function(num) return { mod("DoubleDamageChance", "BASE", num, { type = "ActorCondition", actor = "enemy", var = "Bleeding" }, { type = "SkillName", skillName = "Double Strike" }) } end,
- ["ethereal knives pierces an additional target"] = { mod("PierceCount", "BASE", 1, { type = "SkillName", skillName = "Ethereal Knives" }) },
- ["frost bomb has (%d+)%% increased debuff duration"] = function(num) return { mod("SecondaryDuration", "INC", num, { type = "SkillName", skillName = "Frost Bomb" }) } end,
- ["incinerate has %+(%d+) to maximum stages"] = function(num) return {
- mod("Multiplier:IncinerateStage", "BASE", num/2, 0, 0, { type = "SkillPart", skillPart = 2 }),
- mod("Multiplier:IncinerateStage", "BASE", num, 0, 0, { type = "SkillPart", skillPart = 3 })
- } end,
- ["scourge arrow has (%d+)%% chance to poison per stage"] = function(num) return { mod("PoisonChance", "BASE", num, { type = "SkillName", skillName = "Scourge Arrow" }, { type = "Multiplier", var = "ScourgeArrowStage" }) } end,
- ["winter orb has %+(%d+) maximum stages"] = function(num) return { mod("Multiplier:WinterOrbMaxStage", "BASE", num) } end,
- ["winter orb has (%d+)%% increased area of effect per stage"] = function(num) return { mod("AreaOfEffect", "INC", num, { type = "SkillName", skillName = "Winter Orb"}, { type = "Multiplier", var = "WinterOrbStage" }) } end,
- ["wave of conviction's exposure applies (%-%d+)%% elemental resistance"] = function(num) return { mod("ExtraSkillStat", "LIST", { key = "purge_expose_resist_%_matching_highest_element_damage", value = num }, { type = "SkillName", skillName = "Wave of Conviction" }) } end,
- ["arcane cloak spends an additional (%d+)%% of current mana"] = function(num) return { mod("ExtraSkillStat", "LIST", { key = "arcane_cloak_consume_%_of_mana", value = num }, { type = "SkillName", skillName = "Arcane Cloak" }) } end,
- -- Display-only modifiers
- ["prefixes:"] = { },
- ["suffixes:"] = { },
- ["while your passive skill tree connects to a class' starting location, you gain:"] = { },
- ["socketed lightning spells [hd][ae][va][el] (%d+)%% increased spell damage if triggered"] = { },
- ["manifeste?d? dancing dervish disables both weapon slots"] = { },
- ["manifeste?d? dancing dervish dies when rampage ends"] = { },
-}
-local keystoneList = {
- -- List of keystones that can be found on uniques
- "Acrobatics",
- "Ancestral Bond",
- "Arrow Dancing",
- "Avatar of Fire",
- "Blood Magic",
- "Conduit",
- "Crimson Dance",
- "Eldritch Battery",
- "Elemental Equilibrium",
- "Elemental Overload",
- "Ghost Reaver",
- "Iron Grip",
- "Iron Reflexes",
- "Mind Over Matter",
- "Minion Instability",
- "Mortal Conviction",
- "Pain Attunement",
- "Perfect Agony",
- "Phase Acrobatics",
- "Point Blank",
- "Resolute Technique",
- "Unwavering Stance",
- "Vaal Pact",
- "Zealot's Oath",
-}
-for _, name in pairs(keystoneList) do
- specialModList[name:lower()] = { mod("Keystone", "LIST", name) }
-end
-local oldList = specialModList
-specialModList = { }
-for k, v in pairs(oldList) do
- specialModList["^"..k.."$"] = v
-end
-
--- Modifiers that are recognised but unsupported
-local unsupportedModList = {
- ["culling strike"] = true,
- ["properties are doubled while in a breach"] = true,
-}
-
--- Special lookups used for various modifier forms
-local suffixTypes = {
- ["as extra lightning damage"] = "GainAsLightning",
- ["added as lightning damage"] = "GainAsLightning",
- ["gained as extra lightning damage"] = "GainAsLightning",
- ["as extra cold damage"] = "GainAsCold",
- ["added as cold damage"] = "GainAsCold",
- ["gained as extra cold damage"] = "GainAsCold",
- ["as extra fire damage"] = "GainAsFire",
- ["added as fire damage"] = "GainAsFire",
- ["gained as extra fire damage"] = "GainAsFire",
- ["as extra chaos damage"] = "GainAsChaos",
- ["added as chaos damage"] = "GainAsChaos",
- ["gained as extra chaos damage"] = "GainAsChaos",
- ["converted to lightning"] = "ConvertToLightning",
- ["converted to lightning damage"] = "ConvertToLightning",
- ["converted to cold damage"] = "ConvertToCold",
- ["converted to fire damage"] = "ConvertToFire",
- ["converted to chaos damage"] = "ConvertToChaos",
- ["added as energy shield"] = "GainAsEnergyShield",
- ["as extra maximum energy shield"] = "GainAsEnergyShield",
- ["converted to energy shield"] = "ConvertToEnergyShield",
- ["as physical damage"] = "AsPhysical",
- ["as lightning damage"] = "AsLightning",
- ["as cold damage"] = "AsCold",
- ["as fire damage"] = "AsFire",
- ["as chaos damage"] = "AsChaos",
- ["leeched as life and mana"] = "Leech",
- ["leeched as life"] = "LifeLeech",
- ["is leeched as life"] = "LifeLeech",
- ["leeched as mana"] = "ManaLeech",
- ["is leeched as mana"] = "ManaLeech",
- ["leeched as energy shield"] = "EnergyShieldLeech",
- ["is leeched as energy shield"] = "EnergyShieldLeech",
-}
-local dmgTypes = {
- ["physical"] = "Physical",
- ["lightning"] = "Lightning",
- ["cold"] = "Cold",
- ["fire"] = "Fire",
- ["chaos"] = "Chaos",
-}
-local penTypes = {
- ["lightning resistance"] = "LightningPenetration",
- ["cold resistance"] = "ColdPenetration",
- ["fire resistance"] = "FirePenetration",
- ["elemental resistance"] = "ElementalPenetration",
- ["elemental resistances"] = "ElementalPenetration",
- ["chaos resistance"] = "ChaosPenetration",
-}
-local regenTypes = {
- ["life"] = "LifeRegen",
- ["maximum life"] = "LifeRegen",
- ["life and mana"] = { "LifeRegen", "ManaRegen" },
- ["mana"] = "ManaRegen",
- ["maximum mana"] = "ManaRegen",
- ["energy shield"] = "EnergyShieldRegen",
- ["maximum energy shield"] = "EnergyShieldRegen",
- ["maximum mana and energy shield"] = { "ManaRegen", "EnergyShieldRegen" },
-}
-
--- Build active skill name lookup
-local skillNameList = {
- [" corpse cremation " ] = { tag = { type = "SkillName", skillName = "Cremation" } }, -- Sigh.
-}
-local preSkillNameList = { }
-for gemId, gemData in pairs(data["3_0"].gems) do
- local grantedEffect = gemData.grantedEffect
- if not grantedEffect.hidden and not grantedEffect.support then
- local skillName = grantedEffect.name
- skillNameList[" "..skillName:lower().." "] = { tag = { type = "SkillName", skillName = skillName } }
- preSkillNameList["^"..skillName:lower().." "] = { tag = { type = "SkillName", skillName = skillName } }
- preSkillNameList["^"..skillName:lower().." has ?a? "] = { tag = { type = "SkillName", skillName = skillName } }
- preSkillNameList["^"..skillName:lower().." deals "] = { tag = { type = "SkillName", skillName = skillName } }
- preSkillNameList["^"..skillName:lower().." damage "] = { tag = { type = "SkillName", skillName = skillName } }
- if gemData.tags.totem then
- preSkillNameList["^"..skillName:lower().." totem deals "] = { tag = { type = "SkillName", skillName = skillName } }
- preSkillNameList["^"..skillName:lower().." totem grants "] = { addToSkill = { type = "SkillName", skillName = skillName }, tag = { type = "GlobalEffect", effectType = "Buff" } }
- end
- if grantedEffect.skillTypes[SkillType.Buff] or grantedEffect.baseFlags.buff then
- preSkillNameList["^"..skillName:lower().." grants "] = { addToSkill = { type = "SkillName", skillName = skillName }, tag = { type = "GlobalEffect", effectType = "Buff" } }
- preSkillNameList["^"..skillName:lower().." grants a?n? ?additional "] = { addToSkill = { type = "SkillName", skillName = skillName }, tag = { type = "GlobalEffect", effectType = "Buff" } }
- end
- if gemData.tags.aura or gemData.tags.herald then
- skillNameList["while affected by "..skillName:lower()] = { tag = { type = "Condition", var = "AffectedBy"..skillName:gsub(" ","") } }
- end
- if gemData.tags.mine then
- specialModList["^"..skillName:lower().." has (%d+)%% increased throwing speed"] = function(num) return { mod("ExtraSkillMod", "LIST", { mod = mod("MineLayingSpeed", "INC", num) }, { type = "SkillName", skillName = skillName }) } end
- end
- if gemData.tags.chaining then
- specialModList["^"..skillName:lower().." chains an additional time"] = { mod("ExtraSkillMod", "LIST", { mod = mod("ChainCountMax", "BASE", 1) }, { type = "SkillName", skillName = skillName }) }
- specialModList["^"..skillName:lower().." chains an additional (%d+) times"] = function(num) return { mod("ExtraSkillMod", "LIST", { mod = mod("ChainCountMax", "BASE", num) }, { type = "SkillName", skillName = skillName }) } end
- specialModList["^"..skillName:lower().." chains (%d+) additional times"] = function(num) return { mod("ExtraSkillMod", "LIST", { mod = mod("ChainCountMax", "BASE", num) }, { type = "SkillName", skillName = skillName }) } end
- end
- if gemData.tags.bow then
- specialModList["^"..skillName:lower().." fires (%d+) additional arrows?"] = function(num) return { mod("ExtraSkillMod", "LIST", { mod = mod("ProjectileCount", "BASE", num) }, { type = "SkillName", skillName = skillName }) } end
- end
- if gemData.tags.bow or gemData.tags.projectile then
- specialModList["^"..skillName:lower().." fires an additional projectile"] = { mod("ExtraSkillMod", "LIST", { mod = mod("ProjectileCount", "BASE", 1) }, { type = "SkillName", skillName = skillName }) }
- specialModList["^"..skillName:lower().." fires (%d+) additional projectiles"] = function(num) return { mod("ExtraSkillMod", "LIST", { mod = mod("ProjectileCount", "BASE", num) }, { type = "SkillName", skillName = skillName }) } end
- end
- end
-end
-
--- Radius jewels that modify other nodes
-local function getSimpleConv(srcList, dst, type, remove, factor)
- return function(node, out, data)
- if node then
- for _, src in pairs(srcList) do
- for _, mod in ipairs(node.modList) do
- if mod.name == src and mod.type == type then
- if remove then
- out:MergeNewMod(src, type, -mod.value, mod.source, mod.flags, mod.keywordFlags, unpack(mod))
- end
- if factor then
- out:MergeNewMod(dst, type, math.floor(mod.value * factor), mod.source, mod.flags, mod.keywordFlags, unpack(mod))
- else
- out:MergeNewMod(dst, type, mod.value, mod.source, mod.flags, mod.keywordFlags, unpack(mod))
- end
- end
- end
- end
- end
- end
-end
-local jewelOtherFuncs = {
- ["Strength from Passives in Radius is Transformed to Dexterity"] = getSimpleConv({"Str"}, "Dex", "BASE", true),
- ["Dexterity from Passives in Radius is Transformed to Strength"] = getSimpleConv({"Dex"}, "Str", "BASE", true),
- ["Strength from Passives in Radius is Transformed to Intelligence"] = getSimpleConv({"Str"}, "Int", "BASE", true),
- ["Intelligence from Passives in Radius is Transformed to Strength"] = getSimpleConv({"Int"}, "Str", "BASE", true),
- ["Dexterity from Passives in Radius is Transformed to Intelligence"] = getSimpleConv({"Dex"}, "Int", "BASE", true),
- ["Intelligence from Passives in Radius is Transformed to Dexterity"] = getSimpleConv({"Int"}, "Dex", "BASE", true),
- ["Increases and Reductions to Life in Radius are Transformed to apply to Energy Shield"] = getSimpleConv({"Life"}, "EnergyShield", "INC", true),
- ["Increases and Reductions to Energy Shield in Radius are Transformed to apply to Armour at 200% of their value"] = getSimpleConv({"EnergyShield"}, "Armour", "INC", true, 2),
- ["Increases and Reductions to Life in Radius are Transformed to apply to Mana at 200% of their value"] = getSimpleConv({"Life"}, "Mana", "INC", true, 2),
- ["Increases and Reductions to Physical Damage in Radius are Transformed to apply to Cold Damage"] = getSimpleConv({"PhysicalDamage"}, "ColdDamage", "INC", true),
- ["Increases and Reductions to Cold Damage in Radius are Transformed to apply to Physical Damage"] = getSimpleConv({"ColdDamage"}, "PhysicalDamage", "INC", true),
- ["Increases and Reductions to other Damage Types in Radius are Transformed to apply to Fire Damage"] = getSimpleConv({"PhysicalDamage","ColdDamage","LightningDamage","ChaosDamage"}, "FireDamage", "INC", true),
- ["Passives granting Lightning Resistance or all Elemental Resistances in Radius also grant Chance to Block Spells at 35% of its value"] = getSimpleConv({"LightningResist","ElementalResist"}, "SpellBlockChance", "BASE", false, 0.35),
- ["Passives granting Cold Resistance or all Elemental Resistances in Radius also grant Chance to Dodge Attacks at 35% of its value"] = getSimpleConv({"ColdResist","ElementalResist"}, "AttackDodgeChance", "BASE", false, 0.35),
- ["Passives granting Fire Resistance or all Elemental Resistances in Radius also grant Chance to Block at 35% of its value"] = getSimpleConv({"FireResist","ElementalResist"}, "BlockChance", "BASE", false, 0.35),
- ["Melee and Melee Weapon Type modifiers in Radius are Transformed to Bow Modifiers"] = function(node, out, data)
- if node then
- local mask1 = bor(ModFlag.Axe, ModFlag.Claw, ModFlag.Dagger, ModFlag.Mace, ModFlag.Staff, ModFlag.Sword, ModFlag.Melee)
- local mask2 = bor(ModFlag.Weapon1H, ModFlag.WeaponMelee)
- local mask3 = bor(ModFlag.Weapon2H, ModFlag.WeaponMelee)
- for _, mod in ipairs(node.modList) do
- if band(mod.flags, mask1) ~= 0 or band(mod.flags, mask2) == mask2 or band(mod.flags, mask3) == mask3 then
- out:MergeNewMod(mod.name, mod.type, -mod.value, mod.source, mod.flags, mod.keywordFlags, unpack(mod))
- out:MergeNewMod(mod.name, mod.type, mod.value, mod.source, bor(band(mod.flags, bnot(bor(mask1, mask2, mask3))), ModFlag.Bow), mod.keywordFlags, unpack(mod))
- elseif mod[1] then
- local using = { UsingAxe = true, UsingClaw = true, UsingDagger = true, UsingMace = true, UsingStaff = true, UsingSword = true, UsingMeleeWeapon = true }
- for _, tag in ipairs(mod) do
- if tag.type == "Condition" and using[tag.var] then
- local newTagList = copyTable(mod)
- for _, tag in ipairs(newTagList) do
- if tag.type == "Condition" and using[tag.var] then
- tag.var = "UsingBow"
- break
- end
- end
- out:MergeNewMod(mod.name, mod.type, -mod.value, mod.source, mod.flags, mod.keywordFlags, unpack(mod))
- out:MergeNewMod(mod.name, mod.type, mod.value, mod.source, mod.flags, mod.keywordFlags, unpack(newTagList))
- break
- end
- end
- end
- end
- end
- end,
- ["50% increased Effect of non-Keystone Passive Skills in Radius"] = function(node, out, data)
- if node and node.type ~= "Keystone" then
- out:NewMod("PassiveSkillEffect", "INC", 50, data.modSource)
- end
- end,
- ["Notable Passive Skills in Radius grant nothing"] = function(node, out, data)
- if node and node.type == "Notable" then
- out:NewMod("PassiveSkillHasNoEffect", "FLAG", true, data.modSource)
- end
- end,
- ["Allocated Small Passive Skills in Radius grant nothing"] = function(node, out, data)
- if node and node.type == "Normal" then
- out:NewMod("AllocatedPassiveSkillHasNoEffect", "FLAG", true, data.modSource)
- end
- end,
- ["Passive Skills in Radius also grant: Traps and Mines deal (%d+) to (%d+) added Physical Damage"] = function(min, max)
- return function(node, out, data)
- if node and node.type ~= "Keystone" then
- out:NewMod("PhysicalMin", "BASE", min, data.modSource, 0, bor(KeywordFlag.Trap, KeywordFlag.Mine))
- out:NewMod("PhysicalMax", "BASE", max, data.modSource, 0, bor(KeywordFlag.Trap, KeywordFlag.Mine))
- end
- end
- end,
-}
-
--- Radius jewels that modify the jewel itself based on nearby allocated nodes
-local function getPerStat(dst, modType, flags, stat, factor)
- return function(node, out, data)
- if node then
- data[stat] = (data[stat] or 0) + out:Sum("BASE", nil, stat)
- elseif data[stat] ~= 0 then
- out:NewMod(dst, modType, math.floor((data[stat] or 0) * factor + 0.5), data.modSource, flags)
- end
- end
-end
-local jewelSelfFuncs = {
- ["Adds 1 to maximum Life per 3 Intelligence in Radius"] = getPerStat("Life", "BASE", 0, "Int", 1 / 3),
- ["Adds 1 to Maximum Life per 3 Intelligence Allocated in Radius"] = getPerStat("Life", "BASE", 0, "Int", 1 / 3),
- ["1% increased Evasion Rating per 3 Dexterity Allocated in Radius"] = getPerStat("Evasion", "INC", 0, "Dex", 1 / 3),
- ["1% increased Claw Physical Damage per 3 Dexterity Allocated in Radius"] = getPerStat("PhysicalDamage", "INC", ModFlag.Claw, "Dex", 1 / 3),
- ["1% increased Melee Physical Damage while Unarmed per 3 Dexterity Allocated in Radius"] = getPerStat("PhysicalDamage", "INC", ModFlag.Unarmed, "Dex", 1 / 3),
- ["3% increased Totem Life per 10 Strength in Radius"] = getPerStat("TotemLife", "INC", 0, "Str", 3 / 10),
- ["3% increased Totem Life per 10 Strength Allocated in Radius"] = getPerStat("TotemLife", "INC", 0, "Str", 3 / 10),
- ["Adds 1 maximum Lightning Damage to Attacks per 1 Dexterity Allocated in Radius"] = getPerStat("LightningMax", "BASE", ModFlag.Attack, "Dex", 1),
- ["5% increased Chaos damage per 10 Intelligence from Allocated Passives in Radius"] = getPerStat("ChaosDamage", "INC", 0, "Int", 5 / 10),
- ["Dexterity and Intelligence from passives in Radius count towards Strength Melee Damage bonus"] = function(node, out, data)
- if node then
- data.Dex = (data.Dex or 0) + node.modList:Sum("BASE", nil, "Dex")
- data.Int = (data.Int or 0) + node.modList:Sum("BASE", nil, "Int")
- else
- out:NewMod("DexIntToMeleeBonus", "BASE", data.Dex + data.Int, data.modSource)
- end
- end,
- ["-1 Strength per 1 Strength on Allocated Passives in Radius"] = getPerStat("Str", "BASE", 0, "Str", -1),
- ["1% additional Physical Damage Reduction per 10 Strength on Allocated Passives in Radius"] = getPerStat("PhysicalDamageReduction", "BASE", 0, "Str", 1 / 10),
- ["-1 Intelligence per 1 Intelligence on Allocated Passives in Radius"] = getPerStat("Int", "BASE", 0, "Int", -1),
- ["0.4% of Energy Shield Regenerated per Second for every 10 Intelligence on Allocated Passives in Radius"] = getPerStat("EnergyShieldRegenPercent", "BASE", 0, "Int", 0.4 / 10),
- ["-1 Dexterity per 1 Dexterity on Allocated Passives in Radius"] = getPerStat("Dex", "BASE", 0, "Dex", -1),
- ["2% increased Movement Speed per 10 Dexterity on Allocated Passives in Radius"] = getPerStat("MovementSpeed", "INC", 0, "Dex", 2 / 10),
-}
-local jewelSelfUnallocFuncs = {
- ["+5% to Critical Strike Multiplier per 10 Strength on Unallocated Passives in Radius"] = getPerStat("CritMultiplier", "BASE", 0, "Str", 5 / 10),
- ["+7% to Critical Strike Multiplier per 10 Strength on Unallocated Passives in Radius"] = getPerStat("CritMultiplier", "BASE", 0, "Str", 7 / 10),
- ["+15 to maximum Mana per 10 Dexterity on Unallocated Passives in Radius"] = getPerStat("Mana", "BASE", 0, "Dex", 15 / 10),
- ["+100 to Accuracy Rating per 10 Intelligence on Unallocated Passives in Radius"] = getPerStat("Accuracy", "BASE", 0, "Int", 100 / 10),
- ["+125 to Accuracy Rating per 10 Intelligence on Unallocated Passives in Radius"] = getPerStat("Accuracy", "BASE", 0, "Int", 125 / 10),
- ["Grants all bonuses of Unallocated Small Passive Skills in Radius"] = function(node, out, data)
- if node then
- if node.type == "Normal" then
- data.modList = data.modList or new("ModList")
- data.modList:AddList(out)
- end
- elseif data.modList then
- out:AddList(data.modList)
- end
- end,
-}
-
--- Radius jewels with bonuses conditional upon attributes of nearby nodes
-local function getThreshold(attrib, name, modType, value, ...)
- local baseMod = mod(name, modType, value, "", ...)
- return function(node, out, data)
- if node then
- if type(attrib) == "table" then
- for _, att in ipairs(attrib) do
- local nodeVal = out:Sum("BASE", nil, att)
- data[att] = (data[att] or 0) + nodeVal
- data.total = (data.total or 0) + nodeVal
- end
- else
- local nodeVal = out:Sum("BASE", nil, attrib)
- data[attrib] = (data[attrib] or 0) + nodeVal
- data.total = (data.total or 0) + nodeVal
- end
- elseif (data.total or 0) >= 40 then
- local mod = copyTable(baseMod)
- mod.source = data.modSource
- if type(value) == "table" and value.mod then
- value.mod.source = data.modSource
- end
- out:AddMod(mod)
- end
- end
-end
-local jewelThresholdFuncs = {
- ["With at least 40 Dexterity in Radius, Frost Blades Melee Damage Penetrates 15% Cold Resistance"] = getThreshold("Dex", "ColdPenetration", "BASE", 15, ModFlag.Melee, { type = "SkillName", skillName = "Frost Blades" }),
- ["With at least 40 Dexterity in Radius, Melee Damage dealt by Frost Blades Penetrates 15% Cold Resistance"] = getThreshold("Dex", "ColdPenetration", "BASE", 15, ModFlag.Melee, { type = "SkillName", skillName = "Frost Blades" }),
- ["With at least 40 Dexterity in Radius, Frost Blades has 25% increased Projectile Speed"] = getThreshold("Dex", "ProjectileSpeed", "INC", 25, { type = "SkillName", skillName = "Frost Blades" }),
- ["With at least 40 Dexterity in Radius, Ice Shot has 25% increased Area of Effect"] = getThreshold("Dex", "AreaOfEffect", "INC", 25, { type = "SkillName", skillName = "Ice Shot" }),
- ["Ice Shot Pierces 5 additional Targets with 40 Dexterity in Radius"] = getThreshold("Dex", "PierceCount", "BASE", 5, { type = "SkillName", skillName = "Ice Shot" }),
- ["With at least 40 Dexterity in Radius, Ice Shot Pierces 3 additional Targets"] = getThreshold("Dex", "PierceCount", "BASE", 3, { type = "SkillName", skillName = "Ice Shot" }),
- ["With at least 40 Dexterity in Radius, Ice Shot Pierces 5 additional Targets"] = getThreshold("Dex", "PierceCount", "BASE", 5, { type = "SkillName", skillName = "Ice Shot" }),
- ["With at least 40 Intelligence in Radius, Frostbolt fires 2 additional Projectiles"] = getThreshold("Int", "ProjectileCount", "BASE", 2, { type = "SkillName", skillName = "Frostbolt" }),
- ["With at least 40 Intelligence in Radius, Magma Orb fires an additional Projectile"] = getThreshold("Int", "ProjectileCount", "BASE", 1, { type = "SkillName", skillName = "Magma Orb" }),
- ["With at least 40 Intelligence in Radius, Magma Orb has 10% increased Area of Effect per Chain"] = getThreshold("Int", "AreaOfEffect", "INC", 10, { type = "SkillName", skillName = "Magma Orb" }, { type = "PerStat", stat = "Chain" }),
- ["With at least 40 Dexterity in Radius, Shrapnel Shot has 25% increased Area of Effect"] = getThreshold("Dex", "AreaOfEffect", "INC", 25, { type = "SkillName", skillName = "Shrapnel Shot" }),
- ["With at least 40 Dexterity in Radius, Shrapnel Shot's cone has a 50% chance to deal Double Damage"] = getThreshold("Dex", "DoubleDamageChance", "BASE", 50, { type = "SkillName", skillName = "Shrapnel Shot" }, { type = "SkillPart", skillPart = 2 }),
- ["With at least 40 Intelligence in Radius, Freezing Pulse fires 2 additional Projectiles"] = getThreshold("Int", "ProjectileCount", "BASE", 2, { type = "SkillName", skillName = "Freezing Pulse" }),
- ["With at least 40 Intelligence in Radius, 25% increased Freezing Pulse Damage if you've Shattered an Enemy Recently"] = getThreshold("Int", "Damage", "INC", 25, { type = "SkillName", skillName = "Freezing Pulse" }, { type = "Condition", var = "ShatteredEnemyRecently" }),
- ["With at least 40 Dexterity in Radius, Ethereal Knives fires 10 additional Projectiles"] = getThreshold("Dex", "ProjectileCount", "BASE", 10, { type = "SkillName", skillName = "Ethereal Knives" }),
- ["With at least 40 Dexterity in Radius, Ethereal Knives fires 5 additional Projectiles"] = getThreshold("Dex", "ProjectileCount", "BASE", 5, { type = "SkillName", skillName = "Ethereal Knives" }),
- ["With at least 40 Strength in Radius, Molten Strike fires 2 additional Projectiles"] = getThreshold("Str", "ProjectileCount", "BASE", 2, { type = "SkillName", skillName = "Molten Strike" }),
- ["With at least 40 Strength in Radius, Molten Strike has 25% increased Area of Effect"] = getThreshold("Str", "AreaOfEffect", "INC", 25, { type = "SkillName", skillName = "Molten Strike" }),
- ["With at least 40 Strength in Radius, 25% of Glacial Hammer Physical Damage converted to Cold Damage"] = getThreshold("Str", "SkillPhysicalDamageConvertToCold", "BASE", 25, { type = "SkillName", skillName = "Glacial Hammer" }),
- ["With at least 40 Strength in Radius, Heavy Strike has a 20% chance to deal Double Damage"] = getThreshold("Str", "DoubleDamageChance", "BASE", 20, { type = "SkillName", skillName = "Heavy Strike" }),
- ["With at least 40 Strength in Radius, Heavy Strike has a 20% chance to deal Double Damage."] = getThreshold("Str", "DoubleDamageChance", "BASE", 20, { type = "SkillName", skillName = "Heavy Strike" }),
- ["With at least 40 Dexterity in Radius, Dual Strike has a 20% chance to deal Double Damage with the Main-Hand Weapon"] = getThreshold("Dex", "DoubleDamageChance", "BASE", 20, { type = "SkillName", skillName = "Dual Strike" }, { type = "Condition", var = "MainHandAttack" }),
- ["With at least 40 Intelligence in Radius, Raised Zombies' Slam Attack has 100% increased Cooldown Recovery Speed"] = getThreshold("Int", "MinionModifier", "LIST", { mod = mod("CooldownRecovery", "INC", 100, { type = "SkillId", skillId = "ZombieSlam" }) }),
- ["With at least 40 Intelligence in Radius, Raised Zombies' Slam Attack deals 30% increased Damage"] = getThreshold("Int", "MinionModifier", "LIST", { mod = mod("Damage", "INC", 30, { type = "SkillId", skillId = "ZombieSlam" }) }),
- ["With at least 40 Dexterity in Radius, Viper Strike deals 2% increased Attack Damage for each Poison on the Enemy"] = getThreshold("Dex", "Damage", "INC", 2, ModFlag.Attack, { type = "SkillName", skillName = "Viper Strike" }, { type = "Multiplier", actor = "enemy", var = "PoisonStack" }),
- ["With at least 40 Dexterity in Radius, Viper Strike deals 2% increased Damage with Hits and Poison for each Poison on the Enemy"] = getThreshold("Dex", "Damage", "INC", 2, 0, bor(KeywordFlag.Hit, KeywordFlag.Poison), { type = "SkillName", skillName = "Viper Strike" }, { type = "Multiplier", actor = "enemy", var = "PoisonStack" }),
- ["With at least 40 Intelligence in Radius, Spark fires 2 additional Projectiles"] = getThreshold("Int", "ProjectileCount", "BASE", 2, { type = "SkillName", skillName = "Spark" }),
- ["With at least 40 Intelligence in Radius, Blight has 50% increased Hinder Duration"] = getThreshold("Int", "SecondaryDuration", "INC", 50, { type = "SkillName", skillName = "Blight" }),
- ["With at least 40 Intelligence in Radius, Enemies Hindered by Blight take 25% increased Chaos Damage"] = getThreshold("Int", "ExtraSkillMod", "LIST", { mod = mod("ChaosDamageTaken", "INC", 25, { type = "GlobalEffect", effectType = "Debuff", effectName = "Hinder" }) }, { type = "SkillName", skillName = "Blight" }, { type = "ActorCondition", actor = "enemy", var = "Hindered" }),
- ["With 40 Intelligence in Radius, 20% of Glacial Cascade Physical Damage Converted to Cold Damage"] = getThreshold("Int", "SkillPhysicalDamageConvertToCold", "BASE", 20, { type = "SkillName", skillName = "Glacial Cascade" }),
- ["With at least 40 Intelligence in Radius, 20% of Glacial Cascade Physical Damage Converted to Cold Damage"] = getThreshold("Int", "SkillPhysicalDamageConvertToCold", "BASE", 20, { type = "SkillName", skillName = "Glacial Cascade" }),
- ["With 40 total Intelligence and Dexterity in Radius, Elemental Hit deals 50% less Fire Damage"] = getThreshold({"Int","Dex"}, "FireDamage", "MORE", -50, { type = "SkillName", skillNameList = { "Elemental Hit", "Wild Strike" } }),
- ["With 40 total Strength and Intelligence in Radius, Elemental Hit deals 50% less Cold Damage"] = getThreshold({"Str","Int"}, "ColdDamage", "MORE", -50, { type = "SkillName", skillNameList = { "Elemental Hit", "Wild Strike" } }),
- ["With 40 total Dexterity and Strength in Radius, Elemental Hit deals 50% less Lightning Damage"] = getThreshold({"Dex","Str"}, "LightningDamage", "MORE", -50, { type = "SkillName", skillNameList = { "Elemental Hit", "Wild Strike" } }),
- ["With 40 total Intelligence and Dexterity in Radius, Elemental Hit and Wild Strike deal 50% less Fire Damage"] = getThreshold({"Int","Dex"}, "FireDamage", "MORE", -50, { type = "SkillName", skillNameList = { "Elemental Hit", "Wild Strike" } }),
- ["With 40 total Strength and Intelligence in Radius, Elemental Hit and Wild Strike deal 50% less Cold Damage"] = getThreshold({"Str","Int"}, "ColdDamage", "MORE", -50, { type = "SkillName", skillNameList = { "Elemental Hit", "Wild Strike" } }),
- ["With 40 total Dexterity and Strength in Radius, Elemental Hit and Wild Strike deal 50% less Lightning Damage"] = getThreshold({"Dex","Str"}, "LightningDamage", "MORE", -50, { type = "SkillName", skillNameList = { "Elemental Hit", "Wild Strike" } }),
- ["With 40 total Dexterity and Strength in Radius, Spectral Shield Throw Chains +4 times"] = getThreshold({"Dex","Str"}, "ChainCountMax", "BASE", 4, { type = "SkillName", skillName = "Spectral Shield Throw" }),
- ["With 40 total Dexterity and Strength in Radius, Spectral Shield Throw fires 75% less Shard Projectiles"] = getThreshold({"Dex","Str"}, "ProjectileCount", "MORE", -75, { type = "SkillName", skillName = "Spectral Shield Throw" }),
- ["With at least 40 Strength in Radius, Cleave has +1 to Radius per Nearby Enemy, up to +10"] = getThreshold("Str", "AreaOfEffect", "BASE", 1, { type = "Multiplier", var = "NearbyEnemy", limit = 10, limitTotal = true }, { type = "SkillName", skillName = "Cleave" }),
- --[""] = getThreshold("", "", "", , { type = "SkillName", skillName = "" }),
-}
-
--- Unified list of jewel functions
-local jewelFuncList = { }
-for k, v in pairs(jewelOtherFuncs) do
- jewelFuncList[k:lower()] = { func = v, type = "Other" }
-end
-for k, v in pairs(jewelSelfFuncs) do
- jewelFuncList[k:lower()] = { func = v, type = "Self" }
-end
-for k, v in pairs(jewelSelfUnallocFuncs) do
- jewelFuncList[k:lower()] = { func = v, type = "SelfUnalloc" }
-end
-for k, v in pairs(jewelThresholdFuncs) do
- jewelFuncList[k:lower()] = { func = v, type = "Threshold" }
-end
-
--- Generate list of cluster jewel skills
-local clusterJewelSkills = {}
-for baseName, jewel in pairs(data["3_0"].clusterJewels.jewels) do
- for skillId, skill in pairs(jewel.skills) do
- clusterJewelSkills[table.concat(skill.enchant, " "):lower()] = { mod("JewelData", "LIST", { key = "clusterJewelSkill", value = skillId }) }
- end
-end
-for notable in pairs(data["3_0"].clusterJewels.notableSortOrder) do
- clusterJewelSkills["1 added passive skill is "..notable:lower()] = { mod("ClusterJewelNotable", "LIST", notable) }
-end
-for _, keystone in ipairs(data["3_0"].clusterJewels.keystones) do
- clusterJewelSkills["adds "..keystone:lower()] = { mod("JewelData", "LIST", { key = "clusterJewelKeystone", value = keystone }) }
-end
-
--- Scan a line for the earliest and longest match from the pattern list
--- If a match is found, returns the corresponding value from the pattern list, plus the remainder of the line and a table of captures
-local function scan(line, patternList, plain)
- local bestIndex, bestEndIndex
- local bestPattern = ""
- local bestVal, bestStart, bestEnd, bestCaps
- local lineLower = line:lower()
- for pattern, patternVal in pairs(patternList) do
- local index, endIndex, cap1, cap2, cap3, cap4, cap5 = lineLower:find(pattern, 1, plain)
- if index and (not bestIndex or index < bestIndex or (index == bestIndex and (endIndex > bestEndIndex or (endIndex == bestEndIndex and #pattern > #bestPattern)))) then
- bestIndex = index
- bestEndIndex = endIndex
- bestPattern = pattern
- bestVal = patternVal
- bestStart = index
- bestEnd = endIndex
- bestCaps = { cap1, cap2, cap3, cap4, cap5 }
- end
- end
- if bestVal then
- return bestVal, line:sub(1, bestStart - 1) .. line:sub(bestEnd + 1, -1), bestCaps
- else
- return nil, line
- end
-end
-
-local function parseMod(line, order)
- -- Check if this is a special modifier
- local lineLower = line:lower()
- local bestVal, _, bestCaps = scan(line, jewelFuncList)
- for pattern, patternVal in pairs(jewelFuncList) do
- local _, _, cap1, cap2, cap3, cap4, cap5 = lineLower:find(pattern, 1)
- if cap1 then
- return {mod("JewelFunc", "LIST", {func = patternVal.func(cap1, cap2, cap3, cap4, cap5), type = patternVal.type}) }
- end
- end
- local jewelFunc = jewelFuncList[lineLower]
- if jewelFunc then
- return { mod("JewelFunc", "LIST", jewelFunc) }
- end
- local clusterJewelSkill = clusterJewelSkills[lineLower]
- if clusterJewelSkill then
- return clusterJewelSkill
- end
- if unsupportedModList[lineLower] then
- return { }, line
- end
- local specialMod, specialLine, cap = scan(line, specialModList)
- if specialMod and #specialLine == 0 then
- if type(specialMod) == "function" then
- return specialMod(tonumber(cap[1]), unpack(cap))
- else
- return copyTable(specialMod)
- end
- end
-
- -- Check for add-to-cluster-jewel special
- local addToCluster = line:match("^Added Small Passive Skills also grant: (.+)$")
- if addToCluster then
- return { mod("AddToClusterJewelNode", "LIST", addToCluster) }
- end
-
- line = line .. " "
-
- -- Check for a flag/tag specification at the start of the line
- local preFlag
- preFlag, line = scan(line, preFlagList)
-
- -- Check for skill name at the start of the line
- local skillTag
- skillTag, line = scan(line, preSkillNameList)
-
- -- Scan for modifier form
- local modForm, formCap
- modForm, line, formCap = scan(line, formList)
- if not modForm then
- return nil, line
- end
- local num = tonumber(formCap[1])
-
- -- Check for tags (per-charge, conditionals)
- local modTag, modTag2, tagCap
- modTag, line, tagCap = scan(line, modTagList)
- if type(modTag) == "function" then
- modTag = modTag(tonumber(tagCap[1]), unpack(tagCap))
- end
- if modTag then
- modTag2, line, tagCap = scan(line, modTagList)
- if type(modTag2) == "function" then
- modTag2 = modTag2(tonumber(tagCap[1]), unpack(tagCap))
- end
- end
-
- -- Scan for modifier name and skill name
- local modName
- if order == 2 and not skillTag then
- skillTag, line = scan(line, skillNameList, true)
- end
- if modForm == "PEN" then
- modName, line = scan(line, penTypes, true)
- if not modName then
- return { }, line
- end
- local _
- _, line = scan(line, modNameList, true)
- else
- modName, line = scan(line, modNameList, true)
- end
- if order == 1 and not skillTag then
- skillTag, line = scan(line, skillNameList, true)
- end
-
- -- Scan for flags
- local modFlag
- modFlag, line = scan(line, modFlagList, true)
-
- -- Find modifier value and type according to form
- local modValue = num
- local modType = "BASE"
- local modSuffix
- if modForm == "INC" then
- modType = "INC"
- elseif modForm == "RED" then
- modValue = -num
- modType = "INC"
- elseif modForm == "MORE" then
- modType = "MORE"
- elseif modForm == "LESS" then
- modValue = -num
- modType = "MORE"
- elseif modForm == "BASE" then
- modSuffix, line = scan(line, suffixTypes, true)
- elseif modForm == "CHANCE" then
- elseif modForm == "REGENPERCENT" then
- modName = regenTypes[formCap[2]]
- modSuffix = "Percent"
- elseif modForm == "REGENFLAT" then
- modName = regenTypes[formCap[2]]
- elseif modForm == "DEGEN" then
- local damageType = dmgTypes[formCap[2]]
- if not damageType then
- return { }, line
- end
- modName = damageType .. "Degen"
- modSuffix = ""
- elseif modForm == "DMG" then
- local damageType = dmgTypes[formCap[3]]
- if not damageType then
- return { }, line
- end
- modValue = { tonumber(formCap[1]), tonumber(formCap[2]) }
- modName = { damageType.."Min", damageType.."Max" }
- elseif modForm == "DMGATTACKS" then
- local damageType = dmgTypes[formCap[3]]
- if not damageType then
- return { }, line
- end
- modValue = { tonumber(formCap[1]), tonumber(formCap[2]) }
- modName = { damageType.."Min", damageType.."Max" }
- modFlag = modFlag or { keywordFlags = KeywordFlag.Attack }
- elseif modForm == "DMGSPELLS" then
- local damageType = dmgTypes[formCap[3]]
- if not damageType then
- return { }, line
- end
- modValue = { tonumber(formCap[1]), tonumber(formCap[2]) }
- modName = { damageType.."Min", damageType.."Max" }
- modFlag = modFlag or { keywordFlags = KeywordFlag.Spell }
- elseif modForm == "DMGBOTH" then
- local damageType = dmgTypes[formCap[3]]
- if not damageType then
- return { }, line
- end
- modValue = { tonumber(formCap[1]), tonumber(formCap[2]) }
- modName = { damageType.."Min", damageType.."Max" }
- modFlag = modFlag or { keywordFlags = bor(KeywordFlag.Attack, KeywordFlag.Spell) }
- end
- if not modName then
- return { }, line
- end
-
- -- Combine flags and tags
- local flags = 0
- local keywordFlags = 0
- local tagList = { }
- local misc = { }
- for _, data in pairs({ modName, preFlag, modFlag, modTag, modTag2, skillTag }) do
- if type(data) == "table" then
- flags = bor(flags, data.flags or 0)
- keywordFlags = bor(keywordFlags, data.keywordFlags or 0)
- if data.tag then
- t_insert(tagList, copyTable(data.tag))
- elseif data.tagList then
- for _, tag in ipairs(data.tagList) do
- t_insert(tagList, copyTable(tag))
- end
- end
- for k, v in pairs(data) do
- misc[k] = v
- end
- end
- end
-
- -- Generate modifier list
- local nameList = modName
- local modList = { }
- for i, name in ipairs(type(nameList) == "table" and nameList or { nameList }) do
- modList[i] = {
- name = name .. (modSuffix or misc.modSuffix or ""),
- type = modType,
- value = type(modValue) == "table" and modValue[i] or modValue,
- flags = flags,
- keywordFlags = keywordFlags,
- unpack(tagList)
- }
- end
- if modList[1] then
- -- Special handling for various modifier types
- if misc.addToAura then
- -- Modifiers that add effects to your auras
- for i, effectMod in ipairs(modList) do
- modList[i] = mod("ExtraAuraEffect", "LIST", { mod = effectMod })
- end
- elseif misc.newAura then
- -- Modifiers that add extra auras
- for i, effectMod in ipairs(modList) do
- local tagList = { }
- for i, tag in ipairs(effectMod) do
- tagList[i] = tag
- effectMod[i] = nil
- end
- modList[i] = mod("ExtraAura", "LIST", { mod = effectMod, onlyAllies = misc.newAuraOnlyAllies }, unpack(tagList))
- end
- elseif misc.affectedByAura then
- -- Modifiers that apply to actors affected by your auras
- for i, effectMod in ipairs(modList) do
- modList[i] = mod("AffectedByAuraMod", "LIST", { mod = effectMod })
- end
- elseif misc.addToMinion then
- -- Minion modifiers
- for i, effectMod in ipairs(modList) do
- modList[i] = mod("MinionModifier", "LIST", { mod = effectMod }, misc.addToMinionTag)
- end
- elseif misc.addToSkill then
- -- Skill enchants or socketed gem modifiers that add additional effects
- for i, effectMod in ipairs(modList) do
- modList[i] = mod("ExtraSkillMod", "LIST", { mod = effectMod }, misc.addToSkill)
- end
- end
- end
- return modList, line:match("%S") and line
-end
-
-local cache = { }
-local unsupported = { }
-local count = 0
---local foo = io.open("../unsupported.txt", "w")
---foo:close()
-return function(line, isComb)
- if not cache[line] then
- local modList, extra = parseMod(line, 1)
- if modList and extra then
- modList, extra = parseMod(line, 2)
- end
- cache[line] = { modList, extra }
- if foo and not isComb and not cache[line][1] then
- local form = line:gsub("[%+%-]?%d+%.?%d*","{num}")
- if not unsupported[form] then
- unsupported[form] = true
- count = count + 1
- foo = io.open("../unsupported.txt", "a+")
- foo:write(count, ': ', form, (cache[line][2] and #cache[line][2] < #line and (' {' .. cache[line][2]).. '}') or "", '\n')
- foo:close()
- end
- end
- end
- return unpack(copyTable(cache[line]))
-end, cache
diff --git b/Modules/ModParser.lua a/Modules/ModParser.lua
index 15a10638..eed5b5d2 100644
--- b/Modules/ModParser.lua
+++ a/Modules/ModParser.lua
@@ -85,6 +85,7 @@ local formList = {
["adds (%d+)%-(%d+) (%a+) damage to spells and attacks"] = "DMGBOTH", -- o_O
["adds (%d+) to (%d+) (%a+) damage to hits"] = "DMGBOTH",
["adds (%d+)%-(%d+) (%a+) damage to hits"] = "DMGBOTH",
+ ["^you have "] = "FLAG",
}
-- Map of modifier names
@@ -198,6 +199,7 @@ local modNameList = {
["elemental damage taken from hits"] = "ElementalDamageTakenWhenHit",
["elemental damage taken over time"] = "ElementalDamageTakenOverTime",
["cold and lightning damage taken"] = { "ColdDamageTaken", "LightningDamageTaken" },
+ ["physical and chaos damage taken"] = { "PhysicalDamageTaken", "ChaosDamageTaken" },
["reflected elemental damage taken"] = "ElementalReflectedDamageTaken",
-- Other defences
["to dodge attacks"] = "AttackDodgeChance",
@@ -359,6 +361,7 @@ local modNameList = {
["maximum total recovery per second from energy shield leech"] = "MaxEnergyShieldLeechRate",
["maximum total recovery per second from mana leech"] = "MaxManaLeechRate",
["to impale enemies on hit"] = "ImpaleChance",
+ ["to impale on spell hit"] = { "ImpaleChance", flags = ModFlag.Spell },
["impale effect"] = "ImpaleEffect",
["effect of impales you inflict"] = "ImpaleEffect",
-- Projectile modifiers
@@ -372,7 +375,7 @@ local modNameList = {
["totem duration"] = "TotemDuration",
["maximum number of summoned totems"] = "ActiveTotemLimit",
["maximum number of summoned totems."] = "ActiveTotemLimit", -- Mark plz
- ["maximum number of summoned ballista totems"] = "ActiveBallistaLimit", -- Mark plz
+ ["maximum number of summoned ballista totems"] = { "ActiveBallistaLimit", tag = { type = "SkillType", skillType = SkillType.ProjectileAttack } }, -- Mark plz
["trap throwing speed"] = "TrapThrowingSpeed",
["trap trigger area of effect"] = "TrapTriggerAreaOfEffect",
["trap duration"] = "TrapDuration",
@@ -447,8 +450,8 @@ local modNameList = {
["attack damage"] = { "Damage", flags = ModFlag.Attack },
["attack physical damage"] = { "PhysicalDamage", flags = ModFlag.Attack },
["physical attack damage"] = { "PhysicalDamage", flags = ModFlag.Attack },
- ["minimum physical attack damage"] = { "MinPhysicalDamage", flags = ModFlag.Attack },
- ["maximum physical attack damage"] = { "MaxPhysicalDamage", flags = ModFlag.Attack },
+ ["minimum physical attack damage"] = { "MinPhysicalDamage", tag = { type = "SkillType", skillType = SkillType.Attack } },
+ ["maximum physical attack damage"] = { "MaxPhysicalDamage", tag = { type = "SkillType", skillType = SkillType.Attack } },
["physical weapon damage"] = { "PhysicalDamage", flags = ModFlag.Weapon },
["physical damage with weapons"] = { "PhysicalDamage", flags = ModFlag.Weapon },
["physical melee damage"] = { "PhysicalDamage", flags = ModFlag.Melee },
@@ -496,6 +499,9 @@ local modNameList = {
["to scorch enemies"] = "ScorchChance",
["to inflict brittle"] = "BrittleChance",
["to sap enemies"] = "SapChance",
+ ["effect of scorch"] = "EnemyScorchEffect",
+ ["effect of sap"] = "EnemySapEffect",
+ ["effect of brittle"] = "EnemyBrittleEffect",
["effect of shock"] = "EnemyShockEffect",
["effect of shock you inflict"] = "EnemyShockEffect",
["effect of lightning ailments"] = { "EnemyShockEffect" , "EnemySapEffect" },
@@ -812,6 +818,19 @@ local preFlagList = {
["^socketed golem gems [hgd][ae][via][enl] "] = { addToSkill = { type = "SocketedIn", slotName = "{SlotName}", keyword = "golem" } },
["^socketed golem skills [hgd][ae][via][enl] "] = { addToSkill = { type = "SocketedIn", slotName = "{SlotName}", keyword = "golem" } },
["^socketed golem skills have minions "] = { addToSkill = { type = "SocketedIn", slotName = "{SlotName}", keyword = "golem" } },
+ -- Enemy modifiers
+ ["^enemies withered by you [th]a[vk]e "] = { tag = { type = "MultiplierThreshold", var = "WitheredStack", threshold = 1 }, applyToEnemy = true },
+ ["^enemies (%a+) by you take "] = function(cond)
+ return { tag = { type = "Condition", var = cond:gsub("^%a", string.upper) }, applyToEnemy = true, modSuffix = "Taken" }
+ end,
+ ["^enemies (%a+) by you have "] = function(cond)
+ return { tag = { type = "Condition", var = cond:gsub("^%a", string.upper) }, applyToEnemy = true }
+ end,
+ ["^enemies affected by your spider's webs [th]a[vk]e "] = { tag = { type = "MultiplierThreshold", var = "Spider's WebStack", threshold = 1 }, applyToEnemy = true },
+ ["^enemies you curse take "] = { tag = { type = "Condition", var = "Cursed" }, applyToEnemy = true, modSuffix = "Taken" },
+ ["^enemies you curse have "] = { tag = { type = "Condition", var = "Cursed" }, applyToEnemy = true },
+ ["^nearby enemies take "] = { modSuffix = "Taken", applyToEnemy = true },
+ ["^nearby enemies have "] = { applyToEnemy = true },
-- Other
["^your flasks grant "] = { },
["^when hit, "] = { },
@@ -823,8 +842,7 @@ local preFlagList = {
["^you and allies affected by auras from your skills [hgd][ae][via][enl] "] = { affectedByAura = true },
["^take "] = { modSuffix = "Taken" },
["^fortify buffs you create instead grant "] = { convertFortifyEffect = true },
- ["^marauder: melee skills have "] = { flags = ModFlag.Melee, tag = { type = "Condition", var = "ConnectedToMarauderStart" } },
- ["^marauder: "] = { tag = { type = "Condition", var = "ConnectedToDuelistStart" } },
+ ["^marauder: "] = { tag = { type = "Condition", var = "ConnectedToMarauderStart" } },
["^duelist: "] = { tag = { type = "Condition", var = "ConnectedToDuelistStart" } },
["^ranger: "] = { tag = { type = "Condition", var = "ConnectedToRangerStart" } },
["^shadow: "] = { tag = { type = "Condition", var = "ConnectedToShadowStart" } },
@@ -940,8 +958,8 @@ local modTagList = {
["per (%d+)%% cold resistance above 75%%"] = function(num) return { tag = { type = "PerStat", stat = "ColdResistOver75", div = num } } end,
["per (%d+)%% lightning resistance above 75%%"] = function(num) return { tag = { type = "PerStat", stat = "LightningResistOver75", div = num } } end,
["per (%d+) devotion"] = function(num) return { tag = { type = "PerStat", stat = "Devotion", div = num } } end,
- ["per (%d+)%% missing fire resistance"] = function(num) return { tag = { type = "PerStat", stat = "MissingFireResist", div = num } } end,
- ["per (%d+)%% missing cold resistance"] = function(num) return { tag = { type = "PerStat", stat = "MissingColdResist", div = num } } end,
+ ["per (%d+)%% missing fire resistance, up to a maximum of (%d+)%%"] = function(num, _, limit) return { tag = { type = "PerStat", stat = "MissingFireResist", div = num, limit = tonumber(limit), limitTotal = true } } end,
+ ["per (%d+)%% missing cold resistance, up to a maximum of (%d+)%%"] = function(num, _, limit) return { tag = { type = "PerStat", stat = "MissingColdResist", div = num, limit = tonumber(limit), limitTotal = true } } end,
["per totem"] = { tag = { type = "PerStat", stat = "TotemsSummoned" } },
["per summoned totem"] = { tag = { type = "PerStat", stat = "TotemsSummoned" } },
["for each summoned totem"] = { tag = { type = "PerStat", stat = "TotemsSummoned" } },
@@ -1021,6 +1039,8 @@ local modTagList = {
-- Player status conditions
["wh[ie][ln]e? on low life"] = { tag = { type = "Condition", var = "LowLife" } },
["wh[ie][ln]e? not on low life"] = { tag = { type = "Condition", var = "LowLife", neg = true } },
+ ["wh[ie][ln]e? on low mana"] = { tag = { type = "Condition", var = "LowMana" } },
+ ["wh[ie][ln]e? not on low mana"] = { tag = { type = "Condition", var = "LowMana", neg = true } },
["wh[ie][ln]e? on full life"] = { tag = { type = "Condition", var = "FullLife" } },
["wh[ie][ln]e? not on full life"] = { tag = { type = "Condition", var = "FullLife", neg = true } },
["wh[ie][ln]e? no life is reserved"] = { tag = { type = "StatThreshold", stat = "LifeReserved", threshold = 0, upper = true } },
@@ -1043,12 +1063,15 @@ local modTagList = {
["while you have an endurance charge"] = { tag = { type = "StatThreshold", stat = "EnduranceCharges", threshold = 1 } },
["while at maximum power charges"] = { tag = { type = "StatThreshold", stat = "PowerCharges", thresholdStat = "PowerChargesMax" } },
["while at maximum frenzy charges"] = { tag = { type = "StatThreshold", stat = "FrenzyCharges", thresholdStat = "FrenzyChargesMax" } },
+ ["while on full frenzy charges"] = { tag = { type = "StatThreshold", stat = "FrenzyCharges", thresholdStat = "FrenzyChargesMax" } },
["while at maximum endurance charges"] = { tag = { type = "StatThreshold", stat = "EnduranceCharges", thresholdStat = "EnduranceChargesMax" } },
["while you have at least (%d+) crab barriers"] = function(num) return { tag = { type = "StatThreshold", stat = "CrabBarriers", threshold = num } } end,
["while you have at least (%d+) total endurance, frenzy and power charges"] = function(num) return { tag = { type = "MultiplierThreshold", var = "TotalCharges", threshold = num } } end,
["while you have a totem"] = { tag = { type = "Condition", var = "HaveTotem" } },
["while you have at least one nearby ally"] = { tag = { type = "MultiplierThreshold", var = "NearbyAlly", threshold = 1 } },
["while you have fortify"] = { tag = { type = "Condition", var = "Fortify" } },
+ ["while you have phasing"] = { tag = { type = "Condition", var = "Phasing" } },
+ ["while you have elusive"] = { tag = { type = "Condition", var = "Elusive" } },
["while physical aegis is depleted"] = { tag = { type = "Condition", var = "PhysicalAegisDepleted" } },
["during onslaught"] = { tag = { type = "Condition", var = "Onslaught" } },
["while you have onslaught"] = { tag = { type = "Condition", var = "Onslaught" } },
@@ -1192,6 +1215,8 @@ local modTagList = {
["if you've impaled an enemy recently"] = { tag = { type = "Condition", var = "ImpaledRecently" } },
["if you've changed stance recently"] = { tag = { type = "Condition", var = "ChangedStanceRecently" } },
["if you've gained a power charge recently"] = { tag = { type = "Condition", var = "GainedPowerChargeRecently" } },
+ ["if you haven't gained a power charge recently"] = { tag = { type = "Condition", var = "GainedPowerChargeRecently", neg = true } },
+ ["if you haven't gained a frenzy charge recently"] = { tag = { type = "Condition", var = "GainedFrenzyChargeRecently", neg = true } },
["if you've stopped taking damage over time recently"] = { tag = { type = "Condition", var = "StoppedTakingDamageOverTimeRecently" } },
["during soul gain prevention"] = { tag = { type = "Condition", var = "SoulGainPrevention" } },
["if you detonated mines recently"] = { tag = { type = "Condition", var = "DetonatedMinesRecently" } },
@@ -1214,6 +1239,8 @@ local modTagList = {
["while you have a summoned golem"] = { tag = { type = "Condition", varList = { "HavePhysicalGolem", "HaveLightningGolem", "HaveColdGolem", "HaveFireGolem", "HaveChaosGolem", "HaveCarrionGolem" } } },
["if a minion has died recently"] = { tag = { type = "Condition", var = "MinionsDiedRecently" } },
["while you have sacrificial zeal"] = { tag = { type = "Condition", var = "SacrificialZeal" } },
+ ["while sane"] = { tag = { type = "Condition", var = "Insane", neg = true } },
+ ["while insane"] = { tag = { type = "Condition", var = "Insane" } },
-- Enemy status conditions
["at close range"] = { tag = { type = "Condition", var = "AtCloseRange" } },
["against rare and unique enemies"] = { tag = { type = "ActorCondition", actor = "enemy", var = "RareOrUnique" } },
@@ -1366,7 +1393,7 @@ local specialModList = {
["critical strikes inflict scorch, brittle and sapped"] = { flag("CritAlwaysAltAilments") },
["chance to block attack damage is doubled"] = { mod("BlockChance", "MORE", 100) },
["chance to block spell damage is doubled"] = { mod("SpellBlockChance", "MORE", 100) },
- ["you take (%d+)%% of damage from blocked hits"] = function(num) return { mod("BlockEffect", "BASE", 100 - num) } end,
+ ["you take (%d+)%% of damage from blocked hits"] = function(num) return { mod("BlockEffect", "BASE", num) } end,
["ignore attribute requirements"] = { flag("IgnoreAttributeRequirements") },
["gain no inherent bonuses from attributes"] = { flag("NoAttributeBonuses") },
["all damage taken bypasses energy shield"] = {
@@ -1375,7 +1402,7 @@ local specialModList = {
mod("ColdEnergyShieldBypass", "BASE", 100),
mod("FireEnergyShieldBypass", "BASE", 100),
},
- ["auras from your skills do not affect allies"] = { flag("SelfAurasCannotAffectAllies") },
+ ["auras from your skills do not affect allies"] = { flag("SelfAuraSkillsCannotAffectAllies") },
["auras from your skills have (%d+)%% more effect on you"] = function(num) return { mod("SkillAuraEffectOnSelf", "MORE", num) } end,
["increases and reductions to mana regeneration rate instead apply to rage regeneration rate"] = { flag("ManaRegenToRageRegen") },
["maximum energy shield is (%d+)"] = function(num) return { mod("EnergyShield", "OVERRIDE", num ) } end,
@@ -1415,6 +1442,7 @@ local specialModList = {
},
["(%d+)%% more damage while there is at most one rare or unique enemy nearby"] = function(num) return { mod("Damage", "MORE", num, nil, 0, { type = "Condition", var = "AtMostOneNearbyRareOrUniqueEnemy" }) } end,
["(%d+)%% reduced damage taken while there are at least two rare or unique enemies nearby"] = function(num) return { mod("DamageTaken", "INC", -num, nil, 0, { type = "MultiplierThreshold", var = "NearbyRareOrUniqueEnemies", threshold = 2 }) } end,
+ ["you take no extra damage from critical strikes while elusive"] = function(num) return { mod("ReduceCritExtraDamage", "BASE", 100, { type = "Condition", var = "Elusive" }) } end,
-- Berserker
["gain %d+ rage when you kill an enemy"] = {
flag("Condition:CanGainRage"),
@@ -1445,9 +1473,7 @@ local specialModList = {
},
["exerted attacks deal (%d+)%% more damage if a warcry sacrificed rage recently"] = function(num) return { mod("ExertIncrease", "MORE", num, nil, ModFlag.Attack, 0) } end,
-- Champion
- ["you have fortify"] = { flag("Condition:Fortify") },
["cannot be stunned while you have fortify"] = { mod("AvoidStun", "BASE", 100, { type = "Condition", var = "Fortify" }) },
- ["enemies taunted by you take (%d+)%% increased damage"] = function(num) return { mod("EnemyModifier", "LIST", { mod = mod("DamageTaken", "INC", num, { type = "Condition", var = "Taunted" }) }) } end,
["enemies taunted by you cannot evade attacks"] = { mod("EnemyModifier", "LIST", { mod = flag("CannotEvade", { type = "Condition", var = "Taunted" }) }) },
["if you've impaled an enemy recently, you and nearby allies have %+(%d+) to armour"] = function (num) return { mod("ExtraAura", "LIST", { mod = mod("Armour", "BASE", num) }, { type = "Condition", var = "ImpaledRecently" }) } end,
-- Chieftain
@@ -1559,7 +1585,6 @@ local specialModList = {
flag("ChaosCanShock"),
},
-- Gladiator
- ["enemies maimed by you take (%d+)%% increased physical damage"] = function(num) return { mod("EnemyModifier", "LIST", { mod = mod("PhysicalDamageTaken", "INC", num, { type = "Condition", var = "Maimed" }) }) } end,
["chance to block spell damage is equal to chance to block attack damage"] = { flag("SpellBlockChanceIsBlockChance") },
["maximum chance to block spell damage is equal to maximum chance to block attack damage"] = { flag("SpellBlockChanceMaxIsBlockChanceMax") },
["your counterattacks deal double damage"] = {
@@ -1593,7 +1618,6 @@ local specialModList = {
["critical strikes ignore enemy monster elemental resistances"] = { flag("IgnoreElementalResistances", { type = "Condition", var = "CriticalStrike" }) },
["non%-critical strikes penetrate (%d+)%% of enemy elemental resistances"] = function(num) return { mod("ElementalPenetration", "BASE", num, { type = "Condition", var = "CriticalStrike", neg = true }) } end,
["consecrated ground you create applies (%d+)%% increased damage taken to enemies"] = function(num) return { mod("EnemyModifier", "LIST", { mod = mod("DamageTaken", "INC", num, { type = "Condition", var = "OnConsecratedGround" }) }) } end,
- ["nearby enemies take (%d+)%% increased elemental damage"] = function(num) return { mod("EnemyModifier", "LIST", { mod = mod("ElementalDamageTaken", "INC", num) }) } end,
["you have consecrated ground around you while stationary"] = { flag("Condition:OnConsecratedGround", { type = "Condition", var = "Stationary" }) },
["consecrated ground you create grants immunity to elemental ailments to you and allies"] = {
mod("AvoidChill", "BASE", 100, { type = "Condition", var = "OnConsecratedGround" }),
@@ -1659,10 +1683,6 @@ local specialModList = {
},
-- Raider
["nearby enemies have (%d+)%% less accuracy rating while you have phasing"] = function(num) return { mod("EnemyModifier", "LIST", { mod = mod("Accuracy", "MORE", -num) }, { type = "Condition", var = "Phasing" } )} end,
- ["you have phasing while at maximum frenzy charges"] = { flag("Condition:Phasing", { type = "StatThreshold", stat = "FrenzyCharges", thresholdStat = "FrenzyChargesMax" }) },
- ["you have phasing during onslaught"] = { flag("Condition:Phasing", { type = "Condition", var = "Onslaught" }) },
- ["you have onslaught while on full frenzy charges"] = { flag("Condition:Onslaught", { type = "StatThreshold", stat = "FrenzyCharges", thresholdStat = "FrenzyChargesMax" }) },
- ["you have onslaught while at maximum endurance charges"] = { flag("Condition:Onslaught", { type = "StatThreshold", stat = "EnduranceCharges", thresholdStat = "EnduranceChargesMax" }) },
["immune to elemental ailments while phasing"] = {
mod("AvoidChill", "BASE", 100, { type = "Condition", var = "Phasing" }),
mod("AvoidFreeze", "BASE", 100, { type = "Condition", var = "Phasing" }),
@@ -1688,6 +1708,7 @@ local specialModList = {
["cannot take reflected physical damage"] = { mod("PhysicalReflectedDamageTaken", "MORE", -100) },
["gain (%d+)%% increased movement speed for 20 seconds when you kill an enemy"] = function(num) return { mod("MovementSpeed", "INC", num, { type = "Condition", var = "KilledRecently" }) } end,
["gain (%d+)%% increased attack speed for 20 seconds when you kill a rare or unique enemy"] = function(num) return { mod("Speed", "INC", num, { type = "Condition", var = "KilledUniqueEnemy" }) } end,
+ ["kill enemies that have (%d+)%% or lower life when hit by your skills"] = function(num) return { mod("CullPercent", "MAX", num) } end,
-- Trickster
["(%d+)%% chance to gain (%d+)%% of non%-chaos damage with hits as extra chaos damage"] = function(num, _, perc) return { mod("NonChaosDamageGainAsChaos", "BASE", num / 100 * tonumber(perc)) } end,
["movement skills cost no mana"] = { mod("ManaCost", "MORE", -100, nil, 0, KeywordFlag.Movement) },
@@ -1720,7 +1741,7 @@ local specialModList = {
["%+(%d+) to level of socketed gems"] = function(num) return { mod("GemProperty", "LIST", { keyword = "all", key = "level", value = num }, { type = "SocketedIn", slotName = "{SlotName}" }) } end,
["%+(%d+) to level of socketed ([%a ]+) gems"] = function(num, _, type) return { mod("GemProperty", "LIST", { keyword = type, key = "level", value = num }, { type = "SocketedIn", slotName = "{SlotName}" }) } end,
["%+(%d+)%% to quality of socketed gems"] = function(num, _, type) return { mod("GemProperty", "LIST", { keyword = "all", key = "quality", value = num }, { type = "SocketedIn", slotName = "{SlotName}" }) } end,
- ["%+(%d+)%% to quality of socketed ([%a ]+) gems"] = function(num, _, type) return { mod("GemProperty", "LIST", { keyword = "all", key = "quality", value = num }, { type = "SocketedIn", slotName = "{SlotName}" }) } end,
+ ["%+(%d+)%% to quality of socketed ([%a ]+) gems"] = function(num, _, type) return { mod("GemProperty", "LIST", { keyword = type, key = "quality", value = num }, { type = "SocketedIn", slotName = "{SlotName}" }) } end,
["%+(%d+) to level of active socketed skill gems"] = function(num) return { mod("GemProperty", "LIST", { keyword = "active_skill", key = "level", value = num }, { type = "SocketedIn", slotName = "{SlotName}" }) } end,
["%+(%d+) to level of socketed active skill gems"] = function(num) return { mod("GemProperty", "LIST", { keyword = "active_skill", key = "level", value = num }, { type = "SocketedIn", slotName = "{SlotName}" }) } end,
["%+(%d+) to level of socketed active skill gems per (%d+) player levels"] = function(num, _, div) return { mod("GemProperty", "LIST", { keyword = "active_skill", key = "level", value = num }, { type = "SocketedIn", slotName = "{SlotName}" }, { type = "Multiplier", var = "Level", div = tonumber(div) }) } end,
@@ -1779,6 +1800,7 @@ local specialModList = {
["trigger level (%d+) (.+) when you hit an enemy while cursed"] = function(num, _, skill) return extraSkill(skill, num) end,
["trigger level (%d+) (.+) when you hit a bleeding enemy"] = function(num, _, skill) return extraSkill(skill, num) end,
["trigger level (%d+) (.+) when you hit a rare or unique enemy"] = function(num, _, skill) return extraSkill(skill, num) end,
+ ["trigger level (%d+) (.+) when you hit a frozen enemy"] = function(num, _, skill) return extraSkill(skill, num) end,
["trigger level (%d+) (.+) when you kill a frozen enemy"] = function(num, _, skill) return extraSkill(skill, num) end,
["trigger level (%d+) (.+) when you consume a corpse"] = function(num, _, skill) return skill == "summon phantasm skill" and extraSkill("triggered summon phantasm skill", num) or extraSkill(skill, num) end,
["trigger level (%d+) (.+) when you attack with a bow"] = function(num, _, skill) return extraSkill(skill, num) end,
@@ -1824,6 +1846,7 @@ local specialModList = {
["trigger (.+) on critical strike"] = function( _, skill) return extraSkill(skill, 1, true) end,
["triggers? (.+) when you take a critical strike"] = function( _, skill) return extraSkill(skill, 1, true) end,
["socketed [%a+]* ?gems a?r?e? ?supported by level (%d+) (.+)"] = function(num, _, support) return { mod("ExtraSupport", "LIST", { skillId = gemIdLookup[support] or gemIdLookup[support:gsub("^increased ","")] or "Unknown", level = num }, { type = "SocketedIn", slotName = "{SlotName}" }) } end,
+ ["socketed hex curse skills are triggered by doedre's effigy when summoned"] = { mod("ExtraSupport", "LIST", { skillId = "SupportCursePillarTriggerCurses", level = 20 }, { type = "SocketedIn", slotName = "{SlotName}" }) },
["trigger level (%d+) (.+) every (%d+) seconds"] = function(num, _, skill) return extraSkill(skill, num) end,
["trigger level (%d+) (.+), (.+) or (.+) every (%d+) seconds"] = function(num, _, skill1, skill2, skill3) return {
mod("ExtraSkill", "LIST", { skillId = gemIdLookup[skill1], level = num }),
@@ -1833,6 +1856,9 @@ local specialModList = {
["offering skills triggered this way also affect you"] = { mod("ExtraSkillMod", "LIST", { mod = mod("SkillData", "LIST", { key = "buffNotPlayer", value = false }) }, { type = "SkillName", skillNameList = { "Bone Offering", "Flesh Offering", "Spirit Offering" } }, { type = "SocketedIn", slotName = "{SlotName}" }) },
["trigger level (%d+) (.+) after spending a total of (%d+) mana"] = function(num, _, skill) return extraSkill(skill, num) end,
["consumes a void charge to trigger level (%d+) (.+) when you fire arrows"] = function(num, _, skill) return extraSkill(skill, num) end,
+ ["your hits treat cold resistance as (%d+)%% higher than actual value"] = function(num) return {
+ mod("ColdPenetration", "BASE", -num, nil, 0, KeywordFlag.Hit),
+ } end,
-- Conversion
["increases and reductions to minion damage also affects? you"] = { flag("MinionDamageAppliesToPlayer"), mod("ImprovedMinionDamageAppliesToPlayer", "INC", 100) },
["increases and reductions to minion damage also affects? you at (%d+)%% of their value"] = function(num) return { flag("MinionDamageAppliesToPlayer"), mod("ImprovedMinionDamageAppliesToPlayer", "INC", num) } end,
@@ -1910,7 +1936,6 @@ local specialModList = {
["you can inflict up to (%d+) ignites on an enemy"] = { flag("IgniteCanStack") },
["you can inflict an additional ignite on an enemy"] = { flag("IgniteCanStack"), mod("IgniteStacks", "BASE", 1) },
["enemies chilled by you take (%d+)%% increased burning damage"] = function(num) return { mod("EnemyModifier", "LIST", { mod = mod("FireDamageTakenOverTime", "INC", num) }, { type = "ActorCondition", actor = "enemy", var = "Chilled" }) } end,
- ["enemies frozen by you take (%d+)%% increased damage"] = function(num) return { mod("EnemyModifier", "LIST", { mod = mod("DamageTaken", "INC", num) }, { type = "ActorCondition", actor = "enemy", var = "Frozen" }) } end,
["damaging ailments deal damage (%d+)%% faster"] = function(num) return { mod("IgniteBurnFaster", "INC", num), mod("BleedFaster", "INC", num), mod("PoisonFaster", "INC", num) } end,
["damaging ailments you inflict deal damage (%d+)%% faster while affected by malevolence"] = function(num) return {
mod("IgniteBurnFaster", "INC", num, { type = "Condition", var = "AffectedByMalevolence" }),
@@ -1923,9 +1948,8 @@ local specialModList = {
["ignites you inflict with attacks deal damage (%d+)%% faster"] = function(num) return { mod("IgniteBurnFaster", "INC", num, nil, ModFlag.Attack) } end,
["ignites you inflict deal damage (%d+)%% faster"] = function(num) return { mod("IgniteBurnFaster", "INC", num) } end,
["enemies ignited by you during flask effect take (%d+)%% increased damage"] = function(num) return { mod("EnemyModifier", "LIST", { mod = mod("DamageTaken", "INC", num) }, { type = "ActorCondition", actor = "enemy", var = "Ignited" }) } end,
- ["enemies ignited by you have (%-%d+)%% to fire resistance"] = function(num) return { mod("EnemyModifier", "LIST", { mod = mod("FireResist", "BASE", num) }, { type = "ActorCondition", actor = "enemy", var = "Ignited" }) } end,
["enemies ignited by you take chaos damage instead of fire damage from ignite"] = { flag("IgniteToChaos") },
- ["enemies chilled by your hits are shocked"] = {
+ ["enemies chilled by your hits are shocked"] = {
mod("ShockBase", "BASE", 15, { type = "ActorCondition", actor = "enemy", var = "ChilledByYourHits" } ),
mod("EnemyModifier", "LIST", { mod = mod("Condition:Shocked", "FLAG", true, { type = "Condition", var = "ChilledByYourHits" } ) } )
},
@@ -1950,6 +1974,7 @@ local specialModList = {
mod("EnemyModifier", "LIST", { mod = flag("Condition:Shocked") }, { type = "Condition", var = "Focused" } ),
},
["drops shocked ground while moving, lasting (%d+) seconds"] = { mod("ShockOverride", "BASE", 10, { type = "ActorCondition", actor = "enemy", var = "OnShockedGround"} ) },
+ ["drops scorched ground while moving, lasting (%d+) seconds"] = { mod("ScorchBase", "BASE", 10, { type = "ActorCondition", actor = "enemy", var = "OnScorchedGround"} ) },
-- Bleed
["melee attacks cause bleeding"] = { mod("BleedChance", "BASE", 100, nil, ModFlag.Melee) },
["attacks cause bleeding when hitting cursed enemies"] = { mod("BleedChance", "BASE", 100, nil, ModFlag.Attack, { type = "ActorCondition", actor = "enemy", var = "Cursed" }) },
@@ -1985,20 +2010,10 @@ local specialModList = {
mod("Damage", "MORE", tonumber(more) * num / 200, nil, 0, KeywordFlag.Poison, { type = "Condition", var = "DualWielding"}, { type = "SkillType", skillType = SkillType.Attack }),
mod("Damage", "MORE", tonumber(more) * num / 100, nil, 0, KeywordFlag.Poison, { type = "Condition", var = "DualWielding", neg = true }, { type = "SkillType", skillType = SkillType.Attack })
} end,
- ["enemies poisoned by you have (%-%d+)%% to chaos resistance"] = function(num) return { mod("EnemyModifier", "LIST", { mod = mod("ChaosResist", "BASE", num) }, { type = "ActorCondition", actor = "enemy", var = "Poisoned" }) } end,
-- Buffs/debuffs
["phasing"] = { flag("Condition:Phasing") },
["onslaught"] = { flag("Condition:Onslaught") },
["unholy might"] = { flag("Condition:UnholyMight") },
- ["you have phasing if you've killed recently"] = { flag("Condition:Phasing", { type = "Condition", var = "KilledRecently" }) },
- ["you have phasing if you have blocked recently"] = { flag("Condition:Phasing", { type = "Condition", var = "BlockedRecently" }) },
- ["you have phasing while affected by haste"] = { flag("Condition:Phasing", { type = "Condition", var = "AffectedByHaste" }) },
- ["you have phasing while you have cat's stealth"] = { flag("Condition:Phasing", { type = "Condition", var = "AffectedByCat'sStealth" }) },
- ["you have onslaught while you have cat's agility"] = { flag("Condition:Onslaught", { type = "Condition", var = "AffectedByCat'sAgility" }) },
- ["you have onslaught while on low life"] = { flag("Condition:Onslaught", { type = "Condition", var = "LowLife" }) },
- ["you have onslaught while not on low mana"] = { flag("Condition:Onslaught", { type = "Condition", var = "LowMana", neg = true }) },
- ["you have tailwind if you have dealt a critical strike recently"] = { flag("Condition:Tailwind", { type = "Condition", var = "CritRecently" }) },
- ["you have unholy might while you have no energy shield"] = { flag("Condition:UnholyMight", { type = "Condition", var = "HaveEnergyShield", neg = true }) },
["your aura buffs do not affect allies"] = { flag("SelfAurasCannotAffectAllies") },
["aura buffs from skills have (%d+)%% increased effect on you for each herald affecting you"] = function(num) return { mod("AuraBuffEffect", "INC", num, { type = "Multiplier", var = "Herald"}) } end,
["aura buffs from skills have (%d+)%% increased effect on you for each herald affecting you, up to (%d+)%%"] = function(num, _, limit) return {
@@ -2032,10 +2047,6 @@ local specialModList = {
["([%+%-][%d%.]+) seconds to avian's flight duration"] = function(num) return { mod("SecondaryDuration", "BASE", num, { type = "SkillName", skillName = "Aspect of the Avian" }) } end,
["aspect of the spider can inflict spider's web on enemies an additional time"] = { mod("ExtraSkillMod", "LIST", { mod = mod("Multiplier:SpiderWebApplyStackMax", "BASE", 1) }, { type = "SkillName", skillName = "Aspect of the Spider" }) },
["aspect of the avian also grants avian's might and avian's flight to nearby allies"] = { mod("ExtraSkillMod", "LIST", { mod = mod("BuffEffectOnMinion", "MORE", 100) }, { type = "SkillName", skillName = "Aspect of the Avian" }) },
- ["enemies affected by your spider's webs have (%-%d+)%% to all resistances"] = function(num) return {
- mod("EnemyModifier", "LIST", { mod = mod("ElementalResist", "BASE", num, { type = "MultiplierThreshold", var = "Spider's WebStack", threshold = 1 }) }),
- mod("EnemyModifier", "LIST", { mod = mod("ChaosResist", "BASE", num, { type = "MultiplierThreshold", var = "Spider's WebStack", threshold = 1 }) }),
- } end,
["marked enemy takes (%d+)%% increased damage"] = function(num) return {
mod("EnemyModifier", "LIST", { mod = mod("DamageTaken", "INC", num) }, {type = "ActorCondition", actor = "enemy", var = "Marked"}),
} end,
@@ -2070,13 +2081,12 @@ local specialModList = {
["when you warcry, you and nearby allies gain onslaught for 4 seconds"] = { mod("ExtraAura", "LIST", { mod = flag("Onslaught") }, { type = "Condition", var = "UsedWarcryRecently" }) },
["enemies in your chilling areas take (%d+)%% increased lightning damage"] = function(num) return { mod("EnemyModifier", "LIST", { mod = mod("LightningDamageTaken", "INC", num) }, { type = "ActorCondition", actor = "enemy", var = "InChillingArea" }) } end,
["(%d+)%% chance to sap enemies in chilling areas"] = function(num) return { mod("SapChance", "BASE", num, { type = "ActorCondition", actor = "enemy", var = "InChillingArea" } ) } end,
- ["enemies hindered by you take (%d+)%% increased chaos damage"] = function(num) return { mod("EnemyModifier", "LIST", { mod = mod("ChaosDamageTaken", "INC", num) }, { type = "ActorCondition", actor = "enemy", var = "Hindered" }) } end,
["warcries count as having (%d+) additional nearby enemies"] = function(num) return {
mod("Multiplier:WarcryNearbyEnemies", "BASE", num),
} end,
+ ["enemies taunted by your warcries take (%d+)%% increased damage"] = function(num) return { mod("EnemyModifier", "LIST", { mod = mod("DamageTaken", "INC", num, { type = "Condition", var = "Taunted" }) }, { type = "Condition", var = "UsedWarcryRecently" }) } end,
["warcries share their cooldown"] = { flag("WarcryShareCooldown") },
["warcries have minimum of (%d+) power"] = { flag("CryWolfMinimumPower") },
- ["enemies you curse take (%d+)%% increased damage"] = function(num) return { mod("EnemyModifier", "LIST", { mod = mod("DamageTaken", "INC", num, { type = "Condition", var = "Cursed" }) }) } end,
["(%d+)%% chance to inflict withered for (%d+) seconds on hit"] = { flag("Condition:CanWither") },
["(%d+)%% chance to inflict withered for (%d+) seconds on hit with this weapon"] = { flag("Condition:CanWither") },
["(%d+)%% chance to inflict withered for two seconds on hit if there are (%d+) or fewer withered debuffs on enemy"] = { flag("Condition:CanWither") },
@@ -2084,7 +2094,6 @@ local specialModList = {
["enemies take (%d+)%% increased elemental damage from your hits for"] = { flag("Condition:CanElementalWithered") },
["each withered you have inflicted on them"] = { },
["your hits cannot penetrate or ignore elemental resistances"] = { flag("CannotElePenIgnore") },
- ["you have fortify during effect of any life flask"] = { flag("Condition:Fortify", { type = "Condition", var = "UsingLifeFlask" }) },
["you take (%d+) chaos damage per second for 3 seconds on kill"] = function(num) return { mod("ChaosDegen", "BASE", num, { type = "Condition", var = "KilledLast3Seconds" }) } end,
["regenerate (%d+) life over 1 second for each spell you cast"] = function(num) return { mod("LifeRegen", "BASE", num, { type = "Condition", var = "CastLast1Seconds" }) } end,
["and nearby allies regenerate (%d+) life per second"] = function(num) return { mod("LifeRegen", "BASE", num, { type = "Condition", var = "KilledPosionedLast2Seconds" }) } end,
@@ -2109,6 +2118,18 @@ local specialModList = {
["nearby enemies have lightning exposure while you are affected by herald of thunder"] = {
mod("EnemyModifier", "LIST", { mod = mod("LightningExposure", "BASE", -10) }, { type = "Condition", var = "Effective" }, { type = "Condition", var = "AffectedByHeraldofThunder" }),
},
+ ["modifiers to minimum endurance charges instead apply to minimum brutal charges"] = { flag("MinimumEnduranceChargesEqualsMinimumBrutalCharges") },
+ ["modifiers to minimum frenzy charges instead apply to minimum affliction charges"] = { flag("MinimumFrenzyChargesEqualsMinimumAfflictionCharges") },
+ ["modifiers to minimum power charges instead apply to minimum absorption charges"] = { flag("MinimumPowerChargesEqualsMinimumAbsorptionCharges") },
+ ["maximum brutal charges is equal to maximum endurance charges"] = { flag("MaximumEnduranceChargesEqualsMaximumBrutalCharges") },
+ ["maximum affliction charges is equal to maximum frenzy charges"] = { flag("MaximumFrenzyChargesEqualsMaximumAfflictionCharges") },
+ ["maximum absorption charges is equal to maximum power charges"] = { flag("MaximumPowerChargesEqualsMaximumAbsorptionCharges") },
+ ["gain brutal charges instead of endurance charges"] = { flag("EnduranceChargesConvertToBrutalCharges") },
+ ["gain affliction charges instead of frenzy charges"] = { flag("FrenzyChargesConvertToAfflictionCharges") },
+ ["gain absorption charges instead of power charges"] = { flag("PowerChargesConvertToAbsorptionCharges") },
+ ["regenerate (%d+)%% life over one second when hit while sane"] = function(num) return {
+ mod("LifeRegenPercent", "BASE", num, { type = "Condition", var = "Insane", neg = true }, { type = "Condition", var = "BeenHitRecently" }),
+ } end,
-- Traps, Mines and Totems
["traps and mines deal (%d+)%-(%d+) additional physical damage"] = function(_, min, max) return { mod("PhysicalMin", "BASE", tonumber(min), nil, 0, bor(KeywordFlag.Trap, KeywordFlag.Mine)), mod("PhysicalMax", "BASE", tonumber(max), nil, 0, bor(KeywordFlag.Trap, KeywordFlag.Mine)) } end,
["traps and mines deal (%d+) to (%d+) additional physical damage"] = function(_, min, max) return { mod("PhysicalMin", "BASE", tonumber(min), nil, 0, bor(KeywordFlag.Trap, KeywordFlag.Mine)), mod("PhysicalMax", "BASE", tonumber(max), nil, 0, bor(KeywordFlag.Trap, KeywordFlag.Mine)) } end,
@@ -2171,6 +2192,8 @@ local specialModList = {
mod("Dummy", "DUMMY", 1, { type = "Condition", var = "HitRecentlyWithWeapon" }), -- Make the Configuration option appear
},
["summoned skeleton warriors wield a copy of this weapon while in your main hand"] = { }, -- just make the mod blue, handled in CalcSetup
+ ["each summoned phantasm grants you phantasmal might"] = { flag("Condition:PhantasmalMight") },
+
-- Projectiles
["skills chain %+(%d) times"] = function(num) return { mod("ChainCountMax", "BASE", num) } end,
["skills chain an additional time while at maximum frenzy charges"] = { mod("ChainCountMax", "BASE", 1, { type = "StatThreshold", stat = "FrenzyCharges", thresholdStat = "FrenzyChargesMax" }) },
@@ -2250,7 +2273,6 @@ local specialModList = {
["recover (%d+)%% of your maximum mana when you block"] = function(num) return { mod("ManaOnBlock", "BASE", 1, { type = "PerStat", stat = "Mana", div = 100 / num }) } end,
["recover (%d+)%% of energy shield when you block"] = function(num) return { mod("EnergyShieldOnBlock", "BASE", 1, { type = "PerStat", stat = "EnergyShield", div = 100 / num }) } end,
["replenishes energy shield by (%d+)%% of armour when you block"] = function(num) return { mod("EnergyShieldOnBlock", "BASE", 1, { type = "PerStat", stat = "Armour", div = 100 / num }) } end,
- ["you have no life regeneration"] = { flag("NoLifeRegen") },
["cannot leech or regenerate mana"] = { flag("NoManaRegen"), flag("CannotLeechMana") },
["right ring slot: you cannot regenerate mana" ] = { flag("NoManaRegen", { type = "SlotNumber", num = 2 }) },
["you cannot recharge energy shield"] = { flag("NoEnergyShieldRecharge") },
@@ -2343,7 +2365,7 @@ local specialModList = {
["the effect of chill on you is reversed"] = { flag("SelfChillEffectIsReversed") },
["your movement speed is (%d+)%% of its base value"] = function(num) return { mod("MovementSpeed", "OVERRIDE", num / 100) } end,
["armour also applies to lightning damage taken from hits"] = { flag("ArmourAppliesToLightningDamageTaken") },
- ["lightning resistance does not effect lightning damage taken"] = { flag("SelfIgnoreLightningResistance") },
+ ["lightning resistance does not affect lightning damage taken"] = { flag("SelfIgnoreLightningResistance") },
-- Knockback
["cannot knock enemies back"] = { flag("CannotKnockback") },
["knocks back enemies if you get a critical strike with a staff"] = { mod("EnemyKnockbackChance", "BASE", 100, nil, ModFlag.Staff, { type = "Condition", var = "CriticalStrike" }) },
@@ -2352,6 +2374,14 @@ local specialModList = {
["adds knockback during flask effect"] = { mod("EnemyKnockbackChance", "BASE", 100, { type = "Condition", var = "UsingFlask" }) },
["adds knockback to melee attacks during flask effect"] = { mod("EnemyKnockbackChance", "BASE", 100, nil, ModFlag.Melee, { type = "Condition", var = "UsingFlask" }) },
["knockback direction is reversed"] = { mod("EnemyKnockbackDistance", "MORE", -200) },
+ -- Culling
+ ["culling strike"] = { mod("CullPercent", "MAX", 10) },
+ ["hits with this weapon have culling strike against bleeding enemies"] = { mod("CullPercent", "MAX", 10, { type = "ActorCondition", actor = "enemy", var = "Bleeding" }) },
+ ["you have culling strike against cursed enemies"] = { mod("CullPercent", "MAX", 10, { type = "ActorCondition", actor = "enemy", var = "Cursed" }) },
+ ["critical strikes have culling strike"] = { mod("CriticalCullPercent", "MAX", 10) },
+ ["your spells have culling strike"] = { mod("CullPercent", "MAX", 10, nil, ModFlag.Spell) },
+ ["culling strike against burning enemies"] = { mod("CullPercent", "MAX", 10, { type = "ActorCondition", actor = "enemy", var = "Burning" }) },
+ ["culling strike against marked enemy"] = { mod("CullPercent", "MAX", 10, { type = "ActorCondition", actor = "enemy", var = "Marked" }) },
-- Flasks
["flasks do not apply to you"] = { flag("FlasksDoNotApplyToPlayer") },
["flasks apply to your zombies and spectres"] = { flag("FlasksApplyToMinion", { type = "SkillName", skillNameList = { "Raise Zombie", "Raise Spectre" } }) },
@@ -2469,7 +2499,6 @@ local specialModList = {
["[ct][ar][si][tg]g?e?r? a socketed cold s[pk][ei]ll on melee critical strike"] = { mod("ExtraSupport", "LIST", { skillId = "SupportUniqueCosprisMaliceColdSpellsCastOnMeleeCriticalStrike", level = 1 }, { type = "SocketedIn", slotName = "{SlotName}" }) },
["your curses can apply to hexproof enemies"] = { flag("CursesIgnoreHexproof") },
["your hexes can affect hexproof enemies"] = { flag("CursesIgnoreHexproof") },
- ["you have onslaught while you have fortify"] = { flag("Condition:Onslaught", { type = "Condition", var = "Fortify" }) },
["reserves (%d+)%% of life"] = function(num) return { mod("ExtraLifeReserved", "BASE", num) } end,
["(%d+)%% of cold damage taken as lightning"] = function(num) return { mod("ColdDamageTakenAsLightning", "BASE", num) } end,
["(%d+)%% of fire damage taken as lightning"] = function(num) return { mod("FireDamageTakenAsLightning", "BASE", num) } end,
@@ -2478,7 +2507,7 @@ local specialModList = {
["mana reservation of herald skills is always (%d+)%%"] = function(num) return { mod("SkillData", "LIST", { key = "manaCostForced", value = num }, { type = "SkillType", skillType = SkillType.Herald }) } end,
["([%a%s]+) reserves no mana"] = function(_, name) return { mod("SkillData", "LIST", { key = "manaCostForced", value = 0 }, { type = "SkillId", skillId = gemIdLookup[name] }) } end,
["banner skills reserve no mana"] = { mod("SkillData", "LIST", { key = "manaCostForced", value = 0 }, { type = "SkillName", skillNameList = { "Dread Banner", "War Banner" } }) },
- ["placed banners also grant (%d+)%% increased attack damage to you and allies"] = function(num) return { mod("ExtraAura", "LIST", { mod = mod("Damage", "INC", num, nil, ModFlag.Attack) }, { type = "Condition", var = "BannerPlanted" }) } end,
+ ["placed banners also grant (%d+)%% increased attack damage to you and allies"] = function(num) return { mod("ExtraAuraEffect", "LIST", { mod = mod("Damage", "INC", num, nil, ModFlag.Attack) }, { type = "Condition", var = "BannerPlanted" }) } end,
["your aura skills are disabled"] = { flag("DisableSkill", { type = "SkillType", skillType = SkillType.Aura }) },
["your spells are disabled"] = { flag("DisableSkill", { type = "SkillType", skillType = SkillType.Spell }) },
["strength's damage bonus instead grants (%d+)%% increased melee physical damage per (%d+) strength"] = function(num, _, perStr) return { mod("StrDmgBonusRatioOverride", "BASE", num / tonumber(perStr)) } end,
@@ -2561,9 +2590,6 @@ local specialModList = {
flag("Condition:CreateProfaneGround"),
mod("Dummy", "DUMMY", 1, { type = "Condition", var = "CreateProfaneGround" }), -- Make the Configuration option appear
},
- ["nearby enemies have (%-%d+)%% to fire resistance"] = function(num) return { mod("EnemyModifier", "LIST", { mod = mod("FireResist", "BASE", num) }) } end,
- ["nearby enemies have (%-%d+)%% to lightning resistance"] = function(num) return { mod("EnemyModifier", "LIST", { mod = mod("LightningResist", "BASE", num) }) } end,
- ["nearby enemies take (%d+)%% increased physical damage"] = function(num) return { mod("EnemyModifier", "LIST", { mod = mod("PhysicalDamageTaken", "INC", num) }) } end,
["you count as dual wielding while you are unencumbered"] = { flag("Condition:DualWielding", { type = "Condition", var = "Unencumbered" }) },
["skills supported by intensify have %+(%d) to maximum intensity"] = function(num) return { mod("Multiplier:IntensityLimit", "BASE", num) } end,
["spells which can gain intensity have %+(%d) to maximum intensity"] = function(num) return { mod("Multiplier:IntensityLimit", "BASE", num) } end,
@@ -2626,10 +2652,11 @@ local specialModList = {
["caustic arrow has (%d+)%% chance to inflict withered on hit for (%d+) seconds base duration"] = { mod("ExtraSkillMod", "LIST", { mod = mod("Condition:CanWither", "FLAG", true) }, { type = "SkillName", skillName = "Caustic Arrow" } ) },
["venom gyre has a (%d+)%% chance to inflict withered for (%d+) seconds on hit"] = { mod("ExtraSkillMod", "LIST", { mod = mod("Condition:CanWither", "FLAG", true) }, { type = "SkillName", skillName = "Venom Gyre" } ) },
["sigil of power's buff also grants (%d+)%% increased critical strike chance per stage"] = function(num) return { mod("CritChance", "INC", num, 0, 0, { type = "Multiplier", var = "SigilOfPowerStage", limit = 4 }, { type = "GlobalEffect", effectType = "Buff", effectName = "Sigil of Power" } ) } end,
+ ["cobra lash chains (%d+) additional times"] = function(num) return { mod("ExtraSkillMod", "LIST", { mod = mod("ChainCountMax", "BASE", num) }, { type = "SkillName", skillName = "Cobra Lash" }) } end,
-- Alternate Quality
["quality does not increase physical damage"] = { mod("AlternateQualityWeapon", "BASE", 1) },
["(%d+)%% increased critical strike chance per 4%% quality"] = function(num) return { mod("AlternateQualityLocalCritChancePer4Quality", "INC", num) } end,
- ["grants (%d+)%% increased accuracy per (%d+)%% quality"] = function(num, _, div) return { mod("Accuracy", "INC", num, { type = "Condition", var = "{Hand}Attack" }, { type = "Multiplier", var = "QualityOn{SlotName}", div = tonumber(div) }) } end,
+ ["grants (%d+)%% increased accuracy per (%d+)%% quality"] = function(num, _, div) return { mod("Accuracy", "INC", num, { type = "Multiplier", var = "QualityOn{SlotName}", div = tonumber(div) }) } end,
["(%d+)%% increased attack speed per 8%% quality"] = function(num) return { mod("AlternateQualityLocalAttackSpeedPer8Quality", "INC", num) } end,
["%+(%d+) weapon range per 10%% quality"] = function(num) return { mod("AlternateQualityLocalWeaponRangePer10Quality", "BASE", num) } end,
["grants (%d+)%% increased elemental damage per (%d+)%% quality"] = function(num, _, div) return { mod("ElementalDamage", "INC", num, { type = "Multiplier", var = "QualityOn{SlotName}", div = tonumber(div) }) } end,
@@ -2643,11 +2670,8 @@ local specialModList = {
["grants %+(%d+)%% to fire resistance per (%d+)%% quality"] = function(num, _, div) return { mod("FireResist", "BASE", num, { type = "Multiplier", var = "QualityOn{SlotName}", div = tonumber(div) }) } end,
["grants %+(%d+)%% to cold resistance per (%d+)%% quality"] = function(num, _, div) return { mod("ColdResist", "BASE", num, { type = "Multiplier", var = "QualityOn{SlotName}", div = tonumber(div) }) } end,
["grants %+(%d+)%% to lightning resistance per (%d+)%% quality"] = function(num, _, div) return { mod("LightningResist", "BASE", num, { type = "Multiplier", var = "QualityOn{SlotName}", div = tonumber(div) }) } end,
- ["infernal blow debuff deals an additional (%d+)%% of damage per charge"] = function(num) return { mod("DebuffEffect", "BASE", num, { type = "SkillName", skillName = "Infernal Blow"}) } end,
- -- Quality modifiers
["%+(%d+)%% to quality"] = function(num) return { mod("Quality", "BASE", num) } end,
- ["%+(%d+)%% to maximum quality"] = function(num) return { mod("Quality", "BASE", num) } end,
- ["(%d+)%% to maximum quality"] = function(num) return { mod("Quality", "BASE", num) } end,
+ ["infernal blow debuff deals an additional (%d+)%% of damage per charge"] = function(num) return { mod("DebuffEffect", "BASE", num, { type = "SkillName", skillName = "Infernal Blow"}) } end,
-- Display-only modifiers
["extra gore"] = { },
["prefixes:"] = { },
@@ -2695,7 +2719,6 @@ end
-- Modifiers that are recognised but unsupported
local unsupportedModList = {
- ["culling strike"] = true,
["properties are doubled while in a breach"] = true,
}
@@ -2762,6 +2785,14 @@ local regenTypes = {
["maximum mana and energy shield"] = { "ManaRegen", "EnergyShieldRegen" },
["rage"] = "RageRegen",
}
+local flagTypes = {
+ ["phasing"] = "Condition:Phasing",
+ ["onslaught"] = "Condition:Onslaught",
+ ["fortify"] = "Condition:Fortify",
+ ["unholy might"] = "Condition:UnholyMight",
+ ["tailwind"] = "Condition:Tailwind",
+ ["no life regeneration"] = "NoLifeRegen",
+}
-- Build active skill name lookup
local skillNameList = {
@@ -3026,10 +3057,10 @@ local jewelThresholdFuncs = {
["With at least 40 Dexterity in Radius, Ice Shot Pierces 3 additional Targets"] = getThreshold("Dex", "PierceCount", "BASE", 3, { type = "SkillName", skillName = "Ice Shot" }),
["With at least 40 Dexterity in Radius, Ice Shot Pierces 5 additional Targets"] = getThreshold("Dex", "PierceCount", "BASE", 5, { type = "SkillName", skillName = "Ice Shot" }),
["With at least 40 Intelligence in Radius, Frostbolt fires 2 additional Projectiles"] = getThreshold("Int", "ProjectileCount", "BASE", 2, { type = "SkillName", skillName = "Frostbolt" }),
- ["With at least 40 Intelligence in Radius, Magma Orb fires an additional Projectile"] = getThreshold("Int", "ProjectileCount", "BASE", 1, { type = "SkillName", skillName = "Magma Orb" }),
- ["With at least 40 Intelligence in Radius, Magma Orb has 10% increased Area of Effect per Chain"] = getThreshold("Int", "AreaOfEffect", "INC", 10, { type = "SkillName", skillName = "Magma Orb" }, { type = "PerStat", stat = "Chain" }),
- ["With at least 40 Intelligence in Radius, Magma Orb deals 40% more damage per chain"] = getThreshold("Int", "Damage", "MORE", 40, { type = "SkillName", skillName = "Magma Orb" }, { type = "PerStat", stat = "Chain" }),
- ["With at least 40 Intelligence in Radius, Magma Orb deals 50% less damage"] = getThreshold("Int", "Damage", "MORE", -50, { type = "SkillName", skillName = "Magma Orb" }),
+ ["With at least 40 Intelligence in Radius, Rolling Magma fires an additional Projectile"] = getThreshold("Int", "ProjectileCount", "BASE", 1, { type = "SkillName", skillName = "Rolling Magma" }),
+ ["With at least 40 Intelligence in Radius, Rolling Magma has 10% increased Area of Effect per Chain"] = getThreshold("Int", "AreaOfEffect", "INC", 10, { type = "SkillName", skillName = "Rolling Magma" }, { type = "PerStat", stat = "Chain" }),
+ ["With at least 40 Intelligence in Radius, Rolling Magma deals 40% more damage per chain"] = getThreshold("Int", "Damage", "MORE", 40, { type = "SkillName", skillName = "Rolling Magma" }, { type = "PerStat", stat = "Chain" }),
+ ["With at least 40 Intelligence in Radius, Rolling Magma deals 50% less damage"] = getThreshold("Int", "Damage", "MORE", -50, { type = "SkillName", skillName = "Rolling Magma" }),
["With at least 40 Dexterity in Radius, Shrapnel Shot has 25% increased Area of Effect"] = getThreshold("Dex", "AreaOfEffect", "INC", 25, { type = "SkillName", skillName = "Shrapnel Shot" }),
["With at least 40 Dexterity in Radius, Shrapnel Shot's cone has a 50% chance to deal Double Damage"] = getThreshold("Dex", "DoubleDamageChance", "BASE", 50, { type = "SkillName", skillName = "Shrapnel Shot" }, { type = "SkillPart", skillPart = 2 }),
["With at least 40 Intelligence in Radius, Freezing Pulse fires 2 additional Projectiles"] = getThreshold("Int", "ProjectileCount", "BASE", 2, { type = "SkillName", skillName = "Freezing Pulse" }),
@@ -3182,8 +3213,11 @@ local function parseMod(line, order)
line = line .. " "
-- Check for a flag/tag specification at the start of the line
- local preFlag
- preFlag, line = scan(line, preFlagList)
+ local preFlag, preFlagCap
+ preFlag, line, preFlagCap = scan(line, preFlagList)
+ if type(preFlag) == "function" then
+ preFlag = preFlag(unpack(preFlagCap))
+ end
-- Check for skill name at the start of the line
local skillTag
@@ -3195,7 +3229,6 @@ local function parseMod(line, order)
if not modForm then
return nil, line
end
- local num = tonumber(formCap[1])
-- Check for tags (per-charge, conditionals)
local modTag, modTag2, tagCap
@@ -3222,6 +3255,12 @@ local function parseMod(line, order)
end
local _
_, line = scan(line, modNameList, true)
+ elseif modForm == "FLAG" then
+ formCap[1], line = scan(line, flagTypes, true)
+ if not formCap[1] then
+ return nil, line
+ end
+ modName, line = scan(line, modNameList, true)
else
modName, line = scan(line, modNameList, true)
end
@@ -3234,18 +3273,18 @@ local function parseMod(line, order)
modFlag, line = scan(line, modFlagList, true)
-- Find modifier value and type according to form
- local modValue = num
+ local modValue = tonumber(formCap[1]) or formCap[1]
local modType = "BASE"
local modSuffix
if modForm == "INC" then
modType = "INC"
elseif modForm == "RED" then
- modValue = -num
+ modValue = -modValue
modType = "INC"
elseif modForm == "MORE" then
modType = "MORE"
elseif modForm == "LESS" then
- modValue = -num
+ modValue = -modValue
modType = "MORE"
elseif modForm == "BASE" then
modSuffix, line = scan(line, suffixTypes, true)
@@ -3293,6 +3332,10 @@ local function parseMod(line, order)
modValue = { tonumber(formCap[1]), tonumber(formCap[2]) }
modName = { damageType.."Min", damageType.."Max" }
modFlag = modFlag or { keywordFlags = bor(KeywordFlag.Attack, KeywordFlag.Spell) }
+ elseif modForm == "FLAG" then
+ modName = modValue
+ modValue = true
+ modType = "FLAG"
end
if not modName then
return { }, line
@@ -3369,6 +3412,10 @@ local function parseMod(line, order)
for i, effectMod in ipairs(modList) do
modList[i] = mod("convertFortifyBuff", "LIST", { mod = effectMod })
end
+ elseif misc.applyToEnemy then
+ for i, effectMod in ipairs(modList) do
+ modList[i] = mod("EnemyModifier", "LIST", { mod = effectMod })
+ end
end
end
return modList, line:match("%S") and line
diff --git b/Modules/StatDescriber.lua a/Modules/StatDescriber.lua
index 855c18ee..0eebe89c 100644
--- b/Modules/StatDescriber.lua
+++ a/Modules/StatDescriber.lua
@@ -60,6 +60,10 @@ local function applySpecial(val, spec)
elseif spec.k == "divide_by_fifteen_0dp" then
val[spec.v].min = val[spec.v].min / 15
val[spec.v].max = val[spec.v].max / 15
+ elseif spec.k == "divide_by_twelve" then
+ val[spec.v].min = round(val[spec.v].min / 12, 1)
+ val[spec.v].max = round(val[spec.v].max / 12, 1)
+ val[spec.v].fmt = "g"
elseif spec.k == "divide_by_one_hundred" then
val[spec.v].min = round(val[spec.v].min / 100, 1)
val[spec.v].max = round(val[spec.v].max / 100, 1)
@@ -130,9 +134,15 @@ local function applySpecial(val, spec)
val[spec.v].min = 100 + round(val[spec.v].min / 100, 1)
val[spec.v].max = 100 + round(val[spec.v].max / 100, 1)
val[spec.v].fmt = "g"
- elseif spec.k == "reminderstring" or spec.k == "canonical_line" then
- else
- --ConPrintf("Unknown description function: %s", spec.k)
+ elseif spec.k == "multiply_by_four" then
+ val[spec.v].min = val[spec.v].min * 4
+ val[spec.v].max = val[spec.v].max * 4
+ elseif spec.k == "times_twenty" then
+ val[spec.v].min = val[spec.v].min * 20
+ val[spec.v].max = val[spec.v].max * 20
+ elseif spec.k == "reminderstring" or spec.k == "canonical_line" or spec.k == "_stat" then
+ elseif spec.k then
+ ConPrintf("Unknown description function: %s", spec.k)
end
end
@@ -224,4 +234,4 @@ return function(stats, scopeName)
end
end
return out
-end
\ No newline at end of file
+end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment