diff --git a/spec/System/TestSkills_spec.lua b/spec/System/TestSkills_spec.lua index 9c756768e..266177773 100644 --- a/spec/System/TestSkills_spec.lua +++ b/spec/System/TestSkills_spec.lua @@ -426,4 +426,30 @@ describe("TestSkills", function() runCallback("OnFrame") assert.True(build.calcsTab.mainOutput.TotalDPS > iceShotDPS) end) + + it("Test Pinnacle of Power", function() + build.configTab.input.enemyIsBoss = "None" + build.configTab.input.usePowerCharges = true + build.configTab.input.overridePowerCharges = 3 + build.configTab:BuildModList() + runCallback("OnFrame") + + build.skillsTab:PasteSocketGroup("Fireball 20/0 1") + runCallback("OnFrame") + assert.True(build.calcsTab.calcsOutput.FreezeBuildupAvg == 0) + assert.True(build.calcsTab.calcsOutput.ShockEffectMod == nil) + + build.skillsTab:PasteSocketGroup("Pinnacle of Power 20/0 1") + runCallback("OnFrame") + local basePinnacleDamage = build.calcsTab.calcsOutput.TotalDPS + assert.True(build.calcsTab.calcsOutput.FreezeBuildupAvg > 0) + assert.True(build.calcsTab.calcsOutput.ShockEffectMod ~= nil) + assert.are.equals(build.calcsTab.calcsOutput.BuffList, "Pinnacle of Power") + + + build.skillsTab:PasteSocketGroup("Pinnacle of Power 20/0 1\nHeightened Charges 1/0 1") + runCallback("OnFrame") + -- Heightened Charges should increased the buff effect, therefore Fireball should have more damage than base Pinnacle of Power + assert.True(build.calcsTab.calcsOutput.TotalDPS > basePinnacleDamage) + end) end) diff --git a/src/Classes/ModStore.lua b/src/Classes/ModStore.lua index 2cef9fc10..7feeb9a1c 100644 --- a/src/Classes/ModStore.lua +++ b/src/Classes/ModStore.lua @@ -350,13 +350,6 @@ function ModStoreClass:EvalMod(mod, cfg, globalLimits) tag.div = GetMultiplier(self, tag.divVar, cfg) end local mult = m_floor(base / (tag.div or 1) + 0.0001) - -- scale effects of Multiplier mod - if tag.scalar then - local scalar = 1 + GetMultiplier(target, tag.scalar, cfg) / 100 - if scalar > 1 then - mult = mult * scalar - end - end local limitTotal local limitNegTotal if tag.limit or tag.limitVar then @@ -431,13 +424,6 @@ function ModStoreClass:EvalMod(mod, cfg, globalLimits) if (tag.upper and mult > threshold) or (tag.equals and mult ~= threshold) or (not (tag.upper and tag.exact) and mult < threshold) then return end - -- scale effects of Multiplier mod - if tag.scalar then - local scalar = 1 + GetMultiplier(target, tag.scalar, cfg) / 100 - if scalar > 1 then - value = value * scalar - end - end elseif tag.type == "PerStat" then local base local target = self diff --git a/src/Data/Skills/other.lua b/src/Data/Skills/other.lua index 8f38e21f0..f438067d0 100644 --- a/src/Data/Skills/other.lua +++ b/src/Data/Skills/other.lua @@ -6048,6 +6048,21 @@ skills["PinnacleOfPowerPlayer"] = { label = "Pinnacle of Power", incrementalEffectiveness = 0.054999999701977, statDescriptionScope = "pinnacle_of_power", + statMap = { + ["elemental_power_elemental_damage_+%_final_per_power_charge"] = { + mod("Damage", "MORE", nil, 0, 0, { type = "SkillType", skillTypeList = { SkillType.Cold, SkillType.Fire, SkillType.Lightning } }, { type = "Multiplier", var = "RemovablePowerCharge", scalar = "ConsumedPowerChargeEffect" }, { type = "GlobalEffect", effectType = "Buff" }), + flag("ColdCanIgnite", { type = "GlobalEffect", effectType = "Buff" }), flag("ColdCanShock", { type = "GlobalEffect", effectType = "Buff" }), + flag("FireCanFreeze", { type = "GlobalEffect", effectType = "Buff" }), flag("FireCanShock", { type = "GlobalEffect", effectType = "Buff" }), + flag("LightningCanFreeze", { type = "GlobalEffect", effectType = "Buff" }), flag("LightningCanIgnite", { type = "GlobalEffect", effectType = "Buff" }), + }, + ["elemental_power_buff_duration_per_power_charge_ms"] = { + mod("Duration", "BASE", nil, 0, 0, { type = "Multiplier", var = "RemovablePowerCharge", scalar = "ConsumedPowerChargeEffect" }), + div = 1000, + }, + ["quality_stat_elemental_power_elemental_damage_+%_final_per_power_charge_is_gem"] = { + -- display only + }, + }, baseFlags = { buff = true, duration = true, diff --git a/src/Export/Skills/other.txt b/src/Export/Skills/other.txt index 5a8323f34..2e2d0e22f 100644 --- a/src/Export/Skills/other.txt +++ b/src/Export/Skills/other.txt @@ -435,6 +435,21 @@ statMap = { #skill PinnacleOfPowerPlayer #set PinnacleOfPowerPlayer #flags buff duration +statMap = { + ["elemental_power_elemental_damage_+%_final_per_power_charge"] = { + mod("Damage", "MORE", nil, 0, 0, { type = "SkillType", skillTypeList = { SkillType.Cold, SkillType.Fire, SkillType.Lightning } }, { type = "Multiplier", var = "RemovablePowerCharge", scalar = "ConsumedPowerChargeEffect" }, { type = "GlobalEffect", effectType = "Buff" }), + flag("ColdCanIgnite", { type = "GlobalEffect", effectType = "Buff" }), flag("ColdCanShock", { type = "GlobalEffect", effectType = "Buff" }), + flag("FireCanFreeze", { type = "GlobalEffect", effectType = "Buff" }), flag("FireCanShock", { type = "GlobalEffect", effectType = "Buff" }), + flag("LightningCanFreeze", { type = "GlobalEffect", effectType = "Buff" }), flag("LightningCanIgnite", { type = "GlobalEffect", effectType = "Buff" }), + }, + ["elemental_power_buff_duration_per_power_charge_ms"] = { + mod("Duration", "BASE", nil, 0, 0, { type = "Multiplier", var = "RemovablePowerCharge", scalar = "ConsumedPowerChargeEffect" }), + div = 1000, + }, + ["quality_stat_elemental_power_elemental_damage_+%_final_per_power_charge_is_gem"] = { + -- display only + }, +}, #mods #skillEnd diff --git a/src/Modules/CalcActiveSkill.lua b/src/Modules/CalcActiveSkill.lua index e2e8b453d..7ff9bc0ee 100644 --- a/src/Modules/CalcActiveSkill.lua +++ b/src/Modules/CalcActiveSkill.lua @@ -48,6 +48,23 @@ local function mergeLevelMod(modList, mod, value) end end +-- allow Multiplier mods to be scaled by sources of the multipliedVariableEffect, e.g. var = RemovablePowerCharges, scalar = ConsumedPowerChargeEffect +-- e.g. Pinnacle of Power, I had this scaling logic in ModStore prior as tag.scalar but it was not working with the Buff portion +local function checkForScalarMultiplier(modOrGroup, modList) + local scale = 0 + if modOrGroup.scalar then + scale = modList:Sum("BASE", nil, "Multiplier:"..modOrGroup.scalar) + else + for _, config in ipairs(modOrGroup) do + if config.scalar then + scale = modList:Sum("BASE", nil, "Multiplier:"..config.scalar) + break + end + end + end + return 1 + scale / 100 +end + -- Merge skill effect modifiers with given mod list -- If a stat set is provided, only merge modifiers from that statset function calcs.mergeSkillInstanceMods(env, modList, skillEffect, statSet, extraStats) @@ -56,8 +73,8 @@ function calcs.mergeSkillInstanceMods(env, modList, skillEffect, statSet, extraS if statSet and not isValueInArray(skillEffect.grantedEffect.statSets, statSet) then return end - local grantedEffect = skillEffect.grantedEffect - for _, statSet in ipairs(statSet and {statSet} or grantedEffect.statSets) do + local grantedEffect = skillEffect.grantedEffect + for _, statSet in ipairs(statSet and {statSet} or grantedEffect.statSets) do local stats = calcLib.buildSkillInstanceStats(skillEffect, grantedEffect, statSet) if extraStats and extraStats[1] then for _, stat in pairs(extraStats) do @@ -69,14 +86,16 @@ function calcs.mergeSkillInstanceMods(env, modList, skillEffect, statSet, extraS if map then -- Some mods need different scalars for different stats, but the same value. Putting them in a group allows this for _, modOrGroup in ipairs(map) do + local scalar = checkForScalarMultiplier(modOrGroup, modList) -- Found a mod, since all mods have names if modOrGroup.name then modOrGroup.source = string.format("Skill:%s", grantedEffect.id) - mergeLevelMod(modList, modOrGroup, map.value or statValue * (map.mult or 1) / (map.div or 1) + (map.base or 0)) + mergeLevelMod(modList, modOrGroup, map.value or statValue * (map.mult or 1) * scalar / (map.div or 1) + (map.base or 0)) else for _, mod in ipairs(modOrGroup) do + local scalar = checkForScalarMultiplier(mod) mod.source = string.format("Skill:%s", grantedEffect.id) - mergeLevelMod(modList, mod, modOrGroup.value or statValue * (modOrGroup.mult or 1) / (modOrGroup.div or 1) + (modOrGroup.base or 0)) + mergeLevelMod(modList, mod, modOrGroup.value or statValue * (modOrGroup.mult or 1) * scalar / (modOrGroup.div or 1) + (modOrGroup.base or 0)) end end end @@ -100,7 +119,7 @@ function calcs.createActiveSkill(activeEffect, supportList, env, actor, socketGr } local activeGrantedEffect = activeEffect.grantedEffect - + -- Initialise skill types activeSkill.skillTypes = copyTable(activeGrantedEffect.skillTypes) if activeGrantedEffect.minionSkillTypes then @@ -109,12 +128,12 @@ function calcs.createActiveSkill(activeEffect, supportList, env, actor, socketGr -- Initialise skill flag set ('attack', 'projectile', etc) local statSet, skillFlags - if env.mode == "CALCS" then + if env.mode == "CALCS" then statSet = activeEffect.grantedEffect.statSets[activeEffect.statSetCalcs.index] skillFlags = statSet and copyTable(statSet.baseFlags) or { } activeEffect.statSetCalcs.statSet = statSet activeEffect.statSetCalcs.skillFlags = skillFlags - else + else statSet = activeEffect.grantedEffect.statSets[activeEffect.statSet.index] skillFlags = statSet and copyTable(statSet.baseFlags) or { } activeEffect.statSet.statSet = statSet @@ -261,7 +280,7 @@ local function getTotemBaseStats(activeSkill) totemBase.skillLevel = activeSkill.activeEffect.level elseif activeSkill.skillTypes[SkillType.UsedByTotem] then if activeSkill.activeEffect.grantedEffect.skillTypes[SkillType.UsedByTotem] then -- is totem skill by default - totemBase.grantedEffect = activeSkill.activeEffect.gemData.grantedEffect + totemBase.grantedEffect = activeSkill.activeEffect.gemData.grantedEffect totemBase.gemData = activeSkill.activeEffect.gemData totemBase.skillLevel = activeSkill.activeEffect.level elseif activeSkill.supportList then -- skill is receives totem status via support @@ -703,7 +722,7 @@ function calcs.buildActiveSkillModList(env, activeSkill) skillModList:AddMod(value.mod) t_insert(activeSkill.extraSkillModList, value.mod) end - + applyExtraEmpowerMods(activeSkill) -- Add active mine multiplier @@ -722,7 +741,7 @@ function calcs.buildActiveSkillModList(env, activeSkill) local noPotentialStage = true if activeEffect.grantedEffect.parts then for _, part in ipairs(activeEffect.grantedEffect.parts) do - if part.stages then + if part.stages then noPotentialStage = false break end @@ -808,12 +827,12 @@ function calcs.buildActiveSkillModList(env, activeSkill) minion.parent = env.player minion.enemy = env.enemy end - minion.level = activeSkill.skillData.minionLevelIsEnemyLevel and env.enemyLevel or - activeSkill.skillData.minionLevelIsTriggeredSkillLevel and activeEffect.srcInstance.supportEffect and activeEffect.srcInstance.supportEffect.activeSkillLevel and data.minionLevelTable[activeEffect.srcInstance.supportEffect.activeSkillLevel] or - activeSkill.skillData.minionLevelIsPlayerLevel and (m_min(env.build and env.build.characterLevel or activeSkill.skillData.minionLevel or activeEffect.grantedEffectLevel.levelRequirement, activeSkill.skillData.minionLevelIsPlayerLevel)) or + minion.level = activeSkill.skillData.minionLevelIsEnemyLevel and env.enemyLevel or + activeSkill.skillData.minionLevelIsTriggeredSkillLevel and activeEffect.srcInstance.supportEffect and activeEffect.srcInstance.supportEffect.activeSkillLevel and data.minionLevelTable[activeEffect.srcInstance.supportEffect.activeSkillLevel] or + activeSkill.skillData.minionLevelIsPlayerLevel and (m_min(env.build and env.build.characterLevel or activeSkill.skillData.minionLevel or activeEffect.grantedEffectLevel.levelRequirement, activeSkill.skillData.minionLevelIsPlayerLevel)) or activeSkill.skillData.minionLevel or data.minionLevelTable[activeSkill.activeEffect.level] or 1 -- fix minion level between 1 and 100 - minion.level = m_min(m_max(minion.level,1),100) + minion.level = m_min(m_max(minion.level,1),100) minion.itemList = { } minion.uses = activeGrantedEffect.minionUses minion.lifeTable = env.data.monsterAllyLifeTable @@ -870,7 +889,7 @@ function calcs.buildActiveSkillModList(env, activeSkill) minion.weaponData1 = env.player.weaponData1 end end - if minion.uses["Weapon 2"] then + if minion.uses["Weapon 2"] then if minion.itemSet then local item = env.build.itemsTab.items[minion.itemSet[minion.itemSet.useSecondWeaponSet and "Weapon 2 Swap" or "Weapon 2"].selItemId] if item and item.weaponData then @@ -994,7 +1013,7 @@ function calcs.createMinionSkills(env, activeSkill) } local minionSkillIndex = activeSkill.activeEffect.srcInstance.skillMinionSkill local minionSkillIndexCalcs = activeSkill.activeEffect.srcInstance.skillMinionSkillCalcs - local minionStatSetIndex = activeSkill.activeEffect.srcInstance.skillMinionSkillStatSetIndexLookup and activeSkill.activeEffect.srcInstance.skillMinionSkillStatSetIndexLookup[activeSkill.activeEffect.grantedEffect.id] + local minionStatSetIndex = activeSkill.activeEffect.srcInstance.skillMinionSkillStatSetIndexLookup and activeSkill.activeEffect.srcInstance.skillMinionSkillStatSetIndexLookup[activeSkill.activeEffect.grantedEffect.id] and activeSkill.activeEffect.srcInstance.skillMinionSkillStatSetIndexLookup[activeSkill.activeEffect.grantedEffect.id][minionSkillIndex] or 1 local minionStatSetCalcsIndex = activeSkill.activeEffect.srcInstance.skillMinionSkillStatSetIndexLookupCalcs and activeSkill.activeEffect.srcInstance.skillMinionSkillStatSetIndexLookupCalcs[activeSkill.activeEffect.grantedEffect.id] and activeSkill.activeEffect.srcInstance.skillMinionSkillStatSetIndexLookupCalcs[activeSkill.activeEffect.grantedEffect.id][minionSkillIndexCalcs] or 1 @@ -1018,7 +1037,7 @@ function calcs.createMinionSkills(env, activeSkill) local skillFlags if env.mode == "CALCS" then skillFlags = minionSkill.activeEffect.statSetCalcs.skillFlags - else + else skillFlags = minionSkill.activeEffect.statSet.skillFlags end skillFlags.minion = true @@ -1027,7 +1046,7 @@ function calcs.createMinionSkills(env, activeSkill) minionSkill.skillData.damageEffectiveness = 1 + (activeSkill.skillData.minionDamageEffectiveness or 0) / 100 t_insert(minion.activeSkillList, minionSkill) end - local skillIndex + local skillIndex if env.mode == "CALCS" then skillIndex = m_max(m_min(activeEffect.srcInstance.skillMinionSkillCalcs or 1, #minion.activeSkillList), 1) activeEffect.srcInstance.skillMinionSkillCalcs = skillIndex