Fix globalLimit being sensitive to tag order (#8652)
* FEAT(items): Add showcased uniques to New.lua * FEAT(mods): add handling for new mods on Scornflux * FEAT(mods): add handling for "Take X Fire Damage when you use a Skill" * FEAT(mods): add handling for Damage penetrates "Fire Resistance equal to your overcapped Fire Resistance" * FEAT(mods): add handling for "Warcries have an additional Life Cost equal to 15% of your Maximum Life" * FEAT(mods): add handling for "Warcry Skills have X increased Area of Effect" * FEAT(mods): add handling for `Gain no armour from equipped body armour` * FEAT(mods): port generalized "doubled" mod handling from POB2 https://github.com/PathOfBuildingCommunity/PathOfBuilding-PoE2/pull/1095 * Update spelling and ModCache * FIX(mods): globalLimit style mods did not work sometimes When a mod gots pice wise parsed the tags can be added to the mod table in unpredictable order. The inital implementation was simply checking the first tag which caused the global limit functionality to not work sometimes. This commit moves the logic into EvalMod. * FEAT(mods): add support for "you have no Str/Dex" mods * FIX(spelling): un-rake-able --------- Co-authored-by: Wires77 <Wires77@users.noreply.github.com>
This commit is contained in:
@@ -844,7 +844,102 @@ describe("TestDefence", function()
|
||||
assert.are.equals(0, floor(poolsRemaining.Life))
|
||||
assert.are.equals(0, floor(poolsRemaining.OverkillDamage))
|
||||
end)
|
||||
|
||||
it("Unbreakable + Iron Reflexes", function()
|
||||
build.configTab.input.customMods = [[
|
||||
you have no dexterity
|
||||
]]
|
||||
build.configTab:BuildModList()
|
||||
runCallback("OnFrame")
|
||||
|
||||
-- Get the base to make this test more adaptable to changes
|
||||
local baseArmour = build.calcsTab.mainOutput.Armour
|
||||
local baseEvasion = build.calcsTab.mainOutput.Evasion
|
||||
|
||||
build.configTab.input.customMods = [[
|
||||
Converts all Evasion Rating to Armour. Dexterity provides no bonus to Evasion Rating
|
||||
you have no dexterity
|
||||
]]
|
||||
build.itemsTab:CreateDisplayItemFromRaw([[
|
||||
New Item
|
||||
Shabby Jerkin
|
||||
Quality: 20
|
||||
]])
|
||||
build.itemsTab:AddDisplayItem()
|
||||
build.configTab:BuildModList()
|
||||
runCallback("OnFrame")
|
||||
|
||||
-- Get the base + Shabby Jerkin to make this test more adaptable to changes
|
||||
local ironReflexesArmour = build.calcsTab.mainOutput.Armour - baseArmour - baseEvasion
|
||||
|
||||
print("build.calcsTab.mainOutput.Armour:" .. build.calcsTab.mainOutput.Armour)
|
||||
|
||||
build.configTab.input.customMods = [[
|
||||
Armour from Equipped Body Armour is doubled
|
||||
Converts all Evasion Rating to Armour. Dexterity provides no bonus to Evasion Rating
|
||||
you have no dexterity
|
||||
]]
|
||||
build.configTab:BuildModList()
|
||||
runCallback("OnFrame")
|
||||
|
||||
-- Evasion from Body Armour is converted to Armour before being doubled
|
||||
assert.are.equals(2*ironReflexesArmour + baseArmour + baseEvasion, build.calcsTab.mainOutput.Armour)
|
||||
|
||||
build.configTab.input.customMods = [[
|
||||
Armour from Equipped Body Armour is doubled
|
||||
Converts all Evasion Rating to Armour. Dexterity provides no bonus to Evasion Rating
|
||||
Gain no armour from equipped body armour
|
||||
you have no dexterity
|
||||
]]
|
||||
build.configTab:BuildModList()
|
||||
runCallback("OnFrame")
|
||||
|
||||
-- Only the base armour from the chest is affected.
|
||||
-- Armour converted with Iron Reflexes still applies
|
||||
assert.are.equals(2*ironReflexesArmour + baseArmour + baseEvasion, build.calcsTab.mainOutput.Armour)
|
||||
build.configTab.input.customMods = [[
|
||||
Armour from Equipped Body Armour is doubled
|
||||
Converts all Evasion Rating to Armour. Dexterity provides no bonus to Evasion Rating
|
||||
Gain no armour from equipped body armour
|
||||
defences from equipped body armour are doubled if it has no socketed gems
|
||||
you have no dexterity
|
||||
]]
|
||||
build.configTab:BuildModList()
|
||||
runCallback("OnFrame")
|
||||
|
||||
-- Oath Of Maji double defences stack with Unbreakable
|
||||
assert.are.equals(2*2*ironReflexesArmour + baseArmour + baseEvasion, build.calcsTab.mainOutput.Armour)
|
||||
|
||||
build.configTab.input.customMods = [[
|
||||
Armour from Equipped Body Armour is doubled
|
||||
Armour from Equipped Body Armour is doubled
|
||||
Converts all Evasion Rating to Armour. Dexterity provides no bonus to Evasion Rating
|
||||
Gain no armour from equipped body armour
|
||||
defences from equipped body armour are doubled if it has no socketed gems
|
||||
you have no dexterity
|
||||
]]
|
||||
build.configTab:BuildModList()
|
||||
runCallback("OnFrame")
|
||||
|
||||
-- Mod form unbreakable should apply only once
|
||||
assert.are.equals(2*2*ironReflexesArmour + baseArmour + baseEvasion, build.calcsTab.mainOutput.Armour)
|
||||
|
||||
build.configTab.input.customMods = [[
|
||||
Armour from Equipped Body Armour is doubled
|
||||
Armour from Equipped Body Armour is doubled
|
||||
Converts all Evasion Rating to Armour. Dexterity provides no bonus to Evasion Rating
|
||||
Gain no armour from equipped body armour
|
||||
defences from equipped body armour are doubled if it has no socketed gems
|
||||
defences from equipped body armour are doubled if it has no socketed gems
|
||||
you have no dexterity
|
||||
]]
|
||||
build.configTab:BuildModList()
|
||||
runCallback("OnFrame")
|
||||
|
||||
-- Oath Of Maji should apply only once
|
||||
assert.are.equals(2*2*ironReflexesArmour + baseArmour + baseEvasion, build.calcsTab.mainOutput.Armour)
|
||||
end)
|
||||
|
||||
it("MoM + EB", function()
|
||||
build.configTab.input.enemyIsBoss = "None"
|
||||
-- enough mana and es, 0% and 100% bypass
|
||||
|
||||
@@ -98,14 +98,7 @@ function ModDBClass:SumInternal(context, modType, cfg, flags, keywordFlags, sour
|
||||
local mod = modList[i]
|
||||
if mod.type == modType and band(flags, mod.flags) == mod.flags and MatchKeywordFlags(keywordFlags, mod.keywordFlags) and (not source or ( mod.source and mod.source:match("[^:]+") == source )) then
|
||||
if mod[1] then
|
||||
local value = context:EvalMod(mod, cfg) or 0
|
||||
if mod[1].globalLimit and mod[1].globalLimitKey then
|
||||
globalLimits[mod[1].globalLimitKey] = globalLimits[mod[1].globalLimitKey] or 0
|
||||
if globalLimits[mod[1].globalLimitKey] + value > mod[1].globalLimit then
|
||||
value = mod[1].globalLimit - globalLimits[mod[1].globalLimitKey]
|
||||
end
|
||||
globalLimits[mod[1].globalLimitKey] = globalLimits[mod[1].globalLimitKey] + value
|
||||
end
|
||||
local value = context:EvalMod(mod, cfg, globalLimits) or 0
|
||||
result = result + value
|
||||
else
|
||||
result = result + mod.value
|
||||
@@ -133,14 +126,7 @@ function ModDBClass:MoreInternal(context, cfg, flags, keywordFlags, source, ...)
|
||||
if mod.type == "MORE" and band(flags, mod.flags) == mod.flags and MatchKeywordFlags(keywordFlags, mod.keywordFlags) and (not source or mod.source:match("[^:]+") == source) then
|
||||
local value
|
||||
if mod[1] then
|
||||
value = context:EvalMod(mod, cfg) or 0
|
||||
if mod[1].globalLimit and mod[1].globalLimitKey then
|
||||
globalLimits[mod[1].globalLimitKey] = globalLimits[mod[1].globalLimitKey] or 0
|
||||
if globalLimits[mod[1].globalLimitKey] + value > mod[1].globalLimit then
|
||||
value = mod[1].globalLimit - globalLimits[mod[1].globalLimitKey]
|
||||
end
|
||||
globalLimits[mod[1].globalLimitKey] = globalLimits[mod[1].globalLimitKey] + value
|
||||
end
|
||||
value = context:EvalMod(mod, cfg, globalLimits) or 0
|
||||
else
|
||||
value = mod.value or 0
|
||||
end
|
||||
@@ -249,15 +235,7 @@ function ModDBClass:TabulateInternal(context, result, modType, cfg, flags, keywo
|
||||
if (mod.type == modType or not modType) and band(flags, mod.flags) == mod.flags and MatchKeywordFlags(keywordFlags, mod.keywordFlags) and (not source or mod.source:match("[^:]+") == source) then
|
||||
local value
|
||||
if mod[1] then
|
||||
value = context:EvalMod(mod, cfg)
|
||||
if mod[1].globalLimit and mod[1].globalLimitKey then
|
||||
value = value or 0
|
||||
globalLimits[mod[1].globalLimitKey] = globalLimits[mod[1].globalLimitKey] or 0
|
||||
if globalLimits[mod[1].globalLimitKey] + value > mod[1].globalLimit then
|
||||
value = mod[1].globalLimit - globalLimits[mod[1].globalLimitKey]
|
||||
end
|
||||
globalLimits[mod[1].globalLimitKey] = globalLimits[mod[1].globalLimitKey] + value
|
||||
end
|
||||
value = context:EvalMod(mod, cfg, globalLimits)
|
||||
else
|
||||
value = mod.value
|
||||
end
|
||||
|
||||
@@ -260,12 +260,22 @@ function ModStoreClass:GetStat(stat, cfg)
|
||||
end
|
||||
end
|
||||
|
||||
function ModStoreClass:EvalMod(mod, cfg)
|
||||
function ModStoreClass:EvalMod(mod, cfg, globalLimits)
|
||||
local value = mod.value
|
||||
for _, tag in ipairs(mod) do
|
||||
if tag.type == "Multiplier" then
|
||||
local target = self
|
||||
local limitTarget = self
|
||||
|
||||
if globalLimits and tag.globalLimit and tag.globalLimitKey then
|
||||
value = value or 0
|
||||
globalLimits[tag.globalLimitKey] = globalLimits[tag.globalLimitKey] or 0
|
||||
if globalLimits[tag.globalLimitKey] + value > tag.globalLimit then
|
||||
value = tag.globalLimit - globalLimits[tag.globalLimitKey]
|
||||
end
|
||||
globalLimits[tag.globalLimitKey] = globalLimits[tag.globalLimitKey] + value
|
||||
end
|
||||
|
||||
-- Allow limiting a self multiplier on a parent multiplier (eg. Agony Crawler on player virulence)
|
||||
-- This explicit target is necessary because even though the GetMultiplier method does call self.parent.GetMultiplier, it does so with noMod = true,
|
||||
-- disabling the summation (3rd part): (not noMod and self:Sum("BASE", cfg, multiplierName[var]) or 0)
|
||||
|
||||
@@ -4254,6 +4254,12 @@ local specialModList = {
|
||||
["you have no intelligence"] = {
|
||||
mod("Int", "MORE", -100),
|
||||
},
|
||||
["you have no dexterity"] = {
|
||||
mod("Dex", "MORE", -100),
|
||||
},
|
||||
["you have no strength"] = {
|
||||
mod("Str", "MORE", -100),
|
||||
},
|
||||
["elemental resistances are zero"] = {
|
||||
mod("FireResist", "OVERRIDE", 0),
|
||||
mod("ColdResist", "OVERRIDE", 0),
|
||||
|
||||
Reference in New Issue
Block a user