diff --git a/src/Data/SkillStatMap.lua b/src/Data/SkillStatMap.lua index ecba94d9c8..d860a5492d 100644 --- a/src/Data/SkillStatMap.lua +++ b/src/Data/SkillStatMap.lua @@ -549,6 +549,9 @@ return { ["totem_skill_attack_speed_+%"] = { mod("Speed", "INC", nil, ModFlag.Attack, KeywordFlag.Totem) }, +["grenade_skill_cooldown_speed_+%"] = { + mod("CooldownRecovery", "INC", nil), +}, -- AoE ["active_skill_base_area_of_effect_radius"] = { skill("radius", nil), @@ -2655,5 +2658,7 @@ return { ["quality_display_sandstorm_swipe_is_gem"] = { -- Display Only }, - +["quality_display_base_totem_duration_is_gem"] = { + -- Display Only +}, } diff --git a/src/Data/Skills/act_int.lua b/src/Data/Skills/act_int.lua index d5903069fc..add82789e0 100644 --- a/src/Data/Skills/act_int.lua +++ b/src/Data/Skills/act_int.lua @@ -4628,6 +4628,7 @@ skills["DarkEffigyProjectilePlayer"] = { spell = true, area = true, projectile = true, + totem = true, }, constantStats = { { "skill_disabled_unless_cloned", 2 }, diff --git a/src/Data/Skills/act_str.lua b/src/Data/Skills/act_str.lua index 0190e7c6d9..9d13455b89 100644 --- a/src/Data/Skills/act_str.lua +++ b/src/Data/Skills/act_str.lua @@ -563,13 +563,24 @@ skills["SupportAncestralWarriorTotemPlayer"] = { [39] = { levelRequirement = 0, }, [40] = { levelRequirement = 0, }, }, + addFlags = { + totem = true, + }, statSets = { [1] = { label = "SupportAncestralWarriorTotemPlayer", incrementalEffectiveness = 0.054999999701977, statDescriptionScope = "gem_stat_descriptions", + statMap = { + ["support_ancestral_warrior_totem_attack_speed_+%_final"] = { + mod("Speed", "MORE", nil, ModFlag.Attack), + }, + }, baseFlags = { }, + baseMods = { + mod("ActiveTotemLimit", "BASE", 1), + }, constantStats = { { "skill_disabled_unless_cloned", 2 }, { "support_ancestral_warrior_totem_attack_speed_+%_final", -25 }, @@ -1068,7 +1079,7 @@ skills["AttritionPlayer"] = { {mod("Damage", "MORE", nil, 0, KeywordFlag.Hit, { type = "GlobalEffect", effectType = "Buff"}, { type = "Multiplier", var = "EnemyPresenceSeconds", actor = "enemy", limitVar = "AttritionMaxDamage", div = 2, limitTotal = true }, { type = "ActorCondition", actor = "enemy", var = "RareOrUnique" })}, {mod("CullPercent", "MAX", nil, 0, 0, { type = "GlobalEffect", effectType = "Buff"}, { type = "MultiplierThreshold", var = "EnemyPresenceSeconds", actor = "enemy", thresholdVar = "AttritionCullSeconds"}, { type = "ActorCondition", actor = "enemy", var = "RareOrUnique" }), value = 10,} - }, + }, }, baseFlags = { }, @@ -3688,6 +3699,10 @@ skills["ExplosiveGrenadePlayer"] = { label = "Explosive Grenade", incrementalEffectiveness = 0.054999999701977, statDescriptionScope = "explosive_grenade", + statMap = { + ["base_skill_show_average_damage_instead_of_dps"] = { + }, + }, baseFlags = { attack = true, area = true, @@ -11222,16 +11237,27 @@ skills["SupportMortarCannonPlayer"] = { [39] = { levelRequirement = 0, }, [40] = { levelRequirement = 0, }, }, + addFlags = { + totem = true, + }, statSets = { [1] = { label = "SupportMortarCannonPlayer", incrementalEffectiveness = 0.054999999701977, statDescriptionScope = "gem_stat_descriptions", - addFlags = { - totem = true, + statMap = { + ["support_grenade_ballista_damage_+%_final"] = { + mod("Damage", "MORE", nil), + }, + ["support_grenade_ballista_attack_speed_+%_final"] = { + mod("Speed", "MORE", nil, ModFlag.Attack), }, + }, baseFlags = { }, + baseMods = { + mod("ActiveTotemLimit", "BASE", 1), + }, constantStats = { { "skill_disabled_unless_cloned", 2 }, { "support_grenade_ballista_attack_speed_+%_final", -50 }, @@ -13315,7 +13341,7 @@ skills["SiegeBallistaPlayer"] = { } } skills["SiegeBallistaProjectilePlayer"] = { - name = "", + name = "Artillery", hidden = true, skillTypes = { [SkillType.Attack] = true, [SkillType.RangedAttack] = true, [SkillType.Projectile] = true, [SkillType.Barrageable] = true, [SkillType.AttackInPlaceIsDefault] = true, [SkillType.Fire] = true, [SkillType.Area] = true, [SkillType.CannotChain] = true, [SkillType.UsedByTotem] = true, }, weaponTypes = { diff --git a/src/Export/Skills/act_int.txt b/src/Export/Skills/act_int.txt index 00da8f1d36..7e9fa235f9 100644 --- a/src/Export/Skills/act_int.txt +++ b/src/Export/Skills/act_int.txt @@ -296,7 +296,7 @@ statMap = { #skill DarkEffigyProjectilePlayer #set DarkEffigyProjectilePlayer -#flags spell area projectile +#flags spell area projectile totem #mods #skillEnd diff --git a/src/Export/Skills/act_str.txt b/src/Export/Skills/act_str.txt index ffd2630c39..4516c8e2ee 100644 --- a/src/Export/Skills/act_str.txt +++ b/src/Export/Skills/act_str.txt @@ -30,8 +30,17 @@ local skills, mod, flag, skill = ... #skillEnd #skill SupportAncestralWarriorTotemPlayer +addFlags = { + totem = true, +}, #set SupportAncestralWarriorTotemPlayer #flags +statMap = { + ["support_ancestral_warrior_totem_attack_speed_+%_final"] = { + mod("Speed", "MORE", nil, ModFlag.Attack), + }, +}, +#baseMod mod("ActiveTotemLimit", "BASE", 1) #mods #skillEnd @@ -67,7 +76,7 @@ statMap = { {mod("Damage", "MORE", nil, 0, KeywordFlag.Hit, { type = "GlobalEffect", effectType = "Buff"}, { type = "Multiplier", var = "EnemyPresenceSeconds", actor = "enemy", limitVar = "AttritionMaxDamage", div = 2, limitTotal = true }, { type = "ActorCondition", actor = "enemy", var = "RareOrUnique" })}, {mod("CullPercent", "MAX", nil, 0, 0, { type = "GlobalEffect", effectType = "Buff"}, { type = "MultiplierThreshold", var = "EnemyPresenceSeconds", actor = "enemy", thresholdVar = "AttritionCullSeconds"}, { type = "ActorCondition", actor = "enemy", var = "RareOrUnique" }), value = 10,} - }, + }, }, #mods #skillEnd @@ -272,6 +281,10 @@ statMap = { #skill ExplosiveGrenadePlayer #set ExplosiveGrenadePlayer #flags attack area projectile +statMap = { + ["base_skill_show_average_damage_instead_of_dps"] = { + }, +}, #mods #skillEnd @@ -631,11 +644,20 @@ statMap = { #skillEnd #skill SupportMortarCannonPlayer +addFlags = { + totem = true, +}, #set SupportMortarCannonPlayer - addFlags = { - totem = true, - }, #flags +statMap = { + ["support_grenade_ballista_damage_+%_final"] = { + mod("Damage", "MORE", nil), + }, + ["support_grenade_ballista_attack_speed_+%_final"] = { + mod("Speed", "MORE", nil, ModFlag.Attack), + }, +}, +#baseMod mod("ActiveTotemLimit", "BASE", 1) #mods #skillEnd @@ -743,7 +765,7 @@ statMap = { #mods #skillEnd -#skill SiegeBallistaProjectilePlayer +#skill SiegeBallistaProjectilePlayer Artillery #set SiegeBallistaProjectilePlayer #flags attack projectile totem #mods diff --git a/src/Modules/CalcActiveSkill.lua b/src/Modules/CalcActiveSkill.lua index 68f90812e5..01295eb5b6 100644 --- a/src/Modules/CalcActiveSkill.lua +++ b/src/Modules/CalcActiveSkill.lua @@ -165,8 +165,7 @@ function calcs.createActiveSkill(activeEffect, supportList, env, actor, socketGr if supportEffect.grantedEffect.addFlags and not summonSkill then -- Support skill adds flags to supported skills (eg. Remote Mine adds 'mine') for k in pairs(supportEffect.grantedEffect.addFlags) do - mainSkillFlags[k] = true - calcsSkillFlags[k] = true + skillFlags[k] = true end end end @@ -250,6 +249,45 @@ local function getWeaponFlags(env, weaponData, weaponTypes) return flags, info end +-- Get stats from totem base skill in case of separate active skills or skills that receive totem status via supports +---@param activeSkill table @activeSkill with totem tag +local function getTotemBaseStats(activeSkill) + local totemBase = {} + + if activeSkill.skillTypes[SkillType.SummonsTotem] then -- Skill that summons totems already has stats on activeEffect + totemBase.grantedEffect = activeSkill.activeEffect.grantedEffect + totemBase.gemData = activeSkill.activeEffect.gemData + 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.gemData = activeSkill.activeEffect.gemData + totemBase.skillLevel = activeSkill.activeEffect.level + elseif activeSkill.supportList then -- skill is receives totem status via support + for _, support in ipairs(activeSkill.supportList) do + if support.grantedEffect.addSkillTypes and (not support.superseded) and support.isSupporting[activeSkill.activeEffect.srcInstance] then + for _, skillType in ipairs(support.grantedEffect.addSkillTypes) do + if skillType == SkillType.UsedByTotem then + totemBase.grantedEffect = support.gemData.grantedEffect + totemBase.gemData = support.gemData + break + end + end + end + if totemBase.gemData or totemBase.grantedEffect then + totemBase.skillLevel = support.level + break + end + end + else + -- A totem skill that neither `SummonsTotem` nor `UsedByTotem` should not be possible, but I am leaving this here to alert us in case of unexpected future edge cases + error("Error: Unexpected SkillType behavior for skill with 'totem' flag") + end + end + + return totemBase +end + --- Applies additional modifiers to skills with the "Empowered" flag. --- Checks for "ExtraEmpoweredMod" mods and applies them --- if they match the conditions set by the empowering effect. @@ -484,10 +522,19 @@ function calcs.buildActiveSkillModList(env, activeSkill) skillKeywordFlags = bor(skillKeywordFlags, KeywordFlag.Spell) end - -- Get skill totem ID for totem skills - -- This is used to calculate totem life + -- Find totem base stats if skillFlags.totem then - activeSkill.skillTotemId = activeGrantedEffect.skillTotemId + local totemBase = getTotemBaseStats(activeSkill) + if totemBase.grantedEffect and totemBase.gemData then + activeSkill.skillData.totemBase = totemBase + end + local totemLevelRequirement = activeSkill.skillData.totemBase and activeSkill.skillData.totemBase.grantedEffect.levels[totemBase.skillLevel].levelRequirement or activeEffect.grantedEffect.levels[activeEffect.level].levelRequirement + -- Note: Some active skills related to totem base skills (e.g. on Shockwave Totem) have level requirement = 0, making the additional ` > 0` check necessary + activeSkill.skillData.totemLevel = (totemLevelRequirement and (totemLevelRequirement > 0)) and totemLevelRequirement or 1 + + -- Get skill totem ID for totem skills + -- This is used to calculate totem life + activeSkill.skillTotemId = activeGrantedEffect.skillTotemId or (activeSkill.skillData.totemBase and activeSkill.skillData.totemBase.grantedEffect.skillTotemId) if not activeSkill.skillTotemId then if activeGrantedEffect.color == 2 then activeSkill.skillTotemId = 2 @@ -647,11 +694,6 @@ function calcs.buildActiveSkillModList(env, activeSkill) applyExtraEmpowerMods(activeSkill) - -- Find totem level - if skillFlags.totem then - activeSkill.skillData.totemLevel = 1 or activeEffect.grantedEffect.levels[activeEffect.level].levelRequirement - end - -- Add active mine multiplier if skillFlags.mine then activeSkill.activeMineCount = (env.mode == "CALCS" and activeEffect.srcInstance.skillMineCountCalcs) or (env.mode ~= "CALCS" and activeEffect.srcInstance.skillMineCount) diff --git a/src/Modules/CalcSetup.lua b/src/Modules/CalcSetup.lua index 786d40c380..0822135def 100644 --- a/src/Modules/CalcSetup.lua +++ b/src/Modules/CalcSetup.lua @@ -55,7 +55,6 @@ function calcs.initModDB(env, modDB) modDB:NewMod("WarcryCastTime", "BASE", 0.8, "Base") modDB:NewMod("TotemPlacementTime", "BASE", 0.6, "Base") modDB:NewMod("BallistaPlacementTime", "BASE", 0.35, "Base") - modDB:NewMod("ActiveTotemLimit", "BASE", 1, "Base") modDB:NewMod("ShockStacksMax", "BASE", 1, "Base") modDB:NewMod("ChillStacksMax", "BASE", 1, "Base") modDB:NewMod("ScorchStacksMax", "BASE", 1, "Base") @@ -1786,6 +1785,7 @@ function calcs.initEnv(build, mode, override, specEnv) end end + -- Merge Requirements Tables env.requirementsTable = tableConcat(env.requirementsTableItems, env.requirementsTableGems)