diff --git a/Classes/CheckBoxControl.lua b/Classes/CheckBoxControl.lua index 44c0cb58..aa0a18f9 100644 --- a/Classes/CheckBoxControl.lua +++ b/Classes/CheckBoxControl.lua @@ -91,7 +91,9 @@ function CheckBoxClass:OnKeyUp(key) if key == "LEFTBUTTON" then if self:IsMouseOver() then self.state = not self.state - self.changeFunc(self.state) + if self.changeFunc then + self.changeFunc(self.state) + end end end self.clicked = false diff --git a/Classes/ImportTab.lua b/Classes/ImportTab.lua index 855c9001..c9e3f489 100644 --- a/Classes/ImportTab.lua +++ b/Classes/ImportTab.lua @@ -16,7 +16,7 @@ local ImportTabClass = common.NewClass("ImportTab", "ControlHost", "Control", fu self.charImportMode = "GETACCOUNTNAME" self.charImportStatus = "Idle" - self.controls.sectionCharImport = common.New("SectionControl", {"TOPLEFT",self,"TOPLEFT"}, 10, 18, 600, 210, "Character Import") + self.controls.sectionCharImport = common.New("SectionControl", {"TOPLEFT",self,"TOPLEFT"}, 10, 18, 600, 230, "Character Import") self.controls.charImportStatusLabel = common.New("LabelControl", {"TOPLEFT",self.controls.sectionCharImport,"TOPLEFT"}, 6, 14, 200, 16, function() return "^7Character import status: "..self.charImportStatus end) @@ -89,14 +89,20 @@ You can get this from your web browser's cookies while logged into the Path of E self.controls.charImportTree.enabled = function() return self.charImportMode == "SELECTCHAR" end - self.controls.charImportItems = common.New("ButtonControl", {"LEFT",self.controls.charImportTree, "RIGHT"}, 8, 0, 110, 20, "Items and Skills", function() + self.controls.charImportTreeClearJewels = common.New("CheckBoxControl", {"LEFT",self.controls.charImportTree,"RIGHT"}, 90, 0, 18, "Delete jewels:") + self.controls.charImportTreeClearJewels.tooltip = "Delete all existing jewels when importing." + self.controls.charImportItems = common.New("ButtonControl", {"LEFT",self.controls.charImportTree, "LEFT"}, 0, 36, 110, 20, "Items and Skills", function() self:DownloadItems() end) self.controls.charImportItems.enabled = function() return self.charImportMode == "SELECTCHAR" end - self.controls.charBanditNote = common.New("LabelControl", {"TOPLEFT",self.controls.charImportHeader,"BOTTOMLEFT"}, 0, 14, 200, 14, "^7Tip: After you finish importing a character, make sure you update the bandit choices,\nas these cannot be imported.") - self.controls.charDone = common.New("ButtonControl", {"TOPLEFT",self.controls.charImportHeader,"BOTTOMLEFT"}, 0, 60, 60, 20, "Done", function() + self.controls.charImportItemsClearSkills = common.New("CheckBoxControl", {"LEFT",self.controls.charImportItems,"RIGHT"}, 85, 0, 18, "Delete skills:") + self.controls.charImportItemsClearSkills.tooltip = "Delete all existing skills when importing." + self.controls.charImportItemsClearItems = common.New("CheckBoxControl", {"LEFT",self.controls.charImportItems,"RIGHT"}, 220, 0, 18, "Delete equipment:") + self.controls.charImportItemsClearItems.tooltip = "Delete all equipped items when importing." + self.controls.charBanditNote = common.New("LabelControl", {"TOPLEFT",self.controls.charImportHeader,"BOTTOMLEFT"}, 0, 50, 200, 14, "^7Tip: After you finish importing a character, make sure you update the bandit choices,\nas these cannot be imported.") + self.controls.charDone = common.New("ButtonControl", {"TOPLEFT",self.controls.charImportHeader,"BOTTOMLEFT"}, 0, 90, 60, 20, "Done", function() self.charImportMode = "GETACCOUNTNAME" self.charImportStatus = "Idle" end) @@ -269,6 +275,13 @@ function ImportTabClass:DownloadPassiveTree() end self.charImportStatus = data.colorCodes.POSITIVE.."Passive tree and jewels successfully imported." --ConPrintTable(charPassiveData) + if self.controls.charImportTreeClearJewels.state then + for _, slot in pairs(self.build.itemsTab.slots) do + if slot.selItemId ~= 0 and slot.nodeId then + self.build.itemsTab:DeleteItem(self.build.itemsTab.list[slot.selItemId]) + end + end + end local sockets = { } for i, slot in pairs(charPassiveData.jewel_slots) do sockets[i] = tonumber(slot.passiveSkill.hash) @@ -307,11 +320,67 @@ function ImportTabClass:DownloadItems() self.charImportStatus = data.colorCodes.NEGATIVE.."Error processing character data, try again later." return end + if self.controls.charImportItemsClearItems.state then + for _, slot in pairs(self.build.itemsTab.slots) do + if slot.selItemId ~= 0 and not slot.nodeId then + self.build.itemsTab:DeleteItem(self.build.itemsTab.list[slot.selItemId]) + end + end + end + local skillOrder + if self.controls.charImportItemsClearSkills.state then + skillOrder = { } + for _, socketGroup in ipairs(self.build.skillsTab.socketGroupList) do + for _, gem in ipairs(socketGroup.gemList) do + if gem.data and not gem.data.support then + t_insert(skillOrder, gem.name) + end + end + end + wipeTable(self.build.skillsTab.socketGroupList) + end self.charImportStatus = data.colorCodes.POSITIVE.."Items and skills successfully imported." --ConPrintTable(charItemData) for _, itemData in pairs(charItemData.items) do self:ImportItem(itemData) end + if skillOrder then + local groupOrder = { } + for index, socketGroup in ipairs(self.build.skillsTab.socketGroupList) do + groupOrder[socketGroup] = index + end + table.sort(self.build.skillsTab.socketGroupList, function(a, b) + local orderA + for _, gem in ipairs(a.gemList) do + if gem.data and not gem.data.support then + local i = isValueInArray(skillOrder, gem.name) + if i and (not orderA or i < orderA) then + orderA = i + end + end + end + local orderB + for _, gem in ipairs(b.gemList) do + if gem.data and not gem.data.support then + local i = isValueInArray(skillOrder, gem.name) + if i and (not orderB or i < orderB) then + orderB = i + end + end + end + if orderA and orderB then + if orderA ~= orderB then + return orderA < orderB + else + return groupOrder[a] < groupOrder[b] + end + elseif not orderA and not orderB then + return groupOrder[a] < groupOrder[b] + else + return orderA + end + end) + end self.build.itemsTab:PopulateSlots() self.build.itemsTab:AddUndoState() self.build.skillsTab:AddUndoState() diff --git a/Data/Gems/act_int.lua b/Data/Gems/act_int.lua index c5aabafc..7fb45ab3 100644 --- a/Data/Gems/act_int.lua +++ b/Data/Gems/act_int.lua @@ -4390,8 +4390,8 @@ gems["Wither"] = { baseMods = { skill("castTime", 0.28), mod("ChaosDamageTaken", "INC", 7, 0, 0, { type = "GlobalEffect", effectType = "Debuff" }), --"chaos_damage_taken_+%" = 7 - skill("duration", 0.5), --"base_skill_effect_duration" = 500 - --"base_secondary_skill_effect_duration" = 2000 + nil, --"base_skill_effect_duration" = 500 + skill("duration", 2), --"base_secondary_skill_effect_duration" = 2000 skill("stackCount", 5, { type = "SkillPart", skillPart = 2 }), skill("stackCount", 10, { type = "SkillPart", skillPart = 3 }), skill("stackCount", 20, { type = "SkillPart", skillPart = 4 }), diff --git a/Data/Gems/act_str.lua b/Data/Gems/act_str.lua index 7c6d4e29..25f96870 100644 --- a/Data/Gems/act_str.lua +++ b/Data/Gems/act_str.lua @@ -2785,8 +2785,8 @@ gems["Warlord's Mark"] = { baseMods = { skill("castTime", 0.5), --"chance_to_be_stunned_%" = 10 - --"life_leech_on_any_damage_when_hit_permyriad" = 200 - --"mana_leech_on_any_damage_when_hit_permyriad" = 200 + mod("SelfDamageLifeLeech", "BASE", 200, 0, 0, { type = "GlobalEffect", effectType = "Curse" }), --"life_leech_on_any_damage_when_hit_permyriad" = 200 + mod("SelfDamageManaLeech", "BASE", 200, 0, 0, { type = "GlobalEffect", effectType = "Curse" }), --"mana_leech_on_any_damage_when_hit_permyriad" = 200 --"base_deal_no_damage" = ? skill("debuff", true), }, diff --git a/Data/Gems/sup_dex.lua b/Data/Gems/sup_dex.lua index 2fd6dfa0..e1f4e4de 100644 --- a/Data/Gems/sup_dex.lua +++ b/Data/Gems/sup_dex.lua @@ -922,13 +922,13 @@ gems["Mana Leech"] = { addSkillTypes = { }, excludeSkillTypes = { }, baseMods = { - --"mana_leech_from_any_damage_permyriad" = 200 + mod("DamageManaLeech", "BASE", 2), --"mana_leech_from_any_damage_permyriad" = 200 }, qualityMods = { - --"mana_leech_speed_+%" = 0.5 + mod("ManaLeechRate", "INC", 0.5), --"mana_leech_speed_+%" = 0.5 }, levelMods = { - --[1] = "mana_leech_speed_+%" + [1] = mod("ManaLeechRate", "INC", nil), --"mana_leech_speed_+%" }, levels = { [1] = { 0, }, diff --git a/Data/Gems/sup_str.lua b/Data/Gems/sup_str.lua index 832ec992..a64c5125 100644 --- a/Data/Gems/sup_str.lua +++ b/Data/Gems/sup_str.lua @@ -917,13 +917,13 @@ gems["Life Leech"] = { excludeSkillTypes = { }, baseMods = { mod("ManaCost", "MORE", 30), - --"life_leech_from_any_damage_permyriad" = 200 + mod("DamageLifeLeech", "BASE", 2), --"life_leech_from_any_damage_permyriad" = 200 }, qualityMods = { - --"life_leech_speed_+%" = 0.5 + mod("LifeLeechRate", "INC", 0.5), --"life_leech_speed_+%" = 0.5 }, levelMods = { - --[1] = "life_leech_speed_+%" + [1] = mod("LifeLeechRate", "INC", nil), --"life_leech_speed_+%" }, levels = { [1] = { 0, }, diff --git a/Data/Uniques/amulet.lua b/Data/Uniques/amulet.lua index b22819c1..c8887cce 100644 --- a/Data/Uniques/amulet.lua +++ b/Data/Uniques/amulet.lua @@ -105,12 +105,12 @@ Moving while Bleeding doesn't cause you to take extra Damage Carnage Heart Onyx Amulet Requires Level 20 -+(10 to 16) to all Attributes -+(20 to 40) to all Attributes ++(10-16) to all Attributes ++(20-40) to all Attributes 25% reduced maximum Life 25% reduced maximum Energy Shield -+(10 to 20)% to all Elemental Resistances -(1.2 to 2)% of Physical Attack Damage Leeched as Life ++(10-20)% to all Elemental Resistances +(1.2-2)% of Physical Attack Damage Leeched as Life Extra Gore ]],[[ Daresso's Salute @@ -360,8 +360,8 @@ Requires Level 50 (8 to 12)% increased Cast Speed 12% increased Movement Speed (8 to 12)% reduced Skill Effect Duration -30% increased Life Leech Rate -30% increased Mana Leech Rate +30% increased Life Leeched per second +30% increased Mana Leeched per second ]],[[ Winterheart Gold Amulet diff --git a/Data/Uniques/body.lua b/Data/Uniques/body.lua index 1a4012f7..0460f075 100644 --- a/Data/Uniques/body.lua +++ b/Data/Uniques/body.lua @@ -363,9 +363,9 @@ Full Dragonscale Armour: (871 to 1005) Evasion: (692 to 798) Requires Level 63, 115 Str, 94 Dex -(160 to 200)% increased Armour and Evasion -+(40 to 60) to maximum Life -(0.4 to 0.6)% of Physical Attack Damage Leeched as Life +(160-200)% increased Armour and Evasion ++(40-60) to maximum Life +(0.4-0.6)% of Physical Attack Damage Leeched as Life 6% Chance to Dodge Attacks You lose all Endurance Charges when Hit You gain an Endurance Charge on Kill @@ -449,10 +449,10 @@ Saintly Chainmail Armour: (761 to 878) Energy Shield: (216 to 235) Requires Level 70, 99 Str, 115 Int -+(60 to 80) to Armour -(120 to 140)% increased Armour and Energy Shield -+(80 to 90) to maximum Life -(0.8 to 1)% of Physical Attack Damage Leeched as Life ++(60-80) to Armour +(120-140)% increased Armour and Energy Shield ++(80-90) to maximum Life +(0.8-1)% of Physical Attack Damage Leeched as Life 25% of Elemental Damage taken as Chaos Damage (20 to 30)% increased Light Radius Light Radius is based on Energy Shield instead of Life diff --git a/Data/Uniques/helmet.lua b/Data/Uniques/helmet.lua index fd4d2ada..e5e27005 100644 --- a/Data/Uniques/helmet.lua +++ b/Data/Uniques/helmet.lua @@ -148,11 +148,11 @@ Crown of Eyes Hubris Circlet Energy Shield: (220 to 250) Requires Level 69, 154 Int -+(200 to 250) to Accuracy Rating -(120 to 150)% increased Energy Shield ++(200-250) to Accuracy Rating +(120-150)% increased Energy Shield −30% to Fire Resistance -(0.4 to 0.8)% of Physical Attack Damage Leeched as Life -(0.2 to 0.4)% of Physical Attack Damage Leeched as Mana +(0.4-0.8)% of Physical Attack Damage Leeched as Life +(0.2-0.4)% of Physical Attack Damage Leeched as Mana Increases and Reductions to Spell Damage also apply to Attacks ]],[[ Crown of Thorns @@ -395,8 +395,8 @@ League: Tempest Evasion: (305 to 366) Energy Shield: (90 to 108) Requires Level 52, 58 Dex, 58 Int -(150 to 200)% increased Evasion and Energy Shield -(0.4 to 0.8)% of Physical Attack Damage Leeched as Life +(150-200)% increased Evasion and Energy Shield +(0.4-0.8)% of Physical Attack Damage Leeched as Life Reflects 100 to 150 Physical Damage to Melee Attackers 30% of Damage you Reflect to enemies is gained as Life ]],[[ diff --git a/Data/Uniques/ring.lua b/Data/Uniques/ring.lua index dd30a4fb..2cc21d41 100644 --- a/Data/Uniques/ring.lua +++ b/Data/Uniques/ring.lua @@ -89,11 +89,11 @@ Death Rush Amethyst Ring League: Onslaught Requires Level 46 -+(9 to 13)% to Chaos Resistance -+(300 to 350) to Accuracy Rating -+(60 to 80) to Armour -+(15 to 20)% to Chaos Resistance -(0.6 to 0.8)% of Physical Attack Damage Leeched as Life ++(9-13)% to Chaos Resistance ++(300-350) to Accuracy Rating ++(60-80) to Armour ++(15-20)% to Chaos Resistance +(0.6-0.8)% of Physical Attack Damage Leeched as Life You gain Onslaught for 2 seconds on Kill ]],[[ Doedre's Damning diff --git a/Data/Uniques/sword.lua b/Data/Uniques/sword.lua index 99f0699c..87420f41 100644 --- a/Data/Uniques/sword.lua +++ b/Data/Uniques/sword.lua @@ -35,9 +35,9 @@ Requires Level 32, 57 Str, 57 Dex {variant:2}150% increased Physical Damage +10 to Intelligence 50% increased Global Critical Strike Chance -(0.6 to 1)% of Physical Attack Damage Leeched as Mana +(0.6-1)% of Physical Attack Damage Leeched as Mana 10% reduced Maximum Life -(40 to 50)% increased Maximum Energy Shield +(40-50)% increased Maximum Energy Shield ]],[[ The Goddess Scorned Elegant Sword diff --git a/Modules/CalcSections.lua b/Modules/CalcSections.lua index fc7403fb..80c0b39c 100644 --- a/Modules/CalcSections.lua +++ b/Modules/CalcSections.lua @@ -504,6 +504,9 @@ return { }, }, } }, { 1, "MiscEffects", 1, "Other Effects", data.colorCodes.OFFENCE, { + --[[{ label = "Life Leech Rate", flag = "leechLife", { format = "{1:output:LifeLeechRate}", { breakdown = "LifeLeechRate" }, }, }, + { label = "ES Leech Rate", flag = "leechES", { format = "{1:output:EnergyShieldLeechRate}", { breakdown = "EnergyShieldLeechRate" }, }, }, + { label = "Mana Leech Rate", flag = "leechMana", { format = "{1:output:ManaLeechRate}", { breakdown = "ManaLeechRate" }, }, },]] { label = "Chance to Shock", flag = "shock", { format = "{0:output:ShockChance}%", { breakdown = "MainHand.ShockChance" }, { breakdown = "OffHand.ShockChance" }, diff --git a/Modules/Calcs.lua b/Modules/Calcs.lua index afdd0d8e..800c5ba2 100644 --- a/Modules/Calcs.lua +++ b/Modules/Calcs.lua @@ -635,6 +635,8 @@ local function initEnv(build, mode, override) modDB:NewMod("Damage", "MORE", 4, "Base", { type = "Multiplier", var = "FrenzyCharge" }) modDB:NewMod("EnduranceChargesMax", "BASE", 3, "Base") modDB:NewMod("ElementalResist", "BASE", 4, "Base", { type = "Multiplier", var = "EnduranceCharge" }) + modDB:NewMod("MaxLifeLeechRate", "BASE", 20, "Base") + modDB:NewMod("MaxManaLeechRate", "BASE", 20, "Base") modDB:NewMod("ActiveTrapLimit", "BASE", 3, "Base") modDB:NewMod("ActiveMineLimit", "BASE", 5, "Base") modDB:NewMod("ActiveTotemLimit", "BASE", 1, "Base") @@ -1652,6 +1654,14 @@ local function performCalcs(env) end end + -- Leech caps + if modDB:Sum("FLAG", nil, "GhostReaver") then + output.MaxEnergyShieldLeechRate = output.EnergyShield * modDB:Sum("BASE", nil, "MaxLifeLeechRate") / 100 + else + output.MaxLifeLeechRate = output.Life * modDB:Sum("BASE", nil, "MaxLifeLeechRate") / 100 + end + output.MaxManaLeechRate = output.Mana * modDB:Sum("BASE", nil, "MaxManaLeechRate") / 100 + -- Other defences: block, dodge, stun recovery/avoidance do output.MovementSpeedMod = calcMod(modDB, nil, "MovementSpeed") @@ -2195,6 +2205,8 @@ local function performCalcs(env) for pass = 1, 2 do -- Pass 1 is critical strike damage, pass 2 is non-critical strike condList["CriticalStrike"] = (pass == 1) + local lifeLeechTotal = 0 + local manaLeechTotal = 0 for _, damageType in ipairs(dmgTypeList) do local min, max if skillFlags.hit and canDeal[damageType] then @@ -2253,9 +2265,17 @@ local function performCalcs(env) breakdown[damageType.."EffMult"] = effMultBreakdown(damageType, resist, pen, taken, effMult) end end - if breakdown then + if breakdown then t_insert(breakdown[damageType], s_format("= %d to %d", min, max)) end + local lifeLeech = modDB:Sum("BASE", cfg, "DamageLifeLeech", damageType.."LifeLeech", isElemental[damageType] and "ElementalLifeLeech" or nil) + enemyDB:Sum("BASE", nil, "SelfDamageLifeLeech") / 100 + if lifeLeech > 0 then + lifeLeechTotal = lifeLeechTotal + (min + max) / 2 * lifeLeech / 100 + end + local manaLeech = modDB:Sum("BASE", cfg, "DamageManaLeech", damageType.."ManaLeech", isElemental[damageType] and "ElementalManaLeech" or nil) + enemyDB:Sum("BASE", nil, "SelfDamageManaLeech") / 100 + if manaLeech > 0 then + manaLeechTotal = manaLeechTotal + (min + max) / 2 * manaLeech / 100 + end else min, max = 0, 0 if breakdown then @@ -2278,6 +2298,29 @@ local function performCalcs(env) totalHitMax = totalHitMax + max end end + if pass == 1 then + if modDB:Sum("FLAG", cfg, "InstantLifeLeech") then + output.LifeLeechInstant = (output.LifeLeechInstant or 0) + lifeLeechTotal * output.CritChance / 100 + else + output.LifeLeech = (output.LifeLeech or 0) + lifeLeechTotal * output.CritChance / 100 + end + if modDB:Sum("FLAG", cfg, "InstantManaLeech") then + output.ManaLeechInstant = (output.ManaLeechInstant or 0) + manaLeechTotal * output.CritChance / 100 + else + output.ManaLeech = (output.ManaLeech or 0) + manaLeechTotal * output.CritChance / 100 + end + else + if modDB:Sum("FLAG", cfg, "InstantLifeLeech") then + output.LifeLeechInstant = (output.LifeLeechInstant or 0) + lifeLeechTotal * (1 - output.CritChance / 100) + else + output.LifeLeech = (output.LifeLeech or 0) + lifeLeechTotal * (1 - output.CritChance / 100) + end + if modDB:Sum("FLAG", cfg, "InstantManaLeech") then + output.ManaLeechInstant = (output.ManaLeechInstant or 0) + manaLeechTotal * (1 - output.CritChance / 100) + else + output.ManaLeech = (output.ManaLeech or 0) + manaLeechTotal * (1 - output.CritChance / 100) + end + end end output.TotalMin = totalHitMin output.TotalMax = totalHitMax @@ -2289,6 +2332,15 @@ local function performCalcs(env) enemyDB.conditions.HitByLightningDamage = output.LightningHitAverage > 0 end + -- Calculate leech + local hitRate = output.HitChance / 100 * (globalOutput.HitSpeed or globalOutput.Speed) * (skillData.dpsMultiplier or 1) + output.LifeLeechDuration = (output.LifeLeech or 0) / (modDB:Sum("FLAG", nil, "GhostReaver") and globalOutput.EnergyShield or globalOutput.Life) / 0.02 + output.LifeLeechInstances = output.LifeLeechDuration * hitRate + output.LifeLeechInstantRate = (output.LifeLeechInstant or 0) * hitRate + output.ManaLeechDuration = (output.ManaLeech or 0) / globalOutput.Mana / 0.02 + output.ManaLeechInstances = output.ManaLeechDuration * hitRate + output.ManaLeechInstantRate = (output.ManaLeechInstant or 0) * hitRate + -- Calculate average damage and final DPS output.AverageHit = (totalHitMin + totalHitMax) / 2 * (1 - output.CritChance / 100) + (totalCritMin + totalCritMax) / 2 * output.CritChance / 100 output.AverageDamage = output.AverageHit * output.HitChance / 100 @@ -2317,6 +2369,12 @@ local function performCalcs(env) combineStat("CritMultiplier", "AVERAGE") combineStat("AverageDamage", "DPS") combineStat("TotalDPS", "DPS") + combineStat("LifeLeechDuration", "DPS") + combineStat("LifeLeechInstances", "DPS") + combineStat("LifeLeechInstantRate", "DPS") + combineStat("ManaLeechDuration", "DPS") + combineStat("ManaLeechInstances", "DPS") + combineStat("ManaLeechInstantRate", "DPS") if skillFlags.bothWeaponAttack then if breakdown then breakdown.AverageDamage = { } @@ -2355,6 +2413,57 @@ local function performCalcs(env) t_insert(breakdown.TotalDPS, s_format("= %.1f", output.TotalDPS)) end + -- Calculate leech rates + if modDB:Sum("FLAG", nil, "GhostReaver") then + output.EnergyShieldLeechRate = output.LifeLeechInstantRate + m_min(output.LifeLeechInstances * output.EnergyShield * 0.02 * calcMod(modDB, skillCfg, "LifeLeechRate"), output.MaxEnergyShieldLeechRate) + else + output.LifeLeechRate = output.LifeLeechInstantRate + m_min(output.LifeLeechInstances * output.Life * 0.02 * calcMod(modDB, skillCfg, "LifeLeechRate"), output.MaxLifeLeechRate) + end + output.ManaLeechRate = output.ManaLeechInstantRate + m_min(output.ManaLeechInstances * output.Mana * 0.02 * calcMod(modDB, skillCfg, "ManaLeechRate"), output.MaxManaLeechRate) + skillFlags.leechES = (output.EnergyShieldLeechRate or 0) > 0 + skillFlags.leechLife = (output.LifeLeechRate or 0) > 0 + skillFlags.leechMana = output.ManaLeechRate > 0 + if breakdown then + local function leechBreakdown(instant, instances, pool, rate, max, dur) + local out = { } + if instant > 0 then + t_insert(out, s_format("Instant Leech per second: %.1f", instant)) + end + if instances > 0 then + t_insert(out, "Rate per instance:") + t_insert(out, s_format("%d ^8(size of leech destination pool)", pool)) + t_insert(out, "x 0.02 ^8(base leech rate is 2% per second)") + local rateMod = calcMod(modDB, skillCfg, rate) + if rateMod ~= 1 then + t_insert(out, s_format("x %.2f ^8(leech rate modifier)", rateMod)) + end + t_insert(out, s_format("= %.1f ^8per second", pool * 0.02 * rateMod)) + t_insert(out, "Maximum leech rate against one target:") + t_insert(out, s_format("%.1f", pool * 0.02 * rateMod)) + t_insert(out, s_format("x %.1f ^8(average instances)", instances)) + local total = pool * 0.02 * rateMod * instances + t_insert(out, s_format("= %.1f ^8per second", total)) + if total <= max then + t_insert(out, s_format("Time to reach max: %.1fs", dur)) + end + t_insert(out, s_format("Leech rate cap: %.1f", max)) + if total > max then + t_insert(out, s_format("Time to reach cap: %.1fs", dur / total * max)) + end + end + return out + end + if skillFlags.leechES then + breakdown.EnergyShieldLeechRate = leechBreakdown(output.LifeLeechInstantRate, output.LifeLeechInstances, output.EnergyShield, "LifeLeechRate", output.MaxEnergyShieldLeechRate, output.LifeLeechDuration) + end + if skillFlags.leechLife then + breakdown.LifeLeechRate = leechBreakdown(output.LifeLeechInstantRate, output.LifeLeechInstances, output.Life, "LifeLeechRate", output.MaxLifeLeechRate, output.LifeLeechDuration) + end + if skillFlags.leechMana then + breakdown.ManaLeechRate = leechBreakdown(output.ManaLeechInstantRate, output.ManaLeechInstances, output.Mana, "ManaLeechRate", output.MaxManaLeechRate, output.ManaLeechDuration) + end + end + -- Calculate skill DOT components local dotCfg = { skillName = skillCfg.skillName, diff --git a/Modules/ItemTools.lua b/Modules/ItemTools.lua index ebd830b8..f9a8022f 100644 --- a/Modules/ItemTools.lua +++ b/Modules/ItemTools.lua @@ -517,6 +517,10 @@ function itemLib.buildItemModListForSlotNum(item, baseList, slotNum) weaponData[value.key] = value.value end end + weaponData.AccuracyInc = sumLocal(modList, "Accuracy", "INC", 0) + if weaponData.AccuracyInc > 0 then + modList:NewMod("Accuracy", "MORE", weaponData.AccuracyInc, item.modSource, { type = "Condition", var = (slotNum == 1) and "MainHandAttack" or "OffHandAttack" }) + end for _, mod in ipairs(modList) do -- Convert accuracy modifiers to local if mod.name == "Accuracy" and mod.flags == 0 and mod.keywordFlags == 0 and not mod.tagList[1] then diff --git a/Modules/ModParser.lua b/Modules/ModParser.lua index 220f5794..aff8d881 100644 --- a/Modules/ModParser.lua +++ b/Modules/ModParser.lua @@ -19,6 +19,7 @@ local formList = { ["^(%d+)%% less"] = "LESS", ["^([%+%-][%d%.]+)%%?"] = "BASE", ["^([%+%-][%d%.]+)%%? to"] = "BASE", + ["^([%+%-][%d%.]+)%%? of"] = "BASE", ["^([%+%-][%d%.]+)%%? base"] = "BASE", ["^([%+%-]?[%d%.]+)%%? additional"] = "BASE", ["^you gain ([%d%.]+)"] = "BASE", @@ -143,7 +144,7 @@ local modNameList = { ["maximum endurance charges"] = "EnduranceChargesMax", ["endurance charge duration"] = "EnduranceChargesDuration", ["endurance, frenzy and power charge duration"] = { "PowerChargesDuration", "FrenzyChargesDuration", "EnduranceChargesDuration" }, - -- On hit/kill effects + -- On hit/kill/leech effects ["life gained on kill"] = "LifeOnKill", ["mana gained on kill"] = "ManaOnKill", ["life gained for each enemy hit by attacks"] = { "LifeOnHit", flags = ModFlag.Attack }, @@ -155,6 +156,10 @@ local modNameList = { ["energy shield gained for each enemy hit by attacks"] = { "EnergyShieldOnHit", flags = ModFlag.Attack }, ["energy shield gained for each enemy hit by your attacks"] = { "EnergyShieldOnHit", flags = ModFlag.Attack }, ["life and mana gained for each enemy hit"] = { "LifeOnHit", "ManaOnHit", flags = ModFlag.Attack }, + ["life leeched per second"] = "LifeLeechRate", + ["mana leeched per second"] = "ManaLeechRate", + ["maximum life per second to maximum life leech rate"] = "MaxLifeLeechRate", + ["maximum mana per second to maximum mana leech rate"] = "MaxLifeLeechRate", -- Projectile modifiers ["projectile"] = "ProjectileCount", ["projectiles"] = "ProjectileCount", @@ -267,6 +272,7 @@ local modFlagList = { ["with axes"] = { flags = ModFlag.Axe }, ["with bows"] = { flags = ModFlag.Bow }, ["with claws"] = { flags = ModFlag.Claw }, + ["dealt with claws"] = { flags = ModFlag.Claw }, ["with daggers"] = { flags = ModFlag.Dagger }, ["with maces"] = { flags = ModFlag.Mace }, ["with staves"] = { flags = ModFlag.Staff }, @@ -343,6 +349,7 @@ local preFlagList = { -- List of modifier tags local modTagList = { ["on enemies"] = { }, + [" on critical strike"] = { tag = { type = "Condition", var = "CriticalStrike" } }, -- Multipliers ["per power charge"] = { tag = { type = "Multiplier", var = "PowerCharge" } }, ["per frenzy charge"] = { tag = { type = "Multiplier", var = "FrenzyCharge" } }, @@ -447,6 +454,7 @@ local modTagList = { ["against enemies on low life"] = { tag = { type = "Condition", var = "EnemyLowLife" }, flags = ModFlag.Hit }, ["against enemies that are on low life"] = { tag = { type = "Condition", var = "EnemyLowLife" }, flags = ModFlag.Hit }, ["against rare and unique enemies"] = { tag = { type = "Condition", var = "EnemyRareOrUnique" }, flags = ModFlag.Hit }, + ["against cursed enemies"] = { tag = { type = "Condition", var = "EnemyCursed" }, flags = ModFlag.Hit }, ["against bleeding enemies"] = { tag = { type = "Condition", var = "EnemyBleeding" }, flags = ModFlag.Hit }, ["against poisoned enemies"] = { tag = { type = "Condition", var = "EnemyPoisoned" }, flags = ModFlag.Hit }, ["against hindered enemies"] = { tag = { type = "Condition", var = "EnemyHindered" }, flags = ModFlag.Hit }, @@ -484,7 +492,7 @@ local specialModList = { ["30%% chance to dodge attacks%. 50%% less armour and energy shield, 30%% less chance to block spells and attacks"] = { mod("AttackDodgeChance", "BASE", 30), mod("Armour", "MORE", -50), mod("EnergyShield", "MORE", -50), mod("BlockChance", "MORE", -30), mod("SpellBlockChance", "MORE", -30) }, ["maximum life becomes 1, immune to chaos damage"] = { flag("ChaosInoculation") }, ["life regeneration is applied to energy shield instead"] = { flag("ZealotsOath") }, - ["life leech applies instantly%. life regeneration has no effect%."] = { flag("VaalPact"), flag("NoLifeRegen") }, + ["life leech applies instantly%. life regeneration has no effect%."] = { flag("InstantLifeLeech"), flag("NoLifeRegen") }, ["deal no non%-fire damage"] = { flag("DealNoPhysical"), flag("DealNoLightning"), flag("DealNoCold"), flag("DealNoChaos") }, ["(%d+)%% of physical, cold and lightning damage converted to fire damage"] = function(num) return { mod("PhysicalDamageConvertToFire", "BASE", num), mod("LightningDamageConvertToFire", "BASE", num), mod("ColdDamageConvertToFire", "BASE", num) } end, ["removes all mana%. spend life instead of mana for skills"] = { mod("Mana", "MORE", -100), flag("BloodMagic") }, @@ -500,6 +508,7 @@ local specialModList = { } end, ["projectile attacks deal up to 50%% more damage to targets at the start of their movement, dealing less damage to targets as the projectile travels farther"] = { flag("PointBlank") }, + ["life leech is applied to energy shield instead"] = { flag("GhostReaver") }, -- Ascendancy notables ["movement skills cost no mana"] = { mod("ManaCost", "MORE", -100, nil, 0, KeywordFlag.Movement) }, ["projectiles have (%d+)%% additional chance to pierce targets at the start of their movement, losing this chance as the projectile travels farther"] = function(num) return { mod("PierceChance", "BASE", num, { type = "DistanceRamp", ramp = {{10,1},{120,0}} }) } end, @@ -529,6 +538,7 @@ local specialModList = { ["you and nearby allies have (%d+)%% increased movement speed"] = function(num) return { mod("MovementSpeed", "INC", num) } end, ["skills from your helmet penetrate (%d+)%% elemental resistances"] = function(num) return { mod("ElementalPenetration", "BASE", num, { type = "SocketedIn", slotName = "Helmet" }) } end, ["skills from your gloves have (%d+)%% increased area of effect"] = function(num) return { mod("AreaRadius", "INC", num, { type = "SocketedIn", slotName = "Gloves" }) } end, + ["skills from your boots leech (%d+)%% of damage as life"] = function(num) return { mod("DamageLifeLeech", "BASE", num, { type = "SocketedIn", slotName = "Boots" }) } end, ["(%d+)%% less totem damage per totem"] = function(num) return { mod("Damage", "MORE", -num, nil, 0, KeywordFlag.Totem, { type = "PerStat", stat = "ActiveTotemLimit", div = 1 }) } end, ["poison you inflict with critical strikes deals (%d+)%% more damage"] = function(num) return { mod("PoisonDamageOnCrit", "MORE", 100) } end, ["bleeding you inflict on maimed enemies deals (%d+)%% more damage"] = function(num) return { mod("Damage", "MORE", num, nil, 0, KeywordFlag.Bleed, { type = "Condition", var = "EnemyMaimed"}) } end, @@ -678,6 +688,9 @@ local specialModList = { ["grants level (%d+) (.+) curse aura during flask effect"] = function(num, _, skill) return { mod("ExtraCurse", "LIST", { name = gemNameLookup[skill:gsub(" skill","")] or "Unknown", level = num }, { type = "Condition", var = "UsingFlask" }) } end, ["passives in radius can be allocated without being connected to your tree"] = { mod("Misc", "LIST", { type = "JewelData", key = "intuitiveLeap", value = true }) }, ["your hits inflict decay, dealing (%d+) chaos damage per second for 10 seconds"] = function(num) return { mod("Misc", "LIST", { type = "SkillData", key = "decay", value = num, merge = "MAX" }) } end, + ["leech applies instantly on critical strike"] = { flag("InstantLifeLeech", { type = "Condition", var = "CriticalStrike" }), flag("InstantManaLeech", { type = "Condition", var = "CriticalStrike" }) }, + ["leech applies instantly during flask effect"] = { flag("InstantLifeLeech", { type = "Condition", var = "UsingFlask" }), flag("InstantManaLeech", { type = "Condition", var = "UsingFlask" }) }, + ["life leech from hits with this weapon applies instantly"] = { flag("InstantLifeLeech", { type = "Condition", var = "XHandAttack" }) }, } local keystoneList = { -- List of keystones that can be found on uniques @@ -726,6 +739,9 @@ local convTypes = { ["converted to cold damage"] = "ConvertToCold", ["converted to fire damage"] = "ConvertToFire", ["converted to chaos damage"] = "ConvertToChaos", + ["leeched as life and mana"] = "Leech", + ["leeched as life"] = "LifeLeech", + ["leeched as mana"] = "ManaLeech", } local dmgTypes = { ["physical"] = "Physical", diff --git a/README.md b/README.md index 45bd8fbe..87dd1fe2 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,11 @@ Head over to the [Releases](https://github.com/Openarl/PathOfBuilding/releases) ![ss3](https://cloud.githubusercontent.com/assets/19189971/18089780/f0ff234a-6f04-11e6-8c88-6193fe59a5c4.png) ## Changelog +### 1.3.11 - 2017/02/26 + * When importing a character you can now choose to delete existing data (jewels, skills, equipment) before importing + * Wither now shows the secondary duration (%increased Chaos Damage Taken) instead of the primary duration (Hinder) + * Local increased Accuracy modifiers on weapons are now correctly multiplicative with global increased Accuracy + ### 1.3.10 - 2017/02/23 * Added support for the helmet enchants that grant increased Buff Effect from Golems * Added an option to the Configuration tab for "Is the enemy Rare or Unique?" diff --git a/changelog.txt b/changelog.txt index fd4b6083..2d7b78c1 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,7 @@ +VERSION[1.3.11][2017/02/26] + * When importing a character you can now choose to delete existing data (jewels, skills, equipment) before importing + * Wither now shows the secondary duration (%increased Chaos Damage Taken) instead of the primary duration (Hinder) + * Local increased Accuracy modifiers on weapons are now correctly multiplicative with global increased Accuracy VERSION[1.3.10][2017/02/23] * Added support for the helmet enchants that grant increased Buff Effect from Golems * Added an option to the Configuration tab for "Is the enemy Rare or Unique?" diff --git a/manifest.xml b/manifest.xml index e79c6d5a..94b0ca7e 100644 --- a/manifest.xml +++ b/manifest.xml @@ -1,26 +1,26 @@ - + - + - + - + @@ -44,13 +44,13 @@ - - + + - + - + @@ -59,12 +59,12 @@ - - + + - + - + @@ -84,24 +84,24 @@ - + - + - + - + - +