diff --git a/spec/System/TestFlasks_spec.lua b/spec/System/TestFlasks_spec.lua new file mode 100644 index 0000000000..8018c8620b --- /dev/null +++ b/spec/System/TestFlasks_spec.lua @@ -0,0 +1,53 @@ +describe("Flasks", function() + local function buildFlasks(rawFlasks, customMods) + newBuild() + build.configTab.input.customMods = customMods or "" + build.configTab:BuildModList() + + for index, raw in ipairs(rawFlasks) do + local item = new("Item"):Item(raw) + build.itemsTab:AddItem(item, true) + local slot = build.itemsTab.slots["Flask " .. index] + slot:SetSelItemId(item.id) + slot.active = true + end + build.buildFlag = true + runCallback("OnFrame") + return build.calcsTab.mainOutput + end + + local function buildLifeFlask(customMods) + return buildFlasks({ "Rarity: MAGIC\nTest Life Flask\nDivine Life Flask" }, + "Life Flask Effects are not removed when Unreserved Life is Filled\n" .. (customMods or "")) + end + + local function buildUtilityFlasks(customMods) + return buildFlasks({ "Rarity: NORMAL\nIron Flask", "Rarity: NORMAL\nGranite Flask" }, customMods) + end + + it("applies Master Surgeon's recovery rate to active non-instant life flasks", function() + local baselineOutput = buildLifeFlask() + local reducedOutput = buildLifeFlask("50% less Flask Life Recovery rate") + assert.is_true(baselineOutput.LifeRecovery > 0) + assert.are.equal(baselineOutput.LifeRecovery / 2, reducedOutput.LifeRecovery) + end) + + it("applies Regenerative Runes only to Iron Flasks", function() + local baseline = buildUtilityFlasks() + local increased = buildUtilityFlasks("20% increased Effect of Iron Flasks") + + assert.is_true(baseline.Ward > 0) + assert.is_true(baseline.Armour > 0) + assert.are.equal(math.floor(baseline.Ward * 1.2), increased.Ward) + assert.are.equal(baseline.Armour, increased.Armour) + end) + + it("adds Iron Flask effect to generic flask effect", function() + local generic = buildUtilityFlasks("30% increased Effect of Flasks") + local combined = buildUtilityFlasks("10% increased Effect of Flasks\n20% increased Effect of Iron Flasks") + local genericControl = buildUtilityFlasks("10% increased Effect of Flasks") + + assert.are.equal(generic.Ward, combined.Ward) + assert.are.equal(genericControl.Armour, combined.Armour) + end) +end) diff --git a/spec/System/TestModParser_spec.lua b/spec/System/TestModParser_spec.lua new file mode 100644 index 0000000000..a427b5459b --- /dev/null +++ b/spec/System/TestModParser_spec.lua @@ -0,0 +1,18 @@ +describe("TestModParser", function() + local function getMasterDistillerCritMultiplier(value, skillTypes, hasDiamondFlask) + local mods, extra = modLib.parseMod("If Diamond Flask Charges are consumed, +" .. value .. "% to Critical Strike Multiplier") + assert.is_nil(extra) + local modDB = new("ModDB"):ModDB() + modDB:AddList(mods) + modDB.conditions.HaveDiamondFlask = hasDiamondFlask + return modDB:Sum("BASE", { skillTypes = skillTypes }, "CritMultiplier") + end + + it("applies the Master Distiller diamond flask bonus only to eligible skills while the flask is present", function() + assert.are.equal(100, getMasterDistillerCritMultiplier(100, {}, true)) + assert.are.equal(60, getMasterDistillerCritMultiplier(60, {}, true)) + assert.are.equal(0, getMasterDistillerCritMultiplier(100, {}, false)) + assert.are.equal(0, getMasterDistillerCritMultiplier(100, { [SkillType.Triggered] = true }, true)) + assert.are.equal(0, getMasterDistillerCritMultiplier(100, { [SkillType.Channel] = true }, true)) + end) +end) diff --git a/src/Classes/ItemsTab.lua b/src/Classes/ItemsTab.lua index e370df57fe..fb692b0bc2 100644 --- a/src/Classes/ItemsTab.lua +++ b/src/Classes/ItemsTab.lua @@ -4731,6 +4731,9 @@ function ItemsTabClass:AddItemStatDifferences(tooltip, item, base, slot) local output = self.build.calcsTab.mainOutput local durInc = modDB:Sum("INC", nil, "FlaskDuration") local effectInc = modDB:Sum("INC", { actor = "player" }, "FlaskEffect") + if item.baseName == "Iron Flask" then + effectInc = effectInc + modDB:Sum("INC", { actor = "player" }, "IronFlaskEffect") + end local lifeDur = 0 local manaDur = 0 @@ -4744,6 +4747,7 @@ function ItemsTabClass:AddItemStatDifferences(tooltip, item, base, slot) local lifeInc = modDB:Sum("INC", nil, "FlaskLifeRecovery") local lifeMore = modDB:More(nil, "FlaskLifeRecovery") local lifeRateInc = modDB:Sum("INC", nil, "FlaskLifeRecoveryRate") + local lifeRateMore = modDB:More(nil, "FlaskLifeRecoveryRate") local instantPerc = flaskData.instantPerc + modDB:Sum("BASE", nil, "LifeFlaskInstantRecovery") -- More life recovery while on low life is not affected by flask effect (verified ingame). @@ -4756,7 +4760,7 @@ function ItemsTabClass:AddItemStatDifferences(tooltip, item, base, slot) local base = flaskData.lifeBase * (1 - instantPerc / 100) * (1 + lifeInc / 100) * lifeMore * (1 + effectInc / 100) * (1 + durInc / 100) * lowLifeMulti local grad = base * output.LifeRecoveryRateMod local esGrad = base * output.EnergyShieldRecoveryRateMod - lifeDur = flaskData.duration * (1 + durInc / 100) / (1 + rateInc / 100) / (1 + lifeRateInc / 100) + lifeDur = flaskData.duration * (1 + durInc / 100) / (1 + rateInc / 100) / (1 + lifeRateInc / 100) / lifeRateMore -- LocalLifeFlaskAdditionalLifeRecovery flask mods if flaskData.lifeAdditional > 0 and not self.build.configTab.input.conditionFullLife then diff --git a/src/Modules/CalcPerform.lua b/src/Modules/CalcPerform.lua index d683aab3ce..cda09c7737 100644 --- a/src/Modules/CalcPerform.lua +++ b/src/Modules/CalcPerform.lua @@ -1664,8 +1664,9 @@ function calcs.perform(env, skipEHP) local flaskRecInc = modDB:Sum("INC", nil, "Flask"..type.."Recovery") local flaskRecMore = modDB:More(nil, "Flask"..type.."Recovery") local flaskRateInc = modDB:Sum("INC", nil, "Flask"..type.."RecoveryRate") + local flaskRateMore = modDB:More(nil, "Flask"..type.."RecoveryRate") local flaskTotal = base * (1 - instPerc / 100) * (1 + flaskRecInc / 100) * flaskRecMore * (1 + flaskDurInc / 100) - local flaskDur = dur * (1 + flaskDurInc / 100) / (1 + flaskTotalRateInc / 100) / (1 + flaskRateInc / 100) + local flaskDur = dur * (1 + flaskDurInc / 100) / (1 + flaskTotalRateInc / 100) / (1 + flaskRateInc / 100) / flaskRateMore -- More life recovery while on low life is not affected by flask effect (verified ingame). -- Since this will be multiplied by the flask effect value below we have to counteract this by removing the flask effect from the value beforehand. @@ -1701,6 +1702,10 @@ function calcs.perform(env, skipEHP) local function calcFlaskMods(item, baseName, buffModList, modList, onlyMinion) local flaskEffectInc = effectInc + item.flaskData.effectInc local flaskEffectIncNonPlayer = effectIncNonPlayer + item.flaskData.effectInc + if item.baseName == "Iron Flask" then + flaskEffectInc = flaskEffectInc + modDB:Sum("INC", { actor = "player" }, "IronFlaskEffect") + flaskEffectIncNonPlayer = flaskEffectIncNonPlayer + modDB:Sum("INC", nil, "IronFlaskEffect") + end if item.rarity == "MAGIC" and not (item.base.flask.life or item.base.flask.mana) then flaskEffectInc = flaskEffectInc + effectIncMagic flaskEffectIncNonPlayer = flaskEffectIncNonPlayer + effectIncMagicNonPlayer diff --git a/src/Modules/CalcSections.lua b/src/Modules/CalcSections.lua index 0f5b1c7f1c..dbf9dfbc61 100644 --- a/src/Modules/CalcSections.lua +++ b/src/Modules/CalcSections.lua @@ -1798,6 +1798,7 @@ return { { label = "Inc. Recovery Rate", { format = "{0:mod:1,2}%", { label = "Life Flask Recovery Rate", modName = "FlaskLifeRecoveryRate", modType = "INC"}, { label = "Generic Flask Recovery Rate", modName = "FlaskRecoveryRate", modType = "INC" }}, }, + { label = "More Recovery Rate", { format = "{0:mod:1}%", { modName = "FlaskLifeRecoveryRate", modType = "MORE" }}, }, { label = "Charges/s", { format = "{2:output:LifeFlaskChargeGen}", { label = "Life Flask Charges/s", modName = "LifeFlaskChargesGenerated", modType = "BASE"}, { label = "Generic Flask Charges/s", modName = { "FlaskChargesGenerated", "FlaskChargesGeneratedPerEmptyFlask" }, modType = "BASE" }}, }, diff --git a/src/Modules/ModParser.lua b/src/Modules/ModParser.lua index 9d5d1db3a0..aeb344f32b 100644 --- a/src/Modules/ModParser.lua +++ b/src/Modules/ModParser.lua @@ -857,6 +857,7 @@ local modNameList = { -- Flask and Tincture modifiers ["effect"] = "LocalEffect", ["effect of flasks"] = "FlaskEffect", + ["effect of iron flasks"] = "IronFlaskEffect", ["effect of tinctures"] = "TinctureEffect", ["amount recovered"] = "FlaskRecovery", ["life recovered"] = "FlaskRecovery", @@ -2979,6 +2980,9 @@ local specialModList = { ["if diamond flask charges are consumed, (%d+)%% increased critical strike chance"] = function (num) return { mod("CritChance", "INC", num, { type = "SkillType", skillType = SkillType.Triggered, neg = true }, { type = "SkillType", skillType = SkillType.Channel, neg = true }, { type = "Condition", var = "HaveDiamondFlask" }) } end, + ["if diamond flask charges are consumed, %+(%d+)%% to critical strike multiplier"] = function (num) return { + mod("CritMultiplier", "BASE", num, { type = "SkillType", skillType = SkillType.Triggered, neg = true }, { type = "SkillType", skillType = SkillType.Channel, neg = true }, { type = "Condition", var = "HaveDiamondFlask" }) + } end, ["if bismuth flask charges are consumed, penetrate (%d+)%% elemental resistances"] = function (num) return { mod("ElementalPenetration", "BASE", num, { type = "SkillType", skillType = SkillType.Triggered, neg = true }, { type = "SkillType", skillType = SkillType.Channel, neg = true }, { type = "Condition", var = "HaveBismuthFlask" }) } end,