diff --git a/spec/System/TestDefence_spec.lua b/spec/System/TestDefence_spec.lua index 4fb37d87f8..84a726515a 100644 --- a/spec/System/TestDefence_spec.lua +++ b/spec/System/TestDefence_spec.lua @@ -7,6 +7,26 @@ describe("TestDefence", function() -- newBuild() takes care of resetting everything in setup() end) + it("converts physical damage over time independently of hit damage", function() + build.configTab.input.enemyDamageType = "DamageOverTime" + for _, damageType in ipairs({ "Physical", "Lightning", "Cold", "Fire", "Chaos" }) do + build.configTab.input["enemy"..damageType.."Damage"] = damageType == "Physical" and 1000 or 0 + end + local common = "+300% to Chaos Resistance\n10% of Physical Damage taken as Chaos Damage\n20% of Physical Damage from Hits taken as Chaos Damage" + -- Shared, hit-only and DoT-only conversion must stay separate, including on removal. + for _, value in ipairs({ 10, 0 }) do + build.configTab.input.customMods = common..(value > 0 and "\n"..value.."% of Physical Damage over Time taken as Chaos Damage" or "") + build.configTab:BuildModList() + runCallback("OnFrame") + local actor = build.calcsTab.mainEnv.player + local output = actor.output + assert.are.equal(925 - 7.5 * value, output.TotalDegen) + assert.are.equal(70, actor.damageShiftTable.Physical.Physical) + assert.are.equal(30, actor.damageShiftTable.Physical.Chaos) + assert.are.equal(100, actor.damageOverTimeShiftTable.Cold.Cold) + end + end) + -- a small helper function to calculate damage taken from limited test parameters local function takenHitFromTypeMaxHit(type, enemyDamageMulti) return build.calcsTab.calcs.takenHitFromDamage(build.calcsTab.calcsOutput[type.."MaximumHitTaken"] * (enemyDamageMulti or 1), type, build.calcsTab.calcsEnv.player) diff --git a/spec/System/TestItemMods_spec.lua b/spec/System/TestItemMods_spec.lua index f4ce0c5903..7a5e65b4a3 100644 --- a/spec/System/TestItemMods_spec.lua +++ b/spec/System/TestItemMods_spec.lua @@ -7,6 +7,352 @@ describe("TetsItemMods", function() -- newBuild() takes care of resetting everything in setup() end) + local function setWickermanCustomMods(text) + build.configTab.input.customMods = text + build.configTab:BuildModList() + runCallback("OnFrame") + end + + it("exposes Hinder configuration when the modifier is present", function() + setWickermanCustomMods("10% chance to Hinder Enemies on Hit with Spells") + assert.is_truthy(build.calcsTab.mainEnv.enemyConditionsUsed.Hindered) + setWickermanCustomMods("") + assert.is_falsy(build.calcsTab.mainEnv.enemyConditionsUsed.Hindered) + end) + + it("defaults missing unreserved life from Petrified Blood and lets zero override it", function() + local common = "Regenerate 100 Life per second\n2% increased Life Recovery rate per 5% missing Unreserved Life" + setWickermanCustomMods(common) + assert.are.equal(1, build.calcsTab.mainOutput.LifeRecoveryRateMod) + build.skillsTab:PasteSocketGroup("Petrified Blood 20/0 1") + setWickermanCustomMods(common) + assert.are.near(1.2, build.calcsTab.mainOutput.LifeRecoveryRateMod, 1e-9) + setWickermanCustomMods(common.."\nYou count as on Low Life while at 55% of maximum Life or below") + assert.are.near(1.18, build.calcsTab.mainOutput.LifeRecoveryRateMod, 1e-9) + build.configTab.input.missingUnreservedLifePercent = 25 + setWickermanCustomMods(common) + assert.are.near(1.1, build.calcsTab.mainOutput.LifeRecoveryRateMod, 1e-9) + build.configTab.input.missingUnreservedLifePercent = nil + setWickermanCustomMods(common.."\nReserves 25% of Life") + assert.are.near(1.12, build.calcsTab.mainOutput.LifeRecoveryRateMod, 1e-9) + setWickermanCustomMods(common.."\nReserves 50% of Life") + assert.are.equal(1, build.calcsTab.mainOutput.LifeRecoveryRateMod) + build.configTab.input.missingUnreservedLifePercent = 0 + setWickermanCustomMods(common) + loadBuildFromXML(build:SaveDB("code")) + assert.are.equal(0, build.calcsTab.mainOutput.MissingUnreservedLifePercent) + assert.are.equal(1, build.calcsTab.mainOutput.LifeRecoveryRateMod) + build.configTab.input.missingUnreservedLifePercent = nil + build.configTab:BuildModList() + runCallback("OnFrame") + loadBuildFromXML(build:SaveDB("code")) + assert.are.equal(50, build.calcsTab.mainOutput.MissingUnreservedLifePercent) + assert.are.near(1.2, build.calcsTab.mainOutput.LifeRecoveryRateMod, 1e-9) + end) + + it("poisons on melee critical strikes without granting poison to normal hits or spells", function() + build.itemsTab:CreateDisplayItemFromRaw("Test Sword\nRusted Sword") + build.itemsTab:AddDisplayItem() + for _, skill in ipairs({ "Heavy Strike", "Fireball" }) do + build.skillsTab:PasteSocketGroup(skill.." 20/0 1") + build.mainSocketGroup = #build.skillsTab.socketGroupList + setWickermanCustomMods("Melee Critical Strikes Poison the Enemy") + local output = build.calcsTab.mainOutput.MainHand or build.calcsTab.mainOutput + assert.are.equal(skill == "Heavy Strike" and 100 or 0, output.PoisonChanceOnCrit) + assert.are.equal(0, output.PoisonChanceOnHit) + end + end) + + it("applies faster minion ailments without changing their hit damage", function() + build.skillsTab:PasteSocketGroup("Raise Zombie 20/0 1") + local common = "Minions have 100% chance to Poison on Hit\nMinions have 100% chance to cause Bleeding\nMinions have 100% chance to Ignite\nMinions gain 100% of Physical Damage as Extra Fire Damage" + setWickermanCustomMods(common) + local base = copyTable(build.calcsTab.mainEnv.minion.output, true) + setWickermanCustomMods(common.."\nDamaging Ailments inflicted by Minions deal damage 20% Faster") + local output = build.calcsTab.mainEnv.minion.output + for _, ailment in ipairs({ "Poison", "Bleed", "Ignite" }) do + assert.is_true(base[ailment.."Duration"] > 0) + assert.are.near(base[ailment.."Duration"] / 1.2, output[ailment.."Duration"], 0.001) + end + assert.are.equal(base.AverageDamage, output.AverageDamage) + end) + + it("checks both blind and sand stance for the enemy damage bonus", function() + build.skillsTab:PasteSocketGroup("Fireball 20/0 1") + setWickermanCustomMods("") + local base = build.calcsTab.mainOutput.AverageDamage + for _, sand in ipairs({ false, true }) do + for _, blind in ipairs({ false, true }) do + build.configTab.input.bloodSandStance = sand and "SAND" or "BLOOD" + build.configTab.input.conditionEnemyBlinded = blind + setWickermanCustomMods("Enemies you Blind while in Sand Stance take 20% increased Damage") + assert.are.near(base * (sand and blind and 1.2 or 1), build.calcsTab.mainOutput.AverageDamage, 1) + end + end + build.configTab.input.conditionEnemyBlinded = false + for _, blind in ipairs({ false, true, false }) do + build.configTab.input.conditionBlinded = blind + setWickermanCustomMods("20% increased Damage while Blind") + assert.are.near(base * (blind and 1.2 or 1), build.calcsTab.mainOutput.AverageDamage, 1) + end + end) + + it("grants influence-gated curses as item skills only while the requirement is met", function() + for _, case in ipairs({ { "Vulnerability", "Hunter", "basilisk" }, { "Vulnerability", "Hunter", "basilisk", true }, { "Assassin's Mark", "Elder", "elder", true } }) do + newBuild() + build.skillsTab:PasteSocketGroup("Ethereal Knives 20/0 1") + setWickermanCustomMods("") + local baseDamage = build.calcsTab.mainOutput.AverageDamage + local line = "Curse Enemies with "..case[1].." on Hit if 1 "..case[2].." Items are Equipped" + local item = new("Item"):Item("Test Amulet\nPaua Amulet"..(case[4] and "" or "\n"..line)) + build.itemsTab:AddItem(item, true) + build.itemsTab.slots.Amulet:SetSelItemId(item.id) + if case[4] then setWickermanCustomMods(line) end + for _, equipped in ipairs({ false, true, false }) do + item[case[3]] = equipped + build.buildFlag = true + runCallback("OnFrame") + local count = 0 + for _, group in ipairs(build.skillsTab.socketGroupList) do + if (case[4] and group.source == "Config" or not case[4] and group.sourceItem == item) and group.gemList[1].nameSpec == case[1] then count = count + 1 end + end + assert.are.equal(equipped and 1 or 0, count) + assert.are.equal(equipped, build.calcsTab.mainOutput.AverageDamage > baseDamage) + end + if case[4] then + item[case[3]] = true + setWickermanCustomMods(line) + setWickermanCustomMods("") + assert.are.equal(baseDamage, build.calcsTab.mainOutput.AverageDamage) + for _, group in ipairs(build.skillsTab.socketGroupList) do assert.are_not.equal("Config", group.source) end + end + end + end) + + it("scales only Iron Flask effects", function() + local flask = new("Item"):Item("Rarity: NORMAL\nIron Flask\nQuality: 0") + build.itemsTab:AddItem(flask, true) + build.itemsTab.slots["Flask 1"]:SetSelItemId(flask.id) + build.itemsTab.slots["Flask 1"].active = true + setWickermanCustomMods("") + local baseWard = build.calcsTab.mainOutput.Ward + assert.is_true(baseWard > 0) + setWickermanCustomMods("Iron Flasks applied to you have 50% increased Effect") + assert.are.equal(baseWard * 1.5, build.calcsTab.mainOutput.Ward) + assert.are.equal(0, build.calcsTab.mainEnv.player.modDB:Sum("INC", nil, "FlaskEffect")) + end) + + it("adds offering damage to its recipients", function() + build.skillsTab:PasteSocketGroup("Raise Zombie 20/0 1\n") + build.skillsTab:PasteSocketGroup("Flesh Offering 20/0 1") + setWickermanCustomMods("") + local base = build.calcsTab.mainEnv.minion.output.TotalDPS + setWickermanCustomMods("Offerings you create also grant 20% increased Damage") + assert.are.near(base * 1.2, build.calcsTab.mainEnv.minion.output.TotalDPS, 1) + assert.are.equal(0, build.calcsTab.mainEnv.player.modDB:Sum("INC", nil, "Damage")) + end) + + it("limits boot-socketed mana cost efficiency to boots", function() + for _, slot in ipairs({ "Boots", "Helmet" }) do + newBuild() + build.skillsTab:PasteSocketGroup("Slot: "..slot.."\nFireball 20/0 1") + setWickermanCustomMods(slot == "Boots" and "20% increased Mana Cost Efficiency" or "") + local expected = build.calcsTab.mainOutput.ManaCost + setWickermanCustomMods("Skills socketed in Equipped Boots have 20% increased Mana Cost Efficiency") + assert.are.equal(expected, build.calcsTab.mainOutput.ManaCost) + end + end) + + it("converts light radius and mana regeneration increases without converting more modifiers", function() + local cases = { + { "Light Radius", "Life Regeneration Rate", "Increases and Reductions to Light Radius also apply to Life Regeneration Rate at 50% value", "LifeRegen", "Regenerate 100 Life per second" }, + { "Mana Regeneration Rate", "Energy Shield Recharge Rate", "Increases and Reductions to Mana Regeneration Rate also apply to Energy Shield Recharge Rate at 50% of their value", "EnergyShieldRecharge", "+1000 to maximum Energy Shield" }, + } + for _, case in ipairs(cases) do + for _, value in ipairs({ 40, -20 }) do + setWickermanCustomMods(case[5].."\n"..math.abs(value / 2).."% "..(value < 0 and "reduced " or "increased ")..case[2]) + local expected = build.calcsTab.mainOutput[case[4]] + assert.is_true(expected > 0) + setWickermanCustomMods(case[5].."\n"..math.abs(value).."% "..(value < 0 and "reduced " or "increased ")..case[1].."\n100% more "..case[1].."\n"..case[3]) + assert.are.equal(expected, build.calcsTab.mainOutput[case[4]]) + end + end + end) + + it("grants Onslaught only to recently created minions", function() + build.skillsTab:PasteSocketGroup("Raise Zombie 20/0 1") + setWickermanCustomMods("") + local base = build.calcsTab.mainEnv.minion.output.MovementSpeedMod + for _, enabled in ipairs({ false, true, false }) do + build.configTab.input.minionsConditionCreatedRecently = enabled + setWickermanCustomMods("Minions created Recently have Onslaught") + assert.are.near(base + (enabled and 0.2 or 0), build.calcsTab.mainEnv.minion.output.MovementSpeedMod, 1e-9) + assert.is_false(not not build.calcsTab.mainEnv.player.modDB:Flag(nil, "Condition:Onslaught")) + end + end) + + it("grants double damage only to skills with a positive life cost", function() + build.skillsTab:PasteSocketGroup("Fireball 20/0 1") + for _, costsLife in ipairs({ false, true, false }) do + setWickermanCustomMods("Skills that Cost Life have 20% chance to deal Double Damage"..(costsLife and "\nSkills cost Life instead of Mana" or "")) + assert.are.equal(costsLife and 20 or 0, build.calcsTab.mainOutput.DoubleDamageChance) + end + end) + + it("inverts only the specified elemental resistance", function() + build.skillsTab:PasteSocketGroup("Fireball 20/0 1") + build.configTab.input.enemyFireResist = 50 + build.configTab.input.enemyColdResist = 50 + build.configTab.input.enemyLightningResist = 50 + for _, element in ipairs({ "Cold", "Fire", "Lightning" }) do + for _, chance in ipairs({ 0, 50, 100 }) do + setWickermanCustomMods("Adds 100 to 100 Cold Damage to Spells\nAdds 100 to 100 Lightning Damage to Spells\nHits have "..chance.."% chance to treat Enemy Monster "..element.." Resistance values as inverted") + for _, damageType in ipairs({ "Cold", "Fire", "Lightning" }) do + assert.are.near(0.5 + (damageType == element and chance / 100 or 0), build.calcsTab.calcsOutput[damageType.."EffMult"], 1e-9) + end + end + end + end) + + it("adds to the maximum chill effect", function() + build.skillsTab:PasteSocketGroup("Freezing Pulse 20/0 1") + setWickermanCustomMods("+10% to Maximum Effect of Chill") + assert.are.equal(40, build.calcsTab.mainOutput.MaximumChill) + build.skillsTab:PasteSocketGroup("Cold Snap 20/0 1") + build.mainSocketGroup = #build.skillsTab.socketGroupList + setWickermanCustomMods("+10% to Maximum Effect of Chill\n400% increased Effect of Chill") + assert.are.equal(40, build.calcsTab.mainOutput.ChillSourceEffect) + setWickermanCustomMods("") + assert.are.equal(30, build.calcsTab.mainOutput.MaximumChill) + end) + + it("applies the distance-worded cold bonus and scorch using the nearby model", function() + build.skillsTab:PasteSocketGroup("Fireball 20/0 1") + setWickermanCustomMods("Adds 20 to 30 Cold Damage\nNearby Enemies are Scorched") + local expected = copyTable(build.calcsTab.mainOutput, true) + setWickermanCustomMods("You and Allies within 10 metres deal 20 to 30 additional Cold Damage\nEnemies within 10 metres are Scorched") + assert.are.equal(expected.AverageDamage, build.calcsTab.mainOutput.AverageDamage) + assert.are.equal(10, build.calcsTab.mainOutput.CurrentScorch) + end) + + for _, case in ipairs({ + { "Minions deal 25% increased Damage if you've Hit Recently", "HitRecently", "conditionHitRecently", "TotalDPS", "Minions deal 25% increased Damage" }, + { "Minions have 30% increased Movement Speed if you've used a Minion Skill Recently", "UsedMinionSkillRecently", "conditionUsedMinionSkillRecently", "MovementSpeedMod", "Minions have 30% increased Movement Speed" }, + }) do + it("checks the player for conditional minion "..case[4], function() + build.skillsTab:PasteSocketGroup("Raise Zombie 20/0 1") + setWickermanCustomMods("") + local base = build.calcsTab.mainEnv.minion.output[case[4]] + local playerMovement = build.calcsTab.mainOutput.MovementSpeedMod + -- The unconditional modifier provides a reference that preserves damage rounding. + setWickermanCustomMods(case[5]) + local activeValue = build.calcsTab.mainEnv.minion.output[case[4]] + for _, enabled in ipairs({ false, true, false }) do + build.configTab.input[case[3]] = enabled + setWickermanCustomMods(case[1]) + local env = build.calcsTab.mainEnv + -- The minion satisfies the condition even while the player does not. + assert.is_true(env.minion.modDB:GetCondition(case[2])) + assert.are.equal(enabled and activeValue or base, env.minion.output[case[4]]) + assert.are.equal(playerMovement, env.player.output.MovementSpeedMod) + end + end) + end + + it("supports Hinder immunity and removal", function() + setWickermanCustomMods("Immune to Hinder") + assert.is_true(build.calcsTab.mainOutput.HinderImmunity) + setWickermanCustomMods("") + assert.is_falsy(build.calcsTab.mainOutput.HinderImmunity) + end) + + it("reduces burning damage taken without reducing hits or other damage over time", function() + local base = copyTable(build.calcsTab.mainOutput, true) + setWickermanCustomMods("7% reduced Burning Damage taken") + local output = build.calcsTab.mainOutput + assert.are.near(base.FireTakenDotMult * 0.93, output.FireTakenDotMult, 1e-9) + assert.are.equal(base.FireTakenHitMult, output.FireTakenHitMult) + assert.are.equal(base.ColdTakenDotMult, output.ColdTakenDotMult) + end) + + for _, case in ipairs({ + { "been Hit", "conditionBeenHitRecently", "conditionUsedMovementSkillRecently" }, + { "used a Movement Skill", "conditionUsedMovementSkillRecently", "conditionBeenHitRecently" }, + }) do + it("grants the conditional Constrictor projectile only after "..case[1], function() + build.skillsTab:PasteSocketGroup("Fireball 20/0 1") + setWickermanCustomMods("Skills fire an additional Projectile if you've "..case[1].." Recently") + assert.are.equal(1, build.calcsTab.mainOutput.ProjectileCount) + build.configTab.input[case[3]] = true + build.configTab:BuildModList() + runCallback("OnFrame") + assert.are.equal(1, build.calcsTab.mainOutput.ProjectileCount) + build.configTab.input[case[2]] = true + build.configTab:BuildModList() + runCallback("OnFrame") + assert.are.equal(2, build.calcsTab.mainOutput.ProjectileCount) + end) + end + + for _, case in ipairs({ { "Armour", "Armour, Gloves, and" }, { "Evasion Rating", "Armour Gloves and" } }) do + it("scales "..case[1].." only from the four named armour slots", function() + for _, equipment in ipairs({ + { "Helmet", "Iron Hat" }, { "Body Armour", "Plate Vest" }, + { "Gloves", "Iron Gauntlets" }, { "Boots", "Iron Greaves" }, + { "Weapon 2", "Splintered Tower Shield" }, + }) do + local item = new("Item"):Item("Rarity: Normal\n"..equipment[2]) + -- Synthetic equal defences make shield exclusion and flat global bonuses observable. + build.itemsTab:AddItem(item, true) + item.armourData.Armour = 100 + item.armourData.Evasion = 100 + build.itemsTab.slots[equipment[1]]:SetSelItemId(item.id) + end + local stat = case[1] == "Armour" and "Armour" or "Evasion" + local flat = "+100 to "..case[1] + setWickermanCustomMods(flat) + local base = build.calcsTab.mainOutput[stat] + setWickermanCustomMods(flat.."\n50% increased "..case[1].." from Equipped Helmet, Body "..case[2].." Boots") + assert.are.equal(base + 200, build.calcsTab.mainOutput[stat]) + end) + end + + for _, case in ipairs({ { "Gloves", "Iron Gauntlets" }, { "Boots", "Iron Greaves" } }) do + it("scales "..case[1].." bonuses from a synthetic Wickerman item independently of slot order", function() + local target = new("Item"):Item("Rarity: Rare\nBonus target\n"..case[2].."\nImplicits: 0\n+100 to Strength\n+7 to Dexterity\n20% more maximum Mana") + build.itemsTab:AddItem(target, true) + build.itemsTab.slots[case[1]]:SetSelItemId(target.id) + setWickermanCustomMods("") + local base = copyTable(build.calcsTab.mainOutput, true) + -- Use a normal slot with a synthetic type; no Wickerman base/slot/import framework is required. + local source = new("Item"):Item("Rarity: Rare\nSynthetic Wickerman\nPaua Amulet\nImplicits: 0\n20% increased bonuses gained from Equipped "..case[1]) + source.type = "Wickerman Lower Limb" + build.itemsTab:AddItem(source, true) + build.itemsTab.slots.Amulet:SetSelItemId(source.id) + for _, first in ipairs({ true, false }) do + local slot = build.itemsTab.slots.Amulet + for index, entry in ipairs(build.itemsTab.orderedSlots) do + if entry == slot then table.remove(build.itemsTab.orderedSlots, index) break end + end + table.insert(build.itemsTab.orderedSlots, first and 1 or #build.itemsTab.orderedSlots + 1, slot) + build.buildFlag = true + runCallback("OnFrame") + assert.are.equal(base.Str + 20, build.calcsTab.mainOutput.Str) + assert.are.equal(base.Dex + 1, build.calcsTab.mainOutput.Dex) + assert.are.near(1.24, build.calcsTab.mainEnv.player.modDB:More(nil, "Mana"), 1e-9) + end + setWickermanCustomMods("10% increased bonuses gained from Equipped "..case[1]) + assert.are.equal(base.Str + 30, build.calcsTab.mainOutput.Str) + setWickermanCustomMods("30% reduced bonuses gained from Equipped "..case[1]) + assert.are.equal(base.Str - 10, build.calcsTab.mainOutput.Str) + setWickermanCustomMods("20% reduced bonuses gained from Equipped "..case[1]) + assert.are.equal(base.Str, build.calcsTab.mainOutput.Str) + build.itemsTab.slots.Amulet:SetSelItemId(0) + setWickermanCustomMods("") + assert.are.equal(base.Str, build.calcsTab.mainOutput.Str) + end) + end + local function createCraftedRing() build.itemsTab:CreateDisplayItemFromRaw([[Rarity: Rare Test Ring diff --git a/src/Classes/ConfigTab.lua b/src/Classes/ConfigTab.lua index 7a6bf0b95c..53d72f2f8b 100644 --- a/src/Classes/ConfigTab.lua +++ b/src/Classes/ConfigTab.lua @@ -968,7 +968,8 @@ function ConfigTabClass:Save(xml) t_insert(xml, child) for k, v in pairsSortByKey(configSet.input) do - if v ~= self:GetDefaultState(k, type(v)) then + -- This override distinguishes an explicit zero from the automatic missing-life assumption. + if k == "missingUnreservedLifePercent" or v ~= self:GetDefaultState(k, type(v)) then local node = { elem = "Input", attrib = { name = k } } if type(v) == "number" then node.attrib.number = tostring(v) diff --git a/src/Classes/Item.lua b/src/Classes/Item.lua index ff3e17392b..467a13ce1b 100644 --- a/src/Classes/Item.lua +++ b/src/Classes/Item.lua @@ -2649,6 +2649,8 @@ function ItemClass:BuildModList() source = self.modSource, triggered = skill.triggered, triggerChance = skill.triggerChance, + -- Keep the influence requirement so calculation setup can enable or remove the granted curse. + itemRequirement = skill.itemRequirement, }) end end diff --git a/src/Classes/SkillsTab.lua b/src/Classes/SkillsTab.lua index f1326ddc98..052c0cec33 100644 --- a/src/Classes/SkillsTab.lua +++ b/src/Classes/SkillsTab.lua @@ -382,7 +382,7 @@ function SkillsTabClass:SkillsTab(build) end self.controls.sourceNote = new("LabelControl"):LabelControl({ "TOPLEFT", self.controls.socketsLabel, "TOPLEFT" }, { 0, 30, 0, 16 }) self.controls.sourceNote.shown = function() - return self.displayGroup.source ~= nil + return self.displayGroup.source ~= nil and self.displayGroup.source ~= "Config" end self.controls.sourceNote.label = function() local label @@ -1321,6 +1321,10 @@ function SkillsTabClass:AddSocketGroupTooltip(tooltip, socketGroup) if sourceSingle then tooltip:AddLine(18, "^7Source: " .. colorCodes[sourceSingle.rarity or "NORMAL"] .. sourceSingle.name) tooltip:AddSeparator(10) + -- Identify curses granted by Custom Modifiers in the skill-group tooltip. + elseif socketGroup.source == "Config" then + tooltip:AddLine(18, "^7Source: " .. colorCodes.NORMAL .. "Custom Modifiers") + tooltip:AddSeparator(10) end local gemShown = { } for index, activeSkill in ipairs(socketGroup.displaySkillList) do diff --git a/src/Data/ModCache.lua b/src/Data/ModCache.lua index c0ba5d2363..afd5bd9a43 100755 --- a/src/Data/ModCache.lua +++ b/src/Data/ModCache.lua @@ -261,7 +261,7 @@ c["+1 to maximum number of Summoned Golems"]={{[1]={flags=0,keywordFlags=0,name= c["+1 to maximum number of Summoned Golems if you have 3 Primordial Items Socketed or Equipped"]={{[1]={[1]={threshold=3,type="MultiplierThreshold",var="PrimordialItem"},flags=0,keywordFlags=0,name="ActiveGolemLimit",type="BASE",value=1}},nil} c["+1 to maximum number of Summoned Holy Relics"]={{[1]={flags=0,keywordFlags=0,name="ActiveHolyRelicLimit",type="BASE",value=1}},nil} c["+1 to maximum number of Summoned Totems"]={{[1]={flags=0,keywordFlags=0,name="ActiveTotemLimit",type="BASE",value=1}},nil} -c["+1 to number of Sacred Wisps Summoned"]={{}," number of Sacred Wisps Summoned "} +c["+1 to number of Sacred Wisps Summoned"]={{[1]={flags=0,keywordFlags=0,name="SacredWispsSummoned",type="BASE",value=1}},nil} c["+1 to number of Summoned Arbalists"]={{[1]={flags=0,keywordFlags=0,name="ActiveArbalistLimit",type="BASE",value=1}},nil} c["+1% Chance to Block Attack Damage"]={{[1]={flags=0,keywordFlags=0,name="BlockChance",type="BASE",value=1}},nil} c["+1% Chance to Block Attack Damage per 5% Chance to Block on Equipped Shield"]={{[1]={[1]={div=5,stat="ShieldBlockChance",type="PerStat"},flags=0,keywordFlags=0,name="BlockChance",type="BASE",value=1}},nil} @@ -3580,7 +3580,7 @@ c["10% Chance to Cause Monster to Flee on Block"]={{}," to Cause Monster to Flee c["10% Chance to Cause Monster to Flee on Block 1% of Damage Leeched as Life against Cursed Enemies"]={{[1]={[1]={actor="enemy",type="ActorCondition",var="Cursed"},flags=0,keywordFlags=0,name="Damage",type="BASE",value=10}}," to Cause Monster to Flee on Block 1% of Leeched as Life "} c["10% Chance to Cause Monster to Flee on Block 100% Chance to Cause Monster to Flee on Block"]={{}," to Cause Monster to Flee on Block 100% Chance to Cause Monster to Flee on Block "} c["10% Chance to Inflict Cold Exposure on Hit with Cold Damage"]={{[1]={flags=0,keywordFlags=0,name="ColdExposureChance",type="BASE",value=10}},nil} -c["10% Global chance to Blind Enemies on hit"]={{},"% chance to Blind Enemies on hit "} +c["10% Global chance to Blind Enemies on hit"]={{[1]={flags=0,keywordFlags=0,name="BlindChance",type="BASE",value=10}},nil} c["10% Global chance to Blind Enemies on hit Gain 1 Mana on Kill per Level"]={{[1]={[1]={type="Global"},[2]={type="Condition",var="KilledRecently"},[3]={type="Multiplier",var="Level"},flags=0,keywordFlags=0,name="Mana",type="BASE",value=10}},"% chance to Blind Enemies on hit Gain 1 "} c["10% additional Physical Damage Reduction while stationary"]={{[1]={[1]={type="Condition",var="Stationary"},flags=0,keywordFlags=0,name="PhysicalDamageReduction",type="BASE",value=10}},nil} c["10% chance for Energy Shield Recharge to start when you Kill an Enemy"]={{[1]={[1]={type="Condition",var="KilledRecently"},flags=0,keywordFlags=0,name="EnergyShield",type="BASE",value=10}}," for Recharge to start "} @@ -3622,7 +3622,7 @@ c["10% chance to Freeze"]={{[1]={flags=0,keywordFlags=0,name="EnemyFreezeChance" c["10% chance to Freeze during any Flask Effect"]={{[1]={[1]={type="Condition",var="UsingFlask"},flags=0,keywordFlags=0,name="EnemyFreezeChance",type="BASE",value=10}},nil} c["10% chance to Freeze, Shock and Ignite"]={{[1]={flags=0,keywordFlags=0,name="EnemyFreezeChance",type="BASE",value=10},[2]={flags=0,keywordFlags=0,name="EnemyShockChance",type="BASE",value=10},[3]={flags=0,keywordFlags=0,name="EnemyIgniteChance",type="BASE",value=10}},nil} c["10% chance to Freeze, Shock and Ignite while affected by a Herald"]={{[1]={[1]={type="Condition",var="AffectedByHerald"},flags=0,keywordFlags=0,name="EnemyFreezeChance",type="BASE",value=10},[2]={[1]={type="Condition",var="AffectedByHerald"},flags=0,keywordFlags=0,name="EnemyShockChance",type="BASE",value=10},[3]={[1]={type="Condition",var="AffectedByHerald"},flags=0,keywordFlags=0,name="EnemyIgniteChance",type="BASE",value=10}},nil} -c["10% chance to Hinder Enemies on Hit with Spells"]={{}," to Hinder Enemies "} +c["10% chance to Hinder Enemies on Hit with Spells"]={{[1]={flags=2,keywordFlags=0,name="HinderChance",type="BASE",value=10},[2]={[1]={actor="enemy",type="ActorCondition",var="Hindered"},flags=0,keywordFlags=0,name="Dummy",source="Select enemy Hindered in Configuration",type="DUMMY",value=1}},nil} c["10% chance to Ignite"]={{[1]={flags=0,keywordFlags=0,name="EnemyIgniteChance",type="BASE",value=10}},nil} c["10% chance to Ignite during any Flask Effect"]={{[1]={[1]={type="Condition",var="UsingFlask"},flags=0,keywordFlags=0,name="EnemyIgniteChance",type="BASE",value=10}},nil} c["10% chance to Impale Enemies on Hit with Attacks"]={{[1]={flags=0,keywordFlags=65536,name="ImpaleChance",type="BASE",value=10}},nil} @@ -4459,12 +4459,12 @@ c["12 to 22 Added Physical Damage with Bow Attacks"]={{[1]={flags=131076,keyword c["12% Chance for Traps to Trigger an additional time"]={{}," to Trigger an additional time "} c["12% Chance to Block Attack Damage"]={{[1]={flags=0,keywordFlags=0,name="BlockChance",type="BASE",value=12}},nil} c["12% Chance to Block Spell Damage"]={{[1]={flags=0,keywordFlags=0,name="SpellBlockChance",type="BASE",value=12}},nil} -c["12% Global chance to Blind Enemies on hit"]={{},"% chance to Blind Enemies on hit "} +c["12% Global chance to Blind Enemies on hit"]={{[1]={flags=0,keywordFlags=0,name="BlindChance",type="BASE",value=12}},nil} c["12% chance to Avoid Elemental Ailments per Grand Spectrum"]={{[1]={[1]={type="Multiplier",var="GrandSpectrum"},flags=0,keywordFlags=0,name="AvoidElementalAilments",type="BASE",value=12}},nil} c["12% chance to Avoid Elemental Damage from Hits during Soul Gain Prevention"]={{[1]={[1]={type="Condition",var="SoulGainPrevention"},flags=0,keywordFlags=0,name="AvoidFireDamageChance",type="BASE",value=12},[2]={[1]={type="Condition",var="SoulGainPrevention"},flags=0,keywordFlags=0,name="AvoidColdDamageChance",type="BASE",value=12},[3]={[1]={type="Condition",var="SoulGainPrevention"},flags=0,keywordFlags=0,name="AvoidLightningDamageChance",type="BASE",value=12}},nil} c["12% chance to Avoid being Stunned"]={{[1]={flags=0,keywordFlags=0,name="AvoidStun",type="BASE",value=12}},nil} c["12% chance to Freeze"]={{[1]={flags=0,keywordFlags=0,name="EnemyFreezeChance",type="BASE",value=12}},nil} -c["12% chance to Hinder Enemies on Hit with Spells"]={{}," to Hinder Enemies "} +c["12% chance to Hinder Enemies on Hit with Spells"]={{[1]={flags=2,keywordFlags=0,name="HinderChance",type="BASE",value=12},[2]={[1]={actor="enemy",type="ActorCondition",var="Hindered"},flags=0,keywordFlags=0,name="Dummy",source="Select enemy Hindered in Configuration",type="DUMMY",value=1}},nil} c["12% chance to Ignite"]={{[1]={flags=0,keywordFlags=0,name="EnemyIgniteChance",type="BASE",value=12}},nil} c["12% chance to Impale Enemies on Hit with Attacks"]={{[1]={flags=0,keywordFlags=65536,name="ImpaleChance",type="BASE",value=12}},nil} c["12% chance to Intimidate Enemies for 4 seconds on Hit"]={{}," to Intimidate Enemies "} @@ -4726,7 +4726,7 @@ c["13 to 23 Added Fire Damage with Bow Attacks"]={{[1]={flags=131076,keywordFlag c["13 to 24 Added Cold Damage with Bow Attacks"]={{[1]={flags=131076,keywordFlags=0,name="ColdMin",type="BASE",value=13},[2]={flags=131076,keywordFlags=0,name="ColdMax",type="BASE",value=24}},nil} c["13% Chance to Block Attack Damage"]={{[1]={flags=0,keywordFlags=0,name="BlockChance",type="BASE",value=13}},nil} c["13% Chance to Block Spell Damage"]={{[1]={flags=0,keywordFlags=0,name="SpellBlockChance",type="BASE",value=13}},nil} -c["13% Global chance to Blind Enemies on hit"]={{},"% chance to Blind Enemies on hit "} +c["13% Global chance to Blind Enemies on hit"]={{[1]={flags=0,keywordFlags=0,name="BlindChance",type="BASE",value=13}},nil} c["13% additional Elemental Resistances during Effect"]={{[1]={[1]={type="Condition",var="UsingFlask"},flags=0,keywordFlags=0,name="ElementalResist",type="BASE",value=13}},nil} c["13% additional Physical Damage Reduction while Bleeding"]={{[1]={[1]={type="Condition",var="Bleeding"},flags=0,keywordFlags=0,name="PhysicalDamageReduction",type="BASE",value=13}},nil} c["13% chance for Energy Shield Recharge to start when you use a Skill"]={{[1]={flags=0,keywordFlags=0,name="EnergyShield",type="BASE",value=13}}," for Recharge to start when you use a Skill "} @@ -4932,7 +4932,7 @@ c["138% increased Spell Critical Strike Chance"]={{[1]={flags=2,keywordFlags=0,n c["14 to 24 Added Physical Damage with Bow Attacks"]={{[1]={flags=131076,keywordFlags=0,name="PhysicalMin",type="BASE",value=14},[2]={flags=131076,keywordFlags=0,name="PhysicalMax",type="BASE",value=24}},nil} c["14% Chance to Block Attack Damage"]={{[1]={flags=0,keywordFlags=0,name="BlockChance",type="BASE",value=14}},nil} c["14% Chance to Block Spell Damage"]={{[1]={flags=0,keywordFlags=0,name="SpellBlockChance",type="BASE",value=14}},nil} -c["14% Global chance to Blind Enemies on hit"]={{},"% chance to Blind Enemies on hit "} +c["14% Global chance to Blind Enemies on hit"]={{[1]={flags=0,keywordFlags=0,name="BlindChance",type="BASE",value=14}},nil} c["14% chance to Avoid Elemental Ailments"]={{[1]={flags=0,keywordFlags=0,name="AvoidElementalAilments",type="BASE",value=14}},nil} c["14% chance to Avoid being Frozen"]={{[1]={flags=0,keywordFlags=0,name="AvoidFreeze",type="BASE",value=14}},nil} c["14% chance to Avoid being Ignited"]={{[1]={flags=0,keywordFlags=0,name="AvoidIgnite",type="BASE",value=14}},nil} @@ -5145,7 +5145,7 @@ c["15% chance to Defend with 200% of Armour"]={{[1]={[1]={type="Condition",var=" c["15% chance to Extinguish Enemies on Hit"]={{}," to Extinguish Enemies "} c["15% chance to Freeze"]={{[1]={flags=0,keywordFlags=0,name="EnemyFreezeChance",type="BASE",value=15}},nil} c["15% chance to Freeze, Shock and Ignite"]={{[1]={flags=0,keywordFlags=0,name="EnemyFreezeChance",type="BASE",value=15},[2]={flags=0,keywordFlags=0,name="EnemyShockChance",type="BASE",value=15},[3]={flags=0,keywordFlags=0,name="EnemyIgniteChance",type="BASE",value=15}},nil} -c["15% chance to Hinder Enemies on Hit with Spells"]={{}," to Hinder Enemies "} +c["15% chance to Hinder Enemies on Hit with Spells"]={{[1]={flags=2,keywordFlags=0,name="HinderChance",type="BASE",value=15},[2]={[1]={actor="enemy",type="ActorCondition",var="Hindered"},flags=0,keywordFlags=0,name="Dummy",source="Select enemy Hindered in Configuration",type="DUMMY",value=1}},nil} c["15% chance to Ignite"]={{[1]={flags=0,keywordFlags=0,name="EnemyIgniteChance",type="BASE",value=15}},nil} c["15% chance to Ignore Stuns while Casting"]={{[1]={flags=0,keywordFlags=0,name="AvoidInterruptStun",type="BASE",value=15}},nil} c["15% chance to Impale Enemies on Hit with Attacks"]={{[1]={flags=0,keywordFlags=65536,name="ImpaleChance",type="BASE",value=15}},nil} @@ -6138,7 +6138,7 @@ c["2% chance to Avoid Damage of each Element from Hits per Frenzy Charge"]={{[1] c["2% chance to Blind Enemies on Hit with Attacks"]={{}," to Blind Enemies with Attacks "} c["2% chance to Defend with 150% of Armour per 5% missing Energy Shield"]={{[1]={flags=0,keywordFlags=0,name="Armour",type="BASE",value=2}}," to Defend with 150% of per 5% missing Energy Shield "} c["2% chance to Freeze"]={{[1]={flags=0,keywordFlags=0,name="EnemyFreezeChance",type="BASE",value=2}},nil} -c["2% chance to Hinder Enemies on Hit with Spells"]={{}," to Hinder Enemies "} +c["2% chance to Hinder Enemies on Hit with Spells"]={{[1]={flags=2,keywordFlags=0,name="HinderChance",type="BASE",value=2},[2]={[1]={actor="enemy",type="ActorCondition",var="Hindered"},flags=0,keywordFlags=0,name="Dummy",source="Select enemy Hindered in Configuration",type="DUMMY",value=1}},nil} c["2% chance to Ignite"]={{[1]={flags=0,keywordFlags=0,name="EnemyIgniteChance",type="BASE",value=2}},nil} c["2% chance to Poison on Hit"]={{[1]={flags=0,keywordFlags=0,name="PoisonChance",type="BASE",value=2}},nil} c["2% chance to Shock"]={{[1]={flags=0,keywordFlags=0,name="EnemyShockChance",type="BASE",value=2}},nil} @@ -6311,7 +6311,7 @@ c["20% chance to Extinguish Enemies on Hit"]={{}," to Extinguish Enemies "} c["20% chance to Freeze"]={{[1]={flags=0,keywordFlags=0,name="EnemyFreezeChance",type="BASE",value=20}},nil} c["20% chance to Freeze Enemies for 1 second when they Hit you"]={{[1]={flags=0,keywordFlags=0,name="EnemyFreezeChance",type="BASE",value=20}}," Enemies for 1 second when they Hit you "} c["20% chance to Freeze Enemies for 1 second when they Hit you +2 to Level of all Herald Skill Gems"]={{[1]={flags=0,keywordFlags=0,name="EnemyFreezeChance",type="BASE",value=20}}," Enemies for 1 second when they Hit you +2 to Level of all Herald Skill Gems "} -c["20% chance to Hinder Enemies on Hit with Spells"]={{}," to Hinder Enemies "} +c["20% chance to Hinder Enemies on Hit with Spells"]={{[1]={flags=2,keywordFlags=0,name="HinderChance",type="BASE",value=20},[2]={[1]={actor="enemy",type="ActorCondition",var="Hindered"},flags=0,keywordFlags=0,name="Dummy",source="Select enemy Hindered in Configuration",type="DUMMY",value=1}},nil} c["20% chance to Ignite"]={{[1]={flags=0,keywordFlags=0,name="EnemyIgniteChance",type="BASE",value=20}},nil} c["20% chance to Ignore Stuns while Casting"]={{[1]={flags=0,keywordFlags=0,name="AvoidInterruptStun",type="BASE",value=20}},nil} c["20% chance to Impale Enemies on Hit with Attacks"]={{[1]={flags=0,keywordFlags=65536,name="ImpaleChance",type="BASE",value=20}},nil} @@ -7246,7 +7246,7 @@ c["25% chance to Freeze during any Flask Effect"]={{[1]={[1]={type="Condition",v c["25% chance to Freeze with Melee Weapons"]={{[1]={flags=67108864,keywordFlags=0,name="EnemyFreezeChance",type="BASE",value=25}},nil} c["25% chance to Freeze, Shock and Ignite"]={{[1]={flags=0,keywordFlags=0,name="EnemyFreezeChance",type="BASE",value=25},[2]={flags=0,keywordFlags=0,name="EnemyShockChance",type="BASE",value=25},[3]={flags=0,keywordFlags=0,name="EnemyIgniteChance",type="BASE",value=25}},nil} c["25% chance to Freeze, Shock and Ignite during Effect"]={{[1]={[1]={type="Condition",var="UsingFlask"},flags=0,keywordFlags=0,name="EnemyFreezeChance",type="BASE",value=25},[2]={[1]={type="Condition",var="UsingFlask"},flags=0,keywordFlags=0,name="EnemyShockChance",type="BASE",value=25},[3]={[1]={type="Condition",var="UsingFlask"},flags=0,keywordFlags=0,name="EnemyIgniteChance",type="BASE",value=25}},nil} -c["25% chance to Hinder Enemies on Hit with Spells"]={{}," to Hinder Enemies "} +c["25% chance to Hinder Enemies on Hit with Spells"]={{[1]={flags=2,keywordFlags=0,name="HinderChance",type="BASE",value=25},[2]={[1]={actor="enemy",type="ActorCondition",var="Hindered"},flags=0,keywordFlags=0,name="Dummy",source="Select enemy Hindered in Configuration",type="DUMMY",value=1}},nil} c["25% chance to Ignite"]={{[1]={flags=0,keywordFlags=0,name="EnemyIgniteChance",type="BASE",value=25}},nil} c["25% chance to Ignite during any Flask Effect"]={{[1]={[1]={type="Condition",var="UsingFlask"},flags=0,keywordFlags=0,name="EnemyIgniteChance",type="BASE",value=25}},nil} c["25% chance to Ignite when in Main Hand"]={{[1]={[1]={num=1,type="SlotNumber"},flags=0,keywordFlags=0,name="EnemyIgniteChance",type="BASE",value=25}},nil} @@ -8093,7 +8093,7 @@ c["30% chance to Extinguish Enemies on Hit"]={{}," to Extinguish Enemies "} c["30% chance to Freeze"]={{[1]={flags=0,keywordFlags=0,name="EnemyFreezeChance",type="BASE",value=30}},nil} c["30% chance to Freeze Enemies which are Chilled"]={{[1]={[1]={actor="enemy",type="ActorCondition",var="Chilled"},flags=0,keywordFlags=0,name="EnemyFreezeChance",type="BASE",value=30}},nil} c["30% chance to Freeze during any Flask Effect"]={{[1]={[1]={type="Condition",var="UsingFlask"},flags=0,keywordFlags=0,name="EnemyFreezeChance",type="BASE",value=30}},nil} -c["30% chance to Hinder Enemies on Hit with Spells"]={{}," to Hinder Enemies "} +c["30% chance to Hinder Enemies on Hit with Spells"]={{[1]={flags=2,keywordFlags=0,name="HinderChance",type="BASE",value=30},[2]={[1]={actor="enemy",type="ActorCondition",var="Hindered"},flags=0,keywordFlags=0,name="Dummy",source="Select enemy Hindered in Configuration",type="DUMMY",value=1}},nil} c["30% chance to Ignite"]={{[1]={flags=0,keywordFlags=0,name="EnemyIgniteChance",type="BASE",value=30}},nil} c["30% chance to Ignite during any Flask Effect"]={{[1]={[1]={type="Condition",var="UsingFlask"},flags=0,keywordFlags=0,name="EnemyIgniteChance",type="BASE",value=30}},nil} c["30% chance to Ignore Stuns while Casting"]={{[1]={flags=0,keywordFlags=0,name="AvoidInterruptStun",type="BASE",value=30}},nil} @@ -8692,7 +8692,7 @@ c["35% chance to Aggravate Bleeding on targets you Hit with Attacks"]={{}," to A c["35% chance to Chill Attackers for 4 seconds on Block"]={{}," to Chill Attackers on Block "} c["35% chance to Extinguish Enemies on Hit"]={{}," to Extinguish Enemies "} c["35% chance to Freeze"]={{[1]={flags=0,keywordFlags=0,name="EnemyFreezeChance",type="BASE",value=35}},nil} -c["35% chance to Hinder Enemies on Hit with Spells"]={{}," to Hinder Enemies "} +c["35% chance to Hinder Enemies on Hit with Spells"]={{[1]={flags=2,keywordFlags=0,name="HinderChance",type="BASE",value=35},[2]={[1]={actor="enemy",type="ActorCondition",var="Hindered"},flags=0,keywordFlags=0,name="Dummy",source="Select enemy Hindered in Configuration",type="DUMMY",value=1}},nil} c["35% chance to Ignite"]={{[1]={flags=0,keywordFlags=0,name="EnemyIgniteChance",type="BASE",value=35}},nil} c["35% chance to Ignore Stuns while Casting"]={{[1]={flags=0,keywordFlags=0,name="AvoidInterruptStun",type="BASE",value=35}},nil} c["35% chance to Impale Enemies on Hit with Attacks"]={{[1]={flags=0,keywordFlags=65536,name="ImpaleChance",type="BASE",value=35}},nil} @@ -9063,7 +9063,7 @@ c["4% chance to Blind Enemies on Hit with Attacks"]={{}," to Blind Enemies with c["4% chance to Freeze"]={{[1]={flags=0,keywordFlags=0,name="EnemyFreezeChance",type="BASE",value=4}},nil} c["4% chance to Freeze, Shock and Ignite"]={{[1]={flags=0,keywordFlags=0,name="EnemyFreezeChance",type="BASE",value=4},[2]={flags=0,keywordFlags=0,name="EnemyShockChance",type="BASE",value=4},[3]={flags=0,keywordFlags=0,name="EnemyIgniteChance",type="BASE",value=4}},nil} c["4% chance to Gain Unholy Might for 4 seconds on Melee Kill"]={{[1]={[1]={type="Condition",var="KilledRecently"},flags=67108864,keywordFlags=0,name="Condition:UnholyMight",type="FLAG",value=true}},nil} -c["4% chance to Hinder Enemies on Hit with Spells"]={{}," to Hinder Enemies "} +c["4% chance to Hinder Enemies on Hit with Spells"]={{[1]={flags=2,keywordFlags=0,name="HinderChance",type="BASE",value=4},[2]={[1]={actor="enemy",type="ActorCondition",var="Hindered"},flags=0,keywordFlags=0,name="Dummy",source="Select enemy Hindered in Configuration",type="DUMMY",value=1}},nil} c["4% chance to Ignite"]={{[1]={flags=0,keywordFlags=0,name="EnemyIgniteChance",type="BASE",value=4}},nil} c["4% chance to Impale Enemies on Hit"]={{[1]={flags=0,keywordFlags=0,name="ImpaleChance",type="BASE",value=4}},nil} c["4% chance to Poison on Hit"]={{[1]={flags=0,keywordFlags=0,name="PoisonChance",type="BASE",value=4}},nil} @@ -9226,7 +9226,7 @@ c["40% chance to Chill Attackers for 4 seconds on Block"]={{}," to Chill Attacke c["40% chance to Chill Attackers for 4 seconds on Block 40% chance to Shock Attackers for 4 seconds on Block"]={{[1]={flags=0,keywordFlags=0,name="EnemyShockChance",type="BASE",value=40}}," to Chill Attackers on Block 40% chance Attackers on Block "} c["40% chance to Extinguish Enemies on Hit"]={{}," to Extinguish Enemies "} c["40% chance to Freeze"]={{[1]={flags=0,keywordFlags=0,name="EnemyFreezeChance",type="BASE",value=40}},nil} -c["40% chance to Hinder Enemies on Hit with Spells"]={{}," to Hinder Enemies "} +c["40% chance to Hinder Enemies on Hit with Spells"]={{[1]={flags=2,keywordFlags=0,name="HinderChance",type="BASE",value=40},[2]={[1]={actor="enemy",type="ActorCondition",var="Hindered"},flags=0,keywordFlags=0,name="Dummy",source="Select enemy Hindered in Configuration",type="DUMMY",value=1}},nil} c["40% chance to Ignite"]={{[1]={flags=0,keywordFlags=0,name="EnemyIgniteChance",type="BASE",value=40}},nil} c["40% chance to Impale Enemies on Hit with Attacks"]={{[1]={flags=0,keywordFlags=65536,name="ImpaleChance",type="BASE",value=40}},nil} c["40% chance to Intimidate Enemies for 4 seconds on Hit"]={{}," to Intimidate Enemies "} @@ -9621,7 +9621,7 @@ c["45% chance to Avoid being Shocked"]={{[1]={flags=0,keywordFlags=0,name="Avoid c["45% chance to Blind Enemies on Critical Strike"]={{}," to Blind Enemies "} c["45% chance to Extinguish Enemies on Hit"]={{}," to Extinguish Enemies "} c["45% chance to Freeze"]={{[1]={flags=0,keywordFlags=0,name="EnemyFreezeChance",type="BASE",value=45}},nil} -c["45% chance to Hinder Enemies on Hit with Spells"]={{}," to Hinder Enemies "} +c["45% chance to Hinder Enemies on Hit with Spells"]={{[1]={flags=2,keywordFlags=0,name="HinderChance",type="BASE",value=45},[2]={[1]={actor="enemy",type="ActorCondition",var="Hindered"},flags=0,keywordFlags=0,name="Dummy",source="Select enemy Hindered in Configuration",type="DUMMY",value=1}},nil} c["45% chance to Ignite"]={{[1]={flags=0,keywordFlags=0,name="EnemyIgniteChance",type="BASE",value=45}},nil} c["45% chance to Impale Enemies on Hit with Attacks"]={{[1]={flags=0,keywordFlags=65536,name="ImpaleChance",type="BASE",value=45}},nil} c["45% chance to Intimidate Enemies for 4 seconds on Hit"]={{}," to Intimidate Enemies "} @@ -9831,7 +9831,7 @@ c["5 to 61 Added Spell Lightning Damage while wielding a Two Handed Weapon"]={{[ c["5 to 83 Added Lightning Damage with Bow Attacks"]={{[1]={flags=131076,keywordFlags=0,name="LightningMin",type="BASE",value=5},[2]={flags=131076,keywordFlags=0,name="LightningMax",type="BASE",value=83}},nil} c["5% Chance to Block Attack Damage"]={{[1]={flags=0,keywordFlags=0,name="BlockChance",type="BASE",value=5}},nil} c["5% Chance to Block Spell Damage"]={{[1]={flags=0,keywordFlags=0,name="SpellBlockChance",type="BASE",value=5}},nil} -c["5% Global chance to Blind Enemies on hit"]={{},"% chance to Blind Enemies on hit "} +c["5% Global chance to Blind Enemies on hit"]={{[1]={flags=0,keywordFlags=0,name="BlindChance",type="BASE",value=5}},nil} c["5% additional Physical Damage Reduction"]={{[1]={flags=0,keywordFlags=0,name="PhysicalDamageReduction",type="BASE",value=5}},nil} c["5% additional Physical Damage Reduction against Abyssal Monsters"]={{[1]={flags=0,keywordFlags=0,name="PhysicalDamageReduction",type="BASE",value=5}}," against Abyssal Monsters "} c["5% additional Physical Damage Reduction during any Flask Effect"]={{[1]={[1]={type="Condition",var="UsingFlask"},flags=0,keywordFlags=0,name="PhysicalDamageReduction",type="BASE",value=5}},nil} @@ -9850,7 +9850,7 @@ c["5% chance to Defend with 200% of Armour for each time you've been Hit by an E c["5% chance to Freeze"]={{[1]={flags=0,keywordFlags=0,name="EnemyFreezeChance",type="BASE",value=5}},nil} c["5% chance to Freeze, Shock and Ignite"]={{[1]={flags=0,keywordFlags=0,name="EnemyFreezeChance",type="BASE",value=5},[2]={flags=0,keywordFlags=0,name="EnemyShockChance",type="BASE",value=5},[3]={flags=0,keywordFlags=0,name="EnemyIgniteChance",type="BASE",value=5}},nil} c["5% chance to Gain Unholy Might for 4 seconds on Melee Kill"]={{[1]={[1]={type="Condition",var="KilledRecently"},flags=67108864,keywordFlags=0,name="Condition:UnholyMight",type="FLAG",value=true}},nil} -c["5% chance to Hinder Enemies on Hit with Spells"]={{}," to Hinder Enemies "} +c["5% chance to Hinder Enemies on Hit with Spells"]={{[1]={flags=2,keywordFlags=0,name="HinderChance",type="BASE",value=5},[2]={[1]={actor="enemy",type="ActorCondition",var="Hindered"},flags=0,keywordFlags=0,name="Dummy",source="Select enemy Hindered in Configuration",type="DUMMY",value=1}},nil} c["5% chance to Ignite"]={{[1]={flags=0,keywordFlags=0,name="EnemyIgniteChance",type="BASE",value=5}},nil} c["5% chance to Impale Enemies on Hit with Attacks"]={{[1]={flags=0,keywordFlags=65536,name="ImpaleChance",type="BASE",value=5}},nil} c["5% chance to Intimidate Enemies for 4 seconds on Hit"]={{}," to Intimidate Enemies "} @@ -10082,7 +10082,7 @@ c["50% chance to Cause Bleeding on Critical Strike"]={{[1]={[1]={type="Condition c["50% chance to Cause Poison on Critical Strike"]={{[1]={[1]={type="Condition",var="CriticalStrike"},flags=0,keywordFlags=0,name="PoisonChance",type="BASE",value=50}},nil} c["50% chance to Extinguish Enemies on Hit"]={{}," to Extinguish Enemies "} c["50% chance to Freeze"]={{[1]={flags=0,keywordFlags=0,name="EnemyFreezeChance",type="BASE",value=50}},nil} -c["50% chance to Hinder Enemies on Hit with Spells"]={{}," to Hinder Enemies "} +c["50% chance to Hinder Enemies on Hit with Spells"]={{[1]={flags=2,keywordFlags=0,name="HinderChance",type="BASE",value=50},[2]={[1]={actor="enemy",type="ActorCondition",var="Hindered"},flags=0,keywordFlags=0,name="Dummy",source="Select enemy Hindered in Configuration",type="DUMMY",value=1}},nil} c["50% chance to Ignite"]={{[1]={flags=0,keywordFlags=0,name="EnemyIgniteChance",type="BASE",value=50}},nil} c["50% chance to Impale Enemies on Hit with Attacks"]={{[1]={flags=0,keywordFlags=65536,name="ImpaleChance",type="BASE",value=50}},nil} c["50% chance to Intimidate Enemies for 4 seconds on Hit"]={{}," to Intimidate Enemies "} @@ -10505,7 +10505,7 @@ c["55% chance to Avoid being Ignited"]={{[1]={flags=0,keywordFlags=0,name="Avoid c["55% chance to Avoid being Poisoned"]={{[1]={flags=0,keywordFlags=0,name="AvoidPoison",type="BASE",value=55}},nil} c["55% chance to Avoid being Shocked"]={{[1]={flags=0,keywordFlags=0,name="AvoidShock",type="BASE",value=55}},nil} c["55% chance to Extinguish Enemies on Hit"]={{}," to Extinguish Enemies "} -c["55% chance to Hinder Enemies on Hit with Spells"]={{}," to Hinder Enemies "} +c["55% chance to Hinder Enemies on Hit with Spells"]={{[1]={flags=2,keywordFlags=0,name="HinderChance",type="BASE",value=55},[2]={[1]={actor="enemy",type="ActorCondition",var="Hindered"},flags=0,keywordFlags=0,name="Dummy",source="Select enemy Hindered in Configuration",type="DUMMY",value=1}},nil} c["55% chance to Intimidate Enemies for 4 seconds on Hit"]={{}," to Intimidate Enemies "} c["55% chance to Unnerve Enemies for 4 seconds on Hit"]={{}," to Unnerve Enemies "} c["55% faster start of Energy Shield Recharge"]={{[1]={flags=0,keywordFlags=0,name="EnergyShieldRechargeFaster",type="INC",value=55}},nil} @@ -10656,7 +10656,7 @@ c["6 to 8 Added Physical Damage with Sword Attacks"]={{[1]={flags=4194308,keywor c["6 to 8 Added Physical Damage with Wand Attacks"]={{[1]={flags=8388612,keywordFlags=0,name="PhysicalMin",type="BASE",value=6},[2]={flags=8388612,keywordFlags=0,name="PhysicalMax",type="BASE",value=8}},nil} c["6% Chance to Block Attack Damage"]={{[1]={flags=0,keywordFlags=0,name="BlockChance",type="BASE",value=6}},nil} c["6% Chance to Block Spell Damage"]={{[1]={flags=0,keywordFlags=0,name="SpellBlockChance",type="BASE",value=6}},nil} -c["6% Global chance to Blind Enemies on hit"]={{},"% chance to Blind Enemies on hit "} +c["6% Global chance to Blind Enemies on hit"]={{[1]={flags=0,keywordFlags=0,name="BlindChance",type="BASE",value=6}},nil} c["6% additional Physical Damage Reduction"]={{[1]={flags=0,keywordFlags=0,name="PhysicalDamageReduction",type="BASE",value=6}},nil} c["6% additional Physical Damage Reduction while stationary"]={{[1]={[1]={type="Condition",var="Stationary"},flags=0,keywordFlags=0,name="PhysicalDamageReduction",type="BASE",value=6}},nil} c["6% chance for Flasks you use to not consume Charges"]={{[1]={flags=0,keywordFlags=0,name="FlaskChanceNotConsumeCharges",type="BASE",value=6}},nil} @@ -10832,7 +10832,7 @@ c["60% chance to Avoid being Ignited"]={{[1]={flags=0,keywordFlags=0,name="Avoid c["60% chance to Avoid being Poisoned"]={{[1]={flags=0,keywordFlags=0,name="AvoidPoison",type="BASE",value=60}},nil} c["60% chance to Avoid being Shocked"]={{[1]={flags=0,keywordFlags=0,name="AvoidShock",type="BASE",value=60}},nil} c["60% chance to Extinguish Enemies on Hit"]={{}," to Extinguish Enemies "} -c["60% chance to Hinder Enemies on Hit with Spells"]={{}," to Hinder Enemies "} +c["60% chance to Hinder Enemies on Hit with Spells"]={{[1]={flags=2,keywordFlags=0,name="HinderChance",type="BASE",value=60},[2]={[1]={actor="enemy",type="ActorCondition",var="Hindered"},flags=0,keywordFlags=0,name="Dummy",source="Select enemy Hindered in Configuration",type="DUMMY",value=1}},nil} c["60% chance to Intimidate Enemies for 4 seconds on Hit"]={{}," to Intimidate Enemies "} c["60% chance to Poison on Hit"]={{[1]={flags=0,keywordFlags=0,name="PoisonChance",type="BASE",value=60}},nil} c["60% chance to Poison on Hit against Cursed Enemies"]={{[1]={[1]={actor="enemy",type="ActorCondition",var="Cursed"},flags=0,keywordFlags=0,name="PoisonChance",type="BASE",value=60}},nil} @@ -11029,7 +11029,7 @@ c["65% chance to Avoid being Ignited"]={{[1]={flags=0,keywordFlags=0,name="Avoid c["65% chance to Avoid being Poisoned"]={{[1]={flags=0,keywordFlags=0,name="AvoidPoison",type="BASE",value=65}},nil} c["65% chance to Avoid being Shocked"]={{[1]={flags=0,keywordFlags=0,name="AvoidShock",type="BASE",value=65}},nil} c["65% chance to Extinguish Enemies on Hit"]={{}," to Extinguish Enemies "} -c["65% chance to Hinder Enemies on Hit with Spells"]={{}," to Hinder Enemies "} +c["65% chance to Hinder Enemies on Hit with Spells"]={{[1]={flags=2,keywordFlags=0,name="HinderChance",type="BASE",value=65},[2]={[1]={actor="enemy",type="ActorCondition",var="Hindered"},flags=0,keywordFlags=0,name="Dummy",source="Select enemy Hindered in Configuration",type="DUMMY",value=1}},nil} c["65% chance to Intimidate Enemies for 4 seconds on Hit"]={{}," to Intimidate Enemies "} c["65% chance to Unnerve Enemies for 4 seconds on Hit"]={{}," to Unnerve Enemies "} c["65% increased Armour"]={{[1]={flags=0,keywordFlags=0,name="Armour",type="INC",value=65}},nil} @@ -11133,7 +11133,7 @@ c["7 to 12 Added Cold Damage with Wand Attacks"]={{[1]={flags=8388612,keywordFla c["7 to 13 Added Cold Damage per Frenzy Charge"]={{[1]={[1]={type="Multiplier",var="FrenzyCharge"},flags=0,keywordFlags=0,name="ColdMin",type="BASE",value=7},[2]={[1]={type="Multiplier",var="FrenzyCharge"},flags=0,keywordFlags=0,name="ColdMax",type="BASE",value=13}},nil} c["7% Chance to Block Attack Damage"]={{[1]={flags=0,keywordFlags=0,name="BlockChance",type="BASE",value=7}},nil} c["7% Chance to Block Spell Damage"]={{[1]={flags=0,keywordFlags=0,name="SpellBlockChance",type="BASE",value=7}},nil} -c["7% Global chance to Blind Enemies on hit"]={{},"% chance to Blind Enemies on hit "} +c["7% Global chance to Blind Enemies on hit"]={{[1]={flags=0,keywordFlags=0,name="BlindChance",type="BASE",value=7}},nil} c["7% Global chance to Blind Enemies on hit 10% Global chance to Blind Enemies on hit"]={{},"% chance to Blind Enemies on hit 10% Global chance to Blind Enemies on hit "} c["7% additional Physical Damage Reduction"]={{[1]={flags=0,keywordFlags=0,name="PhysicalDamageReduction",type="BASE",value=7}},nil} c["7% additional Physical Damage Reduction while affected by Determination"]={{[1]={[1]={type="Condition",var="AffectedByDetermination"},flags=0,keywordFlags=0,name="PhysicalDamageReduction",type="BASE",value=7}},nil} @@ -11148,7 +11148,7 @@ c["7% chance to Avoid being Shocked"]={{[1]={flags=0,keywordFlags=0,name="AvoidS c["7% chance to Avoid being Stunned"]={{[1]={flags=0,keywordFlags=0,name="AvoidStun",type="BASE",value=7}},nil} c["7% chance to Freeze"]={{[1]={flags=0,keywordFlags=0,name="EnemyFreezeChance",type="BASE",value=7}},nil} c["7% chance to Gain Unholy Might for 4 seconds on Melee Kill"]={{[1]={[1]={type="Condition",var="KilledRecently"},flags=67108864,keywordFlags=0,name="Condition:UnholyMight",type="FLAG",value=true}},nil} -c["7% chance to Hinder Enemies on Hit with Spells"]={{}," to Hinder Enemies "} +c["7% chance to Hinder Enemies on Hit with Spells"]={{[1]={flags=2,keywordFlags=0,name="HinderChance",type="BASE",value=7},[2]={[1]={actor="enemy",type="ActorCondition",var="Hindered"},flags=0,keywordFlags=0,name="Dummy",source="Select enemy Hindered in Configuration",type="DUMMY",value=1}},nil} c["7% chance to Ignite"]={{[1]={flags=0,keywordFlags=0,name="EnemyIgniteChance",type="BASE",value=7}},nil} c["7% chance to Intimidate Enemies for 4 seconds on Hit"]={{}," to Intimidate Enemies "} c["7% chance to Knock Enemies Back on hit"]={{[1]={flags=0,keywordFlags=0,name="EnemyKnockbackChance",type="BASE",value=7}},nil} @@ -11249,7 +11249,7 @@ c["7% reduced Movement Speed"]={{[1]={flags=0,keywordFlags=0,name="MovementSpeed c["7% reduced Soul Gain Prevention Duration"]={{[1]={flags=0,keywordFlags=0,name="SoulGainPreventionDuration",type="INC",value=-7}},nil} c["70 to 105 Added Cold Damage with Bow Attacks"]={{[1]={flags=131076,keywordFlags=0,name="ColdMin",type="BASE",value=70},[2]={flags=131076,keywordFlags=0,name="ColdMax",type="BASE",value=105}},nil} c["70% chance to Extinguish Enemies on Hit"]={{}," to Extinguish Enemies "} -c["70% chance to Hinder Enemies on Hit with Spells"]={{}," to Hinder Enemies "} +c["70% chance to Hinder Enemies on Hit with Spells"]={{[1]={flags=2,keywordFlags=0,name="HinderChance",type="BASE",value=70},[2]={[1]={actor="enemy",type="ActorCondition",var="Hindered"},flags=0,keywordFlags=0,name="Dummy",source="Select enemy Hindered in Configuration",type="DUMMY",value=1}},nil} c["70% chance to Intimidate Enemies for 4 seconds on Hit"]={{}," to Intimidate Enemies "} c["70% chance to Unnerve Enemies for 4 seconds on Hit"]={{}," to Unnerve Enemies "} c["70% increased Armour"]={{[1]={flags=0,keywordFlags=0,name="Armour",type="INC",value=70}},nil} @@ -11330,7 +11330,7 @@ c["74% reduced Global Physical Damage"]={{[1]={[1]={type="Global"},flags=0,keywo c["74% reduced Lightning Damage"]={{[1]={flags=0,keywordFlags=0,name="LightningDamage",type="INC",value=-74}},nil} c["74% reduced Spell Damage"]={{[1]={flags=2,keywordFlags=0,name="Damage",type="INC",value=-74}},nil} c["75% chance to Extinguish Enemies on Hit"]={{}," to Extinguish Enemies "} -c["75% chance to Hinder Enemies on Hit with Spells"]={{}," to Hinder Enemies "} +c["75% chance to Hinder Enemies on Hit with Spells"]={{[1]={flags=2,keywordFlags=0,name="HinderChance",type="BASE",value=75},[2]={[1]={actor="enemy",type="ActorCondition",var="Hindered"},flags=0,keywordFlags=0,name="Dummy",source="Select enemy Hindered in Configuration",type="DUMMY",value=1}},nil} c["75% chance to Intimidate Enemies for 4 seconds on Hit"]={{}," to Intimidate Enemies "} c["75% chance to Unnerve Enemies for 4 seconds on Hit"]={{}," to Unnerve Enemies "} c["75% chance to cause Enemies to Flee on use"]={{}," to cause Enemies to Flee on use "} @@ -11435,7 +11435,7 @@ c["8 to 23 Added Attack Chaos Damage per 100 Maximum Mana"]={{[1]={[1]={div=100, c["8% Chance for Traps to Trigger an additional time"]={{}," to Trigger an additional time "} c["8% Chance to Block Attack Damage"]={{[1]={flags=0,keywordFlags=0,name="BlockChance",type="BASE",value=8}},nil} c["8% Chance to Block Spell Damage"]={{[1]={flags=0,keywordFlags=0,name="SpellBlockChance",type="BASE",value=8}},nil} -c["8% Global chance to Blind Enemies on hit"]={{},"% chance to Blind Enemies on hit "} +c["8% Global chance to Blind Enemies on hit"]={{[1]={flags=0,keywordFlags=0,name="BlindChance",type="BASE",value=8}},nil} c["8% additional Physical Damage Reduction"]={{[1]={flags=0,keywordFlags=0,name="PhysicalDamageReduction",type="BASE",value=8}},nil} c["8% additional Physical Damage Reduction while affected by Determination"]={{[1]={[1]={type="Condition",var="AffectedByDetermination"},flags=0,keywordFlags=0,name="PhysicalDamageReduction",type="BASE",value=8}},nil} c["8% chance for Flasks you use to not consume Charges"]={{[1]={flags=0,keywordFlags=0,name="FlaskChanceNotConsumeCharges",type="BASE",value=8}},nil} @@ -11449,7 +11449,7 @@ c["8% chance to Cover Rare or Unique Enemies in Ash for 10 Seconds on Hit"]={{}, c["8% chance to Defend with 200% of Armour"]={{[1]={[1]={type="Condition",var="ArmourMax"},flags=0,keywordFlags=0,name="ArmourDefense",source="Armour Mastery: Max Calc",type="MAX",value=100},[2]={[1]={type="Condition",var="ArmourAvg"},flags=0,keywordFlags=0,name="ArmourDefense",source="Armour Mastery: Average Calc",type="MAX",value=8},[3]={[1]={neg=true,type="Condition",var="ArmourMax"},[2]={neg=true,type="Condition",var="ArmourAvg"},flags=0,keywordFlags=0,name="ArmourDefense",source="Armour Mastery: Min Calc",type="MAX",value=0}},nil} c["8% chance to Freeze"]={{[1]={flags=0,keywordFlags=0,name="EnemyFreezeChance",type="BASE",value=8}},nil} c["8% chance to Freeze, Shock and Ignite"]={{[1]={flags=0,keywordFlags=0,name="EnemyFreezeChance",type="BASE",value=8},[2]={flags=0,keywordFlags=0,name="EnemyShockChance",type="BASE",value=8},[3]={flags=0,keywordFlags=0,name="EnemyIgniteChance",type="BASE",value=8}},nil} -c["8% chance to Hinder Enemies on Hit with Spells"]={{}," to Hinder Enemies "} +c["8% chance to Hinder Enemies on Hit with Spells"]={{[1]={flags=2,keywordFlags=0,name="HinderChance",type="BASE",value=8},[2]={[1]={actor="enemy",type="ActorCondition",var="Hindered"},flags=0,keywordFlags=0,name="Dummy",source="Select enemy Hindered in Configuration",type="DUMMY",value=1}},nil} c["8% chance to Ignite"]={{[1]={flags=0,keywordFlags=0,name="EnemyIgniteChance",type="BASE",value=8}},nil} c["8% chance to Impale Enemies on Hit"]={{[1]={flags=0,keywordFlags=0,name="ImpaleChance",type="BASE",value=8}},nil} c["8% chance to Impale Enemies on Hit with Attacks"]={{[1]={flags=0,keywordFlags=65536,name="ImpaleChance",type="BASE",value=8}},nil} @@ -11645,7 +11645,7 @@ c["8% reduced Soul Gain Prevention Duration"]={{[1]={flags=0,keywordFlags=0,name c["80% chance to Avoid being Chilled"]={{[1]={flags=0,keywordFlags=0,name="AvoidChill",type="BASE",value=80}},nil} c["80% chance to Avoid being Shocked"]={{[1]={flags=0,keywordFlags=0,name="AvoidShock",type="BASE",value=80}},nil} c["80% chance to Extinguish Enemies on Hit"]={{}," to Extinguish Enemies "} -c["80% chance to Hinder Enemies on Hit with Spells"]={{}," to Hinder Enemies "} +c["80% chance to Hinder Enemies on Hit with Spells"]={{[1]={flags=2,keywordFlags=0,name="HinderChance",type="BASE",value=80},[2]={[1]={actor="enemy",type="ActorCondition",var="Hindered"},flags=0,keywordFlags=0,name="Dummy",source="Select enemy Hindered in Configuration",type="DUMMY",value=1}},nil} c["80% chance to Intimidate Enemies for 4 seconds on Hit"]={{}," to Intimidate Enemies "} c["80% chance to Unnerve Enemies for 4 seconds on Hit"]={{}," to Unnerve Enemies "} c["80% faster start of Energy Shield Recharge"]={{[1]={flags=0,keywordFlags=0,name="EnergyShieldRechargeFaster",type="INC",value=80}},nil} @@ -11725,7 +11725,7 @@ c["83% increased Fire Damage"]={{[1]={flags=0,keywordFlags=0,name="FireDamage",t c["83% increased Lightning Damage"]={{[1]={flags=0,keywordFlags=0,name="LightningDamage",type="INC",value=83}},nil} c["83% increased Spell Damage"]={{[1]={flags=2,keywordFlags=0,name="Damage",type="INC",value=83}},nil} c["85% chance to Extinguish Enemies on Hit"]={{}," to Extinguish Enemies "} -c["85% chance to Hinder Enemies on Hit with Spells"]={{}," to Hinder Enemies "} +c["85% chance to Hinder Enemies on Hit with Spells"]={{[1]={flags=2,keywordFlags=0,name="HinderChance",type="BASE",value=85},[2]={[1]={actor="enemy",type="ActorCondition",var="Hindered"},flags=0,keywordFlags=0,name="Dummy",source="Select enemy Hindered in Configuration",type="DUMMY",value=1}},nil} c["85% chance to Intimidate Enemies for 4 seconds on Hit"]={{}," to Intimidate Enemies "} c["85% chance to Unnerve Enemies for 4 seconds on Hit"]={{}," to Unnerve Enemies "} c["85% increased Burning Damage"]={{[1]={flags=0,keywordFlags=134217728,name="FireDamage",type="INC",value=85}},nil} @@ -11902,7 +11902,7 @@ c["9% reduced Flask Charges used"]={{[1]={flags=0,keywordFlags=0,name="FlaskChar c["9% reduced Movement Speed"]={{[1]={flags=0,keywordFlags=0,name="MovementSpeed",type="INC",value=-9}},nil} c["9% reduced Physical Damage taken"]={{[1]={flags=0,keywordFlags=0,name="PhysicalDamageTaken",type="INC",value=-9}},nil} c["90% chance to Extinguish Enemies on Hit"]={{}," to Extinguish Enemies "} -c["90% chance to Hinder Enemies on Hit with Spells"]={{}," to Hinder Enemies "} +c["90% chance to Hinder Enemies on Hit with Spells"]={{[1]={flags=2,keywordFlags=0,name="HinderChance",type="BASE",value=90},[2]={[1]={actor="enemy",type="ActorCondition",var="Hindered"},flags=0,keywordFlags=0,name="Dummy",source="Select enemy Hindered in Configuration",type="DUMMY",value=1}},nil} c["90% chance to Intimidate Enemies for 4 seconds on Hit"]={{}," to Intimidate Enemies "} c["90% chance to Unnerve Enemies for 4 seconds on Hit"]={{}," to Unnerve Enemies "} c["90% increased Armour"]={{[1]={flags=0,keywordFlags=0,name="Armour",type="INC",value=90}},nil} @@ -11947,7 +11947,7 @@ c["93% increased Mine Damage"]={{[1]={flags=0,keywordFlags=8192,name="Damage",ty c["93% increased Trap Damage"]={{[1]={flags=0,keywordFlags=4096,name="Damage",type="INC",value=93}},nil} c["94% increased Elemental Damage with Attack Skills"]={{[1]={flags=0,keywordFlags=65536,name="ElementalDamage",type="INC",value=94}},nil} c["95% chance to Extinguish Enemies on Hit"]={{}," to Extinguish Enemies "} -c["95% chance to Hinder Enemies on Hit with Spells"]={{}," to Hinder Enemies "} +c["95% chance to Hinder Enemies on Hit with Spells"]={{[1]={flags=2,keywordFlags=0,name="HinderChance",type="BASE",value=95},[2]={[1]={actor="enemy",type="ActorCondition",var="Hindered"},flags=0,keywordFlags=0,name="Dummy",source="Select enemy Hindered in Configuration",type="DUMMY",value=1}},nil} c["95% chance to Intimidate Enemies for 4 seconds on Hit"]={{}," to Intimidate Enemies "} c["95% chance to Unnerve Enemies for 4 seconds on Hit"]={{}," to Unnerve Enemies "} c["95% increased Physical Damage"]={{[1]={flags=0,keywordFlags=0,name="PhysicalDamage",type="INC",value=95}},nil} @@ -17671,7 +17671,7 @@ c["Melee Attacks have 30% chance to Poison on Hit"]={{[1]={flags=256,keywordFlag c["Melee Attacks have 40% chance to Poison on Hit"]={{[1]={flags=256,keywordFlags=0,name="PoisonChance",type="BASE",value=40}},nil} c["Melee Attacks have 40% chance to cause Bleeding"]={{[1]={flags=256,keywordFlags=0,name="BleedChance",type="BASE",value=40}},nil} c["Melee Attacks have 50% chance to cause Bleeding"]={{[1]={flags=256,keywordFlags=0,name="BleedChance",type="BASE",value=50}},nil} -c["Melee Critical Strikes Poison the Enemy"]={nil,"Melee Critical Strikes Poison the Enemy "} +c["Melee Critical Strikes Poison the Enemy"]={{[1]={[1]={type="Condition",var="CriticalStrike"},flags=256,keywordFlags=0,name="PoisonChance",type="BASE",value=100}},nil} c["Melee Critical Strikes have 25% chance to Poison the Enemy"]={{[1]={[1]={type="Condition",var="CriticalStrike"},flags=256,keywordFlags=0,name="PoisonChance",type="BASE",value=25}},nil} c["Melee Critical Strikes have 25% chance to cause Bleeding"]={{[1]={[1]={type="Condition",var="CriticalStrike"},flags=256,keywordFlags=0,name="BleedChance",type="BASE",value=25}},nil} c["Melee Hits Fortify"]={nil,"Melee Hits Fortify "} @@ -17861,7 +17861,7 @@ c["Minions deal 24% increased Damage"]={{[1]={flags=0,keywordFlags=0,name="Minio c["Minions deal 25 to 36 additional Chaos Damage"]={{[1]={flags=0,keywordFlags=0,name="MinionModifier",type="LIST",value={mod={flags=0,keywordFlags=0,name="ChaosMin",type="BASE",value=25}}},[2]={flags=0,keywordFlags=0,name="MinionModifier",type="LIST",value={mod={flags=0,keywordFlags=0,name="ChaosMax",type="BASE",value=36}}}},nil} c["Minions deal 25 to 36 additional Physical Damage"]={{[1]={flags=0,keywordFlags=0,name="MinionModifier",type="LIST",value={mod={flags=0,keywordFlags=0,name="PhysicalMin",type="BASE",value=25}}},[2]={flags=0,keywordFlags=0,name="MinionModifier",type="LIST",value={mod={flags=0,keywordFlags=0,name="PhysicalMax",type="BASE",value=36}}}},nil} c["Minions deal 25% increased Damage"]={{[1]={flags=0,keywordFlags=0,name="MinionModifier",type="LIST",value={mod={flags=0,keywordFlags=0,name="Damage",type="INC",value=25}}}},nil} -c["Minions deal 25% increased Damage if you've Hit Recently"]={{[1]={flags=0,keywordFlags=0,name="MinionModifier",type="LIST",value={mod={[1]={type="Condition",var="HitRecently"},flags=0,keywordFlags=0,name="Damage",type="INC",value=25}}}},nil} +c["Minions deal 25% increased Damage if you've Hit Recently"]={{[1]={[1]={type="Condition",var="HitRecently"},flags=0,keywordFlags=0,name="MinionModifier",type="LIST",value={mod={flags=0,keywordFlags=0,name="Damage",type="INC",value=25}}}},nil} c["Minions deal 26 to 35 additional Cold Damage"]={{[1]={flags=0,keywordFlags=0,name="MinionModifier",type="LIST",value={mod={flags=0,keywordFlags=0,name="ColdMin",type="BASE",value=26}}},[2]={flags=0,keywordFlags=0,name="MinionModifier",type="LIST",value={mod={flags=0,keywordFlags=0,name="ColdMax",type="BASE",value=35}}}},nil} c["Minions deal 26 to 35 additional Fire Damage"]={{[1]={flags=0,keywordFlags=0,name="MinionModifier",type="LIST",value={mod={flags=0,keywordFlags=0,name="FireMin",type="BASE",value=26}}},[2]={flags=0,keywordFlags=0,name="MinionModifier",type="LIST",value={mod={flags=0,keywordFlags=0,name="FireMax",type="BASE",value=35}}}},nil} c["Minions deal 26% increased Damage"]={{[1]={flags=0,keywordFlags=0,name="MinionModifier",type="LIST",value={mod={flags=0,keywordFlags=0,name="Damage",type="INC",value=26}}}},nil} @@ -17912,7 +17912,7 @@ c["Minions deal 52% increased Damage"]={{[1]={flags=0,keywordFlags=0,name="Minio c["Minions deal 53% increased Damage"]={{[1]={flags=0,keywordFlags=0,name="MinionModifier",type="LIST",value={mod={flags=0,keywordFlags=0,name="Damage",type="INC",value=53}}}},nil} c["Minions deal 58% increased Damage"]={{[1]={flags=0,keywordFlags=0,name="MinionModifier",type="LIST",value={mod={flags=0,keywordFlags=0,name="Damage",type="INC",value=58}}}},nil} c["Minions deal 60% increased Damage"]={{[1]={flags=0,keywordFlags=0,name="MinionModifier",type="LIST",value={mod={flags=0,keywordFlags=0,name="Damage",type="INC",value=60}}}},nil} -c["Minions deal 60% increased Damage if you've Hit Recently"]={{[1]={flags=0,keywordFlags=0,name="MinionModifier",type="LIST",value={mod={[1]={type="Condition",var="HitRecently"},flags=0,keywordFlags=0,name="Damage",type="INC",value=60}}}},nil} +c["Minions deal 60% increased Damage if you've Hit Recently"]={{[1]={[1]={type="Condition",var="HitRecently"},flags=0,keywordFlags=0,name="MinionModifier",type="LIST",value={mod={flags=0,keywordFlags=0,name="Damage",type="INC",value=60}}}},nil} c["Minions deal 61% increased Damage"]={{[1]={flags=0,keywordFlags=0,name="MinionModifier",type="LIST",value={mod={flags=0,keywordFlags=0,name="Damage",type="INC",value=61}}}},nil} c["Minions deal 62% increased Damage"]={{[1]={flags=0,keywordFlags=0,name="MinionModifier",type="LIST",value={mod={flags=0,keywordFlags=0,name="Damage",type="INC",value=62}}}},nil} c["Minions deal 67% increased Damage"]={{[1]={flags=0,keywordFlags=0,name="MinionModifier",type="LIST",value={mod={flags=0,keywordFlags=0,name="Damage",type="INC",value=67}}}},nil} @@ -17921,7 +17921,7 @@ c["Minions deal 7 to 13 additional Physical Damage"]={{[1]={flags=0,keywordFlags c["Minions deal 7 to 14 additional Attack Physical Damage"]={{[1]={flags=0,keywordFlags=0,name="MinionModifier",type="LIST",value={mod={flags=1,keywordFlags=0,name="PhysicalDamage",type="BASE",value=7}}}}," to 14 additional "} c["Minions deal 7% increased Damage"]={{[1]={flags=0,keywordFlags=0,name="MinionModifier",type="LIST",value={mod={flags=0,keywordFlags=0,name="Damage",type="INC",value=7}}}},nil} c["Minions deal 70% increased Damage"]={{[1]={flags=0,keywordFlags=0,name="MinionModifier",type="LIST",value={mod={flags=0,keywordFlags=0,name="Damage",type="INC",value=70}}}},nil} -c["Minions deal 70% increased Damage if you've Hit Recently"]={{[1]={flags=0,keywordFlags=0,name="MinionModifier",type="LIST",value={mod={[1]={type="Condition",var="HitRecently"},flags=0,keywordFlags=0,name="Damage",type="INC",value=70}}}},nil} +c["Minions deal 70% increased Damage if you've Hit Recently"]={{[1]={[1]={type="Condition",var="HitRecently"},flags=0,keywordFlags=0,name="MinionModifier",type="LIST",value={mod={flags=0,keywordFlags=0,name="Damage",type="INC",value=70}}}},nil} c["Minions deal 72% increased Damage"]={{[1]={flags=0,keywordFlags=0,name="MinionModifier",type="LIST",value={mod={flags=0,keywordFlags=0,name="Damage",type="INC",value=72}}}},nil} c["Minions deal 75% increased Damage"]={{[1]={flags=0,keywordFlags=0,name="MinionModifier",type="LIST",value={mod={flags=0,keywordFlags=0,name="Damage",type="INC",value=75}}}},nil} c["Minions deal 77% increased Damage"]={{[1]={flags=0,keywordFlags=0,name="MinionModifier",type="LIST",value={mod={flags=0,keywordFlags=0,name="Damage",type="INC",value=77}}}},nil} diff --git a/src/Modules/CalcDefence.lua b/src/Modules/CalcDefence.lua index 0157e85881..65da49a4fd 100644 --- a/src/Modules/CalcDefence.lua +++ b/src/Modules/CalcDefence.lua @@ -6,6 +6,8 @@ ---@class Calcs local calcs = require("Modules.CalcBase") +local regenerationConversions = { { "LightRadius", "LifeRegen", "LightRadiusAppliesToLifeRegen" }, { "ManaRegen", "EnergyShieldRecharge", "ManaRegenAppliesToEnergyShieldRecharge" } } + local pairs = pairs local ipairs = ipairs local t_insert = table.insert @@ -791,6 +793,18 @@ function calcs.defence(env, actor) output.DamageTakenOnBlock = 100 - output.BlockEffect end + -- Apply Light Radius increases to Life Regeneration, and Mana Regeneration increases to ES Recharge, at the stated percentage. + for _, conversion in ipairs(regenerationConversions) do + local multiplier = (modDB:Max(nil, conversion[3]) or 0) / 100 + if multiplier ~= 0 then + for _, value in ipairs(modDB:Tabulate("INC", nil, conversion[1])) do + local mod = value.mod + local modifiers = calcLib.getConvertedModTags(mod, multiplier) + modDB:NewMod(conversion[2], "INC", m_floor(mod.value * multiplier), mod.source, mod.flags, mod.keywordFlags, unpack(modifiers)) + end + end + end + if modDB:Flag(nil, "ArmourAppliesToEnergyShieldRecharge") then -- Armour to ES Recharge conversion from Armour and Energy Shield Mastery local multiplier = (modDB:Max(nil, "ImprovedArmourAppliesToEnergyShieldRecharge") or 100) / 100 @@ -1210,6 +1224,14 @@ function calcs.defence(env, actor) } end + -- Use the non-flask Petrified Blood recovery ceiling only as a default for this modifier. + local missingLife = 0 + if modDB:GetCondition("AffectedByPetrifiedBlood") and output.LifeUnreserved > 0 then + local currentLife = m_min(output.LifeUnreserved, output.Life * output.LowLifePercentage / 100) + missingLife = 100 * (output.LifeUnreserved - currentLife) / output.LifeUnreserved + end + output.MissingUnreservedLifePercent = modDB:Override(nil, "MissingUnreservedLifePercent") or missingLife + -- Recovery modifiers output.LifeRecoveryRateMod = 1 if not modDB:Flag(nil, "CannotRecoverLifeOutsideLeech") then @@ -1857,9 +1879,11 @@ function calcs.buildDefenceEstimations(env, actor) local dotDestinationTotal = 0 for _, destType in ipairs(dmgTypeList) do if destType ~= damageType then - dotShiftTable[destType] = modDB:Sum("BASE", nil, damageType.."DamageTakenAs"..destType, isElemental[damageType] and "ElementalDamageTakenAs"..destType or nil) + -- Allow Physical DoT taken as Chaos without also converting damage from hits. + local takenAs = modDB:Sum("BASE", nil, damageType.."DamageTakenAs"..destType, isElemental[damageType] and "ElementalDamageTakenAs"..destType or nil) + dotShiftTable[destType] = takenAs + modDB:Sum("BASE", nil, damageType.."DamageTakenOverTimeAs"..destType, isElemental[damageType] and "ElementalDamageTakenOverTimeAs"..destType or nil) dotDestinationTotal = dotDestinationTotal + dotShiftTable[destType] - shiftTable[destType] = dotShiftTable[destType] + modDB:Sum("BASE", nil, damageType.."DamageFromHitsTakenAs"..destType, isElemental[damageType] and "ElementalDamageFromHitsTakenAs"..destType or nil) + shiftTable[destType] = takenAs + modDB:Sum("BASE", nil, damageType.."DamageFromHitsTakenAs"..destType, isElemental[damageType] and "ElementalDamageFromHitsTakenAs"..destType or nil) destTotal = destTotal + shiftTable[destType] end end diff --git a/src/Modules/CalcMirages.lua b/src/Modules/CalcMirages.lua index b6826b567e..82b4f3f0a1 100644 --- a/src/Modules/CalcMirages.lua +++ b/src/Modules/CalcMirages.lua @@ -288,6 +288,7 @@ function calcs.mirages(env) preCalcFunc = function(env, newSkill, newEnv) local lessDamage = newSkill.skillModList:Sum("BASE", env.player.mainSkill.skillCfg, "SacredWispsLessDamage") local wispsMaxCount + local wispsSummoned local wispsCastChance -- Find Wisps summoning skill for cast chance and wisp count @@ -295,16 +296,19 @@ function calcs.mirages(env) if skill.activeEffect.grantedEffect.name == "Summon Sacred Wisps" then wispsCastChance = skill.skillModList:Sum("BASE", env.player.mainSkill.skillCfg, "SacredWispsChance") wispsMaxCount = skill.skillModList:Sum("BASE", env.player.mainSkill.skillCfg, "SacredWispsMaxCount") + wispsSummoned = 2 + skill.skillModList:Sum("BASE", skill.skillCfg, "SacredWispsSummoned") break end end env.player.mainSkill.mirage = { } env.player.mainSkill.mirage.name = newSkill.activeEffect.grantedEffect.name + -- Sustained damage uses the active cap; summons per trigger only affect ramp-up. env.player.mainSkill.mirage.count = wispsMaxCount + env.player.mainSkill.mirage.summoned = wispsSummoned if not env.player.mainSkill.infoMessage then - env.player.mainSkill.infoMessage = tostring(wispsMaxCount) .. " Sacred Wisps using " .. newSkill.activeEffect.grantedEffect.name + env.player.mainSkill.infoMessage = tostring(wispsMaxCount) .. " Sacred Wisps using " .. newSkill.activeEffect.grantedEffect.name .. " (" .. wispsSummoned .. " summoned per trigger)" end -- Add new modifiers to new skill (which already has all the old skill's modifiers) diff --git a/src/Modules/CalcOffence.lua b/src/Modules/CalcOffence.lua index 76f6d5ca25..5baf0c8b6c 100644 --- a/src/Modules/CalcOffence.lua +++ b/src/Modules/CalcOffence.lua @@ -3593,7 +3593,8 @@ function calcs.offence(env, actor, activeSkill) pen = skillModList:Sum("BASE", cfg, "ChaosPenetration") end end - local invertChanceEle = m_max(m_min(skillModList:Sum("CHANCE", cfg, "HitsInvertEleResChance"), 1), 0) + -- Include the matching element-specific resistance inversion chance when calculating hit damage. + local invertChanceEle = m_max(m_min(skillModList:Sum("CHANCE", cfg, "HitsInvertEleResChance", "HitsInvert"..damageType.."ResChance"), 1), 0) local invertChanceChaos = m_max(m_min(skillModList:Sum("CHANCE", cfg, "HitsInvertChaosResChance"), 1), 0) local invertChance = (isElemental[damageType] and invertChanceEle) or (damageType == "Chaos" and invertChanceChaos) or 0 sourceRes = env.modDB:Flag(nil, "Enemy"..sourceRes.."ResistEqualToYours") and "Your "..sourceRes.." Resistance" or (env.partyMembers.modDB:Flag(nil, "Enemy"..sourceRes.."ResistEqualToYours") and "Party Member "..sourceRes.." Resistance" or sourceRes) @@ -5413,11 +5414,12 @@ function calcs.offence(env, actor, activeSkill) local incChillDuration = skillModList:Sum("INC", cfg, "EnemyChillDuration", "EnemyAilmentDuration", "EnemyElementalAilmentDuration") + enemyDB:Sum("INC", nil, "SelfChillDuration", "SelfAilmentDuration", "SelfElementalAilmentDuration") local moreChillDuration = skillModList:More(cfg, "EnemyChillDuration", "EnemyAilmentDuration", "EnemyElementalAilmentDuration") * enemyDB:More(nil, "SelfChillDuration", "SelfAilmentDuration", "SelfElementalAilmentDuration") output.ChillDurationMod = (1 + incChillDuration / 100) * moreChillDuration - output.ChillSourceEffect = m_min(skillModList:Override(nil, "ChillMax") or ailmentData.Chill.max, m_floor(ailmentData.Chill.default * output.ChillEffectMod)) + -- Let +maximum Chill effect also raise the cap for chilling areas such as Cold Snap. + output.ChillSourceEffect = m_min(skillModList:Override(nil, "ChillMax") or (ailmentData.Chill.max + skillModList:Sum("BASE", nil, "ChillMax")), m_floor(ailmentData.Chill.default * output.ChillEffectMod)) if breakdown then breakdown.DotChill = { } breakdown.multiChain(breakdown.DotChill, { - label = s_format("Effect of Chill: ^8(capped at %d%%)", skillModList:Override(nil, "ChillMax") or ailmentData.Chill.max), + label = s_format("Effect of Chill: ^8(capped at %d%%)", skillModList:Override(nil, "ChillMax") or (ailmentData.Chill.max + skillModList:Sum("BASE", nil, "ChillMax"))), base = s_format("%d%% ^8(base)", ailmentData.Chill.default), { "%.2f ^8(increased/reduced effect of chill)", 1 + incChill / 100 }, { "%.2f ^8(more/less effect of chill)", moreChill }, diff --git a/src/Modules/CalcPerform.lua b/src/Modules/CalcPerform.lua index d683aab3ce..11b87dfda1 100644 --- a/src/Modules/CalcPerform.lua +++ b/src/Modules/CalcPerform.lua @@ -1700,6 +1700,10 @@ function calcs.perform(env, skipEHP) local function calcFlaskMods(item, baseName, buffModList, modList, onlyMinion) local flaskEffectInc = effectInc + item.flaskData.effectInc + -- Apply increased Iron Flask effect to the player without boosting other flask types. + if item.baseName == "Iron Flask" then + flaskEffectInc = flaskEffectInc + modDB:Sum("INC", { actor = "player" }, "IronFlaskEffect") + end local flaskEffectIncNonPlayer = effectIncNonPlayer + item.flaskData.effectInc if item.rarity == "MAGIC" and not (item.base.flask.life or item.base.flask.mana) then flaskEffectInc = flaskEffectInc + effectIncMagic diff --git a/src/Modules/CalcSetup.lua b/src/Modules/CalcSetup.lua index 55b3425d02..55bb4417b8 100644 --- a/src/Modules/CalcSetup.lua +++ b/src/Modules/CalcSetup.lua @@ -1031,6 +1031,21 @@ function calcs.initEnv(build, mode, override, specEnv) end end + -- Wickerman slots follow ordinary equipment. Collect their bonus modifiers + -- before scaling gloves/boots so the result does not depend on slot order. + -- Glove/boot bonus scaling must include Custom Modifiers and Wickerman items as well as passive nodes. + local equipmentBonusModDB = new("ModDB"):ModDB(env.initialNodeModDB) + equipmentBonusModDB:AddList(build.configTab.modList) + for _, item in pairs(items) do + if item.type:match("^Wickerman ") then + for _, mod in ipairs(item.modList or { }) do + if mod.name == "EffectOfBonusesFromGloves" or mod.name == "EffectOfBonusesFromBoots" then + equipmentBonusModDB:AddMod(mod) + end + end + end + end + env.itemSlotIndex = { } -- Track which flask slot (1-5) each flask is in, for adjacency checks env.flaskSlotMap = { } @@ -1257,8 +1272,9 @@ function calcs.initEnv(build, mode, override, specEnv) combinedList:MergeMod(mod) end env.itemModDB:ScaleAddList(combinedList, scale) - elseif item.type == "Gloves" and calcLib.mod(env.initialNodeModDB, nil, "EffectOfBonusesFromGloves") ~=1 then - scale = calcLib.mod(env.initialNodeModDB, nil, "EffectOfBonusesFromGloves") - 1 + -- Apply increased/reduced bonuses from equipped gloves from Custom Modifiers and Wickerman items, as well as passives. + elseif item.type == "Gloves" and calcLib.mod(equipmentBonusModDB, nil, "EffectOfBonusesFromGloves") ~=1 then + scale = calcLib.mod(equipmentBonusModDB, nil, "EffectOfBonusesFromGloves") - 1 local combinedList = new("ModList"):ModList() for _, mod in ipairs(srcList) do combinedList:MergeMod(mod) @@ -1269,8 +1285,9 @@ function calcs.initEnv(build, mode, override, specEnv) combinedList:MergeMod(mod, true) end env.itemModDB:AddList(combinedList) - elseif item.type == "Boots" and calcLib.mod(env.initialNodeModDB, nil, "EffectOfBonusesFromBoots") ~= 1 then - scale = calcLib.mod(env.initialNodeModDB, nil, "EffectOfBonusesFromBoots") - 1 + -- Apply increased/reduced bonuses from equipped boots from Custom Modifiers and Wickerman items, as well as passives. + elseif item.type == "Boots" and calcLib.mod(equipmentBonusModDB, nil, "EffectOfBonusesFromBoots") ~= 1 then + scale = calcLib.mod(equipmentBonusModDB, nil, "EffectOfBonusesFromBoots") - 1 local combinedList = new("ModList"):ModList() for _, mod in ipairs(srcList) do combinedList:MergeMod(mod) @@ -1463,6 +1480,22 @@ function calcs.initEnv(build, mode, override, specEnv) -- Merge Granted Skills Tables env.grantedSkills = tableConcat(env.grantedSkillsNodes, env.grantedSkillsItems) + -- Create skill groups for influence-gated curses entered in Custom Modifiers, just like item-granted curses. + for _, skill in ipairs(build.configTab.modList:List(nil, "ExtraSkill")) do + if skill.itemRequirement and env.data.skills[skill.skillId] then + local grantedSkill = copyTable(skill) + grantedSkill.source = "Config" + grantedSkill.nameSpec = env.data.skills[skill.skillId].name + t_insert(env.grantedSkills, grantedSkill) + end + end + -- Only grant these curses while enough items of the required influence are equipped. + for index = #env.grantedSkills, 1, -1 do + local requirement = env.grantedSkills[index].itemRequirement + if requirement and env.itemModDB:GetMultiplier(requirement.multiplier) < requirement.count then + t_remove(env.grantedSkills, index) + end + end if not accelerate.skills then if env.mode == "MAIN" then diff --git a/src/Modules/ConfigOptions.lua b/src/Modules/ConfigOptions.lua index 8ff3e6f8c7..3f0f8fe934 100644 --- a/src/Modules/ConfigOptions.lua +++ b/src/Modules/ConfigOptions.lua @@ -174,6 +174,11 @@ return { { var = "conditionFullLife", type = "check", label = "Are you always on Full ^xE05030Life?", ifCond = "FullLife", tooltip = FullLifeTooltip, apply = function(val, modList, enemyModList) modList:NewMod("Condition:FullLife", "FLAG", true, "Config") end }, + { var = "missingUnreservedLifePercent", type = "countAllowZero", label = "Missing unreserved Life % (override):", ifStat = "MissingUnreservedLifePercent", + tooltip = "For recovery scaling per missing unreserved Life.\nBlank assumes full unreserved Life, or Petrified Blood's non-flask recovery limit:\n50% maximum Life (55% with the Low Life mastery), accounting for reservation.\nAn explicit value, including 0, overrides this assumption.\nDoes not change other Full/Low Life settings.", + apply = function(val, modList, enemyModList) + modList:NewMod("MissingUnreservedLifePercent", "OVERRIDE", math.max(0, math.min(100, val)), "Config") + end }, { var = "conditionLowLife", type = "check", label = "Are you always on Low ^xE05030Life?", ifCond = "LowLife", tooltip = LowLifeTooltip, apply = function(val, modList, enemyModList) modList:NewMod("Condition:LowLife", "FLAG", true, "Config") end }, diff --git a/src/Modules/ModParser.lua b/src/Modules/ModParser.lua index 9d5d1db3a0..020459648b 100644 --- a/src/Modules/ModParser.lua +++ b/src/Modules/ModParser.lua @@ -328,6 +328,8 @@ local modNameList = { ["fire damage taken from attacks"] = "FireDamageTakenFromAttacks", ["fire damage taken from attack hits"] = "FireDamageTakenFromAttacks", ["fire damage taken over time"] = "FireDamageTakenOverTime", + -- Burning damage is fire damage over time; this wording should use the existing damage-taken calculation. + ["burning damage taken"] = "FireDamageTakenOverTime", ["chaos damage taken"] = "ChaosDamageTaken", ["chaos damage from hits taken"] = "ChaosDamageFromHitsTaken", ["chaos damage taken when hit"] = "ChaosDamageTakenWhenHit", @@ -1090,6 +1092,9 @@ local modFlagList = { ["from equipped shield"] = { tag = { type = "SlotName", slotName = "Weapon 2" } }, ["from equipped armour items"] = { tag = { type = "SlotName", slotNameList = { "Helmet", "Body Armour", "Gloves", "Boots", "Weapon 2" } } }, ["from equipped helmet"] = { tag = { type = "SlotName", slotName = "Helmet" } }, + -- Restrict these defence bonuses to the four named armour slots, excluding shields and global defences. + ["from equipped helmet, body armour gloves and boots"] = { tag = { type = "SlotName", slotNameList = { "Helmet", "Body Armour", "Gloves", "Boots" } } }, + ["from equipped helmet, body armour, gloves, and boots"] = { tag = { type = "SlotName", slotNameList = { "Helmet", "Body Armour", "Gloves", "Boots" } } }, ["from equipped gloves and boots"] = { tag = { type = "SlotName", slotNameList = { "Gloves", "Boots" } } }, ["from equipped boots and gloves"] = { tag = { type = "SlotName", slotNameList = { "Gloves", "Boots" } } }, ["from equipped helmet and gloves"] = { tag = { type = "SlotName", slotNameList = { "Helmet", "Gloves" } } }, @@ -1758,7 +1763,8 @@ local modTagList = { ["on chilled ground"] = { tag = { type = "Condition", var = "OnChilledGround" } }, ["on shocked ground"] = { tag = { type = "Condition", var = "OnShockedGround" } }, ["while in a caustic cloud"] = { tag = { type = "Condition", var = "OnCausticCloud" } }, - ["while blinded"] = { tagList = { { type = "Condition", var = "Blinded" }, { type = "Condition", var = "CannotBeBlinded", neg = true } } }, + -- Accept both "while Blind" and "while Blinded" using the existing player condition. + ["while blinde?d?"] = { tagList = { { type = "Condition", var = "Blinded" }, { type = "Condition", var = "CannotBeBlinded", neg = true } } }, ["while burning"] = { tag = { type = "Condition", var = "Burning" } }, ["while ignited"] = { tag = { type = "Condition", var = "Ignited" } }, ["while you are ignited"] = { tag = { type = "Condition", var = "Ignited" } }, @@ -2130,6 +2136,13 @@ local dmgTypes = { -- List of special modifiers ---@type table local specialModList = { + -- Recognize spell-hit Hinder chance and expose the manual enemy Hindered setting; do not assume proc uptime. + ["(%d+)%% chance to hinder enemies on hit with spells"] = function(num) return { mod("HinderChance", "BASE", num, nil, ModFlag.Spell), mod("Dummy", "DUMMY", 1, "Select enemy Hindered in Configuration", { type = "ActorCondition", actor = "enemy", var = "Hindered" }) } end, + -- Recognize the global Blind chance wording; whether the enemy is Blinded remains manually configured. + ["(%d+)%% global chance to blind enemies on hit"] = function(num) return { mod("BlindChance", "BASE", num) } end, + ["(%d+)%% increased life recovery rate per (%d+)%% missing unreserved life"] = function(num, _, div) return { mod("LifeRecoveryRate", "INC", num, { type = "PerStat", stat = "MissingUnreservedLifePercent", div = tonumber(div) }) } end, + -- Track additional Wisps summoned per trigger separately from the maximum active count used for sustained DPS. + ["%+(%d+) to number of sacred wisps summoned"] = function(num) return { mod("SacredWispsSummoned", "BASE", num) } end, -- Explode mods ["enemies you kill have a (%d+)%% chance to explode, dealing a (.+) of their maximum life as (.+) damage"] = function(chance, _, amount, type) -- Obliteration, Unspeakable Gifts (chaos cluster), synth implicit mod, current crusader body mod, Ngamahu Warmonger tattoo return explodeFunc(chance, amount, type) @@ -2500,6 +2513,7 @@ local specialModList = { ["brands have (%d+)%% increased area of effect if (%d+)%% of attached duration expired"] = function(num) return { mod("AreaOfEffect", "INC", num, { type = "Condition", var = "BrandLastHalf" }, { type = "SkillType", skillType = SkillType.Brand }) } end, ["corrupted blood cannot be inflicted on you"] = { flag("CorruptedBloodImmune") }, ["you cannot be hindered"] = { flag("HinderImmune") }, + ["immune to hinder"] = { flag("HinderImmune") }, ["you cannot be maimed"] = { flag("MaimImmune") }, ["you cannot be impaled"] = { flag("ImpaleImmune") }, -- Exerted Attacks @@ -2799,6 +2813,7 @@ local specialModList = { mod("EnemySapEffect", "MORE", num, { type = "Condition", var = "LightningIsHighestDamageType" }), flag("ChecksHighestDamage"), } end, + ["%+(%d+)%% to maximum effect of chill"] = function(num) return { mod("ChillMax", "BASE", num) } end, ["your chills can reduce action speed by up to a maximum of (%d+)%%"] = function(num) return { mod("ChillMax", "OVERRIDE", num) } end, ["your hits always ignite"] = { mod("EnemyIgniteChance", "BASE", 100) }, ["hits always ignite"] = { mod("EnemyIgniteChance", "BASE", 100) }, @@ -2938,6 +2953,7 @@ local specialModList = { ["gain accuracy rating equal to your strength"] = { mod("Accuracy", "BASE", 1, { type = "PerStat", stat = "Str" }) }, ["gain accuracy rating equal to twice your strength"] = { mod("Accuracy", "BASE", 2, { type = "PerStat", stat = "Str" }) }, -- Necromancer + ["offerings you create also grant (%d+)%% increased damage"] = function(num) return { mod("ExtraSkillMod", "LIST", { mod = mod("Damage", "INC", num, { type = "GlobalEffect", effectType = "Buff" }) }, { type = "SkillName", skillNameList = { "Bone Offering", "Flesh Offering", "Spirit Offering", "Blood Offering" } }) } end, ["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", "Blood 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", "Blood Offering" } }) } end, ["your offerings have (%d+)%% increased effect on you"] = function(num) return { mod("ExtraSkillMod", "LIST", { mod = mod("BuffEffectOnPlayer", "INC", num) }, { type = "SkillName", skillNameList = { "Bone Offering", "Flesh Offering", "Spirit Offering", "Blood Offering" } }) } end, @@ -3389,6 +3405,14 @@ local specialModList = { ["(%d+)%% chance to [ct][ar][si][tg]g?e?r? level (%d+) (.+) on %a+"] = function(chance, _, level, skill) return triggerExtraSkill(skill, level, {triggerChance = chance}) end, ["attack with level (%d+) (.+) when you kill a bleeding enemy"] = function(num, _, skill) return triggerExtraSkill(skill, num) end, ["triggers? level (%d+) (.+) when you kill a bleeding enemy"] = function(num, _, skill) return triggerExtraSkill(skill, num) end, + -- Accept multiword curse names and carry the influence requirement into the existing granted-skill system. + ["curse enemies with (.-) on hit if (%d+) (%a+) items are equipped"] = function(_, curse, count, influence) + local mods = triggerExtraSkill(curse, 1, { noSupports = true }) + if mods[1] then + mods[1].value.itemRequirement = { multiplier = firstToUpper(influence).."Item", count = tonumber(count) } + end + return mods + end, ["curse enemies with (%D+) on %a+"] = function(_, skill) return triggerExtraSkill(skill, 1, {noSupports = true}) end, ["curse enemies with (%D+) on %a+, with (%d+)%% increased effect"] = function(_, skill, num) return { mod("ExtraSkill", "LIST", { skillId = gemIdLookup[skill], level = 1, noSupports = true, triggered = true }), @@ -3495,6 +3519,9 @@ local specialModList = { ["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") }, ["modifiers to claw critical strike chance also apply to unarmed critical strike chance with melee skills"] = { flag("ClawCritChanceAppliesToUnarmed") }, + ["increases and reductions to light radius also apply to life regeneration rate at (%d+)%% value"] = function(num) return { mod("LightRadiusAppliesToLifeRegen", "MAX", num) } end, + ["increases and reductions to mana regeneration rate also apply to energy shield recharge rate at (%d+)%% of their value"] = function(num) return { mod("ManaRegenAppliesToEnergyShieldRecharge", "MAX", num) } end, + ["skills socketed in equipped boots have (%d+)%% increased mana cost efficiency"] = function(num) return { mod("ManaCostEfficiency", "INC", num, { type = "SocketedIn", slotName = "Boots" }) } end, ["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") }, @@ -3705,6 +3732,11 @@ local specialModList = { ["you can inflict up to (%d+) ignites on an enemy"] = function(num) return { flag("IgniteCanStack"), mod("IgniteStacks", "OVERRIDE", num) } end, ["you can inflict an additional ignite on [ea][an]c?h? 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, + ["damaging ailments inflicted by minions deal damage (%d+)%% faster"] = function(num) return { + mod("MinionModifier", "LIST", { mod = mod("IgniteBurnFaster", "INC", num) }), + mod("MinionModifier", "LIST", { mod = mod("BleedFaster", "INC", num) }), + mod("MinionModifier", "LIST", { mod = mod("PoisonFaster", "INC", num) }), + } 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" }), @@ -3887,6 +3919,7 @@ local specialModList = { ["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 poison the enemy"] = { mod("PoisonChance", "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" }) }, @@ -4431,9 +4464,14 @@ local specialModList = { ["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, + ["minions created recently have onslaught"] = { mod("MinionModifier", "LIST", { mod = flag("Condition:Onslaught") }, { type = "Condition", var = "MinionsCreatedRecently" }) }, + -- Check whether the player hit recently, rather than checking the minion. + ["minions deal (%d+)%% increased damage if you've 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, ["minions deal (%d+)%% increased damage if you have warcried recently"] = function(num) return { mod("MinionModifier", "LIST", { mod = mod("Damage", "INC", num, { type = "ActorCondition", actor = "parent", var = "UsedWarcryRecently" }) }) } end, ["minions deal (%d+)%% increased 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, + -- Check whether the player used a minion skill recently, rather than checking the minion. + ["minions have (%d+)%% increased movement speed if you've used a minion skill recently"] = function(num) return { mod("MinionModifier", "LIST", { mod = mod("MovementSpeed", "INC", num) }, { type = "Condition", var = "UsedMinionSkillRecently" }) } end, ["minions deal (%d+)%% more damage while they are on low life"] = function(num) return { mod("MinionModifier", "LIST", { mod = mod("Damage", "MORE", num, { type = "Condition", var = "LowLife" }) }) } end, ["minions have (%d+)%% increased attack and cast speed if you or your minions have killed recently"] = function(num) return { mod("MinionModifier", "LIST", { mod = mod("Speed", "INC", num) }, { type = "Condition", varList = { "KilledRecently", "MinionsKilledRecently" } }) } 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, @@ -4574,6 +4612,8 @@ local specialModList = { ["bow attacks fire (%d+) additional arrows if you haven't cast dash recently"] = function(num) return { mod("ProjectileCount", "BASE", num, nil, 0, KeywordFlag.Arrow, { type = "Condition", var = "CastDashRecently", neg = true }) } end, ["wand attacks fire an additional projectile"] = { mod("ProjectileCount", "BASE", 1, nil, ModFlag.Wand) }, ["skills fire an additional projectile"] = { mod("ProjectileCount", "BASE", 1) }, + ["skills fire an additional projectile if you've been hit recently"] = { mod("ProjectileCount", "BASE", 1, { type = "Condition", var = "BeenHitRecently" }) }, + ["skills fire an additional projectile if you've used a movement skill recently"] = { mod("ProjectileCount", "BASE", 1, { type = "Condition", var = "UsedMovementSkillRecently" }) }, ["skills fire an additional projectile if (%d+) (%a+) items are equipped"] = function(_, thresh, influence) return { mod("ProjectileCount", "BASE", 1, { type = "MultiplierThreshold", var = firstToUpper(influence) .. "Item", threshold = tonumber(thresh) }) } end, ["spells [hf][ai][vr]e an additional projectile"] = { mod("ProjectileCount", "BASE", 1, nil, ModFlag.Spell) }, ["spells [hf][ai][vr]e (%d+) additional projectiles"] = function(num) return { mod("ProjectileCount", "BASE", num, nil, ModFlag.Spell) } end, @@ -5233,6 +5273,7 @@ local specialModList = { ["only affects passives in massive ring"] = { mod("JewelData", "LIST", { key = "radiusIndex", value = 10 }) }, ["primordial"] = { mod("Multiplier:PrimordialItem", "BASE", 1) }, ["spectres have a base duration of (%d+) seconds"] = { mod("SkillData", "LIST", { key = "duration", value = 6 }, { type = "SkillName", skillName = "Raise Spectre", includeTransfigured = true }) }, + ["iron flasks applied to you have (%d+)%% increased effect"] = function(num) return { mod("IronFlaskEffect", "INC", num, { type = "ActorCondition", actor = "player" }) } end, ["flasks applied to you have (%d+)%% increased effect"] = function(num) return { mod("FlaskEffect", "INC", num, { type = "ActorCondition", actor = "player"}) } end, ["flasks applied to you have (%d+)%% increased effect per level"] = function(num) return { mod("FlaskEffect", "INC", num, { type = "ActorCondition", actor = "player"}, { type = "Multiplier", var = "Level" }) } end, ["equipped magic flasks have (%d+)%% increased effect on you if no flasks are adjacent to them"] = function(num) return { mod("MagicFlaskNoAdjacentEffect", "INC", num) } end, @@ -5729,6 +5770,18 @@ local specialModList = { } end, ["elusive also grants %+(%d+)%% to critical strike multiplier for skills supported by nightblade"] = function(num) return { mod("NightbladeElusiveCritMultiplier", "BASE", num) } end, ["skills supported by nightblade have (%d+)%% increased effect of elusive"] = function(num) return { mod("NightbladeSupportedElusiveEffect", "INC", num) } end, + -- Use the existing nearby-enemy model; distance is not checked. + ["enemies within ([%d%.]+) metres are scorched"] = { mod("EnemyModifier", "LIST", { mod = flag("Condition:Scorched") }), mod("ScorchBase", "BASE", 10) }, + -- Treat the distance-worded aura as flat added damage for the player and allies, without a metre check. + ["you and allies within ([%d%.]+) metres deal (%d+) to (%d+) additional cold damage"] = function(_, _, min, max) return { + mod("ExtraAura", "LIST", { mod = mod("ColdMin", "BASE", tonumber(min)) }), + mod("ExtraAura", "LIST", { mod = mod("ColdMax", "BASE", tonumber(max)) }), + } end, + ["hits have (%d+)%% chance to treat enemy monster cold resistance values as inverted"] = function(num) return { mod("HitsInvertColdResChance", "CHANCE", num / 100) } end, + ["hits have (%d+)%% chance to treat enemy monster fire resistance values as inverted"] = function(num) return { mod("HitsInvertFireResChance", "CHANCE", num / 100) } end, + ["hits have (%d+)%% chance to treat enemy monster lightning resistance values as inverted"] = function(num) return { mod("HitsInvertLightningResChance", "CHANCE", num / 100) } end, + ["skills that cost life have (%d+)%% chance to deal double damage"] = function(num) return { mod("DoubleDamageChance", "BASE", num, { type = "StatThreshold", stat = "LifeCost", threshold = 1 }) } end, + ["enemies you blind while in sand stance take (%d+)%% increased damage"] = function(num) return { mod("EnemyModifier", "LIST", { mod = mod("DamageTaken", "INC", num, { type = "Condition", var = "Blinded" }) }, { type = "Condition", var = "SandStance" }) } end, ["nearby enemies are scorched"] = { mod("EnemyModifier", "LIST", { mod = flag("Condition:Scorched") }), mod("ScorchBase", "BASE", 10),