Created
March 9, 2021 21:46
-
-
Save tntmarket/18f9cc0531876f9f104b5e0820388a31 to your computer and use it in GitHub Desktop.
The error started happening after I copied over these changes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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