From 0337b44c51e0e090967528b4133dc5ce494e7cb5 Mon Sep 17 00:00:00 2001 From: Mickael Cagnion Date: Sun, 8 Nov 2026 15:02:18 +0100 Subject: [PATCH 1/2] Support Talisman Aspect effect enchantments Scope Cat and Crab buff effect and Spider debuff effect to their skills so the base enchantments affect calculations. Cover equipped Talismans, conditional buffs, unchanged stacks and unrelated buffs. --- spec/System/TestAspectEffect_spec.lua | 132 ++++++++++++++++++++++++++ src/Data/ModCache.lua | 6 +- src/Modules/ModParser.lua | 3 + 3 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 spec/System/TestAspectEffect_spec.lua diff --git a/spec/System/TestAspectEffect_spec.lua b/spec/System/TestAspectEffect_spec.lua new file mode 100644 index 0000000000..a5779f3a8d --- /dev/null +++ b/spec/System/TestAspectEffect_spec.lua @@ -0,0 +1,132 @@ +describe("TestAspectEffect", function() + for _, case in ipairs({ + { base = "Craicic Talisman", aspect = "Crab", effect = "Buff" }, + { base = "Farric Talisman", aspect = "Cat", effect = "Buff" }, + { base = "Fenumal Talisman", aspect = "Spider", effect = "Debuff" }, + }) do + it("parses the base enchant of " .. case.base, function() + local item = new("Item"):Item("Rarity: Normal\n" .. case.base) + local line = item.enchantModLines[1].line + assert.are.equals("100% increased Aspect of the " .. case.aspect .. " " .. case.effect .. " Effect", line) + local mods, extra = modLib.parseMod(line) + assert.is_nil(extra) + assert.is_not_nil(mods) + end) + end + + it("still parses Avian buff effect", function() + local mods, extra = modLib.parseMod("100% increased Aspect of the Avian Buff Effect") + assert.is_nil(extra) + assert.is_not_nil(mods) + end) + + it("scales Crab reduction without adding barriers", function() + newBuild() + build.skillsTab:PasteSocketGroup("Summon Stone Golem 20/0 1\n") + runCallback("OnFrame") + local regen = build.calcsTab.mainOutput.LifeRegen + build.itemsTab:CreateDisplayItemFromRaw("Rarity: Rare\nTest\nCraicic Talisman\nImplicits: 0\nGrants Level 20 Aspect of the Crab Skill") + build.itemsTab:AddDisplayItem() + runCallback("OnFrame") + assert.are.equals(10, build.calcsTab.mainEnv.player.modDB:Sum("BASE", nil, "CrabBarriersMax")) + assert.are.equals(40, build.calcsTab.mainOutput.PhysicalDamageReduction) + assert.are.equals(regen, build.calcsTab.mainOutput.LifeRegen) + build.configTab.input.overrideCrabBarriers = 3 + build.configTab:BuildModList() + runCallback("OnFrame") + assert.are.equals(12, build.calcsTab.mainOutput.PhysicalDamageReduction) + build.calcsTab.input.misc_buffMode = "UNBUFFED" + build.buildFlag = true + runCallback("OnFrame") + assert.are.equals(0, build.calcsTab.calcsOutput.PhysicalDamageReduction) + build.calcsTab.input.misc_buffMode = "BUFFED" + build.buildFlag = true + runCallback("OnFrame") + assert.are.equals(40, build.calcsTab.calcsOutput.PhysicalDamageReduction) + build.configTab.input.overrideCrabBarriers = 0 + build.configTab:BuildModList() + runCallback("OnFrame") + assert.are.equals(0, build.calcsTab.mainOutput.PhysicalDamageReduction) + end) + + it("scales both Cat buffs", function() + newBuild() + build.skillsTab:PasteSocketGroup("Summon Stone Golem 20/0 1\n") + runCallback("OnFrame") + local regen = build.calcsTab.mainOutput.LifeRegen + build.itemsTab:CreateDisplayItemFromRaw("Rarity: Rare\nTest\nFarric Talisman\nImplicits: 0\nGrants Level 20 Aspect of the Cat Skill") + build.itemsTab:AddDisplayItem() + build.configTab.input.aspectOfTheCatCatsStealth = true + build.configTab.input.aspectOfTheCatCatsAgility = true + build.configTab:BuildModList() + runCallback("OnFrame") + local db = build.calcsTab.mainEnv.player.modDB + assert.are.equals(200, db:Sum("INC", nil, "CritChance")) + assert.are.equals(30, build.calcsTab.mainOutput.AvoidAllDamageFromHitsChance) + assert.are.equals(20, db:Sum("INC", { flags = ModFlag.Attack }, "Speed")) + assert.are.equals(20, db:Sum("INC", { flags = ModFlag.Cast }, "Speed")) + assert.are.equals(regen, build.calcsTab.mainOutput.LifeRegen) + build.calcsTab.input.misc_buffMode = "UNBUFFED" + build.buildFlag = true + runCallback("OnFrame") + assert.are.equals(0, build.calcsTab.calcsOutput.AvoidAllDamageFromHitsChance) + build.configTab.input.aspectOfTheCatCatsAgility = false + build.configTab:BuildModList() + runCallback("OnFrame") + assert.are.equals(0, build.calcsTab.mainEnv.player.modDB:Sum("INC", { flags = ModFlag.Attack }, "Speed")) + assert.are.equals(30, build.calcsTab.mainOutput.AvoidAllDamageFromHitsChance) + build.configTab.input.aspectOfTheCatCatsStealth = false + build.configTab.input.aspectOfTheCatCatsAgility = true + build.configTab:BuildModList() + runCallback("OnFrame") + assert.are.equals(0, build.calcsTab.mainOutput.AvoidAllDamageFromHitsChance) + assert.are.equals(20, build.calcsTab.mainEnv.player.modDB:Sum("INC", { flags = ModFlag.Cast }, "Speed")) + end) + + it("scales Spider damage taken without adding webs", function() + newBuild() + build.itemsTab:CreateDisplayItemFromRaw("Rarity: Rare\nTest\nFenumal Talisman\nImplicits: 0\nGrants Level 20 Aspect of the Spider Skill") + build.itemsTab:AddDisplayItem() + build.configTab.input.aspectOfTheSpiderWebStacks = 2 + build.configTab:BuildModList() + runCallback("OnFrame") + assert.are.equals(20, build.calcsTab.mainEnv.enemy.modDB:Sum("INC", nil, "DamageTaken")) + assert.are.equals(2, build.calcsTab.mainEnv.enemy.modDB:Sum("BASE", nil, "Multiplier:Spider's WebStack")) + assert.are.equals(0, build.calcsTab.mainEnv.player.modDB:Sum("INC", nil, "DamageTaken")) + build.configTab.input.aspectOfTheSpiderWebStacks = 3 + build.configTab:BuildModList() + runCallback("OnFrame") + assert.are.equals(30, build.calcsTab.mainEnv.enemy.modDB:Sum("INC", nil, "DamageTaken")) + build.calcsTab.input.misc_buffMode = "COMBAT" + build.buildFlag = true + runCallback("OnFrame") + assert.are.equals(0, build.calcsTab.calcsEnv.enemy.modDB:Sum("INC", nil, "DamageTaken")) + build.configTab.input.aspectOfTheSpiderWebStacks = 0 + build.configTab:BuildModList() + runCallback("OnFrame") + assert.are.equals(0, build.calcsTab.mainEnv.enemy.modDB:Sum("INC", nil, "DamageTaken")) + end) + + it("does not grant an Aspect or scale Avian when only its talisman is equipped", function() + for _, base in ipairs({ "Craicic Talisman", "Farric Talisman", "Fenumal Talisman" }) do + newBuild() + build.itemsTab:CreateDisplayItemFromRaw("Rarity: Rare\nControl\nIron Ring\nImplicits: 0\nGrants Level 20 Aspect of the Avian Skill") + build.itemsTab:AddDisplayItem() + build.configTab.input.aspectOfTheAvianAviansMight = true + build.configTab.input.aspectOfTheAvianAviansFlight = true + build.configTab:BuildModList() + runCallback("OnFrame") + local speed = build.calcsTab.mainOutput.MovementSpeedMod + local doubleDamage = build.calcsTab.mainEnv.player.modDB:Sum("BASE", nil, "DoubleDamageChance") + assert.is_true(doubleDamage > 0) + build.itemsTab:CreateDisplayItemFromRaw("Rarity: Normal\n" .. base) + build.itemsTab:AddDisplayItem() + runCallback("OnFrame") + assert.are.equals(speed, build.calcsTab.mainOutput.MovementSpeedMod) + assert.are.equals(doubleDamage, build.calcsTab.mainEnv.player.modDB:Sum("BASE", nil, "DoubleDamageChance")) + assert.are.equals(0, build.calcsTab.mainOutput.PhysicalDamageReduction) + assert.are.equals(0, build.calcsTab.mainOutput.AvoidAllDamageFromHitsChance) + assert.are.equals(0, build.calcsTab.mainEnv.enemy.modDB:Sum("INC", nil, "DamageTaken")) + end + end) +end) diff --git a/src/Data/ModCache.lua b/src/Data/ModCache.lua index c0ba5d2363..c743dd550b 100755 --- a/src/Data/ModCache.lua +++ b/src/Data/ModCache.lua @@ -4111,9 +4111,9 @@ c["100% increased Armour and Energy Shield from Equipped Body Armour if Equipped c["100% increased Armour and Evasion"]={{[1]={flags=0,keywordFlags=0,name="ArmourAndEvasion",type="INC",value=100}},nil} c["100% increased Armour from Equipped Boots and Gloves"]={{[1]={[1]={slotNameList={[1]="Gloves",[2]="Boots"},type="SlotName"},flags=0,keywordFlags=0,name="Armour",type="INC",value=100}},nil} c["100% increased Aspect of the Avian Buff Effect"]={{[1]={[1]={skillName="Aspect of the Avian",type="SkillName"},flags=0,keywordFlags=0,name="BuffEffect",type="INC",value=100}},nil} -c["100% increased Aspect of the Cat Buff Effect"]={{[1]={flags=0,keywordFlags=0,name="BuffEffect",type="INC",value=100}}," Aspect of the Cat "} -c["100% increased Aspect of the Crab Buff Effect"]={{[1]={flags=0,keywordFlags=0,name="BuffEffect",type="INC",value=100}}," Aspect of the Crab "} -c["100% increased Aspect of the Spider Debuff Effect"]={{[1]={flags=0,keywordFlags=0,name="BuffEffect",type="INC",value=100}}," Aspect of the Spider De "} +c["100% increased Aspect of the Cat Buff Effect"]={{[1]={[1]={skillName="Aspect of the Cat",type="SkillName"},flags=0,keywordFlags=0,name="BuffEffect",type="INC",value=100}},nil} +c["100% increased Aspect of the Crab Buff Effect"]={{[1]={[1]={skillName="Aspect of the Crab",type="SkillName"},flags=0,keywordFlags=0,name="BuffEffect",type="INC",value=100}},nil} +c["100% increased Aspect of the Spider Debuff Effect"]={{[1]={[1]={skillName="Aspect of the Spider",type="SkillName"},flags=0,keywordFlags=0,name="DebuffEffect",type="INC",value=100}},nil} c["100% increased Attack Critical Strike Chance while Dual Wielding"]={{[1]={[1]={type="Condition",var="DualWielding"},flags=1,keywordFlags=0,name="CritChance",type="INC",value=100}},nil} c["100% increased Attack Damage"]={{[1]={flags=1,keywordFlags=0,name="Damage",type="INC",value=100}},nil} c["100% increased Attack Speed"]={{[1]={flags=1,keywordFlags=0,name="Speed",type="INC",value=100}},nil} diff --git a/src/Modules/ModParser.lua b/src/Modules/ModParser.lua index 9d5d1db3a0..fc678100b4 100644 --- a/src/Modules/ModParser.lua +++ b/src/Modules/ModParser.lua @@ -494,6 +494,9 @@ local modNameList = { ["effect of withered"] = "WitherEffect", ["warcry effect"] = { "BuffEffect", keywordFlags = KeywordFlag.Warcry }, ["aspect of the avian buff effect"] = { "BuffEffect", tag = { type = "SkillName", skillName = "Aspect of the Avian" } }, + ["aspect of the cat buff effect"] = { "BuffEffect", tag = { type = "SkillName", skillName = "Aspect of the Cat" } }, + ["aspect of the crab buff effect"] = { "BuffEffect", tag = { type = "SkillName", skillName = "Aspect of the Crab" } }, + ["aspect of the spider debuff effect"] = { "DebuffEffect", tag = { type = "SkillName", skillName = "Aspect of the Spider" } }, ["maximum rage"] = "MaximumRage", ["minimum rage"] = "MinimumRage", ["rage effect"] = "RageEffect", From 26ccc1176a3ba626f9919f56c17df4c8023c3918 Mon Sep 17 00:00:00 2001 From: Mickael Cagnion Date: Tue, 10 Nov 2026 00:53:51 +0100 Subject: [PATCH 2/2] Simplify Talisman Aspect regression tests Keep scoped parsing and one calculation per Aspect. Drop repeated checks of existing modes, stacks and unrelated skills to keep the tests proportional to the parser fix. --- spec/System/TestAspectEffect_spec.lua | 101 ++------------------------ 1 file changed, 7 insertions(+), 94 deletions(-) diff --git a/spec/System/TestAspectEffect_spec.lua b/spec/System/TestAspectEffect_spec.lua index a5779f3a8d..7deaa96cc8 100644 --- a/spec/System/TestAspectEffect_spec.lua +++ b/spec/System/TestAspectEffect_spec.lua @@ -6,84 +6,34 @@ describe("TestAspectEffect", function() }) do it("parses the base enchant of " .. case.base, function() local item = new("Item"):Item("Rarity: Normal\n" .. case.base) - local line = item.enchantModLines[1].line - assert.are.equals("100% increased Aspect of the " .. case.aspect .. " " .. case.effect .. " Effect", line) - local mods, extra = modLib.parseMod(line) + local mods, extra = modLib.parseMod(item.enchantModLines[1].line) assert.is_nil(extra) - assert.is_not_nil(mods) + assert.are.equals(case.effect .. "Effect", mods[1].name) + assert.are.same({ type = "SkillName", skillName = "Aspect of the " .. case.aspect }, mods[1][1]) end) end - it("still parses Avian buff effect", function() - local mods, extra = modLib.parseMod("100% increased Aspect of the Avian Buff Effect") - assert.is_nil(extra) - assert.is_not_nil(mods) - end) - - it("scales Crab reduction without adding barriers", function() + it("scales Crab damage reduction", function() newBuild() - build.skillsTab:PasteSocketGroup("Summon Stone Golem 20/0 1\n") - runCallback("OnFrame") - local regen = build.calcsTab.mainOutput.LifeRegen build.itemsTab:CreateDisplayItemFromRaw("Rarity: Rare\nTest\nCraicic Talisman\nImplicits: 0\nGrants Level 20 Aspect of the Crab Skill") build.itemsTab:AddDisplayItem() - runCallback("OnFrame") - assert.are.equals(10, build.calcsTab.mainEnv.player.modDB:Sum("BASE", nil, "CrabBarriersMax")) - assert.are.equals(40, build.calcsTab.mainOutput.PhysicalDamageReduction) - assert.are.equals(regen, build.calcsTab.mainOutput.LifeRegen) build.configTab.input.overrideCrabBarriers = 3 build.configTab:BuildModList() runCallback("OnFrame") assert.are.equals(12, build.calcsTab.mainOutput.PhysicalDamageReduction) - build.calcsTab.input.misc_buffMode = "UNBUFFED" - build.buildFlag = true - runCallback("OnFrame") - assert.are.equals(0, build.calcsTab.calcsOutput.PhysicalDamageReduction) - build.calcsTab.input.misc_buffMode = "BUFFED" - build.buildFlag = true - runCallback("OnFrame") - assert.are.equals(40, build.calcsTab.calcsOutput.PhysicalDamageReduction) - build.configTab.input.overrideCrabBarriers = 0 - build.configTab:BuildModList() - runCallback("OnFrame") - assert.are.equals(0, build.calcsTab.mainOutput.PhysicalDamageReduction) end) - it("scales both Cat buffs", function() + it("scales Cat critical strike chance", function() newBuild() - build.skillsTab:PasteSocketGroup("Summon Stone Golem 20/0 1\n") - runCallback("OnFrame") - local regen = build.calcsTab.mainOutput.LifeRegen build.itemsTab:CreateDisplayItemFromRaw("Rarity: Rare\nTest\nFarric Talisman\nImplicits: 0\nGrants Level 20 Aspect of the Cat Skill") build.itemsTab:AddDisplayItem() build.configTab.input.aspectOfTheCatCatsStealth = true - build.configTab.input.aspectOfTheCatCatsAgility = true build.configTab:BuildModList() runCallback("OnFrame") - local db = build.calcsTab.mainEnv.player.modDB - assert.are.equals(200, db:Sum("INC", nil, "CritChance")) - assert.are.equals(30, build.calcsTab.mainOutput.AvoidAllDamageFromHitsChance) - assert.are.equals(20, db:Sum("INC", { flags = ModFlag.Attack }, "Speed")) - assert.are.equals(20, db:Sum("INC", { flags = ModFlag.Cast }, "Speed")) - assert.are.equals(regen, build.calcsTab.mainOutput.LifeRegen) - build.calcsTab.input.misc_buffMode = "UNBUFFED" - build.buildFlag = true - runCallback("OnFrame") - assert.are.equals(0, build.calcsTab.calcsOutput.AvoidAllDamageFromHitsChance) - build.configTab.input.aspectOfTheCatCatsAgility = false - build.configTab:BuildModList() - runCallback("OnFrame") - assert.are.equals(0, build.calcsTab.mainEnv.player.modDB:Sum("INC", { flags = ModFlag.Attack }, "Speed")) - assert.are.equals(30, build.calcsTab.mainOutput.AvoidAllDamageFromHitsChance) - build.configTab.input.aspectOfTheCatCatsStealth = false - build.configTab.input.aspectOfTheCatCatsAgility = true - build.configTab:BuildModList() - runCallback("OnFrame") - assert.are.equals(0, build.calcsTab.mainOutput.AvoidAllDamageFromHitsChance) - assert.are.equals(20, build.calcsTab.mainEnv.player.modDB:Sum("INC", { flags = ModFlag.Cast }, "Speed")) + assert.are.equals(200, build.calcsTab.mainEnv.player.modDB:Sum("INC", nil, "CritChance")) end) - it("scales Spider damage taken without adding webs", function() + it("scales Spider damage taken", function() newBuild() build.itemsTab:CreateDisplayItemFromRaw("Rarity: Rare\nTest\nFenumal Talisman\nImplicits: 0\nGrants Level 20 Aspect of the Spider Skill") build.itemsTab:AddDisplayItem() @@ -91,42 +41,5 @@ describe("TestAspectEffect", function() build.configTab:BuildModList() runCallback("OnFrame") assert.are.equals(20, build.calcsTab.mainEnv.enemy.modDB:Sum("INC", nil, "DamageTaken")) - assert.are.equals(2, build.calcsTab.mainEnv.enemy.modDB:Sum("BASE", nil, "Multiplier:Spider's WebStack")) - assert.are.equals(0, build.calcsTab.mainEnv.player.modDB:Sum("INC", nil, "DamageTaken")) - build.configTab.input.aspectOfTheSpiderWebStacks = 3 - build.configTab:BuildModList() - runCallback("OnFrame") - assert.are.equals(30, build.calcsTab.mainEnv.enemy.modDB:Sum("INC", nil, "DamageTaken")) - build.calcsTab.input.misc_buffMode = "COMBAT" - build.buildFlag = true - runCallback("OnFrame") - assert.are.equals(0, build.calcsTab.calcsEnv.enemy.modDB:Sum("INC", nil, "DamageTaken")) - build.configTab.input.aspectOfTheSpiderWebStacks = 0 - build.configTab:BuildModList() - runCallback("OnFrame") - assert.are.equals(0, build.calcsTab.mainEnv.enemy.modDB:Sum("INC", nil, "DamageTaken")) - end) - - it("does not grant an Aspect or scale Avian when only its talisman is equipped", function() - for _, base in ipairs({ "Craicic Talisman", "Farric Talisman", "Fenumal Talisman" }) do - newBuild() - build.itemsTab:CreateDisplayItemFromRaw("Rarity: Rare\nControl\nIron Ring\nImplicits: 0\nGrants Level 20 Aspect of the Avian Skill") - build.itemsTab:AddDisplayItem() - build.configTab.input.aspectOfTheAvianAviansMight = true - build.configTab.input.aspectOfTheAvianAviansFlight = true - build.configTab:BuildModList() - runCallback("OnFrame") - local speed = build.calcsTab.mainOutput.MovementSpeedMod - local doubleDamage = build.calcsTab.mainEnv.player.modDB:Sum("BASE", nil, "DoubleDamageChance") - assert.is_true(doubleDamage > 0) - build.itemsTab:CreateDisplayItemFromRaw("Rarity: Normal\n" .. base) - build.itemsTab:AddDisplayItem() - runCallback("OnFrame") - assert.are.equals(speed, build.calcsTab.mainOutput.MovementSpeedMod) - assert.are.equals(doubleDamage, build.calcsTab.mainEnv.player.modDB:Sum("BASE", nil, "DoubleDamageChance")) - assert.are.equals(0, build.calcsTab.mainOutput.PhysicalDamageReduction) - assert.are.equals(0, build.calcsTab.mainOutput.AvoidAllDamageFromHitsChance) - assert.are.equals(0, build.calcsTab.mainEnv.enemy.modDB:Sum("INC", nil, "DamageTaken")) - end end) end)