diff --git a/spec/System/TestItemMods_spec.lua b/spec/System/TestItemMods_spec.lua index b15074c41..3b0036f0e 100644 --- a/spec/System/TestItemMods_spec.lua +++ b/spec/System/TestItemMods_spec.lua @@ -7,6 +7,26 @@ describe("TetsItemMods", function() -- newBuild() takes care of resetting everything in setup() end) + it("Queen of the Forest preserves sprinting movement speed only", function() + build.configTab.input.customMods = [[ ++60000 to Evasion Rating +30% increased Movement Speed +12% increased Movement Speed while Sprinting +Increases Movement Speed by 25%, plus 1% per 600 Evasion Rating, up to a maximum of 75% +Other Modifiers to Movement Speed except for Sprinting do not apply +]] + build.configTab:BuildModList() + runCallback("OnFrame") + + assert.are.equals(1.75, build.calcsTab.mainOutput.MovementSpeedMod) + + build.configTab.input.conditionSprinting = true + build.configTab:BuildModList() + runCallback("OnFrame") + + assert.are.equals(2.37, build.calcsTab.mainOutput.MovementSpeedMod) + end) + it("Both slots mod (evasion and es mastery)", function() build.configTab.input.customMods = "\z diff --git a/src/Data/ModCache.lua b/src/Data/ModCache.lua index ad786db07..f8ae91d16 100644 --- a/src/Data/ModCache.lua +++ b/src/Data/ModCache.lua @@ -5340,12 +5340,12 @@ c["Immune to Poison if Equipped Helmet has higher Evasion Rating than Armour"]={ c["Immune to Shock"]={{[1]={flags=0,keywordFlags=0,name="ShockImmune",type="FLAG",value=true}},nil} c["Immune to Shock if a majority of your Socketed Support Gems are Green"]={{[1]={[1]={type="Condition",var="MajorityGreenSocketedSupports"},flags=0,keywordFlags=0,name="ShockImmune",type="FLAG",value=true}},nil} c["Immune to Shock while affected by an Archon Buff"]={nil,"Immune to Shock while affected by an Archon Buff "} -c["Increases Movement Speed by 25%, plus 1% per 500 Evasion Rating, up to a maximum of 75%"]={nil,"Increases Movement Speed by 25%, plus 1% per 500 Evasion Rating, up to a maximum of 75% "} -c["Increases Movement Speed by 25%, plus 1% per 500 Evasion Rating, up to a maximum of 75% Other Modifiers to Movement Speed except for Sprinting do not apply"]={nil,"Increases Movement Speed by 25%, plus 1% per 500 Evasion Rating, up to a maximum of 75% Other Modifiers to Movement Speed except for Sprinting do not apply "} -c["Increases Movement Speed by 25%, plus 1% per 600 Evasion Rating, up to a maximum of 75%"]={nil,"Increases Movement Speed by 25%, plus 1% per 600 Evasion Rating, up to a maximum of 75% "} -c["Increases Movement Speed by 25%, plus 1% per 600 Evasion Rating, up to a maximum of 75% Other Modifiers to Movement Speed except for Sprinting do not apply"]={nil,"Increases Movement Speed by 25%, plus 1% per 600 Evasion Rating, up to a maximum of 75% Other Modifiers to Movement Speed except for Sprinting do not apply "} -c["Increases Movement Speed by 25%, plus 1% per 800 Evasion Rating, up to a maximum of 75%"]={nil,"Increases Movement Speed by 25%, plus 1% per 800 Evasion Rating, up to a maximum of 75% "} -c["Increases Movement Speed by 25%, plus 1% per 800 Evasion Rating, up to a maximum of 75% Other Modifiers to Movement Speed except for Sprinting do not apply"]={nil,"Increases Movement Speed by 25%, plus 1% per 800 Evasion Rating, up to a maximum of 75% Other Modifiers to Movement Speed except for Sprinting do not apply "} +c["Increases Movement Speed by 25%, plus 1% per 500 Evasion Rating, up to a maximum of 75%"]={{[1]={[1]={base=25,div=500,limit=75,limitTotal=true,stat="Evasion",type="PerStat"},flags=0,keywordFlags=0,name="MovementSpeedOnlySprinting",type="INC",value=1}},nil} +c["Increases Movement Speed by 25%, plus 1% per 500 Evasion Rating, up to a maximum of 75% Other Modifiers to Movement Speed except for Sprinting do not apply"]={{[1]={[1]={base=25,div=500,limit=75,limitTotal=true,stat="Evasion",type="PerStat"},flags=0,keywordFlags=0,name="MovementSpeedOnlySprinting",type="INC",value=1},[2]={flags=0,keywordFlags=0,name="OnlySprintingMovementSpeedApplies",type="FLAG",value=true}},nil} +c["Increases Movement Speed by 25%, plus 1% per 600 Evasion Rating, up to a maximum of 75%"]={{[1]={[1]={base=25,div=600,limit=75,limitTotal=true,stat="Evasion",type="PerStat"},flags=0,keywordFlags=0,name="MovementSpeedOnlySprinting",type="INC",value=1}},nil} +c["Increases Movement Speed by 25%, plus 1% per 600 Evasion Rating, up to a maximum of 75% Other Modifiers to Movement Speed except for Sprinting do not apply"]={{[1]={[1]={base=25,div=600,limit=75,limitTotal=true,stat="Evasion",type="PerStat"},flags=0,keywordFlags=0,name="MovementSpeedOnlySprinting",type="INC",value=1},[2]={flags=0,keywordFlags=0,name="OnlySprintingMovementSpeedApplies",type="FLAG",value=true}},nil} +c["Increases Movement Speed by 25%, plus 1% per 800 Evasion Rating, up to a maximum of 75%"]={{[1]={[1]={base=25,div=800,limit=75,limitTotal=true,stat="Evasion",type="PerStat"},flags=0,keywordFlags=0,name="MovementSpeedOnlySprinting",type="INC",value=1}},nil} +c["Increases Movement Speed by 25%, plus 1% per 800 Evasion Rating, up to a maximum of 75% Other Modifiers to Movement Speed except for Sprinting do not apply"]={{[1]={[1]={base=25,div=800,limit=75,limitTotal=true,stat="Evasion",type="PerStat"},flags=0,keywordFlags=0,name="MovementSpeedOnlySprinting",type="INC",value=1},[2]={flags=0,keywordFlags=0,name="OnlySprintingMovementSpeedApplies",type="FLAG",value=true}},nil} c["Increases and Reductions to Armour also apply to Energy Shield"]={nil,"Increases and Reductions to Armour also apply to Energy Shield "} c["Increases and Reductions to Armour also apply to Energy Shield Recharge Rate at 40% of their value"]={{[1]={flags=0,keywordFlags=0,name="ArmourAppliesToEnergyShieldRecharge",type="FLAG",value=true},[2]={flags=0,keywordFlags=0,name="ImprovedArmourAppliesToEnergyShieldRecharge",type="MAX",value=40}},nil} c["Increases and Reductions to Companion Damage also apply to you"]={{[1]={flags=0,keywordFlags=0,name="CompanionDamageAppliesToPlayer",type="FLAG",value=true}},nil} @@ -5678,9 +5678,9 @@ c["Only affects Passives in Very Large Ring"]={{[1]={flags=0,keywordFlags=0,name c["Only affects Passives in Very Small Ring"]={{[1]={flags=0,keywordFlags=0,name="JewelData",type="LIST",value={key="radiusIndex",value=5}}},nil} c["Onslaught"]={{[1]={flags=0,keywordFlags=0,name="Condition:Onslaught",type="FLAG",value=true}},nil} c["Orb Skills have +1 to Limit"]={nil,"Orb Skills have +1 to Limit "} -c["Other Modifiers to Movement Speed except for Sprinting do not apply"]={nil,"Other Modifiers to Movement Speed except for Sprinting do not apply "} -c["Other Modifiers to Movement Speed except for Sprinting do not apply Increases Movement Speed by 25%, plus 1% per 600 Evasion Rating, up to a maximum of 75%"]={nil,"Other Modifiers to Movement Speed except for Sprinting do not apply Increases Movement Speed by 25%, plus 1% per 600 Evasion Rating, up to a maximum of 75% "} -c["Other Modifiers to Movement Speed except for Sprinting do not apply Increases Movement Speed by 25%, plus 1% per 800 Evasion Rating, up to a maximum of 75%"]={nil,"Other Modifiers to Movement Speed except for Sprinting do not apply Increases Movement Speed by 25%, plus 1% per 800 Evasion Rating, up to a maximum of 75% "} +c["Other Modifiers to Movement Speed except for Sprinting do not apply"]={{[1]={flags=0,keywordFlags=0,name="OnlySprintingMovementSpeedApplies",type="FLAG",value=true}},nil} +c["Other Modifiers to Movement Speed except for Sprinting do not apply Increases Movement Speed by 25%, plus 1% per 600 Evasion Rating, up to a maximum of 75%"]={{[1]={[1]={base=25,div=600,limit=75,limitTotal=true,stat="Evasion",type="PerStat"},flags=0,keywordFlags=0,name="MovementSpeedOnlySprinting",type="INC",value=1},[2]={flags=0,keywordFlags=0,name="OnlySprintingMovementSpeedApplies",type="FLAG",value=true}},nil} +c["Other Modifiers to Movement Speed except for Sprinting do not apply Increases Movement Speed by 25%, plus 1% per 800 Evasion Rating, up to a maximum of 75%"]={{[1]={[1]={base=25,div=800,limit=75,limitTotal=true,stat="Evasion",type="PerStat"},flags=0,keywordFlags=0,name="MovementSpeedOnlySprinting",type="INC",value=1},[2]={flags=0,keywordFlags=0,name="OnlySprintingMovementSpeedApplies",type="FLAG",value=true}},nil} c["Overgrown Plant Skills Break 50% increased Armour"]={nil,"Overgrown Plant Skills Break 50% increased Armour "} c["Pain Attunement"]={{[1]={flags=0,keywordFlags=0,name="Keystone",type="LIST",value="Pain Attunement"}},nil} c["Parried enemies take more Spell Damage instead of more Attack Damage"]={nil,"Parried enemies take more Spell Damage instead of more Attack Damage "} diff --git a/src/Modules/CalcDefence.lua b/src/Modules/CalcDefence.lua index 6c9ff685f..314bb5628 100644 --- a/src/Modules/CalcDefence.lua +++ b/src/Modules/CalcDefence.lua @@ -22,6 +22,42 @@ local tempTable1 = { } local isElemental = { Fire = true, Cold = true, Lightning = true } +local function hasSprintingCondition(mod) + for _, tag in ipairs(mod) do + if tag.type == "Condition" and tag.var == "Sprinting" and not tag.neg then + return true + end + end + return false +end + +local function sumSprintingMovementSpeedMods(modDB, modType) + local sum = 0 + for _, value in ipairs(modDB:Tabulate(modType, nil, "MovementSpeed")) do + if hasSprintingCondition(value.mod) then + sum = sum + value.value + end + end + return sum +end + +local function moreSprintingMovementSpeedMods(modDB) + local more = 1 + for _, value in ipairs(modDB:Tabulate("MORE", nil, "MovementSpeed")) do + if hasSprintingCondition(value.mod) then + more = more * (1 + value.value / 100) + end + end + return more +end + +local function calcOnlySprintingMovementSpeedMod(modDB) + local base = modDB:Sum("BASE", nil, "MovementSpeedOnlySprinting") + sumSprintingMovementSpeedMods(modDB, "BASE") + local inc = modDB:Sum("INC", nil, "MovementSpeedOnlySprinting") + sumSprintingMovementSpeedMods(modDB, "INC") + local more = modDB:More(nil, "MovementSpeedOnlySprinting") * moreSprintingMovementSpeedMods(modDB) + return round((1 + base) * (1 + inc / 100) * more, 3) +end + -- List of all damage types, ordered according to the conversion sequence local hitSourceList = {"Attack", "Spell"} local dmgTypeList = {"Physical", "Lightning", "Cold", "Fire", "Chaos"} @@ -1871,7 +1907,11 @@ function calcs.defence(env, actor) end -- Miscellaneous: move speed, avoidance, weapon swap speed - output.MovementSpeedMod = modDB:Override(nil, "MovementSpeed") or (modDB:Flag(nil, "MovementSpeedEqualHighestLinkedPlayers") and actor.partyMembers.output.MovementSpeedMod or (round((1 + modDB:Sum("BASE", nil, "MovementSpeed")) * calcLib.mod(modDB, nil, "MovementSpeed"), 3))) + if modDB:Flag(nil, "OnlySprintingMovementSpeedApplies") then + output.MovementSpeedMod = calcOnlySprintingMovementSpeedMod(modDB) + else + output.MovementSpeedMod = modDB:Override(nil, "MovementSpeed") or (modDB:Flag(nil, "MovementSpeedEqualHighestLinkedPlayers") and actor.partyMembers.output.MovementSpeedMod or (round((1 + modDB:Sum("BASE", nil, "MovementSpeed", "MovementSpeedOnlySprinting")) * calcLib.mod(modDB, nil, "MovementSpeed", "MovementSpeedOnlySprinting"), 3))) + end if modDB:Flag(nil, "MovementSpeedCannotBeBelowBase") then output.MovementSpeedMod = m_max(output.MovementSpeedMod, 1) end diff --git a/src/Modules/ModParser.lua b/src/Modules/ModParser.lua index cc20d66c3..fdb775565 100644 --- a/src/Modules/ModParser.lua +++ b/src/Modules/ModParser.lua @@ -2125,6 +2125,39 @@ local function flag(name, ...) return mod(name, "FLAG", true, ...) end +local function movementSpeedOnlySprintingPerStat(base, per, stat, div, limit) + return mod("MovementSpeedOnlySprinting", "INC", tonumber(per), { + type = "PerStat", + stat = stat, + div = tonumber(div), + base = tonumber(base), + limit = tonumber(limit), + limitTotal = true, + }) +end + +local function onlySprintingMovementSpeedFlag() + return flag("OnlySprintingMovementSpeedApplies") +end + +local function movementSpeedPerEvasionOnlySprinting(base, _, per, div, limit) + return { movementSpeedOnlySprintingPerStat(base, per, "Evasion", div, limit) } +end + +local function movementSpeedPerEvasionWithOnlySprinting(base, _, per, div, limit) + return { + movementSpeedOnlySprintingPerStat(base, per, "Evasion", div, limit), + onlySprintingMovementSpeedFlag(), + } +end + +local function movementSpeedPerSpiritWithOnlySprinting(per, _, div, limit) + return { + movementSpeedOnlySprintingPerStat(0, per, "Spirit", div, limit), + onlySprintingMovementSpeedFlag(), + } +end + local gems = {} for id in pairs(data.gems) do table.insert(gems, id) @@ -2214,6 +2247,12 @@ end -- List of special modifiers local specialModList = { + -- Movement speed replacement mods + ["increases movement speed by (%d+)%%, plus (%d+)%% per (%d+) evasion rating, up to a maximum of (%d+)%%"] = movementSpeedPerEvasionOnlySprinting, + ["increases movement speed by (%d+)%%, plus (%d+)%% per (%d+) evasion rating, up to a maximum of (%d+)%% other modifiers to movement speed except for sprinting do not apply"] = movementSpeedPerEvasionWithOnlySprinting, + ["other modifiers to movement speed except for sprinting do not apply increases movement speed by (%d+)%%, plus (%d+)%% per (%d+) evasion rating, up to a maximum of (%d+)%%"] = movementSpeedPerEvasionWithOnlySprinting, + ["(%d+)%% increased movement speed per (%d+) spirit, up to a maximum of (%d+)%% other modifiers to movement speed except for sprinting do not apply"] = movementSpeedPerSpiritWithOnlySprinting, + ["other modifiers to movement speed except for sprinting do not apply"] = { onlySprintingMovementSpeedFlag() }, -- Explode mods ["enemies you kill have a (%d+)%% chance to explode, dealing a (.+) of their maximum life as (.+) damage"] = function(chance, _, amount, type) -- Obliteration, Unspeakable Gifts (chaos cluster), synth implicit mod, current crusader body mod, Ngamahu Warmonger tattoo return explodeFunc(chance, amount, type)