From bf562e782a73b3ebb8b9cf809c939d50ace6d8e6 Mon Sep 17 00:00:00 2001 From: Peechey <92683202+Peechey@users.noreply.github.com> Date: Sat, 25 Jul 2026 10:53:33 -0500 Subject: [PATCH 1/4] add support for the four pacts sans trigger work add tests --- spec/System/TestSkills_spec.lua | 71 ++++++- src/Data/SkillStatMap.lua | 3 + src/Data/Skills/other.lua | 318 ++++++++++++++++++++++++++++++++ src/Export/Skills/other.txt | 51 +++++ src/Modules/CalcOffence.lua | 131 +++++++++++++ src/Modules/CalcPerform.lua | 20 ++ src/Modules/CalcSections.lua | 40 ++++ 7 files changed, 633 insertions(+), 1 deletion(-) diff --git a/spec/System/TestSkills_spec.lua b/spec/System/TestSkills_spec.lua index 69b21f46775..8ccc5e3478c 100644 --- a/spec/System/TestSkills_spec.lua +++ b/spec/System/TestSkills_spec.lua @@ -1,4 +1,4 @@ -describe("TestAttacks", function() +describe("TestSkills", function() before_each(function() newBuild() end) @@ -249,4 +249,73 @@ describe("TestAttacks", function() assert.are.equals(0, nonDurationSkill.skillModList:Sum("BASE", nonDurationSkill.skillCfg, "BaseFlagTest")) assert.are.equals(1, nonDurationSkill.skillModList:Sum("BASE", nonDurationSkill.skillCfg, "NegatedBaseFlagTest")) end) + + it("Test Empowered Spells - Pact of Beidat", function() + build.skillsTab:PasteSocketGroup("Fireball 20/0 1") + runCallback("OnFrame") + local baseDPS = build.calcsTab.calcsOutput.TotalDPS + + build.skillsTab:PasteSocketGroup("Pact of Beidat 1/0 1") + runCallback("OnFrame") + assert.True(build.calcsTab.calcsOutput.BeidatUpTimeRatio > 0) + assert.True(build.calcsTab.calcsOutput.TotalDPS > baseDPS) + + newBuild() + build.skillsTab:PasteSocketGroup("Vaal Arc 20/0 1\nPact of Beidat 1/0 1") + runCallback("OnFrame") + assert.True(build.calcsTab.calcsOutput.BeidatUpTimeRatio == nil) + end) + + it("Test Empowered Spells - Pact of Ghorr", function() + build.skillsTab:PasteSocketGroup("Creeping Frost 20/0 1") + runCallback("OnFrame") + local baseDPS = build.calcsTab.calcsOutput.TotalDotDPS + + build.skillsTab:PasteSocketGroup("Pact of Ghorr 1/0 1") + runCallback("OnFrame") + assert.True(build.calcsTab.calcsOutput.GhorrUpTimeRatio > 0) + assert.True(build.calcsTab.calcsOutput.TotalDotDPS > baseDPS) + + newBuild() + build.skillsTab:PasteSocketGroup("Vaal Arc 20/0 1\n Pact of Ghorr 1/0 1") + runCallback("OnFrame") + assert.True(build.calcsTab.calcsOutput.GhorrUpTimeRatio == nil) + end) + + it("Test Empowered Spells - Pact of K'Tash", function() + build.skillsTab:PasteSocketGroup("Vaal Arc 20/0 1") + runCallback("OnFrame") + local baseDPS = build.calcsTab.calcsOutput.TotalDPS + + build.skillsTab:PasteSocketGroup("Pact of K'Tash 1/0 1") + runCallback("OnFrame") + assert.True(build.calcsTab.calcsOutput.KtashUpTimeRatio > 0) + assert.True(build.calcsTab.calcsOutput.TotalDPS > baseDPS) + + newBuild() + build.skillsTab:PasteSocketGroup("Fireball 20/0 1\nPact of K'Tash 1/0 1") + runCallback("OnFrame") + assert.True(build.calcsTab.calcsOutput.KtashUpTimeRatio == nil) + end) + + it("Test Empowered Spells - Pact of Lycia", function() + build.skillsTab:PasteSocketGroup("Lightning Tendrils 20/0 1") + runCallback("OnFrame") + local baseDPS = build.calcsTab.calcsOutput.TotalDPS + + build.skillsTab:PasteSocketGroup("Pact of Lycia 1/0 1") + runCallback("OnFrame") + assert.True(build.calcsTab.calcsOutput.LyciaUpTimeRatio > 0) + assert.True(build.calcsTab.calcsOutput.TotalDPS > baseDPS) + + newBuild() + build.skillsTab:PasteSocketGroup("Fireball 20/0 1\nPact of Lycia 1/0 1") + runCallback("OnFrame") + assert.True(build.calcsTab.calcsOutput.LyciaUpTimeRatio == nil) + + newBuild() + build.skillsTab:PasteSocketGroup("Vaal Arc 20/0 1\nPact of Lycia 1/0 1") + runCallback("OnFrame") + assert.True(build.calcsTab.calcsOutput.LyciaUpTimeRatio == nil) + end) end) diff --git a/src/Data/SkillStatMap.lua b/src/Data/SkillStatMap.lua index 795acd976cd..379e4971bfa 100644 --- a/src/Data/SkillStatMap.lua +++ b/src/Data/SkillStatMap.lua @@ -2365,4 +2365,7 @@ return { ["is_totem"] = { -- Display only }, +["pact_empower_limitation_specifier_for_stat_description"] = { + -- display only +}, } diff --git a/src/Data/Skills/other.lua b/src/Data/Skills/other.lua index 16ea68e6000..319295a4bf2 100644 --- a/src/Data/Skills/other.lua +++ b/src/Data/Skills/other.lua @@ -3182,6 +3182,324 @@ skills["Pacify"] = { [20] = { levelRequirement = 70, cost = { Mana = 33, }, }, }, } +skills["PactOfBeidat"] = { + name = "Pact of Beidat", + baseTypeName = "Pact of Beidat", + flavourText = {"Wield the gifts of Beidat, luminary of the pale unseeing,", "whose honeyed words bore tunnels through pliant minds.", }, + color = 4, + description = "Cast a pact, Empowering non-channelling spells that either fire projectiles, apply an effect to an area around a targeted location or fire chaining beams, causing them to deal more damage and have enhanced coverage. However, using this skill also burdens you with an Affliction.", + skillTypes = { [SkillType.Spell] = true, [SkillType.Pact] = true, [SkillType.Buff] = true, [SkillType.Cooldown] = true, [SkillType.InstantNoRepeatWhenHeld] = true, [SkillType.InstantShiftAttackForLeftMouse] = true, [SkillType.Triggerable] = true, }, + statDescriptionScope = "skill_stat_descriptions", + castTime = 0.8, + statMap = { + ["skill_empowers_next_x_spells_cast"] = { + mod("BeidatEmpoweredSpells", "BASE", nil), + }, + ["pact_skill_damage_+%_final_with_hits_and_ailments_to_grant"] = { + mod("BeidatPactDamage", "BASE", nil), + }, + }, + baseFlags = { + spell = true, + pact = true, + }, + qualityStats = { + { "skill_empowers_next_x_spells_cast", 0.1 }, + }, + constantStats = { + { "skill_empowers_next_x_spells_cast", 6 }, + { "pact_empower_limitation_specifier_for_stat_description", 0 }, + }, + stats = { + "pact_skill_additional_beam_only_chains", + "pact_skill_grant_x_additional_projectiles_fired_in_nova", + "pact_skill_grant_x_cascades_to_do_in_spiral", + "pact_skill_damage_+%_final_with_hits_and_ailments_to_grant", + "base_deal_no_attack_damage", + "base_deal_no_spell_damage", + "base_deal_no_secondary_damage", + "cannot_cancel_skill_before_contact_point", + }, + levels = { + [1] = { 4, 4, 5, 16, cooldown = 4, levelRequirement = 24, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 16, }, }, + [2] = { 4, 4, 5, 17, cooldown = 4, levelRequirement = 27, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 17, }, }, + [3] = { 4, 4, 5, 18, cooldown = 4, levelRequirement = 30, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 18, }, }, + [4] = { 4, 4, 5, 19, cooldown = 4, levelRequirement = 33, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 19, }, }, + [5] = { 4, 4, 6, 20, cooldown = 4, levelRequirement = 36, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 21, }, }, + [6] = { 5, 4, 6, 21, cooldown = 4, levelRequirement = 39, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 22, }, }, + [7] = { 5, 5, 6, 22, cooldown = 4, levelRequirement = 42, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 23, }, }, + [8] = { 5, 5, 6, 23, cooldown = 4, levelRequirement = 45, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 24, }, }, + [9] = { 5, 5, 6, 24, cooldown = 4, levelRequirement = 48, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 25, }, }, + [10] = { 5, 5, 6, 25, cooldown = 4, levelRequirement = 50, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 26, }, }, + [11] = { 5, 5, 7, 26, cooldown = 4, levelRequirement = 52, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 26, }, }, + [12] = { 5, 5, 7, 27, cooldown = 4, levelRequirement = 54, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 27, }, }, + [13] = { 5, 5, 7, 28, cooldown = 4, levelRequirement = 56, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 28, }, }, + [14] = { 6, 5, 7, 29, cooldown = 4, levelRequirement = 58, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 29, }, }, + [15] = { 6, 6, 7, 30, cooldown = 4, levelRequirement = 60, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 29, }, }, + [16] = { 6, 6, 7, 31, cooldown = 4, levelRequirement = 62, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 30, }, }, + [17] = { 6, 6, 8, 32, cooldown = 4, levelRequirement = 64, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 31, }, }, + [18] = { 6, 6, 8, 33, cooldown = 4, levelRequirement = 66, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 31, }, }, + [19] = { 6, 6, 8, 34, cooldown = 4, levelRequirement = 68, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 32, }, }, + [20] = { 6, 6, 8, 35, cooldown = 4, levelRequirement = 70, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 33, }, }, + [21] = { 6, 6, 8, 36, cooldown = 4, levelRequirement = 72, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 34, }, }, + [22] = { 7, 6, 8, 37, cooldown = 4, levelRequirement = 74, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 34, }, }, + [23] = { 7, 7, 8, 38, cooldown = 4, levelRequirement = 76, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 35, }, }, + [24] = { 7, 7, 9, 39, cooldown = 4, levelRequirement = 78, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 36, }, }, + [25] = { 7, 7, 9, 40, cooldown = 4, levelRequirement = 80, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 36, }, }, + [26] = { 7, 7, 9, 41, cooldown = 4, levelRequirement = 82, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 37, }, }, + [27] = { 7, 7, 9, 42, cooldown = 4, levelRequirement = 84, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 38, }, }, + [28] = { 7, 7, 9, 43, cooldown = 4, levelRequirement = 86, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 39, }, }, + [29] = { 7, 7, 9, 44, cooldown = 4, levelRequirement = 88, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 39, }, }, + [30] = { 8, 7, 10, 44, cooldown = 4, levelRequirement = 90, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 40, }, }, + [31] = { 8, 7, 10, 45, cooldown = 4, levelRequirement = 91, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 40, }, }, + [32] = { 8, 8, 10, 45, cooldown = 4, levelRequirement = 92, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 41, }, }, + [33] = { 8, 8, 10, 46, cooldown = 4, levelRequirement = 93, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 41, }, }, + [34] = { 8, 8, 10, 46, cooldown = 4, levelRequirement = 94, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 42, }, }, + [35] = { 8, 8, 10, 47, cooldown = 4, levelRequirement = 95, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 42, }, }, + [36] = { 8, 8, 10, 47, cooldown = 4, levelRequirement = 96, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 42, }, }, + [37] = { 8, 8, 10, 48, cooldown = 4, levelRequirement = 97, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 43, }, }, + [38] = { 8, 8, 10, 48, cooldown = 4, levelRequirement = 98, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 43, }, }, + [39] = { 8, 8, 10, 49, cooldown = 4, levelRequirement = 99, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 43, }, }, + [40] = { 8, 8, 10, 49, cooldown = 4, levelRequirement = 100, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 44, }, }, + }, +} +skills["PactOfGhorr"] = { + name = "Pact of Ghorr", + baseTypeName = "Pact of Ghorr", + flavourText = {"Offer yourself to Ghorr, the gluttonous, the cage of", "flesh trapping souls in the cycle of consumption.", }, + color = 4, + description = "Cast a pact, Empowering your damage over time spells but burdening you with an Affliction. This skill also grants the Hunger of Ghorr buff for a duration causing Fleshrend to trigger periodically.", + skillTypes = { [SkillType.Spell] = true, [SkillType.Pact] = true, [SkillType.Buff] = true, [SkillType.Cooldown] = true, [SkillType.InstantNoRepeatWhenHeld] = true, [SkillType.InstantShiftAttackForLeftMouse] = true, [SkillType.Triggerable] = true, }, + statDescriptionScope = "skill_stat_descriptions", + castTime = 0.8, + statMap = { + ["skill_empowers_next_x_spells_cast"] = { + mod("GhorrEmpoweredSpells", "BASE", nil), + }, + ["pact_skill_grant_damage_over_time_+%_final_to_spells"] = { + mod("GhorrPactDamage", "BASE", nil), + }, + }, + baseFlags = { + spell = true, + pact = true, + duration = true, + }, + constantStats = { + { "skill_empowers_next_x_spells_cast", 3 }, + { "pact_empower_limitation_specifier_for_stat_description", 1 }, + { "pact_skill_grant_damage_over_time_+%_final_to_spells", 40 }, + { "skill_grants_bloodrite_for_base_X_ms_on_use", 6000 }, + { "bloodrite_base_trigger_frequency_ms", 2000 }, + { "bloodrite_trigger_frequency_+%_per_stage", 150 }, + }, + stats = { + "base_deal_no_attack_damage", + "base_deal_no_spell_damage", + "base_deal_no_secondary_damage", + "cannot_cancel_skill_before_contact_point", + }, + levels = { + [1] = { cooldown = 4, levelRequirement = 24, storedUses = 1, cost = { Mana = 16, }, }, + [2] = { cooldown = 4, levelRequirement = 27, storedUses = 1, cost = { Mana = 17, }, }, + [3] = { cooldown = 4, levelRequirement = 30, storedUses = 1, cost = { Mana = 18, }, }, + [4] = { cooldown = 4, levelRequirement = 33, storedUses = 1, cost = { Mana = 19, }, }, + [5] = { cooldown = 4, levelRequirement = 36, storedUses = 1, cost = { Mana = 21, }, }, + [6] = { cooldown = 4, levelRequirement = 39, storedUses = 1, cost = { Mana = 22, }, }, + [7] = { cooldown = 4, levelRequirement = 42, storedUses = 1, cost = { Mana = 23, }, }, + [8] = { cooldown = 4, levelRequirement = 45, storedUses = 1, cost = { Mana = 24, }, }, + [9] = { cooldown = 4, levelRequirement = 48, storedUses = 1, cost = { Mana = 25, }, }, + [10] = { cooldown = 4, levelRequirement = 50, storedUses = 1, cost = { Mana = 26, }, }, + [11] = { cooldown = 4, levelRequirement = 52, storedUses = 1, cost = { Mana = 26, }, }, + [12] = { cooldown = 4, levelRequirement = 54, storedUses = 1, cost = { Mana = 27, }, }, + [13] = { cooldown = 4, levelRequirement = 56, storedUses = 1, cost = { Mana = 28, }, }, + [14] = { cooldown = 4, levelRequirement = 58, storedUses = 1, cost = { Mana = 29, }, }, + [15] = { cooldown = 4, levelRequirement = 60, storedUses = 1, cost = { Mana = 29, }, }, + [16] = { cooldown = 4, levelRequirement = 62, storedUses = 1, cost = { Mana = 30, }, }, + [17] = { cooldown = 4, levelRequirement = 64, storedUses = 1, cost = { Mana = 31, }, }, + [18] = { cooldown = 4, levelRequirement = 66, storedUses = 1, cost = { Mana = 31, }, }, + [19] = { cooldown = 4, levelRequirement = 68, storedUses = 1, cost = { Mana = 32, }, }, + [20] = { cooldown = 4, levelRequirement = 70, storedUses = 1, cost = { Mana = 33, }, }, + [21] = { cooldown = 4, levelRequirement = 72, storedUses = 1, cost = { Mana = 34, }, }, + [22] = { cooldown = 4, levelRequirement = 74, storedUses = 1, cost = { Mana = 34, }, }, + [23] = { cooldown = 4, levelRequirement = 76, storedUses = 1, cost = { Mana = 35, }, }, + [24] = { cooldown = 4, levelRequirement = 78, storedUses = 1, cost = { Mana = 36, }, }, + [25] = { cooldown = 4, levelRequirement = 80, storedUses = 1, cost = { Mana = 36, }, }, + [26] = { cooldown = 4, levelRequirement = 82, storedUses = 1, cost = { Mana = 37, }, }, + [27] = { cooldown = 4, levelRequirement = 84, storedUses = 1, cost = { Mana = 38, }, }, + [28] = { cooldown = 4, levelRequirement = 86, storedUses = 1, cost = { Mana = 39, }, }, + [29] = { cooldown = 4, levelRequirement = 88, storedUses = 1, cost = { Mana = 39, }, }, + [30] = { cooldown = 4, levelRequirement = 90, storedUses = 1, cost = { Mana = 40, }, }, + [31] = { cooldown = 4, levelRequirement = 91, storedUses = 1, cost = { Mana = 40, }, }, + [32] = { cooldown = 4, levelRequirement = 92, storedUses = 1, cost = { Mana = 41, }, }, + [33] = { cooldown = 4, levelRequirement = 93, storedUses = 1, cost = { Mana = 41, }, }, + [34] = { cooldown = 4, levelRequirement = 94, storedUses = 1, cost = { Mana = 42, }, }, + [35] = { cooldown = 4, levelRequirement = 95, storedUses = 1, cost = { Mana = 42, }, }, + [36] = { cooldown = 4, levelRequirement = 96, storedUses = 1, cost = { Mana = 42, }, }, + [37] = { cooldown = 4, levelRequirement = 97, storedUses = 1, cost = { Mana = 43, }, }, + [38] = { cooldown = 4, levelRequirement = 98, storedUses = 1, cost = { Mana = 43, }, }, + [39] = { cooldown = 4, levelRequirement = 99, storedUses = 1, cost = { Mana = 43, }, }, + [40] = { cooldown = 4, levelRequirement = 100, storedUses = 1, cost = { Mana = 44, }, }, + }, +} +skills["PactOfKtash"] = { + name = "Pact of K'Tash", + baseTypeName = "Pact of K'Tash", + flavourText = {"Unleash the fury of K'Tash, incarnation of hate.", "Endless bodies burn beneath the flames of his pursuit.", }, + color = 4, + description = "Cast a pact, Empowering your damaging Vaal spells but burdening you with an Affliction. Empowered Vaal spells deal more damage, have less soul gain prevention duration and regain consumed souls on use.", + skillTypes = { [SkillType.Spell] = true, [SkillType.Pact] = true, [SkillType.Buff] = true, [SkillType.Cooldown] = true, [SkillType.InstantNoRepeatWhenHeld] = true, [SkillType.InstantShiftAttackForLeftMouse] = true, [SkillType.Triggerable] = true, }, + statDescriptionScope = "skill_stat_descriptions", + castTime = 0.8, + statMap = { + ["skill_empowers_next_x_spells_cast"] = { + mod("KtashEmpoweredSpells", "BASE", nil), + }, + ["pact_skill_grant_damage_+%_final_to_exerted_skills"] = { + mod("KtashPactDamage", "BASE", nil), + }, + ["active_skill_osm_vaal_skill_soul_gain_prevention_+%_final_to_grant"] = { + mod("KtashPactSoulGainPrevention", "BASE", nil), + }, + }, + baseFlags = { + spell = true, + pact = true, + }, + qualityStats = { + { "base_cooldown_speed_+%", 0.5 }, + }, + constantStats = { + { "skill_empowers_next_x_spells_cast", 1 }, + { "pact_empower_limitation_specifier_for_stat_description", 2 }, + { "active_skill_osm_vaal_skill_soul_refund_chance_%_to_grant", 100 }, + { "active_skill_osm_vaal_skill_soul_gain_prevention_+%_final_to_grant", -50 }, + }, + stats = { + "pact_skill_grant_damage_+%_final_to_exerted_skills", + "base_deal_no_attack_damage", + "base_deal_no_spell_damage", + "base_deal_no_secondary_damage", + "cannot_cancel_skill_before_contact_point", + }, + levels = { + [1] = { 10, cooldown = 6, levelRequirement = 24, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 16, }, }, + [2] = { 11, cooldown = 6, levelRequirement = 27, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 17, }, }, + [3] = { 12, cooldown = 6, levelRequirement = 30, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 18, }, }, + [4] = { 13, cooldown = 6, levelRequirement = 33, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 19, }, }, + [5] = { 14, cooldown = 6, levelRequirement = 36, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 21, }, }, + [6] = { 15, cooldown = 6, levelRequirement = 39, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 22, }, }, + [7] = { 16, cooldown = 6, levelRequirement = 42, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 23, }, }, + [8] = { 17, cooldown = 6, levelRequirement = 45, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 24, }, }, + [9] = { 18, cooldown = 6, levelRequirement = 48, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 25, }, }, + [10] = { 19, cooldown = 6, levelRequirement = 50, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 26, }, }, + [11] = { 20, cooldown = 6, levelRequirement = 52, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 26, }, }, + [12] = { 21, cooldown = 6, levelRequirement = 54, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 27, }, }, + [13] = { 22, cooldown = 6, levelRequirement = 56, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 28, }, }, + [14] = { 23, cooldown = 6, levelRequirement = 58, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 29, }, }, + [15] = { 24, cooldown = 6, levelRequirement = 60, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 29, }, }, + [16] = { 25, cooldown = 6, levelRequirement = 62, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 30, }, }, + [17] = { 26, cooldown = 6, levelRequirement = 64, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 31, }, }, + [18] = { 27, cooldown = 6, levelRequirement = 66, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 31, }, }, + [19] = { 28, cooldown = 6, levelRequirement = 68, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 32, }, }, + [20] = { 29, cooldown = 6, levelRequirement = 70, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 33, }, }, + [21] = { 30, cooldown = 6, levelRequirement = 72, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 34, }, }, + [22] = { 31, cooldown = 6, levelRequirement = 74, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 34, }, }, + [23] = { 32, cooldown = 6, levelRequirement = 76, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 35, }, }, + [24] = { 33, cooldown = 6, levelRequirement = 78, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 36, }, }, + [25] = { 34, cooldown = 6, levelRequirement = 80, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 36, }, }, + [26] = { 35, cooldown = 6, levelRequirement = 82, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 37, }, }, + [27] = { 36, cooldown = 6, levelRequirement = 84, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 38, }, }, + [28] = { 37, cooldown = 6, levelRequirement = 86, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 39, }, }, + [29] = { 38, cooldown = 6, levelRequirement = 88, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 39, }, }, + [30] = { 39, cooldown = 6, levelRequirement = 90, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 40, }, }, + [31] = { 39, cooldown = 6, levelRequirement = 91, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 40, }, }, + [32] = { 40, cooldown = 6, levelRequirement = 92, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 41, }, }, + [33] = { 40, cooldown = 6, levelRequirement = 93, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 41, }, }, + [34] = { 41, cooldown = 6, levelRequirement = 94, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 42, }, }, + [35] = { 41, cooldown = 6, levelRequirement = 95, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 42, }, }, + [36] = { 42, cooldown = 6, levelRequirement = 96, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 42, }, }, + [37] = { 42, cooldown = 6, levelRequirement = 97, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 43, }, }, + [38] = { 43, cooldown = 6, levelRequirement = 98, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 43, }, }, + [39] = { 43, cooldown = 6, levelRequirement = 99, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 43, }, }, + [40] = { 44, cooldown = 6, levelRequirement = 100, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 44, }, }, + }, +} +skills["PactOfLycia"] = { + name = "Pact of Lycia", + baseTypeName = "Pact of Lycia", + flavourText = {"Call upon Lycia, conduit of the Scourge. She forfeit", "her soul for the power to condemn lies told by bones.", }, + color = 4, + description = "Cast a pact, Empowering the next channelling spells you cast yourself but burdening you with an Affliction. Empowered spells deal more damage and periodically trigger lightning bolts while channelling.", + skillTypes = { [SkillType.Spell] = true, [SkillType.Pact] = true, [SkillType.Buff] = true, [SkillType.Cooldown] = true, [SkillType.InstantNoRepeatWhenHeld] = true, [SkillType.InstantShiftAttackForLeftMouse] = true, [SkillType.Triggerable] = true, }, + statDescriptionScope = "skill_stat_descriptions", + castTime = 0.8, + statMap = { + ["skill_empowers_next_x_spells_cast"] = { + mod("LyciaEmpoweredSpells", "BASE", nil), + }, + ["pact_skill_grant_damage_+%_final_to_exerted_skills"] = { + mod("LyciaPactDamage", "BASE", nil), + }, + }, + baseFlags = { + spell = true, + pact = true, + }, + constantStats = { + { "skill_empowers_next_x_spells_cast", 3 }, + { "pact_empower_limitation_specifier_for_stat_description", 3 }, + { "pact_skill_grant_damage_+%_final_to_exerted_skills", 40 }, + }, + stats = { + "base_deal_no_attack_damage", + "base_deal_no_spell_damage", + "base_deal_no_secondary_damage", + "cannot_cancel_skill_before_contact_point", + }, + levels = { + [1] = { cooldown = 4, levelRequirement = 24, storedUses = 1, cost = { Mana = 16, }, }, + [2] = { cooldown = 4, levelRequirement = 27, storedUses = 1, cost = { Mana = 17, }, }, + [3] = { cooldown = 4, levelRequirement = 30, storedUses = 1, cost = { Mana = 18, }, }, + [4] = { cooldown = 4, levelRequirement = 33, storedUses = 1, cost = { Mana = 19, }, }, + [5] = { cooldown = 4, levelRequirement = 36, storedUses = 1, cost = { Mana = 21, }, }, + [6] = { cooldown = 4, levelRequirement = 39, storedUses = 1, cost = { Mana = 22, }, }, + [7] = { cooldown = 4, levelRequirement = 42, storedUses = 1, cost = { Mana = 23, }, }, + [8] = { cooldown = 4, levelRequirement = 45, storedUses = 1, cost = { Mana = 24, }, }, + [9] = { cooldown = 4, levelRequirement = 48, storedUses = 1, cost = { Mana = 25, }, }, + [10] = { cooldown = 4, levelRequirement = 50, storedUses = 1, cost = { Mana = 26, }, }, + [11] = { cooldown = 4, levelRequirement = 52, storedUses = 1, cost = { Mana = 26, }, }, + [12] = { cooldown = 4, levelRequirement = 54, storedUses = 1, cost = { Mana = 27, }, }, + [13] = { cooldown = 4, levelRequirement = 56, storedUses = 1, cost = { Mana = 28, }, }, + [14] = { cooldown = 4, levelRequirement = 58, storedUses = 1, cost = { Mana = 29, }, }, + [15] = { cooldown = 4, levelRequirement = 60, storedUses = 1, cost = { Mana = 29, }, }, + [16] = { cooldown = 4, levelRequirement = 62, storedUses = 1, cost = { Mana = 30, }, }, + [17] = { cooldown = 4, levelRequirement = 64, storedUses = 1, cost = { Mana = 31, }, }, + [18] = { cooldown = 4, levelRequirement = 66, storedUses = 1, cost = { Mana = 31, }, }, + [19] = { cooldown = 4, levelRequirement = 68, storedUses = 1, cost = { Mana = 32, }, }, + [20] = { cooldown = 4, levelRequirement = 70, storedUses = 1, cost = { Mana = 33, }, }, + [21] = { cooldown = 4, levelRequirement = 72, storedUses = 1, cost = { Mana = 34, }, }, + [22] = { cooldown = 4, levelRequirement = 74, storedUses = 1, cost = { Mana = 34, }, }, + [23] = { cooldown = 4, levelRequirement = 76, storedUses = 1, cost = { Mana = 35, }, }, + [24] = { cooldown = 4, levelRequirement = 78, storedUses = 1, cost = { Mana = 36, }, }, + [25] = { cooldown = 4, levelRequirement = 80, storedUses = 1, cost = { Mana = 36, }, }, + [26] = { cooldown = 4, levelRequirement = 82, storedUses = 1, cost = { Mana = 37, }, }, + [27] = { cooldown = 4, levelRequirement = 84, storedUses = 1, cost = { Mana = 38, }, }, + [28] = { cooldown = 4, levelRequirement = 86, storedUses = 1, cost = { Mana = 39, }, }, + [29] = { cooldown = 4, levelRequirement = 88, storedUses = 1, cost = { Mana = 39, }, }, + [30] = { cooldown = 4, levelRequirement = 90, storedUses = 1, cost = { Mana = 40, }, }, + [31] = { cooldown = 4, levelRequirement = 91, storedUses = 1, cost = { Mana = 40, }, }, + [32] = { cooldown = 4, levelRequirement = 92, storedUses = 1, cost = { Mana = 41, }, }, + [33] = { cooldown = 4, levelRequirement = 93, storedUses = 1, cost = { Mana = 41, }, }, + [34] = { cooldown = 4, levelRequirement = 94, storedUses = 1, cost = { Mana = 42, }, }, + [35] = { cooldown = 4, levelRequirement = 95, storedUses = 1, cost = { Mana = 42, }, }, + [36] = { cooldown = 4, levelRequirement = 96, storedUses = 1, cost = { Mana = 42, }, }, + [37] = { cooldown = 4, levelRequirement = 97, storedUses = 1, cost = { Mana = 43, }, }, + [38] = { cooldown = 4, levelRequirement = 98, storedUses = 1, cost = { Mana = 43, }, }, + [39] = { cooldown = 4, levelRequirement = 99, storedUses = 1, cost = { Mana = 43, }, }, + [40] = { cooldown = 4, levelRequirement = 100, storedUses = 1, cost = { Mana = 44, }, }, + }, +} skills["Ravenous"] = { name = "Ravenous", hidden = true, diff --git a/src/Export/Skills/other.txt b/src/Export/Skills/other.txt index 6e0289cc2a0..d552451e914 100644 --- a/src/Export/Skills/other.txt +++ b/src/Export/Skills/other.txt @@ -745,6 +745,57 @@ local skills, mod, flag, skill = ... #baseMod skill("debuff", true) #mods +#skill PactOfBeidat +#flags spell pact + statMap = { + ["skill_empowers_next_x_spells_cast"] = { + mod("BeidatEmpoweredSpells", "BASE", nil), + }, + ["pact_skill_damage_+%_final_with_hits_and_ailments_to_grant"] = { + mod("BeidatPactDamage", "BASE", nil), + }, + }, +#mods + +#skill PactOfGhorr +#flags spell pact duration + statMap = { + ["skill_empowers_next_x_spells_cast"] = { + mod("GhorrEmpoweredSpells", "BASE", nil), + }, + ["pact_skill_grant_damage_over_time_+%_final_to_spells"] = { + mod("GhorrPactDamage", "BASE", nil), + }, + }, +#mods + +#skill PactOfKtash +#flags spell pact + statMap = { + ["skill_empowers_next_x_spells_cast"] = { + mod("KtashEmpoweredSpells", "BASE", nil), + }, + ["pact_skill_grant_damage_+%_final_to_exerted_skills"] = { + mod("KtashPactDamage", "BASE", nil), + }, + ["active_skill_osm_vaal_skill_soul_gain_prevention_+%_final_to_grant"] = { + mod("KtashPactSoulGainPrevention", "BASE", nil), + }, + }, +#mods + +#skill PactOfLycia +#flags spell pact + statMap = { + ["skill_empowers_next_x_spells_cast"] = { + mod("LyciaEmpoweredSpells", "BASE", nil), + }, + ["pact_skill_grant_damage_+%_final_to_exerted_skills"] = { + mod("LyciaPactDamage", "BASE", nil), + }, + }, +#mods + #skill Ravenous #flags spell fromTree = true, diff --git a/src/Modules/CalcOffence.lua b/src/Modules/CalcOffence.lua index 85ecb9f772c..46973ebfa5b 100644 --- a/src/Modules/CalcOffence.lua +++ b/src/Modules/CalcOffence.lua @@ -2783,6 +2783,137 @@ function calcs.offence(env, actor, activeSkill) globalOutput.ExertedAttackUptimeRatioCalculated = true end end + + -- Pacts // Empowered Spells + if activeSkill.skillTypes[SkillType.Spell] and not activeSkill.skillTypes[SkillType.Instant] and not activeSkill.skillTypes[SkillType.Brand] then + for index, value in ipairs(actor.activeSkillList) do + if not activeSkill.skillTypes[SkillType.Vaal] then + if value.activeEffect.grantedEffect.name == "Pact of Beidat" and not globalOutput.PactofBeidatCalculated and not value.skillFlags.disable + and (skillFlags.projectile or skillFlags.area or skillFlags.chaining) and not activeSkill.skillTypes[SkillType.Channel] then + globalOutput.CreatePactOffensiveCalcSection = true + globalOutput.PactOfBeidatDuration = calcSkillDuration(value.skillModList, value.skillCfg, value.skillData, env, enemyDB) + globalOutput.PactOfBeidatCooldown = calcSkillCooldown(value.skillModList, value.skillCfg, value.skillData) + globalOutput.PactOfBeidatCastTime = modDB:Sum("BASE", nil, "BeidatBaseCastTime") + globalOutput.BeidatEmpoweredCount = skillModList:Sum("BASE", nil, "BeidatEmpoweredSpells") + local baseUptimeRatio = m_min((globalOutput.BeidatEmpoweredCount / globalOutput.Speed) / (globalOutput.PactOfBeidatCooldown + globalOutput.PactOfBeidatCastTime), 1) * 100 + local storedUses = value.skillData.storedUses + value.skillModList:Sum("BASE", value.skillCfg, "AdditionalCooldownUses") + globalOutput.BeidatUpTimeRatio = m_min(100, baseUptimeRatio * storedUses) + if globalBreakdown then + globalBreakdown.BeidatUpTimeRatio = { } + t_insert(globalBreakdown.BeidatUpTimeRatio, s_format("(%d ^8(number of Empowered)", globalOutput.BeidatEmpoweredCount)) + t_insert(globalBreakdown.BeidatUpTimeRatio, s_format("/ %.2f) ^8(casts per second)", globalOutput.Speed)) + if globalOutput.PactOfBeidatCastTime > 0 then + t_insert(globalBreakdown.BeidatUpTimeRatio, s_format("/ (%.2f ^8(pact cooldown)", globalOutput.PactOfBeidatCooldown)) + t_insert(globalBreakdown.BeidatUpTimeRatio, s_format("+ %.2f) ^8(pact casttime)", globalOutput.PactOfBeidatCastTime)) + else + t_insert(globalBreakdown.BeidatUpTimeRatio, s_format("/ %.2f ^8(average pact cooldown)", globalOutput.PactOfBeidatCooldown)) + end + t_insert(globalBreakdown.BeidatUpTimeRatio, s_format("* %d ^8(stored uses)", storedUses)) + t_insert(globalBreakdown.BeidatUpTimeRatio, s_format("= %d%%", globalOutput.BeidatUpTimeRatio)) + end + local beidatDamage = modDB:Sum("BASE", nil, "BeidatPactDamage") + if beidatDamage > 0 then + local beidatUptime = skillModList:Flag(nil, "Condition:PactMaxHit") and 100 or globalOutput.BeidatUpTimeRatio + skillModList:NewMod("Damage", "MORE", beidatDamage * beidatUptime / 100, "Uptime Scaled Pact of Beidat", ModFlag.Spell, bor(KeywordFlag.Hit, KeywordFlag.Ailment)) + end + globalOutput.PactofBeidatCalculated = true + end + if value.activeEffect.grantedEffect.name == "Pact of Ghorr" and not globalOutput.PactofGhorrCalculated and not value.skillFlags.disable + and activeSkill.skillTypes[SkillType.DamageOverTime] then + globalOutput.CreatePactOffensiveCalcSection = true + globalOutput.PactOfGhorrDuration = calcSkillDuration(value.skillModList, value.skillCfg, value.skillData, env, enemyDB) + globalOutput.PactOfGhorrCooldown = calcSkillCooldown(value.skillModList, value.skillCfg, value.skillData) + globalOutput.PactOfGhorrCastTime = modDB:Sum("BASE", nil, "GhorrBaseCastTime") + globalOutput.GhorrEmpoweredCount = skillModList:Sum("BASE", nil, "GhorrEmpoweredSpells") + local baseUptimeRatio = m_min((globalOutput.GhorrEmpoweredCount / globalOutput.Speed) / (globalOutput.PactOfGhorrCooldown + globalOutput.PactOfGhorrCastTime), 1) * 100 + local storedUses = value.skillData.storedUses + value.skillModList:Sum("BASE", value.skillCfg, "AdditionalCooldownUses") + globalOutput.GhorrUpTimeRatio = m_min(100, baseUptimeRatio * storedUses) + if globalBreakdown then + globalBreakdown.GhorrUpTimeRatio = { } + t_insert(globalBreakdown.GhorrUpTimeRatio, s_format("(%d ^8(number of Empowered)", globalOutput.GhorrEmpoweredCount)) + t_insert(globalBreakdown.GhorrUpTimeRatio, s_format("/ %.2f) ^8(casts per second)", globalOutput.Speed)) + if globalOutput.PactOfGhorrCastTime > 0 then + t_insert(globalBreakdown.GhorrUpTimeRatio, s_format("/ (%.2f ^8(pact cooldown)", globalOutput.PactOfGhorrCooldown)) + t_insert(globalBreakdown.GhorrUpTimeRatio, s_format("+ %.2f) ^8(pact casttime)", globalOutput.PactOfGhorrCastTime)) + else + t_insert(globalBreakdown.GhorrUpTimeRatio, s_format("/ %.2f ^8(average pact cooldown)", globalOutput.PactOfGhorrCooldown)) + end + t_insert(globalBreakdown.GhorrUpTimeRatio, s_format("* %d ^8(stored uses)", storedUses)) + t_insert(globalBreakdown.GhorrUpTimeRatio, s_format("= %d%%", globalOutput.GhorrUpTimeRatio)) + end + local GhorrDamage = modDB:Sum("BASE", nil, "GhorrPactDamage") + if GhorrDamage > 0 then + local GhorrUptime = skillModList:Flag(nil, "Condition:PactMaxHit") and 100 or globalOutput.GhorrUpTimeRatio + skillModList:NewMod("Damage", "MORE", GhorrDamage * GhorrUptime / 100, "Uptime Scaled Pact of Ghorr", ModFlag.Dot) + end + globalOutput.PactofGhorrCalculated = true + end + if value.activeEffect.grantedEffect.name == "Pact of Lycia" and not globalOutput.PactofLyciaCalculated and not value.skillFlags.disable + and activeSkill.skillTypes[SkillType.Channel] then + globalOutput.CreatePactOffensiveCalcSection = true + globalOutput.PactOfLyciaDuration = calcSkillDuration(value.skillModList, value.skillCfg, value.skillData, env, enemyDB) + globalOutput.PactOfLyciaCooldown = calcSkillCooldown(value.skillModList, value.skillCfg, value.skillData) + globalOutput.PactOfLyciaCastTime = modDB:Sum("BASE", nil, "LyciaBaseCastTime") + globalOutput.LyciaEmpoweredCount = skillModList:Sum("BASE", nil, "LyciaEmpoweredSpells") + local baseUptimeRatio = m_min((globalOutput.LyciaEmpoweredCount / globalOutput.Speed) / (globalOutput.PactOfLyciaCooldown + globalOutput.PactOfLyciaCastTime), 1) * 100 + local storedUses = value.skillData.storedUses + value.skillModList:Sum("BASE", value.skillCfg, "AdditionalCooldownUses") + globalOutput.LyciaUpTimeRatio = m_min(100, baseUptimeRatio * storedUses) + if globalBreakdown then + globalBreakdown.LyciaUpTimeRatio = { } + t_insert(globalBreakdown.LyciaUpTimeRatio, s_format("(%d ^8(number of Empowered)", globalOutput.LyciaEmpoweredCount)) + t_insert(globalBreakdown.LyciaUpTimeRatio, s_format("/ %.2f) ^8(casts per second)", globalOutput.Speed)) + if globalOutput.PactOfLyciaCastTime > 0 then + t_insert(globalBreakdown.LyciaUpTimeRatio, s_format("/ (%.2f ^8(pact cooldown)", globalOutput.PactOfLyciaCooldown)) + t_insert(globalBreakdown.LyciaUpTimeRatio, s_format("+ %.2f) ^8(pact casttime)", globalOutput.PactOfLyciaCastTime)) + else + t_insert(globalBreakdown.LyciaUpTimeRatio, s_format("/ %.2f ^8(average pact cooldown)", globalOutput.PactOfLyciaCooldown)) + end + t_insert(globalBreakdown.LyciaUpTimeRatio, s_format("* %d ^8(stored uses)", storedUses)) + t_insert(globalBreakdown.LyciaUpTimeRatio, s_format("= %d%%", globalOutput.LyciaUpTimeRatio)) + end + local LyciaDamage = modDB:Sum("BASE", nil, "LyciaPactDamage") + if LyciaDamage > 0 then + local LyciaUptime = skillModList:Flag(nil, "Condition:PactMaxHit") and 100 or globalOutput.LyciaUpTimeRatio + skillModList:NewMod("Damage", "MORE", LyciaDamage * LyciaUptime / 100, "Uptime Scaled Pact of Lycia", ModFlag.Spell) + end + globalOutput.PactofLyciaCalculated = true + end + else + if value.activeEffect.grantedEffect.name == "Pact of K'Tash" and not globalOutput.PactofKtashCalculated and not value.skillFlags.disable then + globalOutput.CreatePactOffensiveCalcSection = true + globalOutput.PactOfKtashDuration = calcSkillDuration(value.skillModList, value.skillCfg, value.skillData, env, enemyDB) + globalOutput.PactOfKtashCooldown = calcSkillCooldown(value.skillModList, value.skillCfg, value.skillData) + globalOutput.PactOfKtashCastTime = modDB:Sum("BASE", nil, "KtashBaseCastTime") + globalOutput.KtashEmpoweredCount = skillModList:Sum("BASE", nil, "KtashEmpoweredSpells") + local baseUptimeRatio = m_min((globalOutput.KtashEmpoweredCount / globalOutput.Speed) / (globalOutput.PactOfKtashCooldown + globalOutput.PactOfKtashCastTime), 1) * 100 + local storedUses = value.skillData.storedUses + value.skillModList:Sum("BASE", value.skillCfg, "AdditionalCooldownUses") + globalOutput.KtashUpTimeRatio = m_min(100, baseUptimeRatio * storedUses) + if globalBreakdown then + globalBreakdown.KtashUpTimeRatio = { } + t_insert(globalBreakdown.KtashUpTimeRatio, s_format("(%d ^8(number of Empowered)", globalOutput.KtashEmpoweredCount)) + t_insert(globalBreakdown.KtashUpTimeRatio, s_format("/ %.2f) ^8(casts per second)", globalOutput.Speed)) + if globalOutput.PactOfKtashCastTime > 0 then + t_insert(globalBreakdown.KtashUpTimeRatio, s_format("/ (%.2f ^8(pact cooldown)", globalOutput.PactOfKtashCooldown)) + t_insert(globalBreakdown.KtashUpTimeRatio, s_format("+ %.2f) ^8(pact casttime)", globalOutput.PactOfKtashCastTime)) + else + t_insert(globalBreakdown.KtashUpTimeRatio, s_format("/ %.2f ^8(average pact cooldown)", globalOutput.PactOfKtashCooldown)) + end + t_insert(globalBreakdown.KtashUpTimeRatio, s_format("* %d ^8(stored uses)", storedUses)) + t_insert(globalBreakdown.KtashUpTimeRatio, s_format("= %d%%", globalOutput.KtashUpTimeRatio)) + end + local KtashDamage = modDB:Sum("BASE", nil, "KtashPactDamage") + local KtashSoulGainPrevention = modDB:Sum("BASE", nil, "KtashPactSoulGainPrevention") + if KtashDamage > 0 then + local KtashUptime = skillModList:Flag(nil, "Condition:PactMaxHit") and 100 or globalOutput.KtashUpTimeRatio + skillModList:NewMod("Damage", "MORE", KtashDamage * KtashUptime / 100, "Uptime Scaled Pact of Ktash", ModFlag.Spell) + -- happens too late, duration is calculated earlier + -- skillModList:NewMod("SoulGainPreventionDuration", "MORE", KtashSoulGainPrevention * KtashUptime / 100, "Uptime Scaled Pact of Ktash") + end + globalOutput.PactofKtashCalculated = true + end + end + end + end end output.RuthlessBlowHitEffect = 1 diff --git a/src/Modules/CalcPerform.lua b/src/Modules/CalcPerform.lua index f70d31ff8ff..370c80a7e54 100644 --- a/src/Modules/CalcPerform.lua +++ b/src/Modules/CalcPerform.lua @@ -1430,6 +1430,26 @@ function calcs.perform(env, skipEHP) modDB:NewMod("SeismicActive", "FLAG", true) -- Prevents effect from applying multiple times end end + local function calcCastTime(baseSpeed, skillModList, skillCfg, actor) + local castTime = baseSpeed / (calcLib.mod(skillModList, skillCfg, "Speed") * calcs.actionSpeedMod(actor)) + castTime = m_min(castTime, data.misc.ServerTickRate) + return castTime + end + if env.mode_buffs and activeSkill.skillFlags.pact then + for _, pactName in ipairs({ "Beidat", "Ghorr", "Ktash", "Lycia" }) do + if (activeSkill.activeEffect.grantedEffect.name == (("Pact of "..pactName)) or + activeSkill.activeEffect.grantedEffect.name == "Pact of K'Tash" and pactName == "Ktash") + and not modDB:Flag(nil, pactName.."Active") then + env.player.modDB:NewMod(pactName.."PactDamage", "BASE", activeSkill.skillModList:Sum("BASE", env.player.mainSkill.skillCfg, pactName.."PactDamage")) + env.player.modDB:NewMod(pactName.."EmpoweredSpells", "BASE", activeSkill.skillModList:Sum("BASE", env.player.mainSkill.skillCfg, pactName.."EmpoweredSpells")) + env.player.modDB:NewMod(pactName.."BaseCastTime", "BASE", calcCastTime(activeSkill.activeEffect.grantedEffect.castTime, activeSkill.skillModList, activeSkill.skillCfg, env.player)) + if pactName == "Ktash" then + env.player.modDB:NewMod("KtashPactSoulGainPrevention", "BASE", activeSkill.skillModList:Sum("BASE", env.player.mainSkill.skillCfg, "KtashPactSoulGainPrevention")) + end + modDB:NewMod(pactName.."Active", "FLAG", true) -- Prevents effect from applying multiple times + end + end + end if activeSkill.skillData.triggeredOnDeath and not activeSkill.skillFlags.minion then activeSkill.skillData.triggered = true for _, value in ipairs(activeSkill.skillModList:Tabulate("INC", env.player.mainSkill.skillCfg, "TriggeredDamage")) do diff --git a/src/Modules/CalcSections.lua b/src/Modules/CalcSections.lua index 0dc6da8a034..b4519781c29 100644 --- a/src/Modules/CalcSections.lua +++ b/src/Modules/CalcSections.lua @@ -416,6 +416,46 @@ return { }, } } } }, +{ 3, "Pacts", 1, colorCodes.OFFENCE, { { defaultCollapsed = false, label = "Empowered Spells", data = { + colWidth = 114, + { + haveOutput = "CreatePactOffensiveCalcSection", + { format = "Effective Impact" }, + { format = "Avg Dmg Effect" }, + { format = "Uptime" }, + { format = "# of Empowered" }, + { format = "Max 1-Hit Impact" }, + }, + { label = "Pact of Beidat", haveOutput = "BeidatUpTimeRatio", + { format = "" }, + { format = "" }, + { format = "{0:output:BeidatUpTimeRatio}%", { breakdown = "BeidatUpTimeRatio" }, }, + { format = "{0:output:BeidatEmpoweredCount}" }, + { format = "" }, + }, + { label = "Pact of Ghorr", haveOutput = "GhorrUpTimeRatio", + { format = "" }, + { format = "" }, + { format = "{0:output:GhorrUpTimeRatio}%", { breakdown = "GhorrUpTimeRatio" }, }, + { format = "{0:output:GhorrEmpoweredCount}" }, + { format = "" }, + }, + { label = "Pact of K'Tash", haveOutput = "KtashUpTimeRatio", + { format = "" }, + { format = "" }, + { format = "{0:output:KtashUpTimeRatio}%", { breakdown = "KtashUpTimeRatio" }, }, + { format = "{0:output:KtashEmpoweredCount}" }, + { format = "" }, + }, + { label = "Pact of Lycia", haveOutput = "LyciaUpTimeRatio", + { format = "" }, + { format = "" }, + { format = "{0:output:LyciaUpTimeRatio}%", { breakdown = "LyciaUpTimeRatio" }, }, + { format = "{0:output:LyciaEmpoweredCount}" }, + { format = "" }, + }, +} } +} }, { 3, "Dot", 1, colorCodes.OFFENCE, {{ defaultCollapsed = false, label = "Skill Damage over Time", data = { extra = "{1:output:TotalDotCalcSection} total DoT", flag = "dot", From 921aa3488d32d718a639dccbf0c5779d69c72162 Mon Sep 17 00:00:00 2001 From: Peechey <92683202+Peechey@users.noreply.github.com> Date: Sat, 25 Jul 2026 13:40:32 -0500 Subject: [PATCH 2/4] spelling --- src/Modules/CalcOffence.lua | 42 ++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/Modules/CalcOffence.lua b/src/Modules/CalcOffence.lua index 46973ebfa5b..4d013020d5f 100644 --- a/src/Modules/CalcOffence.lua +++ b/src/Modules/CalcOffence.lua @@ -2788,7 +2788,7 @@ function calcs.offence(env, actor, activeSkill) if activeSkill.skillTypes[SkillType.Spell] and not activeSkill.skillTypes[SkillType.Instant] and not activeSkill.skillTypes[SkillType.Brand] then for index, value in ipairs(actor.activeSkillList) do if not activeSkill.skillTypes[SkillType.Vaal] then - if value.activeEffect.grantedEffect.name == "Pact of Beidat" and not globalOutput.PactofBeidatCalculated and not value.skillFlags.disable + if value.activeEffect.grantedEffect.name == "Pact of Beidat" and not globalOutput.PactOfBeidatCalculated and not value.skillFlags.disable and (skillFlags.projectile or skillFlags.area or skillFlags.chaining) and not activeSkill.skillTypes[SkillType.Channel] then globalOutput.CreatePactOffensiveCalcSection = true globalOutput.PactOfBeidatDuration = calcSkillDuration(value.skillModList, value.skillCfg, value.skillData, env, enemyDB) @@ -2816,9 +2816,9 @@ function calcs.offence(env, actor, activeSkill) local beidatUptime = skillModList:Flag(nil, "Condition:PactMaxHit") and 100 or globalOutput.BeidatUpTimeRatio skillModList:NewMod("Damage", "MORE", beidatDamage * beidatUptime / 100, "Uptime Scaled Pact of Beidat", ModFlag.Spell, bor(KeywordFlag.Hit, KeywordFlag.Ailment)) end - globalOutput.PactofBeidatCalculated = true + globalOutput.PactOfBeidatCalculated = true end - if value.activeEffect.grantedEffect.name == "Pact of Ghorr" and not globalOutput.PactofGhorrCalculated and not value.skillFlags.disable + if value.activeEffect.grantedEffect.name == "Pact of Ghorr" and not globalOutput.PactOfGhorrCalculated and not value.skillFlags.disable and activeSkill.skillTypes[SkillType.DamageOverTime] then globalOutput.CreatePactOffensiveCalcSection = true globalOutput.PactOfGhorrDuration = calcSkillDuration(value.skillModList, value.skillCfg, value.skillData, env, enemyDB) @@ -2841,14 +2841,14 @@ function calcs.offence(env, actor, activeSkill) t_insert(globalBreakdown.GhorrUpTimeRatio, s_format("* %d ^8(stored uses)", storedUses)) t_insert(globalBreakdown.GhorrUpTimeRatio, s_format("= %d%%", globalOutput.GhorrUpTimeRatio)) end - local GhorrDamage = modDB:Sum("BASE", nil, "GhorrPactDamage") - if GhorrDamage > 0 then - local GhorrUptime = skillModList:Flag(nil, "Condition:PactMaxHit") and 100 or globalOutput.GhorrUpTimeRatio - skillModList:NewMod("Damage", "MORE", GhorrDamage * GhorrUptime / 100, "Uptime Scaled Pact of Ghorr", ModFlag.Dot) + local ghorrDamage = modDB:Sum("BASE", nil, "GhorrPactDamage") + if ghorrDamage > 0 then + local ghorrUptime = skillModList:Flag(nil, "Condition:PactMaxHit") and 100 or globalOutput.GhorrUpTimeRatio + skillModList:NewMod("Damage", "MORE", ghorrDamage * ghorrUptime / 100, "Uptime Scaled Pact of Ghorr", ModFlag.Dot) end - globalOutput.PactofGhorrCalculated = true + globalOutput.PactOfGhorrCalculated = true end - if value.activeEffect.grantedEffect.name == "Pact of Lycia" and not globalOutput.PactofLyciaCalculated and not value.skillFlags.disable + if value.activeEffect.grantedEffect.name == "Pact of Lycia" and not globalOutput.PactOfLyciaCalculated and not value.skillFlags.disable and activeSkill.skillTypes[SkillType.Channel] then globalOutput.CreatePactOffensiveCalcSection = true globalOutput.PactOfLyciaDuration = calcSkillDuration(value.skillModList, value.skillCfg, value.skillData, env, enemyDB) @@ -2871,15 +2871,15 @@ function calcs.offence(env, actor, activeSkill) t_insert(globalBreakdown.LyciaUpTimeRatio, s_format("* %d ^8(stored uses)", storedUses)) t_insert(globalBreakdown.LyciaUpTimeRatio, s_format("= %d%%", globalOutput.LyciaUpTimeRatio)) end - local LyciaDamage = modDB:Sum("BASE", nil, "LyciaPactDamage") - if LyciaDamage > 0 then - local LyciaUptime = skillModList:Flag(nil, "Condition:PactMaxHit") and 100 or globalOutput.LyciaUpTimeRatio - skillModList:NewMod("Damage", "MORE", LyciaDamage * LyciaUptime / 100, "Uptime Scaled Pact of Lycia", ModFlag.Spell) + local lyciaDamage = modDB:Sum("BASE", nil, "LyciaPactDamage") + if lyciaDamage > 0 then + local lyciaUptime = skillModList:Flag(nil, "Condition:PactMaxHit") and 100 or globalOutput.LyciaUpTimeRatio + skillModList:NewMod("Damage", "MORE", lyciaDamage * lyciaUptime / 100, "Uptime Scaled Pact of Lycia", ModFlag.Spell) end - globalOutput.PactofLyciaCalculated = true + globalOutput.PactOfLyciaCalculated = true end else - if value.activeEffect.grantedEffect.name == "Pact of K'Tash" and not globalOutput.PactofKtashCalculated and not value.skillFlags.disable then + if value.activeEffect.grantedEffect.name == "Pact of K'Tash" and not globalOutput.PactOfKtashCalculated and not value.skillFlags.disable then globalOutput.CreatePactOffensiveCalcSection = true globalOutput.PactOfKtashDuration = calcSkillDuration(value.skillModList, value.skillCfg, value.skillData, env, enemyDB) globalOutput.PactOfKtashCooldown = calcSkillCooldown(value.skillModList, value.skillCfg, value.skillData) @@ -2901,15 +2901,15 @@ function calcs.offence(env, actor, activeSkill) t_insert(globalBreakdown.KtashUpTimeRatio, s_format("* %d ^8(stored uses)", storedUses)) t_insert(globalBreakdown.KtashUpTimeRatio, s_format("= %d%%", globalOutput.KtashUpTimeRatio)) end - local KtashDamage = modDB:Sum("BASE", nil, "KtashPactDamage") - local KtashSoulGainPrevention = modDB:Sum("BASE", nil, "KtashPactSoulGainPrevention") - if KtashDamage > 0 then - local KtashUptime = skillModList:Flag(nil, "Condition:PactMaxHit") and 100 or globalOutput.KtashUpTimeRatio - skillModList:NewMod("Damage", "MORE", KtashDamage * KtashUptime / 100, "Uptime Scaled Pact of Ktash", ModFlag.Spell) + local ktashDamage = modDB:Sum("BASE", nil, "KtashPactDamage") + local ktashSoulGainPrevention = modDB:Sum("BASE", nil, "KtashPactSoulGainPrevention") + if ktashDamage > 0 then + local ktashUptime = skillModList:Flag(nil, "Condition:PactMaxHit") and 100 or globalOutput.KtashUpTimeRatio + skillModList:NewMod("Damage", "MORE", ktashDamage * ktashUptime / 100, "Uptime Scaled Pact of Ktash", ModFlag.Spell) -- happens too late, duration is calculated earlier -- skillModList:NewMod("SoulGainPreventionDuration", "MORE", KtashSoulGainPrevention * KtashUptime / 100, "Uptime Scaled Pact of Ktash") end - globalOutput.PactofKtashCalculated = true + globalOutput.PactOfKtashCalculated = true end end end From ae05d0d4b1b5b7853663fc7f23dfc4378cff92ca Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Wed, 5 Aug 2026 18:44:36 +1000 Subject: [PATCH 3/4] Fix skill export --- src/Data/Skills/other.lua | 368 +++++------------------------------- src/Export/Skills/other.txt | 62 +++--- 2 files changed, 76 insertions(+), 354 deletions(-) diff --git a/src/Data/Skills/other.lua b/src/Data/Skills/other.lua index 10014f1d13b..2a292ccfb05 100644 --- a/src/Data/Skills/other.lua +++ b/src/Data/Skills/other.lua @@ -2778,7 +2778,17 @@ skills["PactOfBeidat"] = { skillTypes = { [SkillType.Spell] = true, [SkillType.Pact] = true, [SkillType.Buff] = true, [SkillType.Cooldown] = true, [SkillType.InstantNoRepeatWhenHeld] = true, [SkillType.InstantShiftAttackForLeftMouse] = true, [SkillType.Triggerable] = true, }, statDescriptionScope = "skill_stat_descriptions", castTime = 0.8, + statMap = { + ["skill_empowers_next_x_spells_cast"] = { + mod("BeidatEmpoweredSpells", "BASE", nil), + }, + ["pact_skill_damage_+%_final_with_hits_and_ailments_to_grant"] = { + mod("BeidatPactDamage", "BASE", nil), + }, + }, baseFlags = { + spell = true, + pact = true, }, qualityStats = { { "skill_empowers_next_x_spells_cast", 0.1 }, @@ -2849,7 +2859,18 @@ skills["PactOfGhorr"] = { skillTypes = { [SkillType.Spell] = true, [SkillType.Pact] = true, [SkillType.Buff] = true, [SkillType.Cooldown] = true, [SkillType.InstantNoRepeatWhenHeld] = true, [SkillType.InstantShiftAttackForLeftMouse] = true, [SkillType.Triggerable] = true, }, statDescriptionScope = "skill_stat_descriptions", castTime = 0.8, + statMap = { + ["skill_empowers_next_x_spells_cast"] = { + mod("GhorrEmpoweredSpells", "BASE", nil), + }, + ["pact_skill_grant_damage_over_time_+%_final_to_spells"] = { + mod("GhorrPactDamage", "BASE", nil), + }, + }, baseFlags = { + spell = true, + pact = true, + duration = true, }, constantStats = { { "skill_empowers_next_x_spells_cast", 3 }, @@ -2920,6 +2941,9 @@ skills["TriggeredBloodrend"] = { statDescriptionScope = "debuff_skill_stat_descriptions", castTime = 0, baseFlags = { + spell = true, + pact = true, + projectile = true, }, qualityStats = { { "skill_effect_duration_+%", 1 }, @@ -2990,7 +3014,20 @@ skills["PactOfKtash"] = { skillTypes = { [SkillType.Spell] = true, [SkillType.Pact] = true, [SkillType.Buff] = true, [SkillType.Cooldown] = true, [SkillType.InstantNoRepeatWhenHeld] = true, [SkillType.InstantShiftAttackForLeftMouse] = true, [SkillType.Triggerable] = true, }, statDescriptionScope = "skill_stat_descriptions", castTime = 0.8, + statMap = { + ["skill_empowers_next_x_spells_cast"] = { + mod("KtashEmpoweredSpells", "BASE", nil), + }, + ["pact_skill_grant_damage_+%_final_to_exerted_skills"] = { + mod("KtashPactDamage", "BASE", nil), + }, + ["active_skill_osm_vaal_skill_soul_gain_prevention_+%_final_to_grant"] = { + mod("KtashPactSoulGainPrevention", "BASE", nil), + }, + }, baseFlags = { + spell = true, + pact = true, }, qualityStats = { { "base_cooldown_speed_+%", 0.5 }, @@ -3060,7 +3097,17 @@ skills["PactOfLycia"] = { skillTypes = { [SkillType.Spell] = true, [SkillType.Pact] = true, [SkillType.Buff] = true, [SkillType.Cooldown] = true, [SkillType.InstantNoRepeatWhenHeld] = true, [SkillType.InstantShiftAttackForLeftMouse] = true, [SkillType.Triggerable] = true, }, statDescriptionScope = "skill_stat_descriptions", castTime = 0.8, + statMap = { + ["skill_empowers_next_x_spells_cast"] = { + mod("LyciaEmpoweredSpells", "BASE", nil), + }, + ["pact_skill_grant_damage_+%_final_to_exerted_skills"] = { + mod("LyciaPactDamage", "BASE", nil), + }, + }, baseFlags = { + spell = true, + pact = true, }, constantStats = { { "skill_empowers_next_x_spells_cast", 3 }, @@ -3128,6 +3175,9 @@ skills["TriggeredHeavensScourge"] = { statDescriptionScope = "skill_stat_descriptions", castTime = 1, baseFlags = { + spell = true, + pact = true, + area = true, }, qualityStats = { { "active_skill_base_area_of_effect_radius", 0.1 }, @@ -3299,324 +3349,6 @@ skills["Pacify"] = { [20] = { levelRequirement = 70, cost = { Mana = 33, }, }, }, } -skills["PactOfBeidat"] = { - name = "Pact of Beidat", - baseTypeName = "Pact of Beidat", - flavourText = {"Wield the gifts of Beidat, luminary of the pale unseeing,", "whose honeyed words bore tunnels through pliant minds.", }, - color = 4, - description = "Cast a pact, Empowering non-channelling spells that either fire projectiles, apply an effect to an area around a targeted location or fire chaining beams, causing them to deal more damage and have enhanced coverage. However, using this skill also burdens you with an Affliction.", - skillTypes = { [SkillType.Spell] = true, [SkillType.Pact] = true, [SkillType.Buff] = true, [SkillType.Cooldown] = true, [SkillType.InstantNoRepeatWhenHeld] = true, [SkillType.InstantShiftAttackForLeftMouse] = true, [SkillType.Triggerable] = true, }, - statDescriptionScope = "skill_stat_descriptions", - castTime = 0.8, - statMap = { - ["skill_empowers_next_x_spells_cast"] = { - mod("BeidatEmpoweredSpells", "BASE", nil), - }, - ["pact_skill_damage_+%_final_with_hits_and_ailments_to_grant"] = { - mod("BeidatPactDamage", "BASE", nil), - }, - }, - baseFlags = { - spell = true, - pact = true, - }, - qualityStats = { - { "skill_empowers_next_x_spells_cast", 0.1 }, - }, - constantStats = { - { "skill_empowers_next_x_spells_cast", 6 }, - { "pact_empower_limitation_specifier_for_stat_description", 0 }, - }, - stats = { - "pact_skill_additional_beam_only_chains", - "pact_skill_grant_x_additional_projectiles_fired_in_nova", - "pact_skill_grant_x_cascades_to_do_in_spiral", - "pact_skill_damage_+%_final_with_hits_and_ailments_to_grant", - "base_deal_no_attack_damage", - "base_deal_no_spell_damage", - "base_deal_no_secondary_damage", - "cannot_cancel_skill_before_contact_point", - }, - levels = { - [1] = { 4, 4, 5, 16, cooldown = 4, levelRequirement = 24, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 16, }, }, - [2] = { 4, 4, 5, 17, cooldown = 4, levelRequirement = 27, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 17, }, }, - [3] = { 4, 4, 5, 18, cooldown = 4, levelRequirement = 30, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 18, }, }, - [4] = { 4, 4, 5, 19, cooldown = 4, levelRequirement = 33, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 19, }, }, - [5] = { 4, 4, 6, 20, cooldown = 4, levelRequirement = 36, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 21, }, }, - [6] = { 5, 4, 6, 21, cooldown = 4, levelRequirement = 39, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 22, }, }, - [7] = { 5, 5, 6, 22, cooldown = 4, levelRequirement = 42, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 23, }, }, - [8] = { 5, 5, 6, 23, cooldown = 4, levelRequirement = 45, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 24, }, }, - [9] = { 5, 5, 6, 24, cooldown = 4, levelRequirement = 48, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 25, }, }, - [10] = { 5, 5, 6, 25, cooldown = 4, levelRequirement = 50, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 26, }, }, - [11] = { 5, 5, 7, 26, cooldown = 4, levelRequirement = 52, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 26, }, }, - [12] = { 5, 5, 7, 27, cooldown = 4, levelRequirement = 54, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 27, }, }, - [13] = { 5, 5, 7, 28, cooldown = 4, levelRequirement = 56, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 28, }, }, - [14] = { 6, 5, 7, 29, cooldown = 4, levelRequirement = 58, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 29, }, }, - [15] = { 6, 6, 7, 30, cooldown = 4, levelRequirement = 60, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 29, }, }, - [16] = { 6, 6, 7, 31, cooldown = 4, levelRequirement = 62, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 30, }, }, - [17] = { 6, 6, 8, 32, cooldown = 4, levelRequirement = 64, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 31, }, }, - [18] = { 6, 6, 8, 33, cooldown = 4, levelRequirement = 66, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 31, }, }, - [19] = { 6, 6, 8, 34, cooldown = 4, levelRequirement = 68, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 32, }, }, - [20] = { 6, 6, 8, 35, cooldown = 4, levelRequirement = 70, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 33, }, }, - [21] = { 6, 6, 8, 36, cooldown = 4, levelRequirement = 72, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 34, }, }, - [22] = { 7, 6, 8, 37, cooldown = 4, levelRequirement = 74, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 34, }, }, - [23] = { 7, 7, 8, 38, cooldown = 4, levelRequirement = 76, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 35, }, }, - [24] = { 7, 7, 9, 39, cooldown = 4, levelRequirement = 78, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 36, }, }, - [25] = { 7, 7, 9, 40, cooldown = 4, levelRequirement = 80, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 36, }, }, - [26] = { 7, 7, 9, 41, cooldown = 4, levelRequirement = 82, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 37, }, }, - [27] = { 7, 7, 9, 42, cooldown = 4, levelRequirement = 84, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 38, }, }, - [28] = { 7, 7, 9, 43, cooldown = 4, levelRequirement = 86, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 39, }, }, - [29] = { 7, 7, 9, 44, cooldown = 4, levelRequirement = 88, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 39, }, }, - [30] = { 8, 7, 10, 44, cooldown = 4, levelRequirement = 90, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 40, }, }, - [31] = { 8, 7, 10, 45, cooldown = 4, levelRequirement = 91, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 40, }, }, - [32] = { 8, 8, 10, 45, cooldown = 4, levelRequirement = 92, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 41, }, }, - [33] = { 8, 8, 10, 46, cooldown = 4, levelRequirement = 93, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 41, }, }, - [34] = { 8, 8, 10, 46, cooldown = 4, levelRequirement = 94, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 42, }, }, - [35] = { 8, 8, 10, 47, cooldown = 4, levelRequirement = 95, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 42, }, }, - [36] = { 8, 8, 10, 47, cooldown = 4, levelRequirement = 96, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 42, }, }, - [37] = { 8, 8, 10, 48, cooldown = 4, levelRequirement = 97, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 43, }, }, - [38] = { 8, 8, 10, 48, cooldown = 4, levelRequirement = 98, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 43, }, }, - [39] = { 8, 8, 10, 49, cooldown = 4, levelRequirement = 99, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 43, }, }, - [40] = { 8, 8, 10, 49, cooldown = 4, levelRequirement = 100, storedUses = 1, statInterpolation = { 1, 1, 1, 1, }, cost = { Mana = 44, }, }, - }, -} -skills["PactOfGhorr"] = { - name = "Pact of Ghorr", - baseTypeName = "Pact of Ghorr", - flavourText = {"Offer yourself to Ghorr, the gluttonous, the cage of", "flesh trapping souls in the cycle of consumption.", }, - color = 4, - description = "Cast a pact, Empowering your damage over time spells but burdening you with an Affliction. This skill also grants the Hunger of Ghorr buff for a duration causing Fleshrend to trigger periodically.", - skillTypes = { [SkillType.Spell] = true, [SkillType.Pact] = true, [SkillType.Buff] = true, [SkillType.Cooldown] = true, [SkillType.InstantNoRepeatWhenHeld] = true, [SkillType.InstantShiftAttackForLeftMouse] = true, [SkillType.Triggerable] = true, }, - statDescriptionScope = "skill_stat_descriptions", - castTime = 0.8, - statMap = { - ["skill_empowers_next_x_spells_cast"] = { - mod("GhorrEmpoweredSpells", "BASE", nil), - }, - ["pact_skill_grant_damage_over_time_+%_final_to_spells"] = { - mod("GhorrPactDamage", "BASE", nil), - }, - }, - baseFlags = { - spell = true, - pact = true, - duration = true, - }, - constantStats = { - { "skill_empowers_next_x_spells_cast", 3 }, - { "pact_empower_limitation_specifier_for_stat_description", 1 }, - { "pact_skill_grant_damage_over_time_+%_final_to_spells", 40 }, - { "skill_grants_bloodrite_for_base_X_ms_on_use", 6000 }, - { "bloodrite_base_trigger_frequency_ms", 2000 }, - { "bloodrite_trigger_frequency_+%_per_stage", 150 }, - }, - stats = { - "base_deal_no_attack_damage", - "base_deal_no_spell_damage", - "base_deal_no_secondary_damage", - "cannot_cancel_skill_before_contact_point", - }, - levels = { - [1] = { cooldown = 4, levelRequirement = 24, storedUses = 1, cost = { Mana = 16, }, }, - [2] = { cooldown = 4, levelRequirement = 27, storedUses = 1, cost = { Mana = 17, }, }, - [3] = { cooldown = 4, levelRequirement = 30, storedUses = 1, cost = { Mana = 18, }, }, - [4] = { cooldown = 4, levelRequirement = 33, storedUses = 1, cost = { Mana = 19, }, }, - [5] = { cooldown = 4, levelRequirement = 36, storedUses = 1, cost = { Mana = 21, }, }, - [6] = { cooldown = 4, levelRequirement = 39, storedUses = 1, cost = { Mana = 22, }, }, - [7] = { cooldown = 4, levelRequirement = 42, storedUses = 1, cost = { Mana = 23, }, }, - [8] = { cooldown = 4, levelRequirement = 45, storedUses = 1, cost = { Mana = 24, }, }, - [9] = { cooldown = 4, levelRequirement = 48, storedUses = 1, cost = { Mana = 25, }, }, - [10] = { cooldown = 4, levelRequirement = 50, storedUses = 1, cost = { Mana = 26, }, }, - [11] = { cooldown = 4, levelRequirement = 52, storedUses = 1, cost = { Mana = 26, }, }, - [12] = { cooldown = 4, levelRequirement = 54, storedUses = 1, cost = { Mana = 27, }, }, - [13] = { cooldown = 4, levelRequirement = 56, storedUses = 1, cost = { Mana = 28, }, }, - [14] = { cooldown = 4, levelRequirement = 58, storedUses = 1, cost = { Mana = 29, }, }, - [15] = { cooldown = 4, levelRequirement = 60, storedUses = 1, cost = { Mana = 29, }, }, - [16] = { cooldown = 4, levelRequirement = 62, storedUses = 1, cost = { Mana = 30, }, }, - [17] = { cooldown = 4, levelRequirement = 64, storedUses = 1, cost = { Mana = 31, }, }, - [18] = { cooldown = 4, levelRequirement = 66, storedUses = 1, cost = { Mana = 31, }, }, - [19] = { cooldown = 4, levelRequirement = 68, storedUses = 1, cost = { Mana = 32, }, }, - [20] = { cooldown = 4, levelRequirement = 70, storedUses = 1, cost = { Mana = 33, }, }, - [21] = { cooldown = 4, levelRequirement = 72, storedUses = 1, cost = { Mana = 34, }, }, - [22] = { cooldown = 4, levelRequirement = 74, storedUses = 1, cost = { Mana = 34, }, }, - [23] = { cooldown = 4, levelRequirement = 76, storedUses = 1, cost = { Mana = 35, }, }, - [24] = { cooldown = 4, levelRequirement = 78, storedUses = 1, cost = { Mana = 36, }, }, - [25] = { cooldown = 4, levelRequirement = 80, storedUses = 1, cost = { Mana = 36, }, }, - [26] = { cooldown = 4, levelRequirement = 82, storedUses = 1, cost = { Mana = 37, }, }, - [27] = { cooldown = 4, levelRequirement = 84, storedUses = 1, cost = { Mana = 38, }, }, - [28] = { cooldown = 4, levelRequirement = 86, storedUses = 1, cost = { Mana = 39, }, }, - [29] = { cooldown = 4, levelRequirement = 88, storedUses = 1, cost = { Mana = 39, }, }, - [30] = { cooldown = 4, levelRequirement = 90, storedUses = 1, cost = { Mana = 40, }, }, - [31] = { cooldown = 4, levelRequirement = 91, storedUses = 1, cost = { Mana = 40, }, }, - [32] = { cooldown = 4, levelRequirement = 92, storedUses = 1, cost = { Mana = 41, }, }, - [33] = { cooldown = 4, levelRequirement = 93, storedUses = 1, cost = { Mana = 41, }, }, - [34] = { cooldown = 4, levelRequirement = 94, storedUses = 1, cost = { Mana = 42, }, }, - [35] = { cooldown = 4, levelRequirement = 95, storedUses = 1, cost = { Mana = 42, }, }, - [36] = { cooldown = 4, levelRequirement = 96, storedUses = 1, cost = { Mana = 42, }, }, - [37] = { cooldown = 4, levelRequirement = 97, storedUses = 1, cost = { Mana = 43, }, }, - [38] = { cooldown = 4, levelRequirement = 98, storedUses = 1, cost = { Mana = 43, }, }, - [39] = { cooldown = 4, levelRequirement = 99, storedUses = 1, cost = { Mana = 43, }, }, - [40] = { cooldown = 4, levelRequirement = 100, storedUses = 1, cost = { Mana = 44, }, }, - }, -} -skills["PactOfKtash"] = { - name = "Pact of K'Tash", - baseTypeName = "Pact of K'Tash", - flavourText = {"Unleash the fury of K'Tash, incarnation of hate.", "Endless bodies burn beneath the flames of his pursuit.", }, - color = 4, - description = "Cast a pact, Empowering your damaging Vaal spells but burdening you with an Affliction. Empowered Vaal spells deal more damage, have less soul gain prevention duration and regain consumed souls on use.", - skillTypes = { [SkillType.Spell] = true, [SkillType.Pact] = true, [SkillType.Buff] = true, [SkillType.Cooldown] = true, [SkillType.InstantNoRepeatWhenHeld] = true, [SkillType.InstantShiftAttackForLeftMouse] = true, [SkillType.Triggerable] = true, }, - statDescriptionScope = "skill_stat_descriptions", - castTime = 0.8, - statMap = { - ["skill_empowers_next_x_spells_cast"] = { - mod("KtashEmpoweredSpells", "BASE", nil), - }, - ["pact_skill_grant_damage_+%_final_to_exerted_skills"] = { - mod("KtashPactDamage", "BASE", nil), - }, - ["active_skill_osm_vaal_skill_soul_gain_prevention_+%_final_to_grant"] = { - mod("KtashPactSoulGainPrevention", "BASE", nil), - }, - }, - baseFlags = { - spell = true, - pact = true, - }, - qualityStats = { - { "base_cooldown_speed_+%", 0.5 }, - }, - constantStats = { - { "skill_empowers_next_x_spells_cast", 1 }, - { "pact_empower_limitation_specifier_for_stat_description", 2 }, - { "active_skill_osm_vaal_skill_soul_refund_chance_%_to_grant", 100 }, - { "active_skill_osm_vaal_skill_soul_gain_prevention_+%_final_to_grant", -50 }, - }, - stats = { - "pact_skill_grant_damage_+%_final_to_exerted_skills", - "base_deal_no_attack_damage", - "base_deal_no_spell_damage", - "base_deal_no_secondary_damage", - "cannot_cancel_skill_before_contact_point", - }, - levels = { - [1] = { 10, cooldown = 6, levelRequirement = 24, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 16, }, }, - [2] = { 11, cooldown = 6, levelRequirement = 27, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 17, }, }, - [3] = { 12, cooldown = 6, levelRequirement = 30, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 18, }, }, - [4] = { 13, cooldown = 6, levelRequirement = 33, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 19, }, }, - [5] = { 14, cooldown = 6, levelRequirement = 36, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 21, }, }, - [6] = { 15, cooldown = 6, levelRequirement = 39, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 22, }, }, - [7] = { 16, cooldown = 6, levelRequirement = 42, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 23, }, }, - [8] = { 17, cooldown = 6, levelRequirement = 45, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 24, }, }, - [9] = { 18, cooldown = 6, levelRequirement = 48, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 25, }, }, - [10] = { 19, cooldown = 6, levelRequirement = 50, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 26, }, }, - [11] = { 20, cooldown = 6, levelRequirement = 52, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 26, }, }, - [12] = { 21, cooldown = 6, levelRequirement = 54, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 27, }, }, - [13] = { 22, cooldown = 6, levelRequirement = 56, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 28, }, }, - [14] = { 23, cooldown = 6, levelRequirement = 58, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 29, }, }, - [15] = { 24, cooldown = 6, levelRequirement = 60, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 29, }, }, - [16] = { 25, cooldown = 6, levelRequirement = 62, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 30, }, }, - [17] = { 26, cooldown = 6, levelRequirement = 64, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 31, }, }, - [18] = { 27, cooldown = 6, levelRequirement = 66, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 31, }, }, - [19] = { 28, cooldown = 6, levelRequirement = 68, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 32, }, }, - [20] = { 29, cooldown = 6, levelRequirement = 70, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 33, }, }, - [21] = { 30, cooldown = 6, levelRequirement = 72, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 34, }, }, - [22] = { 31, cooldown = 6, levelRequirement = 74, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 34, }, }, - [23] = { 32, cooldown = 6, levelRequirement = 76, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 35, }, }, - [24] = { 33, cooldown = 6, levelRequirement = 78, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 36, }, }, - [25] = { 34, cooldown = 6, levelRequirement = 80, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 36, }, }, - [26] = { 35, cooldown = 6, levelRequirement = 82, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 37, }, }, - [27] = { 36, cooldown = 6, levelRequirement = 84, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 38, }, }, - [28] = { 37, cooldown = 6, levelRequirement = 86, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 39, }, }, - [29] = { 38, cooldown = 6, levelRequirement = 88, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 39, }, }, - [30] = { 39, cooldown = 6, levelRequirement = 90, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 40, }, }, - [31] = { 39, cooldown = 6, levelRequirement = 91, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 40, }, }, - [32] = { 40, cooldown = 6, levelRequirement = 92, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 41, }, }, - [33] = { 40, cooldown = 6, levelRequirement = 93, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 41, }, }, - [34] = { 41, cooldown = 6, levelRequirement = 94, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 42, }, }, - [35] = { 41, cooldown = 6, levelRequirement = 95, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 42, }, }, - [36] = { 42, cooldown = 6, levelRequirement = 96, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 42, }, }, - [37] = { 42, cooldown = 6, levelRequirement = 97, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 43, }, }, - [38] = { 43, cooldown = 6, levelRequirement = 98, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 43, }, }, - [39] = { 43, cooldown = 6, levelRequirement = 99, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 43, }, }, - [40] = { 44, cooldown = 6, levelRequirement = 100, storedUses = 1, statInterpolation = { 1, }, cost = { Mana = 44, }, }, - }, -} -skills["PactOfLycia"] = { - name = "Pact of Lycia", - baseTypeName = "Pact of Lycia", - flavourText = {"Call upon Lycia, conduit of the Scourge. She forfeit", "her soul for the power to condemn lies told by bones.", }, - color = 4, - description = "Cast a pact, Empowering the next channelling spells you cast yourself but burdening you with an Affliction. Empowered spells deal more damage and periodically trigger lightning bolts while channelling.", - skillTypes = { [SkillType.Spell] = true, [SkillType.Pact] = true, [SkillType.Buff] = true, [SkillType.Cooldown] = true, [SkillType.InstantNoRepeatWhenHeld] = true, [SkillType.InstantShiftAttackForLeftMouse] = true, [SkillType.Triggerable] = true, }, - statDescriptionScope = "skill_stat_descriptions", - castTime = 0.8, - statMap = { - ["skill_empowers_next_x_spells_cast"] = { - mod("LyciaEmpoweredSpells", "BASE", nil), - }, - ["pact_skill_grant_damage_+%_final_to_exerted_skills"] = { - mod("LyciaPactDamage", "BASE", nil), - }, - }, - baseFlags = { - spell = true, - pact = true, - }, - constantStats = { - { "skill_empowers_next_x_spells_cast", 3 }, - { "pact_empower_limitation_specifier_for_stat_description", 3 }, - { "pact_skill_grant_damage_+%_final_to_exerted_skills", 40 }, - }, - stats = { - "base_deal_no_attack_damage", - "base_deal_no_spell_damage", - "base_deal_no_secondary_damage", - "cannot_cancel_skill_before_contact_point", - }, - levels = { - [1] = { cooldown = 4, levelRequirement = 24, storedUses = 1, cost = { Mana = 16, }, }, - [2] = { cooldown = 4, levelRequirement = 27, storedUses = 1, cost = { Mana = 17, }, }, - [3] = { cooldown = 4, levelRequirement = 30, storedUses = 1, cost = { Mana = 18, }, }, - [4] = { cooldown = 4, levelRequirement = 33, storedUses = 1, cost = { Mana = 19, }, }, - [5] = { cooldown = 4, levelRequirement = 36, storedUses = 1, cost = { Mana = 21, }, }, - [6] = { cooldown = 4, levelRequirement = 39, storedUses = 1, cost = { Mana = 22, }, }, - [7] = { cooldown = 4, levelRequirement = 42, storedUses = 1, cost = { Mana = 23, }, }, - [8] = { cooldown = 4, levelRequirement = 45, storedUses = 1, cost = { Mana = 24, }, }, - [9] = { cooldown = 4, levelRequirement = 48, storedUses = 1, cost = { Mana = 25, }, }, - [10] = { cooldown = 4, levelRequirement = 50, storedUses = 1, cost = { Mana = 26, }, }, - [11] = { cooldown = 4, levelRequirement = 52, storedUses = 1, cost = { Mana = 26, }, }, - [12] = { cooldown = 4, levelRequirement = 54, storedUses = 1, cost = { Mana = 27, }, }, - [13] = { cooldown = 4, levelRequirement = 56, storedUses = 1, cost = { Mana = 28, }, }, - [14] = { cooldown = 4, levelRequirement = 58, storedUses = 1, cost = { Mana = 29, }, }, - [15] = { cooldown = 4, levelRequirement = 60, storedUses = 1, cost = { Mana = 29, }, }, - [16] = { cooldown = 4, levelRequirement = 62, storedUses = 1, cost = { Mana = 30, }, }, - [17] = { cooldown = 4, levelRequirement = 64, storedUses = 1, cost = { Mana = 31, }, }, - [18] = { cooldown = 4, levelRequirement = 66, storedUses = 1, cost = { Mana = 31, }, }, - [19] = { cooldown = 4, levelRequirement = 68, storedUses = 1, cost = { Mana = 32, }, }, - [20] = { cooldown = 4, levelRequirement = 70, storedUses = 1, cost = { Mana = 33, }, }, - [21] = { cooldown = 4, levelRequirement = 72, storedUses = 1, cost = { Mana = 34, }, }, - [22] = { cooldown = 4, levelRequirement = 74, storedUses = 1, cost = { Mana = 34, }, }, - [23] = { cooldown = 4, levelRequirement = 76, storedUses = 1, cost = { Mana = 35, }, }, - [24] = { cooldown = 4, levelRequirement = 78, storedUses = 1, cost = { Mana = 36, }, }, - [25] = { cooldown = 4, levelRequirement = 80, storedUses = 1, cost = { Mana = 36, }, }, - [26] = { cooldown = 4, levelRequirement = 82, storedUses = 1, cost = { Mana = 37, }, }, - [27] = { cooldown = 4, levelRequirement = 84, storedUses = 1, cost = { Mana = 38, }, }, - [28] = { cooldown = 4, levelRequirement = 86, storedUses = 1, cost = { Mana = 39, }, }, - [29] = { cooldown = 4, levelRequirement = 88, storedUses = 1, cost = { Mana = 39, }, }, - [30] = { cooldown = 4, levelRequirement = 90, storedUses = 1, cost = { Mana = 40, }, }, - [31] = { cooldown = 4, levelRequirement = 91, storedUses = 1, cost = { Mana = 40, }, }, - [32] = { cooldown = 4, levelRequirement = 92, storedUses = 1, cost = { Mana = 41, }, }, - [33] = { cooldown = 4, levelRequirement = 93, storedUses = 1, cost = { Mana = 41, }, }, - [34] = { cooldown = 4, levelRequirement = 94, storedUses = 1, cost = { Mana = 42, }, }, - [35] = { cooldown = 4, levelRequirement = 95, storedUses = 1, cost = { Mana = 42, }, }, - [36] = { cooldown = 4, levelRequirement = 96, storedUses = 1, cost = { Mana = 42, }, }, - [37] = { cooldown = 4, levelRequirement = 97, storedUses = 1, cost = { Mana = 43, }, }, - [38] = { cooldown = 4, levelRequirement = 98, storedUses = 1, cost = { Mana = 43, }, }, - [39] = { cooldown = 4, levelRequirement = 99, storedUses = 1, cost = { Mana = 43, }, }, - [40] = { cooldown = 4, levelRequirement = 100, storedUses = 1, cost = { Mana = 44, }, }, - }, -} skills["Ravenous"] = { name = "Ravenous", hidden = true, diff --git a/src/Export/Skills/other.txt b/src/Export/Skills/other.txt index f825bffd0c0..fb120be03ab 100644 --- a/src/Export/Skills/other.txt +++ b/src/Export/Skills/other.txt @@ -740,42 +740,6 @@ local skills, mod, flag, skill = ... fromItem = true, #mods -#skill PactOfBeidat -#mods - -#skill PactOfGhorr -#mods - -#skill TriggeredBloodrend -#mods - -#skill PactOfKtash -#mods - -#skill PactOfLycia -#mods - -#skill TriggeredHeavensScourge -#mods - -#skill Portal -#flags spell -#mods - -#skill QueensDemand - fromItem = true, -#mods - -#skill PrimalAegis - fromTree = true, -#mods - -#skill Pacify -#flags spell curse area duration hex - fromTree = true, -#baseMod skill("debuff", true) -#mods - #skill PactOfBeidat #flags spell pact statMap = { @@ -800,6 +764,10 @@ local skills, mod, flag, skill = ... }, #mods +#skill TriggeredBloodrend +#flags spell pact projectile +#mods + #skill PactOfKtash #flags spell pact statMap = { @@ -827,6 +795,28 @@ local skills, mod, flag, skill = ... }, #mods +#skill TriggeredHeavensScourge +#flags spell pact area +#mods + +#skill Portal +#flags spell +#mods + +#skill QueensDemand + fromItem = true, +#mods + +#skill PrimalAegis + fromTree = true, +#mods + +#skill Pacify +#flags spell curse area duration hex + fromTree = true, +#baseMod skill("debuff", true) +#mods + #skill Ravenous #flags spell fromTree = true, From 48ea653c2b9ad4d666150c041d59f07cb0b3a30e Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Wed, 5 Aug 2026 19:24:04 +1000 Subject: [PATCH 4/4] Simplify handling of pacts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor Pact calculations to use the modifiers exported by each Pact skill and share one uptime calculation. Add uptime-scaled Beidat coverage bonuses and K’Tash utility effects, support instant damaging Vaal skills, and prevent Lycia from affecting totem skills. Move damage restrictions into the skill stat mappings and add focused tests for Pact effects and eligibility. --- spec/System/TestSkills_spec.lua | 119 +++++++++++--------- src/Data/SkillStatMap.lua | 2 +- src/Data/Skills/other.lua | 20 +++- src/Export/Skills/other.txt | 20 +++- src/Modules/CalcOffence.lua | 189 ++++++++++++-------------------- src/Modules/CalcPerform.lua | 20 ---- src/Modules/CalcSections.lua | 41 +++---- src/Modules/ConfigOptions.lua | 5 + 8 files changed, 195 insertions(+), 221 deletions(-) diff --git a/spec/System/TestSkills_spec.lua b/spec/System/TestSkills_spec.lua index 0bb633d673e..b3c7963cafd 100644 --- a/spec/System/TestSkills_spec.lua +++ b/spec/System/TestSkills_spec.lua @@ -366,72 +366,89 @@ describe("TestSkills", function() assert.are.equals(1, nonDurationSkill.skillModList:Sum("BASE", nonDurationSkill.skillCfg, "NegatedBaseFlagTest")) end) - it("Test Empowered Spells - Pact of Beidat", function() - build.skillsTab:PasteSocketGroup("Fireball 20/0 1") - runCallback("OnFrame") - local baseDPS = build.calcsTab.calcsOutput.TotalDPS - - build.skillsTab:PasteSocketGroup("Pact of Beidat 1/0 1") - runCallback("OnFrame") - assert.True(build.calcsTab.calcsOutput.BeidatUpTimeRatio > 0) - assert.True(build.calcsTab.calcsOutput.TotalDPS > baseDPS) + it("applies Pact of Beidat coverage based on uptime", function() + for _, test in ipairs({ + { skill = "Fireball", output = "ProjectileCount", bonus = "BeidatAdditionalProjectiles", amount = 4 }, + { skill = "Arc", output = "ChainMax", bonus = "BeidatAdditionalBeamChains", amount = 4 }, + { skill = "Firestorm", bonus = "BeidatAdditionalCascades", amount = 5 }, + }) do + newBuild() + build.skillsTab:PasteSocketGroup(test.skill.." 20/0 1") + runCallback("OnFrame") + local baseOutput = test.output and build.calcsTab.mainOutput[test.output] + local baseDPS = build.calcsTab.mainOutput.TotalDPS + + build.skillsTab:PasteSocketGroup("Pact of Beidat 1/0 1") + runCallback("OnFrame") + local output = build.calcsTab.mainOutput + local expectedBonus = test.amount * output.BeidatUpTimeRatio / 100 + assert.are.near(expectedBonus, output[test.bonus], 10 ^ -9) + if test.output then + assert.are.near(baseOutput + expectedBonus, output[test.output], 10 ^ -9) + end + assert.is_true(output.TotalDPS > baseDPS) + end newBuild() - build.skillsTab:PasteSocketGroup("Vaal Arc 20/0 1\nPact of Beidat 1/0 1") + build.skillsTab:PasteSocketGroup("Fireball 20/0 1\nPact of Beidat 1/0 1") + build.configTab.input.pactMode = "MAX" + build.configTab:BuildModList() runCallback("OnFrame") - assert.True(build.calcsTab.calcsOutput.BeidatUpTimeRatio == nil) + assert.are.equals(4, build.calcsTab.mainOutput.BeidatAdditionalProjectiles) end) - it("Test Empowered Spells - Pact of Ghorr", function() - build.skillsTab:PasteSocketGroup("Creeping Frost 20/0 1") - runCallback("OnFrame") - local baseDPS = build.calcsTab.calcsOutput.TotalDotDPS - - build.skillsTab:PasteSocketGroup("Pact of Ghorr 1/0 1") - runCallback("OnFrame") - assert.True(build.calcsTab.calcsOutput.GhorrUpTimeRatio > 0) - assert.True(build.calcsTab.calcsOutput.TotalDotDPS > baseDPS) + it("applies Pact damage to eligible spells", function() + for _, test in ipairs({ + { skill = "Creeping Frost", pact = "Pact of Ghorr", uptime = "GhorrUpTimeRatio", dps = "TotalDotDPS" }, + { skill = "Lightning Tendrils", pact = "Pact of Lycia", uptime = "LyciaUpTimeRatio", dps = "TotalDPS" }, + }) do + newBuild() + build.skillsTab:PasteSocketGroup(test.skill.." 20/0 1") + runCallback("OnFrame") + local baseDPS = build.calcsTab.mainOutput[test.dps] + build.skillsTab:PasteSocketGroup(test.pact.." 1/0 1") + runCallback("OnFrame") + local output = build.calcsTab.mainOutput + assert.is_true(output[test.uptime] > 0) + assert.is_true(output[test.dps] > baseDPS) + end newBuild() - build.skillsTab:PasteSocketGroup("Vaal Arc 20/0 1\n Pact of Ghorr 1/0 1") + build.skillsTab:PasteSocketGroup("Lightning Tendrils 20/0 1\nSpell Totem 20/0 1") runCallback("OnFrame") - assert.True(build.calcsTab.calcsOutput.GhorrUpTimeRatio == nil) + local baseDPS = build.calcsTab.mainOutput.TotalDPS + build.skillsTab:PasteSocketGroup("Pact of Lycia 1/0 1") + runCallback("OnFrame") + assert.is_nil(build.calcsTab.mainOutput.LyciaUpTimeRatio) + assert.are.equals(baseDPS, build.calcsTab.mainOutput.TotalDPS) end) - it("Test Empowered Spells - Pact of K'Tash", function() - build.skillsTab:PasteSocketGroup("Vaal Arc 20/0 1") + it("applies Pact of K'Tash to instant damaging Vaal spells", function() + build.skillsTab:PasteSocketGroup("Vaal Righteous Fire 20/0 1") runCallback("OnFrame") - local baseDPS = build.calcsTab.calcsOutput.TotalDPS + local baseDPS = build.calcsTab.mainOutput.TotalDotDPS + local basePrevention = build.calcsTab.mainOutput.SoulGainPreventionDuration build.skillsTab:PasteSocketGroup("Pact of K'Tash 1/0 1") runCallback("OnFrame") - assert.True(build.calcsTab.calcsOutput.KtashUpTimeRatio > 0) - assert.True(build.calcsTab.calcsOutput.TotalDPS > baseDPS) - - newBuild() - build.skillsTab:PasteSocketGroup("Fireball 20/0 1\nPact of K'Tash 1/0 1") - runCallback("OnFrame") - assert.True(build.calcsTab.calcsOutput.KtashUpTimeRatio == nil) + local output = build.calcsTab.mainOutput + assert.are.equals(100, output.KtashUpTimeRatio) + assert.are.near(baseDPS * 1.1, output.TotalDotDPS, 10 ^ -6) + assert.are.equals(100, output.KtashSoulRefundChance) + assert.are.equals(math.max(math.ceil(basePrevention * 0.5 * data.misc.ServerTickRate), 1) / data.misc.ServerTickRate, output.SoulGainPreventionDuration) end) - it("Test Empowered Spells - Pact of Lycia", function() - build.skillsTab:PasteSocketGroup("Lightning Tendrils 20/0 1") - runCallback("OnFrame") - local baseDPS = build.calcsTab.calcsOutput.TotalDPS - - build.skillsTab:PasteSocketGroup("Pact of Lycia 1/0 1") - runCallback("OnFrame") - assert.True(build.calcsTab.calcsOutput.LyciaUpTimeRatio > 0) - assert.True(build.calcsTab.calcsOutput.TotalDPS > baseDPS) - - newBuild() - build.skillsTab:PasteSocketGroup("Fireball 20/0 1\nPact of Lycia 1/0 1") - runCallback("OnFrame") - assert.True(build.calcsTab.calcsOutput.LyciaUpTimeRatio == nil) - - newBuild() - build.skillsTab:PasteSocketGroup("Vaal Arc 20/0 1\nPact of Lycia 1/0 1") - runCallback("OnFrame") - assert.True(build.calcsTab.calcsOutput.LyciaUpTimeRatio == nil) + it("does not apply Pacts to ineligible spells", function() + for _, test in ipairs({ + { "Vaal Arc", "Pact of Beidat", "BeidatUpTimeRatio" }, + { "Vaal Arc", "Pact of Ghorr", "GhorrUpTimeRatio" }, + { "Fireball", "Pact of K'Tash", "KtashUpTimeRatio" }, + { "Fireball", "Pact of Lycia", "LyciaUpTimeRatio" }, + }) do + newBuild() + build.skillsTab:PasteSocketGroup(test[1].." 20/0 1\n"..test[2].." 1/0 1") + runCallback("OnFrame") + assert.is_nil(build.calcsTab.mainOutput[test[3]]) + end end) end) diff --git a/src/Data/SkillStatMap.lua b/src/Data/SkillStatMap.lua index fea75ce61ef..83cc8886c6b 100644 --- a/src/Data/SkillStatMap.lua +++ b/src/Data/SkillStatMap.lua @@ -2369,6 +2369,6 @@ return { -- Display only }, ["pact_empower_limitation_specifier_for_stat_description"] = { - -- display only + -- Display only }, } diff --git a/src/Data/Skills/other.lua b/src/Data/Skills/other.lua index 2a292ccfb05..2e4d7b41bc3 100644 --- a/src/Data/Skills/other.lua +++ b/src/Data/Skills/other.lua @@ -2782,8 +2782,17 @@ skills["PactOfBeidat"] = { ["skill_empowers_next_x_spells_cast"] = { mod("BeidatEmpoweredSpells", "BASE", nil), }, + ["pact_skill_additional_beam_only_chains"] = { + mod("BeidatAdditionalBeamChains", "BASE", nil), + }, + ["pact_skill_grant_x_additional_projectiles_fired_in_nova"] = { + mod("BeidatAdditionalProjectiles", "BASE", nil), + }, + ["pact_skill_grant_x_cascades_to_do_in_spiral"] = { + mod("BeidatAdditionalCascades", "BASE", nil), + }, ["pact_skill_damage_+%_final_with_hits_and_ailments_to_grant"] = { - mod("BeidatPactDamage", "BASE", nil), + mod("BeidatPactDamage", "LIST", { mod = mod("Damage", "MORE", nil, ModFlag.Spell, bit.bor(KeywordFlag.Hit, KeywordFlag.Ailment)) }), }, }, baseFlags = { @@ -2864,7 +2873,7 @@ skills["PactOfGhorr"] = { mod("GhorrEmpoweredSpells", "BASE", nil), }, ["pact_skill_grant_damage_over_time_+%_final_to_spells"] = { - mod("GhorrPactDamage", "BASE", nil), + mod("GhorrPactDamage", "LIST", { mod = mod("Damage", "MORE", nil, ModFlag.Dot) }), }, }, baseFlags = { @@ -3019,11 +3028,14 @@ skills["PactOfKtash"] = { mod("KtashEmpoweredSpells", "BASE", nil), }, ["pact_skill_grant_damage_+%_final_to_exerted_skills"] = { - mod("KtashPactDamage", "BASE", nil), + mod("KtashPactDamage", "LIST", { mod = mod("Damage", "MORE", nil) }), }, ["active_skill_osm_vaal_skill_soul_gain_prevention_+%_final_to_grant"] = { mod("KtashPactSoulGainPrevention", "BASE", nil), }, + ["active_skill_osm_vaal_skill_soul_refund_chance_%_to_grant"] = { + mod("KtashPactSoulRefundChance", "BASE", nil), + }, }, baseFlags = { spell = true, @@ -3102,7 +3114,7 @@ skills["PactOfLycia"] = { mod("LyciaEmpoweredSpells", "BASE", nil), }, ["pact_skill_grant_damage_+%_final_to_exerted_skills"] = { - mod("LyciaPactDamage", "BASE", nil), + mod("LyciaPactDamage", "LIST", { mod = mod("Damage", "MORE", nil) }), }, }, baseFlags = { diff --git a/src/Export/Skills/other.txt b/src/Export/Skills/other.txt index fb120be03ab..be8b414c1a9 100644 --- a/src/Export/Skills/other.txt +++ b/src/Export/Skills/other.txt @@ -746,8 +746,17 @@ local skills, mod, flag, skill = ... ["skill_empowers_next_x_spells_cast"] = { mod("BeidatEmpoweredSpells", "BASE", nil), }, + ["pact_skill_additional_beam_only_chains"] = { + mod("BeidatAdditionalBeamChains", "BASE", nil), + }, + ["pact_skill_grant_x_additional_projectiles_fired_in_nova"] = { + mod("BeidatAdditionalProjectiles", "BASE", nil), + }, + ["pact_skill_grant_x_cascades_to_do_in_spiral"] = { + mod("BeidatAdditionalCascades", "BASE", nil), + }, ["pact_skill_damage_+%_final_with_hits_and_ailments_to_grant"] = { - mod("BeidatPactDamage", "BASE", nil), + mod("BeidatPactDamage", "LIST", { mod = mod("Damage", "MORE", nil, ModFlag.Spell, bit.bor(KeywordFlag.Hit, KeywordFlag.Ailment)) }), }, }, #mods @@ -759,7 +768,7 @@ local skills, mod, flag, skill = ... mod("GhorrEmpoweredSpells", "BASE", nil), }, ["pact_skill_grant_damage_over_time_+%_final_to_spells"] = { - mod("GhorrPactDamage", "BASE", nil), + mod("GhorrPactDamage", "LIST", { mod = mod("Damage", "MORE", nil, ModFlag.Dot) }), }, }, #mods @@ -775,11 +784,14 @@ local skills, mod, flag, skill = ... mod("KtashEmpoweredSpells", "BASE", nil), }, ["pact_skill_grant_damage_+%_final_to_exerted_skills"] = { - mod("KtashPactDamage", "BASE", nil), + mod("KtashPactDamage", "LIST", { mod = mod("Damage", "MORE", nil) }), }, ["active_skill_osm_vaal_skill_soul_gain_prevention_+%_final_to_grant"] = { mod("KtashPactSoulGainPrevention", "BASE", nil), }, + ["active_skill_osm_vaal_skill_soul_refund_chance_%_to_grant"] = { + mod("KtashPactSoulRefundChance", "BASE", nil), + }, }, #mods @@ -790,7 +802,7 @@ local skills, mod, flag, skill = ... mod("LyciaEmpoweredSpells", "BASE", nil), }, ["pact_skill_grant_damage_+%_final_to_exerted_skills"] = { - mod("LyciaPactDamage", "BASE", nil), + mod("LyciaPactDamage", "LIST", { mod = mod("Damage", "MORE", nil) }), }, }, #mods diff --git a/src/Modules/CalcOffence.lua b/src/Modules/CalcOffence.lua index 28eb49d028e..164ff716669 100644 --- a/src/Modules/CalcOffence.lua +++ b/src/Modules/CalcOffence.lua @@ -2801,132 +2801,85 @@ function calcs.offence(env, actor, activeSkill) end end - -- Pacts // Empowered Spells - if activeSkill.skillTypes[SkillType.Spell] and not activeSkill.skillTypes[SkillType.Instant] and not activeSkill.skillTypes[SkillType.Brand] then - for index, value in ipairs(actor.activeSkillList) do - if not activeSkill.skillTypes[SkillType.Vaal] then - if value.activeEffect.grantedEffect.name == "Pact of Beidat" and not globalOutput.PactOfBeidatCalculated and not value.skillFlags.disable - and (skillFlags.projectile or skillFlags.area or skillFlags.chaining) and not activeSkill.skillTypes[SkillType.Channel] then - globalOutput.CreatePactOffensiveCalcSection = true - globalOutput.PactOfBeidatDuration = calcSkillDuration(value.skillModList, value.skillCfg, value.skillData, env, enemyDB) - globalOutput.PactOfBeidatCooldown = calcSkillCooldown(value.skillModList, value.skillCfg, value.skillData) - globalOutput.PactOfBeidatCastTime = modDB:Sum("BASE", nil, "BeidatBaseCastTime") - globalOutput.BeidatEmpoweredCount = skillModList:Sum("BASE", nil, "BeidatEmpoweredSpells") - local baseUptimeRatio = m_min((globalOutput.BeidatEmpoweredCount / globalOutput.Speed) / (globalOutput.PactOfBeidatCooldown + globalOutput.PactOfBeidatCastTime), 1) * 100 + -- Each Pact has different eligible spell types, but shares the same uptime calculation. + if activeSkill.skillTypes[SkillType.Spell] and not activeSkill.skillTypes[SkillType.Brand] then + for _, value in ipairs(actor.activeSkillList) do + local pactName = value.activeEffect.grantedEffect.name + local pactKey = pactName:match("^Pact of (.+)$") + if pactKey then + pactKey = pactKey == "K'Tash" and "Ktash" or pactKey + local isVaal = activeSkill.skillTypes[SkillType.Vaal] + local pactApplies = pactKey == "Beidat" and not isVaal and not activeSkill.skillTypes[SkillType.Channel] + and (skillFlags.projectile or activeSkill.skillTypes[SkillType.Cascadable] or skillFlags.chaining and not skillFlags.projectile) + or pactKey == "Ghorr" and not isVaal and activeSkill.skillTypes[SkillType.DamageOverTime] + or pactKey == "Ktash" and isVaal and (activeSkill.skillTypes[SkillType.Damage] or activeSkill.skillTypes[SkillType.DamageOverTime]) + or pactKey == "Lycia" and not isVaal and activeSkill.skillTypes[SkillType.Channel] and skillFlags.selfCast + local calculated = "PactOf"..pactKey.."Calculated" + if pactApplies and not globalOutput[calculated] and not value.skillFlags.disable then + local cooldown = calcSkillCooldown(value.skillModList, value.skillCfg, value.skillData) + local castRate = 1 / value.activeEffect.grantedEffect.castTime * calcLib.mod(value.skillModList, value.skillCfg, "Speed") * calcs.actionSpeedMod(actor) + local castTime = 1 / m_min(castRate, data.misc.ServerTickRate) + local count = value.skillModList:Sum("BASE", value.skillCfg, pactKey.."EmpoweredSpells") local storedUses = value.skillData.storedUses + value.skillModList:Sum("BASE", value.skillCfg, "AdditionalCooldownUses") - globalOutput.BeidatUpTimeRatio = m_min(100, baseUptimeRatio * storedUses) - if globalBreakdown then - globalBreakdown.BeidatUpTimeRatio = { } - t_insert(globalBreakdown.BeidatUpTimeRatio, s_format("(%d ^8(number of Empowered)", globalOutput.BeidatEmpoweredCount)) - t_insert(globalBreakdown.BeidatUpTimeRatio, s_format("/ %.2f) ^8(casts per second)", globalOutput.Speed)) - if globalOutput.PactOfBeidatCastTime > 0 then - t_insert(globalBreakdown.BeidatUpTimeRatio, s_format("/ (%.2f ^8(pact cooldown)", globalOutput.PactOfBeidatCooldown)) - t_insert(globalBreakdown.BeidatUpTimeRatio, s_format("+ %.2f) ^8(pact casttime)", globalOutput.PactOfBeidatCastTime)) - else - t_insert(globalBreakdown.BeidatUpTimeRatio, s_format("/ %.2f ^8(average pact cooldown)", globalOutput.PactOfBeidatCooldown)) - end - t_insert(globalBreakdown.BeidatUpTimeRatio, s_format("* %d ^8(stored uses)", storedUses)) - t_insert(globalBreakdown.BeidatUpTimeRatio, s_format("= %d%%", globalOutput.BeidatUpTimeRatio)) - end - local beidatDamage = modDB:Sum("BASE", nil, "BeidatPactDamage") - if beidatDamage > 0 then - local beidatUptime = skillModList:Flag(nil, "Condition:PactMaxHit") and 100 or globalOutput.BeidatUpTimeRatio - skillModList:NewMod("Damage", "MORE", beidatDamage * beidatUptime / 100, "Uptime Scaled Pact of Beidat", ModFlag.Spell, bor(KeywordFlag.Hit, KeywordFlag.Ailment)) - end - globalOutput.PactOfBeidatCalculated = true - end - if value.activeEffect.grantedEffect.name == "Pact of Ghorr" and not globalOutput.PactOfGhorrCalculated and not value.skillFlags.disable - and activeSkill.skillTypes[SkillType.DamageOverTime] then + local uptime = (globalOutput.Speed or 0) == 0 and 100 or m_min((count / globalOutput.Speed) / (cooldown + castTime), 1) * 100 + uptime = m_min(100, uptime * storedUses) + local effect = skillModList:Flag(nil, "Condition:PactMaxHit") and 100 or uptime + local effectMult = effect / 100 + globalOutput.CreatePactOffensiveCalcSection = true - globalOutput.PactOfGhorrDuration = calcSkillDuration(value.skillModList, value.skillCfg, value.skillData, env, enemyDB) - globalOutput.PactOfGhorrCooldown = calcSkillCooldown(value.skillModList, value.skillCfg, value.skillData) - globalOutput.PactOfGhorrCastTime = modDB:Sum("BASE", nil, "GhorrBaseCastTime") - globalOutput.GhorrEmpoweredCount = skillModList:Sum("BASE", nil, "GhorrEmpoweredSpells") - local baseUptimeRatio = m_min((globalOutput.GhorrEmpoweredCount / globalOutput.Speed) / (globalOutput.PactOfGhorrCooldown + globalOutput.PactOfGhorrCastTime), 1) * 100 - local storedUses = value.skillData.storedUses + value.skillModList:Sum("BASE", value.skillCfg, "AdditionalCooldownUses") - globalOutput.GhorrUpTimeRatio = m_min(100, baseUptimeRatio * storedUses) + globalOutput["PactOf"..pactKey.."Cooldown"] = cooldown + globalOutput["PactOf"..pactKey.."CastTime"] = castTime + globalOutput[pactKey.."EmpoweredCount"] = count + globalOutput[pactKey.."UpTimeRatio"] = uptime if globalBreakdown then - globalBreakdown.GhorrUpTimeRatio = { } - t_insert(globalBreakdown.GhorrUpTimeRatio, s_format("(%d ^8(number of Empowered)", globalOutput.GhorrEmpoweredCount)) - t_insert(globalBreakdown.GhorrUpTimeRatio, s_format("/ %.2f) ^8(casts per second)", globalOutput.Speed)) - if globalOutput.PactOfGhorrCastTime > 0 then - t_insert(globalBreakdown.GhorrUpTimeRatio, s_format("/ (%.2f ^8(pact cooldown)", globalOutput.PactOfGhorrCooldown)) - t_insert(globalBreakdown.GhorrUpTimeRatio, s_format("+ %.2f) ^8(pact casttime)", globalOutput.PactOfGhorrCastTime)) - else - t_insert(globalBreakdown.GhorrUpTimeRatio, s_format("/ %.2f ^8(average pact cooldown)", globalOutput.PactOfGhorrCooldown)) - end - t_insert(globalBreakdown.GhorrUpTimeRatio, s_format("* %d ^8(stored uses)", storedUses)) - t_insert(globalBreakdown.GhorrUpTimeRatio, s_format("= %d%%", globalOutput.GhorrUpTimeRatio)) + globalBreakdown[pactKey.."UpTimeRatio"] = { + s_format("(%.2f ^8(number of Empowered)", count), + s_format("/ %.2f) ^8(casts per second)", globalOutput.Speed or 0), + s_format("/ (%.2f ^8(pact cooldown)", cooldown), + s_format("+ %.2f) ^8(pact cast time)", castTime), + s_format("* %.2f ^8(stored uses)", storedUses), + s_format("= %.2f%%", uptime), + } end - local ghorrDamage = modDB:Sum("BASE", nil, "GhorrPactDamage") - if ghorrDamage > 0 then - local ghorrUptime = skillModList:Flag(nil, "Condition:PactMaxHit") and 100 or globalOutput.GhorrUpTimeRatio - skillModList:NewMod("Damage", "MORE", ghorrDamage * ghorrUptime / 100, "Uptime Scaled Pact of Ghorr", ModFlag.Dot) + + local damage = value.skillModList:List(value.skillCfg, pactKey.."PactDamage")[1] + if damage then + local mod = damage.mod + skillModList:NewMod(mod.name, mod.type, mod.value * effectMult, "Uptime Scaled "..pactName, mod.flags, mod.keywordFlags, unpack(mod)) end - globalOutput.PactOfGhorrCalculated = true - end - if value.activeEffect.grantedEffect.name == "Pact of Lycia" and not globalOutput.PactOfLyciaCalculated and not value.skillFlags.disable - and activeSkill.skillTypes[SkillType.Channel] then - globalOutput.CreatePactOffensiveCalcSection = true - globalOutput.PactOfLyciaDuration = calcSkillDuration(value.skillModList, value.skillCfg, value.skillData, env, enemyDB) - globalOutput.PactOfLyciaCooldown = calcSkillCooldown(value.skillModList, value.skillCfg, value.skillData) - globalOutput.PactOfLyciaCastTime = modDB:Sum("BASE", nil, "LyciaBaseCastTime") - globalOutput.LyciaEmpoweredCount = skillModList:Sum("BASE", nil, "LyciaEmpoweredSpells") - local baseUptimeRatio = m_min((globalOutput.LyciaEmpoweredCount / globalOutput.Speed) / (globalOutput.PactOfLyciaCooldown + globalOutput.PactOfLyciaCastTime), 1) * 100 - local storedUses = value.skillData.storedUses + value.skillModList:Sum("BASE", value.skillCfg, "AdditionalCooldownUses") - globalOutput.LyciaUpTimeRatio = m_min(100, baseUptimeRatio * storedUses) - if globalBreakdown then - globalBreakdown.LyciaUpTimeRatio = { } - t_insert(globalBreakdown.LyciaUpTimeRatio, s_format("(%d ^8(number of Empowered)", globalOutput.LyciaEmpoweredCount)) - t_insert(globalBreakdown.LyciaUpTimeRatio, s_format("/ %.2f) ^8(casts per second)", globalOutput.Speed)) - if globalOutput.PactOfLyciaCastTime > 0 then - t_insert(globalBreakdown.LyciaUpTimeRatio, s_format("/ (%.2f ^8(pact cooldown)", globalOutput.PactOfLyciaCooldown)) - t_insert(globalBreakdown.LyciaUpTimeRatio, s_format("+ %.2f) ^8(pact casttime)", globalOutput.PactOfLyciaCastTime)) - else - t_insert(globalBreakdown.LyciaUpTimeRatio, s_format("/ %.2f ^8(average pact cooldown)", globalOutput.PactOfLyciaCooldown)) + + if pactKey == "Beidat" then + -- Coverage bonuses are averaged with the same uptime as the damage bonus. + if skillFlags.projectile and not skillModList:Flag(skillCfg, "NoAdditionalProjectiles") and not skillModList:Flag(skillCfg, "SingleProjectile") then + globalOutput.BeidatAdditionalProjectiles = value.skillModList:Sum("BASE", value.skillCfg, "BeidatAdditionalProjectiles") * effectMult + globalOutput.ProjectileCount = globalOutput.ProjectileCount + globalOutput.BeidatAdditionalProjectiles end - t_insert(globalBreakdown.LyciaUpTimeRatio, s_format("* %d ^8(stored uses)", storedUses)) - t_insert(globalBreakdown.LyciaUpTimeRatio, s_format("= %d%%", globalOutput.LyciaUpTimeRatio)) - end - local lyciaDamage = modDB:Sum("BASE", nil, "LyciaPactDamage") - if lyciaDamage > 0 then - local lyciaUptime = skillModList:Flag(nil, "Condition:PactMaxHit") and 100 or globalOutput.LyciaUpTimeRatio - skillModList:NewMod("Damage", "MORE", lyciaDamage * lyciaUptime / 100, "Uptime Scaled Pact of Lycia", ModFlag.Spell) - end - globalOutput.PactOfLyciaCalculated = true - end - else - if value.activeEffect.grantedEffect.name == "Pact of K'Tash" and not globalOutput.PactOfKtashCalculated and not value.skillFlags.disable then - globalOutput.CreatePactOffensiveCalcSection = true - globalOutput.PactOfKtashDuration = calcSkillDuration(value.skillModList, value.skillCfg, value.skillData, env, enemyDB) - globalOutput.PactOfKtashCooldown = calcSkillCooldown(value.skillModList, value.skillCfg, value.skillData) - globalOutput.PactOfKtashCastTime = modDB:Sum("BASE", nil, "KtashBaseCastTime") - globalOutput.KtashEmpoweredCount = skillModList:Sum("BASE", nil, "KtashEmpoweredSpells") - local baseUptimeRatio = m_min((globalOutput.KtashEmpoweredCount / globalOutput.Speed) / (globalOutput.PactOfKtashCooldown + globalOutput.PactOfKtashCastTime), 1) * 100 - local storedUses = value.skillData.storedUses + value.skillModList:Sum("BASE", value.skillCfg, "AdditionalCooldownUses") - globalOutput.KtashUpTimeRatio = m_min(100, baseUptimeRatio * storedUses) - if globalBreakdown then - globalBreakdown.KtashUpTimeRatio = { } - t_insert(globalBreakdown.KtashUpTimeRatio, s_format("(%d ^8(number of Empowered)", globalOutput.KtashEmpoweredCount)) - t_insert(globalBreakdown.KtashUpTimeRatio, s_format("/ %.2f) ^8(casts per second)", globalOutput.Speed)) - if globalOutput.PactOfKtashCastTime > 0 then - t_insert(globalBreakdown.KtashUpTimeRatio, s_format("/ (%.2f ^8(pact cooldown)", globalOutput.PactOfKtashCooldown)) - t_insert(globalBreakdown.KtashUpTimeRatio, s_format("+ %.2f) ^8(pact casttime)", globalOutput.PactOfKtashCastTime)) - else - t_insert(globalBreakdown.KtashUpTimeRatio, s_format("/ %.2f ^8(average pact cooldown)", globalOutput.PactOfKtashCooldown)) + if skillFlags.chaining and not skillFlags.projectile and not skillModList:Flag(skillCfg, "CannotChain") and not skillModList:Flag(skillCfg, "NoAdditionalChains") then + globalOutput.BeidatAdditionalBeamChains = value.skillModList:Sum("BASE", value.skillCfg, "BeidatAdditionalBeamChains") * effectMult + globalOutput.ChainMax = globalOutput.ChainMax + globalOutput.BeidatAdditionalBeamChains + globalOutput.ChainMaxString = globalOutput.ChainMax + globalOutput.ChainRemaining = m_max(0, globalOutput.ChainMax - globalOutput.Chain) + end + if activeSkill.skillTypes[SkillType.Cascadable] then + globalOutput.BeidatAdditionalCascades = value.skillModList:Sum("BASE", value.skillCfg, "BeidatAdditionalCascades") * effectMult + end + elseif pactKey == "Ktash" then + globalOutput.KtashSoulRefundChance = value.skillModList:Sum("BASE", value.skillCfg, "KtashPactSoulRefundChance") * effectMult + if globalOutput.SoulGainPreventionDuration then + local duration = globalOutput.SoulGainPreventionDuration + local durationMod = 1 + value.skillModList:Sum("BASE", value.skillCfg, "KtashPactSoulGainPrevention") * effectMult / 100 + globalOutput.SoulGainPreventionDuration = m_max(m_ceil(duration * durationMod * data.misc.ServerTickRate), 1) / data.misc.ServerTickRate + if globalBreakdown then + globalBreakdown.SoulGainPreventionDuration = { + s_format("%.3fs ^8(before Pact of K'Tash)", duration), + s_format("x %.4f ^8(average Pact modifier)", durationMod), + "rounded up to nearest server tick", + s_format("= %.3fs", globalOutput.SoulGainPreventionDuration), + } + end end - t_insert(globalBreakdown.KtashUpTimeRatio, s_format("* %d ^8(stored uses)", storedUses)) - t_insert(globalBreakdown.KtashUpTimeRatio, s_format("= %d%%", globalOutput.KtashUpTimeRatio)) - end - local ktashDamage = modDB:Sum("BASE", nil, "KtashPactDamage") - local ktashSoulGainPrevention = modDB:Sum("BASE", nil, "KtashPactSoulGainPrevention") - if ktashDamage > 0 then - local ktashUptime = skillModList:Flag(nil, "Condition:PactMaxHit") and 100 or globalOutput.KtashUpTimeRatio - skillModList:NewMod("Damage", "MORE", ktashDamage * ktashUptime / 100, "Uptime Scaled Pact of Ktash", ModFlag.Spell) - -- happens too late, duration is calculated earlier - -- skillModList:NewMod("SoulGainPreventionDuration", "MORE", KtashSoulGainPrevention * KtashUptime / 100, "Uptime Scaled Pact of Ktash") end - globalOutput.PactOfKtashCalculated = true + globalOutput[calculated] = true end end end diff --git a/src/Modules/CalcPerform.lua b/src/Modules/CalcPerform.lua index 2340099a9b1..9217e3b8d76 100644 --- a/src/Modules/CalcPerform.lua +++ b/src/Modules/CalcPerform.lua @@ -1434,26 +1434,6 @@ function calcs.perform(env, skipEHP) modDB:NewMod("SeismicActive", "FLAG", true) -- Prevents effect from applying multiple times end end - local function calcCastTime(baseSpeed, skillModList, skillCfg, actor) - local castTime = baseSpeed / (calcLib.mod(skillModList, skillCfg, "Speed") * calcs.actionSpeedMod(actor)) - castTime = m_min(castTime, data.misc.ServerTickRate) - return castTime - end - if env.mode_buffs and activeSkill.skillFlags.pact then - for _, pactName in ipairs({ "Beidat", "Ghorr", "Ktash", "Lycia" }) do - if (activeSkill.activeEffect.grantedEffect.name == (("Pact of "..pactName)) or - activeSkill.activeEffect.grantedEffect.name == "Pact of K'Tash" and pactName == "Ktash") - and not modDB:Flag(nil, pactName.."Active") then - env.player.modDB:NewMod(pactName.."PactDamage", "BASE", activeSkill.skillModList:Sum("BASE", env.player.mainSkill.skillCfg, pactName.."PactDamage")) - env.player.modDB:NewMod(pactName.."EmpoweredSpells", "BASE", activeSkill.skillModList:Sum("BASE", env.player.mainSkill.skillCfg, pactName.."EmpoweredSpells")) - env.player.modDB:NewMod(pactName.."BaseCastTime", "BASE", calcCastTime(activeSkill.activeEffect.grantedEffect.castTime, activeSkill.skillModList, activeSkill.skillCfg, env.player)) - if pactName == "Ktash" then - env.player.modDB:NewMod("KtashPactSoulGainPrevention", "BASE", activeSkill.skillModList:Sum("BASE", env.player.mainSkill.skillCfg, "KtashPactSoulGainPrevention")) - end - modDB:NewMod(pactName.."Active", "FLAG", true) -- Prevents effect from applying multiple times - end - end - end if activeSkill.skillData.triggeredOnDeath and not activeSkill.skillFlags.minion then activeSkill.skillData.triggered = true for _, value in ipairs(activeSkill.skillModList:Tabulate("INC", env.player.mainSkill.skillCfg, "TriggeredDamage")) do diff --git a/src/Modules/CalcSections.lua b/src/Modules/CalcSections.lua index 51878cb2453..599b5974c77 100644 --- a/src/Modules/CalcSections.lua +++ b/src/Modules/CalcSections.lua @@ -432,39 +432,33 @@ return { colWidth = 114, { haveOutput = "CreatePactOffensiveCalcSection", - { format = "Effective Impact" }, - { format = "Avg Dmg Effect" }, { format = "Uptime" }, { format = "# of Empowered" }, - { format = "Max 1-Hit Impact" }, + { format = "Projectiles" }, + { format = "Beam Chains" }, + { format = "Cascades" }, }, { label = "Pact of Beidat", haveOutput = "BeidatUpTimeRatio", - { format = "" }, - { format = "" }, - { format = "{0:output:BeidatUpTimeRatio}%", { breakdown = "BeidatUpTimeRatio" }, }, - { format = "{0:output:BeidatEmpoweredCount}" }, - { format = "" }, + { format = "{2:output:BeidatUpTimeRatio}%", { breakdown = "BeidatUpTimeRatio" }, }, + { format = "{2:output:BeidatEmpoweredCount}" }, + { format = "+{2:output:BeidatAdditionalProjectiles}" }, + { format = "+{2:output:BeidatAdditionalBeamChains}" }, + { format = "+{2:output:BeidatAdditionalCascades}" }, }, { label = "Pact of Ghorr", haveOutput = "GhorrUpTimeRatio", - { format = "" }, - { format = "" }, - { format = "{0:output:GhorrUpTimeRatio}%", { breakdown = "GhorrUpTimeRatio" }, }, - { format = "{0:output:GhorrEmpoweredCount}" }, - { format = "" }, + { format = "{2:output:GhorrUpTimeRatio}%", { breakdown = "GhorrUpTimeRatio" }, }, + { format = "{2:output:GhorrEmpoweredCount}" }, + { format = "" }, { format = "" }, { format = "" }, }, { label = "Pact of K'Tash", haveOutput = "KtashUpTimeRatio", - { format = "" }, - { format = "" }, - { format = "{0:output:KtashUpTimeRatio}%", { breakdown = "KtashUpTimeRatio" }, }, - { format = "{0:output:KtashEmpoweredCount}" }, - { format = "" }, + { format = "{2:output:KtashUpTimeRatio}%", { breakdown = "KtashUpTimeRatio" }, }, + { format = "{2:output:KtashEmpoweredCount}" }, + { format = "" }, { format = "" }, { format = "" }, }, { label = "Pact of Lycia", haveOutput = "LyciaUpTimeRatio", - { format = "" }, - { format = "" }, - { format = "{0:output:LyciaUpTimeRatio}%", { breakdown = "LyciaUpTimeRatio" }, }, - { format = "{0:output:LyciaEmpoweredCount}" }, - { format = "" }, + { format = "{2:output:LyciaUpTimeRatio}%", { breakdown = "LyciaUpTimeRatio" }, }, + { format = "{2:output:LyciaEmpoweredCount}" }, + { format = "" }, { format = "" }, { format = "" }, }, } } } }, @@ -710,6 +704,7 @@ return { { label = "Aura Duration", haveOutput = "AuraDuration", { format = "{3:output:AuraDuration}s", { breakdown = "AuraDuration" }, }, }, { label = "Reserve Duration", haveOutput = "ReserveDuration", { format = "{3:output:ReserveDuration}s", { breakdown = "ReserveDuration" }, }, }, { label = "Soul Gain Prevent.", haveOutput = "SoulGainPreventionDuration", { format = "{3:output:SoulGainPreventionDuration}s", { breakdown = "SoulGainPreventionDuration" }, }, }, + { label = "Soul Refund Chance", haveOutput = "KtashSoulRefundChance", { format = "{2:output:KtashSoulRefundChance}%" }, }, { label = "Uptime", haveOutput = "DurationUptime", { format = "{2:output:DurationUptime}%", { breakdown = "DurationUptime" }, }, }, { label = "Secondary Uptime", haveOutput = "DurationSecondaryUptime", { format = "{2:output:DurationSecondaryUptime}%", { breakdown = "DurationSecondaryUptime" }, }, }, { label = "Tertiary Uptime", haveOutput = "DurationTertiaryUptime", { format = "{2:output:DurationTertiaryUptime}%", { breakdown = "DurationTertiaryUptime" }, }, }, diff --git a/src/Modules/ConfigOptions.lua b/src/Modules/ConfigOptions.lua index 36025fcc534..2a4b9df8ee8 100644 --- a/src/Modules/ConfigOptions.lua +++ b/src/Modules/ConfigOptions.lua @@ -237,6 +237,11 @@ return { modList:NewMod("Condition:WarcryMaxHit", "FLAG", true, "Config") end end }, + { var = "pactMode", type = "list", label = "Pact calc mode:", ifSkill = { "Pact of Beidat", "Pact of Ghorr", "Pact of K'Tash", "Pact of Lycia" }, tooltip = "Controls how Empowered spells from Pacts are calculated:\nAverage: Averages the effect based on Pact uptime.\nMax Hit: Applies the full effect to one Empowered spell.", list = {{val="AVERAGE",label="Average"},{val="MAX",label="Max Hit"}}, apply = function(val, modList, enemyModList) + if val == "MAX" then + modList:NewMod("Condition:PactMaxHit", "FLAG", true, "Config") + end + end }, { var = "EVBypass", type = "check", label = "Disable Emperor's Vigilance Bypass", ifCond = "EVBypass", apply = function(val, modList, enemyModList) modList:NewMod("Condition:EVBypass", "FLAG", true, "Config") end },