Skip to content

Commit 33e8a90

Browse files
authored
Merge branch 'dev' into copy-augment-socket-bound
2 parents 51a044f + a82a33b commit 33e8a90

63 files changed

Lines changed: 36513 additions & 35660 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,29 @@
11
# Changelog
22

3+
## [v0.21.0](https://github.com/PathOfBuildingCommunity/PathOfBuilding-PoE2/tree/v0.21.0) (2026/06/13)
4+
5+
[Full Changelog](https://github.com/PathOfBuildingCommunity/PathOfBuilding-PoE2/compare/v0.20.0...v0.21.0)
6+
7+
8+
## What's Changed
9+
### New to Path of Building
10+
- Add 0.5.2 Passive tree changes [\#2162](https://github.com/PathOfBuildingCommunity/PathOfBuilding-PoE2/pull/2162) ([trompetin17](https://github.com/justjuangui))
11+
- Add support for very large time-lost jewel mod [\#2182](https://github.com/PathOfBuildingCommunity/PathOfBuilding-PoE2/pull/2182) ([vaisest](https://github.com/vaisest))
12+
### User Interface
13+
- Fix window title changing in compare tab [\#2179](https://github.com/PathOfBuildingCommunity/PathOfBuilding-PoE2/pull/2179) ([vaisest](https://github.com/vaisest))
14+
### Fixed Calculations
15+
- Fix many supports not adding cooldown [\#2185](https://github.com/PathOfBuildingCommunity/PathOfBuilding-PoE2/pull/2185) ([Peechey](https://github.com/Peechey))
16+
- Fix calculation of inverted Resistances when having a source of Penetration [\#2177](https://github.com/PathOfBuildingCommunity/PathOfBuilding-PoE2/pull/2177) ([LocalIdentity](https://github.com/LocalIdentity))
17+
- Fix Discipline not being converted by Eldritch Battery [\#2172](https://github.com/PathOfBuildingCommunity/PathOfBuilding-PoE2/pull/2172) ([LocalIdentity](https://github.com/LocalIdentity))
18+
### Fixed Behaviours
19+
- Fix Split Personality not working with Monk and Druid starting points [\#2171](https://github.com/PathOfBuildingCommunity/PathOfBuilding-PoE2/pull/2171) ([LocalIdentity](https://github.com/LocalIdentity))
20+
- Fix mods duplicating when crafting some Crossbows [\#2173](https://github.com/PathOfBuildingCommunity/PathOfBuilding-PoE2/pull/2173) ([LocalIdentity](https://github.com/LocalIdentity))
21+
- Fix Full DPS breakdown and gem dropdowns showing the wrong name for Companion / Spectre skills [\#2160](https://github.com/PathOfBuildingCommunity/PathOfBuilding-PoE2/pull/2160) ([ltogniolli](https://github.com/ltogniolli))
22+
- Fix PoB Trader search not working correctly after comparing a build [\#2180](https://github.com/PathOfBuildingCommunity/PathOfBuilding-PoE2/pull/2180) ([vaisest](https://github.com/vaisest))
23+
- Fix Mageblood mods merging in item crafter [\#2186](https://github.com/PathOfBuildingCommunity/PathOfBuilding-PoE2/pull/2186) ([LocalIdentity](https://github.com/LocalIdentity))
24+
25+
26+
327
## [v0.20.0](https://github.com/PathOfBuildingCommunity/PathOfBuilding-PoE2/tree/v0.20.0) (2026/06/11)
428

529
[Full Changelog](https://github.com/PathOfBuildingCommunity/PathOfBuilding-PoE2/compare/v0.19.0...v0.20.0)

changelog.txt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,25 @@
1+
VERSION[0.21.0][2026/06/13]
2+
3+
--- New to Path of Building ---
4+
* Add 0.5.2 Passive tree changes (trompetin17)
5+
* Add support for very large time-lost jewel mod (vaisest)
6+
7+
--- User Interface ---
8+
* Fix window title changing in compare tab (vaisest)
9+
10+
--- Fixed Calculations ---
11+
* Fix many supports not adding cooldown (Peechey)
12+
* Fix calculation of inverted Resistances when having a source of Penetration (LocalIdentity)
13+
* Fix Discipline not being converted by Eldritch Battery (LocalIdentity)
14+
15+
--- Fixed Behaviours ---
16+
* Fix Split Personality not working with Monk and Druid starting points (LocalIdentity)
17+
* Fix mods duplicating when crafting some Crossbows (LocalIdentity)
18+
* Fix Full DPS breakdown and gem dropdowns showing the wrong name for Companion / Spectre skills (ltogniolli)
19+
* Fix PoB Trader search not working correctly after comparing a build (vaisest)
20+
* Fix Mageblood mods merging in item crafter (LocalIdentity)
21+
22+
123
VERSION[0.20.0][2026/06/11]
224

325
--- New to Path of Building ---

manifest.xml

Lines changed: 51 additions & 51 deletions
Large diffs are not rendered by default.

spec/System/TestDefence_spec.lua

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,25 @@ describe("TestDefence", function()
3030
return build.calcsTab.calcs.reducePoolsByDamage(nil, takenDamages, build.calcsTab.calcsEnv.player)
3131
end
3232

33+
it("converts total energy shield modifiers to mana", function()
34+
build.configTab.input.customMods = "Convert 100% of maximum Energy Shield to maximum Mana\n100% increased maximum Mana"
35+
build.configTab:BuildModList()
36+
runCallback("OnFrame")
37+
38+
local manaWithoutTotalEnergyShield = build.calcsTab.mainOutput.Mana
39+
local env = build.calcsTab.mainEnv
40+
local player = env.player
41+
42+
player.modDB:NewMod("EnergyShieldTotal", "BASE", 100, "Test")
43+
player.output = { }
44+
build.calcsTab.calcs.defence(env, player)
45+
46+
assert.are.equals(0, player.output.EnergyShield)
47+
assert.are.equals(0, player.modDB:Sum("BASE", nil, "ExtraMana"))
48+
assert.are.equals(100, player.modDB:Sum("BASE", nil, "ManaTotal"))
49+
assert.are.equals(manaWithoutTotalEnergyShield + 100, player.output.Mana)
50+
end)
51+
3352
it("no armour max hits", function()
3453
build.configTab.input.enemyIsBoss = "None"
3554
build.configTab.input.customMods = ""

spec/System/TestImportTab_spec.lua

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -47,17 +47,18 @@ describe("ImportTab", function()
4747
it("imports Split Personality alternate class start from character JSON", function()
4848
local spec = build.spec
4949
local socketNode = spec.nodes[60735]
50-
local rangerStartPassive = spec.nodes[56651]
50+
local templarStartPassive = spec.nodes[13855]
5151
assert.is_not_nil(socketNode)
52-
assert.is_not_nil(rangerStartPassive)
52+
assert.is_not_nil(templarStartPassive)
53+
assert.is_nil(spec.tree.classNameMap.Templar)
54+
assert.are.equals(61525, spec.tree.classStartNodeNameMap.Templar)
5355

54-
local hashes = { socketNode.id, rangerStartPassive.id }
56+
local hashes = { socketNode.id, templarStartPassive.id }
5557
for _, pathNode in ipairs(socketNode.path or { }) do
5658
table.insert(hashes, pathNode.id)
5759
end
5860

59-
local rangerStart = spec.nodes[spec.tree.classes[spec.tree.classNameMap.Ranger].startNodeId]
60-
local importPayload = {
61+
build.importTab:ImportPassiveTreeAndJewels({
6162
name = "Split Import Test",
6263
class = "Witch2",
6364
league = "Test",
@@ -73,7 +74,7 @@ describe("ImportTab", function()
7374
ilvl = 84,
7475
properties = { },
7576
explicitMods = {
76-
"Can Allocate Passive Skills from the Ranger's starting point",
77+
"Can Allocate Passive Skills from the Templar's starting point",
7778
},
7879
},
7980
},
@@ -84,17 +85,14 @@ describe("ImportTab", function()
8485
jewel_data = { },
8586
quest_stats = { },
8687
},
87-
}
88-
89-
build.importTab:ImportPassiveTreeAndJewels(importPayload)
88+
})
9089

9190
local importedSpec = build.spec
9291
local importedJewel = build.itemsTab.items[importedSpec.jewels[socketNode.id]]
93-
assert.are.equals("Ranger", importedJewel.jewelData.alternateClassStart)
94-
95-
assert.are.equals(0, importedSpec.nodes[rangerStart.id].pathDist)
96-
assert.True(importedSpec.nodes[rangerStartPassive.id].alloc)
97-
assert.True(importedSpec.nodes[rangerStartPassive.id].connectedToStart)
92+
assert.are.equals("Templar", importedJewel.jewelData.alternateClassStart)
93+
assert.are.equals(0, importedSpec.nodes[spec.tree.classStartNodeNameMap.Templar].pathDist)
94+
assert.True(importedSpec.nodes[templarStartPassive.id].alloc)
95+
assert.True(importedSpec.nodes[templarStartPassive.id].connectedToStart)
9896
end)
9997
end)
10098

spec/System/TestItemMods_spec.lua

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,32 @@ describe("TetsItemMods", function()
77
-- newBuild() takes care of resetting everything in setup()
88
end)
99

10+
it("shows duplicate selected variants in item tooltips when enabled", function()
11+
local item = new("Item", [[
12+
Rarity: Unique
13+
Mageblood
14+
Utility Belt
15+
Has Alt Variant: true
16+
Selected Variant: 1
17+
Selected Alt Variant: 1
18+
Allow Duplicate Variants: true
19+
Variant: Legacy of Amethyst
20+
Implicits: 0
21+
{variant:1}Legacy of Amethyst
22+
]])
23+
local tooltip = new("Tooltip")
24+
25+
build.itemsTab:AddItemTooltip(tooltip, item)
26+
27+
local legacyLines = 0
28+
for _, line in ipairs(tooltip.lines) do
29+
if line.text and line.text:find("Legacy of Amethyst", 1, true) then
30+
legacyLines = legacyLines + 1
31+
end
32+
end
33+
assert.are.equals(2, legacyLines)
34+
end)
35+
1036
it("aggregates matching ring item rarity lines before applying ring bonus effect", function()
1137
build.configTab.input.customMods = "30% increased bonuses gained from left Equipped Ring"
1238
build.configTab:BuildModList()

spec/System/TestItemParse_spec.lua

Lines changed: 95 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,45 @@ describe("TestItemParse", function()
8585
--it("Variant name", function()
8686
--end)
8787

88-
--it("variant", function()
89-
--end)
88+
it("allows duplicate selected variants when enabled", function()
89+
local item = new("Item", [[
90+
Rarity: Unique
91+
Mageblood
92+
Utility Belt
93+
Has Alt Variant: true
94+
Has Alt Variant Two: true
95+
Has Alt Variant Three: true
96+
Selected Variant: 1
97+
Selected Alt Variant: 1
98+
Selected Alt Variant Two: 2
99+
Selected Alt Variant Three: 2
100+
Allow Duplicate Variants: true
101+
Variant: Legacy of Amethyst
102+
Variant: Legacy of Basalt
103+
Implicits: 0
104+
{variant:1}Legacy of Amethyst
105+
{variant:2}Legacy of Basalt
106+
]])
107+
108+
assert.are.equals(2, item.baseModList:Sum("BASE", nil, "LegacyOfAmethyst"))
109+
assert.are.equals(2, item.baseModList:Sum("BASE", nil, "LegacyOfBasalt"))
110+
end)
111+
112+
it("does not duplicate selected variants by default", function()
113+
local item = new("Item", [[
114+
Rarity: Unique
115+
Mageblood
116+
Utility Belt
117+
Has Alt Variant: true
118+
Selected Variant: 1
119+
Selected Alt Variant: 1
120+
Variant: Legacy of Amethyst
121+
Implicits: 0
122+
{variant:1}Legacy of Amethyst
123+
]])
124+
125+
assert.are.equals(1, item.baseModList:Sum("BASE", nil, "LegacyOfAmethyst"))
126+
end)
90127

91128
--TODO: Alt variants for POB2
92129
--it("Alt Variant", function()
@@ -248,6 +285,62 @@ describe("TestItemParse", function()
248285
assert.are.equals("+44 to Spirit", item.explicitModLines[1].line)
249286
end)
250287

288+
it("Crafted affixes matching base implicits stay explicit", function()
289+
local item = new("Item", [[
290+
Rarity: Rare
291+
New Item
292+
Gemini Crossbow
293+
Crafted: true
294+
Prefix: None
295+
Prefix: None
296+
Prefix: None
297+
Suffix: {range:0}AdditionalAmmo1
298+
Suffix: None
299+
Suffix: None
300+
Implicits: 1
301+
Loads an additional bolt
302+
]])
303+
304+
item:Craft()
305+
assert.are.equals(1, #item.implicitModLines)
306+
assert.are.equals("Loads an additional bolt", item.implicitModLines[1].line)
307+
assert.are.equals(1, #item.explicitModLines)
308+
assert.are.equals("Loads an additional bolt", item.explicitModLines[1].line)
309+
310+
item.suffixes[1].range = 0.2
311+
item:Craft()
312+
assert.are.equals(1, #item.implicitModLines)
313+
assert.are.equals(1, #item.explicitModLines)
314+
assert.are.equals("Loads an additional bolt", item.explicitModLines[1].line)
315+
end)
316+
317+
it("Pasted affixes matching base implicits stay explicit", function()
318+
local item = new("Item", [[
319+
Item Class: Crossbows
320+
Rarity: Rare
321+
New Item
322+
Gemini Crossbow
323+
--------
324+
Physical Damage: 28-112
325+
Critical Hit Chance: 5.00%
326+
Attacks per Second: 1.60
327+
Reload Time: 1.10
328+
--------
329+
Requires: Level 78, 89 Str, 89 Dex
330+
--------
331+
Item Level: 82
332+
--------
333+
Loads an additional bolt (implicit)
334+
--------
335+
Loads an additional bolt
336+
]])
337+
338+
assert.are.equals(1, #item.implicitModLines)
339+
assert.are.equals("Loads an additional bolt", item.implicitModLines[1].line)
340+
assert.are.equals(1, #item.explicitModLines)
341+
assert.are.equals("Loads an additional bolt", item.explicitModLines[1].line)
342+
end)
343+
251344
--TODO: POB2 Leagues?
252345
--it("League", function()
253346
--end)

spec/System/TestSkills_spec.lua

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,27 @@ describe("TestSkills", function()
377377
end
378378
end)
379379

380+
it("uses selected companion names in skill displays", function()
381+
build.skillsTab:PasteSocketGroup("Companion: Lightless Abomination 20/0 1")
382+
build.skillsTab:PasteSocketGroup("Companion: Lightless Moray 20/0 1")
383+
build.skillsTab.socketGroupList[1].includeInFullDPS = true
384+
build.skillsTab.socketGroupList[2].includeInFullDPS = true
385+
runCallback("OnFrame")
386+
387+
local skillNames = { }
388+
for _, skill in ipairs(build.calcsTab.mainOutput.SkillDPS) do
389+
skillNames[skill.name] = true
390+
end
391+
assert.is_true(skillNames["Companion: Lightless Abomination"])
392+
assert.is_true(skillNames["Companion: Lightless Moray"])
393+
394+
build:RefreshSkillSelectControls(build.controls, 1, "")
395+
assert.are.equals("Companion: Lightless Abomination", build.controls.mainSkill.list[1].label)
396+
397+
build:RefreshSkillSelectControls(build.controls, 2, "")
398+
assert.are.equals("Companion: Lightless Moray", build.controls.mainSkill.list[1].label)
399+
end)
400+
380401
it("Inspiring Ally only mirrors companion damage, not generic minion damage", function()
381402
build.itemsTab:CreateDisplayItemFromRaw([[
382403
New Item
@@ -798,6 +819,20 @@ describe("TestSkills", function()
798819
assert.True(build.calcsTab.mainEnv.enemyDB:Sum("BASE", nil, "FireResist") < fireResistWithoutPotentExposure)
799820
end)
800821

822+
it("averages inverted elemental resistance after penetration", function()
823+
build.skillsTab:PasteSocketGroup("Fireball 20/0 1")
824+
build.configTab.input.enemyIsBoss = "None"
825+
build.configTab.input.enemyFireResist = 50
826+
build.configTab.input.customMods = "Hits have 50% chance to treat Enemy Monster Elemental Resistance values as inverted\nDamage Penetrates 50% of Enemy Fire Resistance"
827+
build.configTab:BuildModList()
828+
runCallback("OnFrame")
829+
830+
assert.are.equals(1.25, build.calcsTab.calcsOutput.FireEffMult)
831+
local breakdownText = table.concat(build.calcsTab.calcsEnv.player.breakdown.FireEffMult, "\n")
832+
assert.truthy(breakdownText:match("inverted hit"))
833+
assert.truthy(breakdownText:match("weighted average"))
834+
end)
835+
801836
it("Test granted skills with exposure stats make exposure configurable", function()
802837
build.skillsTab:PasteSocketGroup("Fireball 20/0 1")
803838
local spec = build.spec

src/Classes/CompareEntry.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ function CompareEntryClass:RefreshSkillSelectControls(controls, mainGroup, suffi
326326
local explodeSource = activeSkill.activeEffect.srcInstance.explodeSource
327327
local explodeSourceName = explodeSource and (explodeSource.name or explodeSource.dn)
328328
local colourCoded = explodeSourceName and ("From "..colorCodes[explodeSource.rarity or "NORMAL"]..explodeSourceName)
329-
t_insert(controls.mainSkill.list, { val = i, label = colourCoded or activeSkill.activeEffect.grantedEffect.name })
329+
t_insert(controls.mainSkill.list, { val = i, label = colourCoded or self.calcsTab.calcs.getActiveSkillDisplayName(activeSkill) })
330330
end
331331
controls.mainSkill.enabled = #displaySkillList > 1
332332
controls.mainSkill.selIndex = mainActiveSkill

src/Classes/CompareTab.lua

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1310,6 +1310,10 @@ function CompareTabClass:ImportBuild(xmlText, label)
13101310
t_insert(self.compareEntries, entry)
13111311
self.activeCompareIndex = #self.compareEntries
13121312
self:UpdateBuildSelector()
1313+
-- Restore primary build's window title
1314+
if self.primaryBuild.spec then
1315+
self.primaryBuild.spec:SetWindowTitleWithBuildClass()
1316+
end
13131317
return true
13141318
end
13151319
return false

0 commit comments

Comments
 (0)