Skip to content

Commit 6f7a6bc

Browse files
author
LocalIdentity
committed
Merge branch 'dev' into feat/05treev2
2 parents 61bfcf1 + 17ba16c commit 6f7a6bc

45 files changed

Lines changed: 263 additions & 22 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
-- Tests for Facebreaker-style gloves: empty-handed gloves that grant their own base
2+
-- weapon damage and let you attack as though using a One Hand Mace.
3+
describe("TestFacebreaker", function()
4+
before_each(function()
5+
newBuild()
6+
end)
7+
8+
teardown(function()
9+
-- newBuild() takes care of resetting everything in setup()
10+
end)
11+
12+
-- Physical variant (Facebreaker)
13+
local function equipFacebreaker()
14+
build.itemsTab:CreateDisplayItemFromRaw([[
15+
New Item
16+
Stocky Mitts
17+
Has 8 to 12 Physical damage, +3 to +4 per Boss's Face Broken
18+
Can Attack as though using a One Handed Mace while both of your hand slots are empty
19+
Unarmed Attacks that would use an Equipped One Hand Mace's damage use this Item's damage
20+
]])
21+
build.itemsTab:AddDisplayItem()
22+
runCallback("OnFrame")
23+
end
24+
25+
it("grants its base Physical damage to Unarmed attacks", function()
26+
equipFacebreaker()
27+
local weaponData1 = build.calcsTab.mainEnv.player.weaponData1
28+
assert.are.equals(8, weaponData1.FacebreakerPhysicalMin)
29+
assert.are.equals(12, weaponData1.FacebreakerPhysicalMax)
30+
end)
31+
32+
it("lets you attack as though using a One Hand Mace while unarmed", function()
33+
equipFacebreaker()
34+
local weaponData1 = build.calcsTab.mainEnv.player.weaponData1
35+
assert.is_true(weaponData1.asThoughUsing ~= nil and weaponData1.asThoughUsing["One Hand Mace"] == true)
36+
end)
37+
38+
it("scales its base damage per Boss's Face Broken", function()
39+
equipFacebreaker()
40+
build.configTab.input.configBossFaceBroken = 10
41+
build.configTab:BuildModList()
42+
runCallback("OnFrame")
43+
local weaponData1 = build.calcsTab.mainEnv.player.weaponData1
44+
-- 8 base + 3 per face broken * 10, 12 base + 4 per face broken * 10
45+
assert.are.equals(8 + 3 * 10, weaponData1.FacebreakerPhysicalMin)
46+
assert.are.equals(12 + 4 * 10, weaponData1.FacebreakerPhysicalMax)
47+
end)
48+
49+
it("matches the in-game resolved damage at 60 Boss's Faces Broken (188-252)", function()
50+
-- Real in-game Facebreaker shows "Physical Damage: 188-252" at 60 Boss's Faces Broken
51+
equipFacebreaker()
52+
build.configTab.input.configBossFaceBroken = 60
53+
build.configTab:BuildModList()
54+
runCallback("OnFrame")
55+
build.skillsTab:PasteSocketGroup("Boneshatter 1/0 1")
56+
runCallback("OnFrame")
57+
build.calcsTab:BuildOutput()
58+
runCallback("OnFrame")
59+
local mainHand = build.calcsTab.mainOutput.MainHand
60+
assert.are.equals(188, mainHand.PhysicalMinBase)
61+
assert.are.equals(252, mainHand.PhysicalMaxBase)
62+
end)
63+
64+
it("makes One Hand Mace skills usable unarmed and applies 'more Unarmed Damage per Strength' to them", function()
65+
build.itemsTab:CreateDisplayItemFromRaw([[
66+
New Item
67+
Stocky Mitts
68+
Has 8 to 12 Physical damage, +3 to +4 per Boss's Face Broken
69+
1% more Unarmed Damage per 5 Strength
70+
Can Attack as though using a One Handed Mace while both of your hand slots are empty
71+
Unarmed Attacks that would use an Equipped One Hand Mace's damage use this Item's damage
72+
]])
73+
build.itemsTab:AddDisplayItem()
74+
-- strip enemy Armour so the small base damage still resolves to a positive hit
75+
build.configTab.input.customMods = "Nearby Enemies have 100% less Armour"
76+
build.configTab:BuildModList()
77+
runCallback("OnFrame")
78+
-- Boneshatter requires a One/Two Hand Mace (no "None"): only usable unarmed thanks to Facebreaker
79+
build.skillsTab:PasteSocketGroup("Boneshatter 1/0 1")
80+
runCallback("OnFrame")
81+
build.calcsTab:BuildOutput()
82+
runCallback("OnFrame")
83+
local mainSkill = build.calcsTab.mainEnv.player.mainSkill
84+
assert.is_truthy(mainSkill)
85+
-- the Mace-only skill resolves to a real (unarmed) attack producing a positive hit
86+
assert.are.equals("Boneshatter", mainSkill.activeEffect.grantedEffect.name)
87+
assert.is_truthy(build.calcsTab.mainOutput.MainHand)
88+
assert.is_true(build.calcsTab.mainOutput.MainHand.AverageHit > 0)
89+
local modDB = build.calcsTab.mainEnv.player.modDB
90+
-- 'more Unarmed Damage per 5 Strength' applies to unarmed Hits (which is what Facebreaker mace attacks are)...
91+
assert.is_true(modDB:Sum("MORE", { flags = ModFlag.Unarmed + ModFlag.Hit }, "Damage") > 0)
92+
-- ...but not to actual weapon (e.g. Sword) Hits
93+
assert.are.equals(0, modDB:Sum("MORE", { flags = ModFlag.Sword + ModFlag.Hit }, "Damage"))
94+
end)
95+
96+
it("auto-imports the Boss's Faces Broken count from character quest stats", function()
97+
build.importTab:ImportQuestRewardConfig({ "57 [BrokenFace|Broken Boss Faces]" })
98+
assert.is_nil(build.configTab.input.configBossFaceBroken)
99+
assert.are.equals(57, build.configTab.placeholder.configBossFaceBroken)
100+
end)
101+
102+
it("supports the Fire damage variant", function()
103+
build.itemsTab:CreateDisplayItemFromRaw([[
104+
New Item
105+
Stocky Mitts
106+
Has 9 to 14 Fire damage, +3 to +5 per Boss's Face Broken
107+
Can Attack as though using a One Handed Mace while both of your hand slots are empty
108+
Unarmed Attacks that would use an Equipped One Hand Mace's damage use this Item's damage
109+
]])
110+
build.itemsTab:AddDisplayItem()
111+
runCallback("OnFrame")
112+
local weaponData1 = build.calcsTab.mainEnv.player.weaponData1
113+
assert.are.equals(9, weaponData1.FacebreakerFireMin)
114+
assert.are.equals(14, weaponData1.FacebreakerFireMax)
115+
end)
116+
117+
it("uses Facebreaker item damage instead of Hollow Palm added Physical damage for mace-compatible staff skills", function()
118+
equipFacebreaker()
119+
build.configTab.input.customMods = "Hollow Palm Technique"
120+
build.configTab:BuildModList()
121+
runCallback("OnFrame")
122+
build.skillsTab:PasteSocketGroup("Rain of Blades 1/0 1")
123+
runCallback("OnFrame")
124+
local skillModList = build.calcsTab.mainEnv.player.mainSkill.skillModList
125+
assert.are.equals(0, skillModList:Sum("BASE", { flags = ModFlag.Attack }, "PhysicalMin"))
126+
assert.are.equals(0, skillModList:Sum("BASE", { flags = ModFlag.Attack }, "PhysicalMax"))
127+
end)
128+
129+
it("keeps Hollow Palm added Physical damage for quarterstaff-only skills", function()
130+
equipFacebreaker()
131+
build.configTab.input.customMods = "Hollow Palm Technique"
132+
build.configTab:BuildModList()
133+
runCallback("OnFrame")
134+
build.skillsTab:PasteSocketGroup("Quarterstaff Strike 1/0 1")
135+
runCallback("OnFrame")
136+
local skillModList = build.calcsTab.mainEnv.player.mainSkill.skillModList
137+
assert.is_true(skillModList:Sum("BASE", { flags = ModFlag.Attack }, "PhysicalMin") > 0)
138+
assert.is_true(skillModList:Sum("BASE", { flags = ModFlag.Attack }, "PhysicalMax") > 0)
139+
end)
140+
end)

spec/System/TestImportTab_spec.lua

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,4 +159,29 @@ describe("ImportTab quest reward import", function()
159159
assert.are.equals(tribal.Options[1], input[tribalVar])
160160
assert.are.equals(seven.Options[3], input[sevenVar])
161161
end)
162+
163+
it("imports Boss's Faces Broken without affecting quest reward matching", function()
164+
local sevenVar, seven = findQuest("Seven Pillars")
165+
local input = importStats({
166+
"+20 to maximum Life",
167+
"5% increased maximum Life",
168+
"5% increased maximum Mana",
169+
"+100 to [Spirit|Spirit]",
170+
"30% increased [Charm] Effect Duration",
171+
"+5% to [Resistances|Fire Resistance]",
172+
"+15% to [Resistances|Cold Resistance]",
173+
"+15% to [Resistances|Lightning Resistance]",
174+
"30% increased Life Recovery from [Flask|Flasks]",
175+
"45% increased Global [Armour], [Evasion] and [EnergyShield|Energy Shield]",
176+
"25% increased [StunThreshold|Stun Threshold]",
177+
"+1 [Charm] Slot",
178+
"58 [BrokenFace|Broken Boss Faces]",
179+
})
180+
181+
assert.is_nil(input.configBossFaceBroken)
182+
assert.are.equals(58, build.configTab.placeholder.configBossFaceBroken)
183+
assert.are.equals(seven.Options[3], input[sevenVar])
184+
runCallback("OnFrame")
185+
assert.is_false(build.configTab.varControls.configBossFaceBroken.shown())
186+
end)
162187
end)

src/Classes/CalcsTab.lua

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -546,10 +546,22 @@ function CalcsTabClass:PowerBuilder()
546546
for nodeId, node in pairs(self.build.spec.nodes) do
547547
wipeTable(node.power)
548548
if node.modKey ~= "" and not self.mainEnv.grantedPassives[nodeId] then
549-
distanceMap[node.pathDist or 1000] = distanceMap[node.pathDist or 1000] or { }
550-
distanceMap[node.pathDist or 1000][nodeId] = node
551-
if not (self.nodePowerMaxDepth and self.nodePowerMaxDepth < node.pathDist) then
552-
total = total + 1
549+
local hiddenByLockedAscendancyNode = false
550+
if node.unlockConstraint then
551+
for _, unlockNodeId in ipairs(node.unlockConstraint.nodes) do
552+
local unlockNode = self.build.spec.nodes[unlockNodeId]
553+
if unlockNode and unlockNode.ascendancyName and not unlockNode.alloc then
554+
hiddenByLockedAscendancyNode = true
555+
break
556+
end
557+
end
558+
end
559+
if not hiddenByLockedAscendancyNode then
560+
distanceMap[node.pathDist or 1000] = distanceMap[node.pathDist or 1000] or { }
561+
distanceMap[node.pathDist or 1000][nodeId] = node
562+
if not (self.nodePowerMaxDepth and self.nodePowerMaxDepth < node.pathDist) then
563+
total = total + 1
564+
end
553565
end
554566
end
555567
end

src/Classes/ImportTab.lua

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -656,9 +656,17 @@ function ImportTabClass:ImportQuestRewardConfig(questStats)
656656
end
657657

658658
local statTotals = {}
659+
local updated = false
659660
for _, stat in ipairs(questStats) do
660661
local key, value = statKey(stat)
661-
statTotals[key] = (statTotals[key] or 0) + value
662+
if key == "# broken boss faces" then
663+
if configTab.placeholder.configBossFaceBroken ~= value then
664+
configTab.placeholder.configBossFaceBroken = value
665+
updated = true
666+
end
667+
else
668+
statTotals[key] = (statTotals[key] or 0) + value
669+
end
662670
end
663671

664672
-- Stats shared by 3+ quests can't be split greedily (two +30 Spirit quests make 40/70 ambiguous),
@@ -703,7 +711,6 @@ function ImportTabClass:ImportQuestRewardConfig(questStats)
703711
return true
704712
end
705713

706-
local updated = false
707714
for _, quest in ipairs(data.questRewards) do
708715
if quest.useConfig == true then
709716
local var = "quest" .. quest.Description .. quest.Area .. quest.Info
@@ -956,10 +963,10 @@ function ImportTabClass:ImportItemsAndSkills(charData)
956963

957964
-- This could be done better with the character melee skills data at some point.
958965
if typeLine:match("Mace Strike") then
959-
local weapon1Sel = self.build.itemsTab.activeItemSet["Weapon 1"].selItemId or 0
960-
local weapon2Sel = self.build.itemsTab.activeItemSet["Weapon 2"].selItemId or 0
966+
local weapon1Sel = self.build.itemsTab.activeItemSet["Weapon 1"] and self.build.itemsTab.activeItemSet["Weapon 1"].selItemId or 0
967+
local weapon2Sel = self.build.itemsTab.activeItemSet["Weapon 2"] and self.build.itemsTab.activeItemSet["Weapon 2"].selItemId or 0
961968
if weapon2Sel == 0 then
962-
if self.build.itemsTab.items[weapon1Sel].base.type == "One Hand Mace" then
969+
if weapon1Sel == 0 or self.build.itemsTab.items[weapon1Sel].base.type == "One Hand Mace" then -- Facebreaker uses single handed mace strike
963970
gemId = "Metadata/Items/Gems/SkillGemPlayerDefault1HMace"
964971
elseif self.build.itemsTab.items[weapon1Sel].base.type == "Two Hand Mace" then
965972
gemId = "Metadata/Items/Gems/SkillGemPlayerDefault2HMace"

0 commit comments

Comments
 (0)