Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions spec/System/TestSkills_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -909,6 +909,77 @@ describe("TestSkills", function()
assert.are.equals(warcryFirstDps, supportFirstDps)
end)

it("support-granted active skills inherit from the supported skill regardless of meta gem order", function()
local expectedDamage
for _, socketGroupText in ipairs({
"Cast on Critical 10/0 1\nDespair 20/0 1\nDoedre's Undoing 1/0 1",
"Despair 20/0 1\nCast on Critical 10/0 1\nDoedre's Undoing 1/0 1",
"Doedre's Undoing 1/0 1\nCast on Critical 10/0 1\nDespair 20/0 1",
}) do
newBuild()
build.skillsTab:PasteSocketGroup(socketGroupText)
local socketGroup = build.skillsTab.socketGroupList[#build.skillsTab.socketGroupList]
recalculate()
local darkConsequences = selectActiveSkillById(socketGroup, "ChaosFrogExplosionPlayer")
assert.is_not_nil(darkConsequences)
assert.are.equals(20, darkConsequences.activeEffect.level)
assert.are.equals(20, build.calcsTab.mainOutput.GemLevel)
assert.are.equals(20, build.calcsTab.calcsOutput.GemLevel)
local baseDamage = build.calcsTab.mainOutput.AverageDamage
assert.True(baseDamage > 0)
expectedDamage = expectedDamage or baseDamage
assert.are.equals(expectedDamage, baseDamage)

-- Inherit the curse's final level, including supports and global bonuses,
-- without adding the support gem's own matching chaos level bonus again.
newBuild()
build.skillsTab:PasteSocketGroup(socketGroupText .. "\nChaos Mastery 1/0 1")
socketGroup = build.skillsTab.socketGroupList[#build.skillsTab.socketGroupList]
build.configTab.input.customMods = "+2 to Level of all Chaos Skills"
build.configTab:BuildModList()
recalculate()
darkConsequences = selectActiveSkillById(socketGroup, "ChaosFrogExplosionPlayer")
assert.are.equals(23, darkConsequences.activeEffect.level)
assert.are.equals(23, build.calcsTab.mainOutput.GemLevel)
assert.are.equals(23, build.calcsTab.calcsOutput.GemLevel)
assert.True(build.calcsTab.mainOutput.AverageDamage > baseDamage)

local calcFunc, calcBase = build.calcsTab:GetMiscCalculator()
assert.are.equals(23, calcBase.GemLevel)
assert.are.equals(23, calcFunc().GemLevel)
end
end)

it("support-granted active skills inherit tree gem levels from the linked skill", function()
build.skillsTab:PasteSocketGroup("Despair 20/0 1\nDoedre's Undoing 1/0 1")
local socketGroup = build.skillsTab.socketGroupList[#build.skillsTab.socketGroupList]
recalculate()
local darkConsequences = selectActiveSkillById(socketGroup, "ChaosFrogExplosionPlayer")
assert.is_not_nil(darkConsequences)
assert.are.equals(20, darkConsequences.activeEffect.level)
assert.are.equals(20, build.calcsTab.mainOutput.GemLevel)
local baseDamage = build.calcsTab.mainOutput.AverageDamage

local chaosMasteryNode = build.spec.nodes[63074]
assert.are.equals("Dark Entries", chaosMasteryNode.dn)
assert.are.equals("+1 to Level of all Chaos Skills", chaosMasteryNode.sd[1])
chaosMasteryNode.alloc = true
build.spec.allocNodes[chaosMasteryNode.id] = chaosMasteryNode
recalculate()

darkConsequences = selectActiveSkillById(socketGroup, "ChaosFrogExplosionPlayer")
-- the support gem also matches "all Chaos Skills", but its own gem level modifiers
-- must not stack on top of the level inherited from Despair
assert.are.equals(21, darkConsequences.activeEffect.level)
assert.are.equals(21, build.calcsTab.mainOutput.GemLevel)
assert.True(build.calcsTab.mainOutput.AverageDamage > baseDamage)

local calcFunc, calcBase = build.calcsTab:GetMiscCalculator()
local withoutNode = calcFunc({ removeNodes = { [chaosMasteryNode] = true } })
assert.are.equals(21, calcBase.GemLevel)
assert.are.equals(20, withoutNode.GemLevel)
assert.True(withoutNode.AverageDamage < calcBase.AverageDamage)
end)
it("Flame Breath attack speed scales DPS and is not capped by its channel cooldown", function()
build.itemsTab:CreateDisplayItemFromRaw([[
New Item
Expand Down
26 changes: 21 additions & 5 deletions src/Modules/CalcActiveSkill.lua
Original file line number Diff line number Diff line change
Expand Up @@ -437,11 +437,25 @@ function calcs.buildActiveSkillModList(env, activeSkill)
skillFlags = activeEffect.statSet.skillFlags
end
-- Active skills granted by support gems inherit the level of the skill that support applied to.
local supportGrantedInheritedLevel
if activeEffect.gemData and activeEffect.gemData.grantedEffect.support then
for _, supportEffect in ipairs(activeSkill.supportList) do
if supportEffect.srcInstance == activeEffect.srcInstance and supportEffect.activeSkillLevel then
activeEffect.level = supportEffect.activeSkillLevel
break
for _, skill in ipairs(env.player.activeSkillList) do
local effect = skill.activeEffect
if effect ~= activeEffect and effect.level and skill.socketGroup == activeSkill.socketGroup
and not (effect.gemData and effect.gemData.grantedEffect.support) then
-- A meta group can contain unrelated active skills. Only inherit from
-- a skill to which this specific support instance was applied.
for _, supportEffect in ipairs(skill.effectList) do
if supportEffect.grantedEffect.support and supportEffect.srcInstance == activeEffect.srcInstance then
supportGrantedInheritedLevel = effect.level
activeEffect.level = effect.level
activeSkill.skillData.inheritsGemLevel = true
break
end
end
if supportGrantedInheritedLevel then
break
end
end
end
end
Expand Down Expand Up @@ -770,7 +784,9 @@ function calcs.buildActiveSkillModList(env, activeSkill)
end

-- Apply gem/quality modifiers from support gems
skillModList:NewMod("GemLevel", "BASE", activeSkill.activeEffect.srcInstance and activeSkill.activeEffect.srcInstance.level or activeSkill.activeEffect.level, "Max Level")
local gemMaxLevel = supportGrantedInheritedLevel or (activeSkill.activeEffect.srcInstance and activeSkill.activeEffect.srcInstance.level) or activeSkill.activeEffect.level
local gemMaxLevelSource = supportGrantedInheritedLevel and "Inherited Max Level" or "Max Level"
skillModList:NewMod("GemLevel", "BASE", gemMaxLevel, gemMaxLevelSource)
if activeSkill.activeEffect.srcInstance and activeSkill.activeEffect.srcInstance.corrupted and not (activeSkill.activeEffect.srcInstance.fromItem or activeSkill.activeEffect.srcInstance.fromTree or activeSkill.activeEffect.grantedEffect.fromItem or activeSkill.activeEffect.grantedEffect.fromTree) then
skillModList:NewMod("GemCorruptionLevel", "BASE", activeSkill.activeEffect.srcInstance.corruptLevel, "Corruption")
activeSkill.skillCfg.skillCond["GemCorrupted"] = true
Expand Down
26 changes: 15 additions & 11 deletions src/Modules/CalcPerform.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3658,22 +3658,26 @@ function calcs.perform(env, skipEHP)
local totalSupportLevel = mainSkill.skillModList:Sum("BASE", mainSkill.skillCfg, "GemSupportLevel")
local totalCorruptionLevel = mainSkill.skillModList:Sum("BASE", mainSkill.skillCfg, "GemCorruptionLevel")

local inheritsLevel = mainSkill.skillData.inheritsGemLevel
output.GemHasLevel = true
output.GemLevel = m_max(baseLevel + totalSupportLevel + totalItemLevel + totalCorruptionLevel, 1)
output.GemLevel = m_max(inheritsLevel and baseLevel or (baseLevel + totalSupportLevel + totalItemLevel + totalCorruptionLevel), 1)

if env.player.breakdown then
env.player.breakdown.GemLevel = {}
t_insert(env.player.breakdown.GemLevel, s_format("%d ^8(level from gem)", baseLevel))
if totalSupportLevel > 0 then
t_insert(env.player.breakdown.GemLevel, s_format("+ %d ^8(level from support)", totalSupportLevel))
end
if totalItemLevel > 0 then
t_insert(env.player.breakdown.GemLevel, s_format("+ %d ^8(level from items)", totalItemLevel))
end
if totalCorruptionLevel > 0 then
t_insert(env.player.breakdown.GemLevel, s_format("+ %d ^8(level from corruption)", totalCorruptionLevel))
elseif totalCorruptionLevel < 0 then
t_insert(env.player.breakdown.GemLevel, s_format("%d ^8(level from corruption)", totalCorruptionLevel))
-- support gems inherit levels from the parent gem, which means showing level modifiers is pointless
if not inheritsLevel then
if totalSupportLevel > 0 then
t_insert(env.player.breakdown.GemLevel, s_format("+ %d ^8(level from support)", totalSupportLevel))
end
if totalItemLevel > 0 then
t_insert(env.player.breakdown.GemLevel, s_format("+ %d ^8(level from items)", totalItemLevel))
end
if totalCorruptionLevel > 0 then
t_insert(env.player.breakdown.GemLevel, s_format("+ %d ^8(level from corruption)", totalCorruptionLevel))
elseif totalCorruptionLevel < 0 then
t_insert(env.player.breakdown.GemLevel, s_format("%d ^8(level from corruption)", totalCorruptionLevel))
end
end
t_insert(env.player.breakdown.GemLevel, s_format("= %d", output.GemLevel))
end
Expand Down
2 changes: 1 addition & 1 deletion src/Modules/CalcSections.lua
Original file line number Diff line number Diff line change
Expand Up @@ -698,7 +698,7 @@ return {
} }
} },
{ 1, "SkillTypeStats", 1, colorCodes.OFFENCE, {{ defaultCollapsed = false, label = "Skill type-specific Stats", data = {
{ label = "Gem Level", notFlag = "thorns", haveOutput = "GemHasLevel", { format = "{0:output:GemLevel}", { breakdown = "GemLevel" }, { modName = { "GemLevel" }, cfg = "skill" },{ modName = { "GemSupportLevel" }, cfg = "skill" }, { modName = { "GemItemLevel" }, cfg = "skill" }, { modName = { "GemCorruptionLevel" }, cfg = "skill" } }, },
{ label = "Gem Level", notFlag = "thorns", haveOutput = "GemHasLevel", { format = "{0:output:GemLevel}", { breakdown = "GemLevel" }, { modName = { "GemLevel" }, cfg = "skill" }, { modName = { "GemSupportLevel" }, cfg = "skill", notSkillData = "inheritsGemLevel" }, { modName = { "GemItemLevel" }, cfg = "skill", notSkillData = "inheritsGemLevel" }, { modName = { "GemCorruptionLevel" }, cfg = "skill", notSkillData = "inheritsGemLevel" } }, },
{ label = "Spirit Cost", color = colorCodes.SPIRIT, haveOutput = "SpiritHasCost", { format = "{0:output:SpiritCost}", { breakdown = "SpiritCost" }, { modName = { "SpiritCost", "Cost", "SpiritCostNoMult" }, cfg = "skill" }, }, },
{ label = "Spirit % Cost", color = colorCodes.SPIRIT, haveOutput = "SpiritPercentHasCost", { format = "{0:output:SpiritPercentCost}", { breakdown = "SpiritPercentCost" }, { modName = { "SpiritCost", "Cost", "SpiritCostNoMult" }, cfg = "skill" }, }, },
{ label = "Mana Cost", color = colorCodes.MANA, haveOutput = "ManaHasCost", { format = "{0:output:ManaCost}", { breakdown = "ManaCost" }, { modName = { "ManaCost", "Cost", "ManaCostNoMult" }, cfg = "skill" }, }, },
Expand Down
10 changes: 10 additions & 0 deletions src/Modules/CalcSetup.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2461,8 +2461,18 @@ function calcs.initEnv(build, mode, override, specEnv)
t_insert(env.player.activeSkillList, env.player.mainSkill)
end

-- some active skills, like those granted by support gems depend on the stats of other gems,
-- which means we have to do them last.
local deferredActiveSkills = {}
-- Build skill modifier lists
for _, activeSkill in pairs(env.player.activeSkillList) do
if activeSkill.activeEffect.gemData and activeSkill.activeEffect.gemData.grantedEffect.support then
table.insert(deferredActiveSkills, activeSkill)
else
calcs.buildActiveSkillModList(env, activeSkill)
end
end
for _, activeSkill in ipairs(deferredActiveSkills) do
calcs.buildActiveSkillModList(env, activeSkill)
end

Expand Down
Loading