Skip to content

Commit 5ab823a

Browse files
github-actions[bot]mcagnionLocalIdentity
authored
[pob1-port] Preserve skill selection on character reimport (#2079)
* Apply changes from PathOfBuildingCommunity/PathOfBuilding#9486 * Fix merge issues * Add back reimport test * Fix tests * Fix minion imports * Fix skill stat set import --------- Co-authored-by: mcagnion <mcagnion@users.noreply.github.com> Co-authored-by: LocalIdentity <localidentity2@gmail.com>
1 parent dc39a69 commit 5ab823a

3 files changed

Lines changed: 367 additions & 1 deletion

File tree

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
describe("TestImportReimport", function()
2+
local DEFAULT_CHARACTER_LEVEL = 12
3+
local DEFAULT_ITEM_LEVEL = 10
4+
local TEST_IMPORT_ITEM_ID = "test-import-item-1"
5+
6+
before_each(function()
7+
newBuild()
8+
end)
9+
10+
local function makeGemProperties(level)
11+
return {
12+
{ name = "Level", values = { { tostring(level), 0 } } },
13+
{ name = "Quality", values = { { "+0%", 0 } } },
14+
}
15+
end
16+
17+
local function makeGemEntry(support, typeLine, level, socketedItems)
18+
return {
19+
support = support,
20+
typeLine = typeLine,
21+
properties = makeGemProperties(level),
22+
socketedItems = socketedItems,
23+
}
24+
end
25+
26+
-- Build a minimal import item so the tests stay focused on state, not fixture noise.
27+
local function makeImportItem(itemTypeLine, inventoryId, itemId)
28+
return {
29+
id = itemId or TEST_IMPORT_ITEM_ID,
30+
frameType = 0,
31+
name = "",
32+
typeLine = itemTypeLine,
33+
inventoryId = inventoryId,
34+
ilvl = DEFAULT_ITEM_LEVEL,
35+
properties = {},
36+
}
37+
end
38+
39+
-- Build a minimal import payload so the tests stay focused on state, not fixture noise.
40+
local function buildImportPayload(items, skills)
41+
return {
42+
level = DEFAULT_CHARACTER_LEVEL,
43+
equipment = items,
44+
skills = skills,
45+
}
46+
end
47+
48+
local function reimportSkillsWithOptions(itemTypeLine, inventoryId, skills, clearItems)
49+
build.importTab.controls.charImportItemsClearSkills.state = true
50+
build.importTab.controls.charImportItemsClearItems.state = clearItems
51+
build.importTab:ImportItemsAndSkills(buildImportPayload({
52+
makeImportItem(itemTypeLine, inventoryId),
53+
}, skills))
54+
runCallback("OnFrame")
55+
end
56+
57+
local function reimportSingleGemWithOptions(itemTypeLine, inventoryId, gemName, clearItems)
58+
reimportSkillsWithOptions(itemTypeLine, inventoryId, {
59+
makeGemEntry(false, gemName, 20),
60+
}, clearItems)
61+
end
62+
63+
local function reimportSingleGem(itemTypeLine, inventoryId, gemName)
64+
reimportSingleGemWithOptions(itemTypeLine, inventoryId, gemName, false)
65+
end
66+
67+
local function assertReimportPreservesSkillSubstate(itemTypeLine, inventoryId, gemName, fieldName, fieldValue)
68+
build.skillsTab:PasteSocketGroup(string.format([[
69+
%s 20/0 1
70+
]], gemName))
71+
runCallback("OnFrame")
72+
73+
local socketGroup = build.skillsTab.socketGroupList[1]
74+
local srcInstance = socketGroup.displaySkillList[1].activeEffect.srcInstance
75+
srcInstance[fieldName] = fieldValue
76+
srcInstance[fieldName.."Calcs"] = fieldValue
77+
build.modFlag = true
78+
build.buildFlag = true
79+
runCallback("OnFrame")
80+
81+
reimportSingleGem(itemTypeLine, inventoryId, gemName)
82+
83+
socketGroup = build.skillsTab.socketGroupList[1]
84+
srcInstance = socketGroup.displaySkillList[1].activeEffect.srcInstance
85+
assert.are.equal(fieldValue, srcInstance[fieldName])
86+
assert.are.equal(fieldValue, srcInstance[fieldName.."Calcs"])
87+
end
88+
89+
it("preserves full DPS state and manually disabled gems when reimporting items and skills", function()
90+
build.skillsTab:PasteSocketGroup([[
91+
Slot: Gloves
92+
Dark Effigy 1/0 1
93+
Controlled Destruction 1/0 DISABLED 1
94+
]])
95+
runCallback("OnFrame")
96+
97+
local socketGroup = build.skillsTab.socketGroupList[1]
98+
socketGroup.includeInFullDPS = true
99+
socketGroup.mainActiveSkill = 2
100+
runCallback("OnFrame")
101+
102+
build.importTab.controls.charImportItemsClearSkills.state = true
103+
build.importTab.controls.charImportItemsClearItems.state = false
104+
build.importTab:ImportItemsAndSkills(buildImportPayload({
105+
makeImportItem("Wrapped Cap", "Helm"),
106+
}, {
107+
makeGemEntry(false, "Dark Effigy", 2, {
108+
makeGemEntry(true, "Controlled Destruction", 1),
109+
}),
110+
}))
111+
runCallback("OnFrame")
112+
113+
socketGroup = build.skillsTab.socketGroupList[1]
114+
assert.is_true(socketGroup.includeInFullDPS)
115+
assert.are.equal(2, socketGroup.mainActiveSkill)
116+
assert.are.equal(2, socketGroup.gemList[1].level)
117+
assert.is_false(socketGroup.gemList[2].enabled)
118+
end)
119+
120+
it("preserves full DPS state and disabled gems when reimporting with deleted equipment", function()
121+
build.skillsTab:PasteSocketGroup([[
122+
Dark Effigy 1/0 1
123+
Controlled Destruction 1/0 DISABLED 1
124+
]])
125+
runCallback("OnFrame")
126+
127+
local socketGroup = build.skillsTab.socketGroupList[1]
128+
socketGroup.includeInFullDPS = true
129+
socketGroup.mainActiveSkill = 2
130+
runCallback("OnFrame")
131+
132+
reimportSkillsWithOptions("Wrapped Cap", "Helm", {
133+
makeGemEntry(false, "Dark Effigy", 2, {
134+
makeGemEntry(true, "Controlled Destruction", 1),
135+
}),
136+
}, true)
137+
138+
socketGroup = build.skillsTab.socketGroupList[1]
139+
assert.is_true(socketGroup.includeInFullDPS)
140+
assert.are.equal(2, socketGroup.mainActiveSkill)
141+
assert.are.equal(2, socketGroup.gemList[1].level)
142+
assert.is_false(socketGroup.gemList[2].enabled)
143+
end)
144+
145+
it("preserves two socket groups when reimporting items and skills", function()
146+
build.skillsTab:PasteSocketGroup([[
147+
Dark Effigy 1/0 1
148+
Controlled Destruction 1/0 DISABLED 1
149+
]])
150+
runCallback("OnFrame")
151+
152+
build.skillsTab:PasteSocketGroup([[
153+
Fireball 20/0 1
154+
]])
155+
runCallback("OnFrame")
156+
157+
local darkEffigyGroup = build.skillsTab.socketGroupList[1]
158+
darkEffigyGroup.includeInFullDPS = true
159+
darkEffigyGroup.mainActiveSkill = 2
160+
local fireballGroup = build.skillsTab.socketGroupList[2]
161+
fireballGroup.enabled = false
162+
runCallback("OnFrame")
163+
164+
build.importTab.controls.charImportItemsClearSkills.state = true
165+
build.importTab.controls.charImportItemsClearItems.state = false
166+
build.importTab:ImportItemsAndSkills(buildImportPayload({
167+
makeImportItem("Wrapped Cap", "Helm", "test-import-item-helmet"),
168+
makeImportItem("Linen Wraps", "Gloves", "test-import-item-gloves"),
169+
}, {
170+
makeGemEntry(false, "Dark Effigy", 1, {
171+
makeGemEntry(true, "Controlled Destruction", 1),
172+
}),
173+
makeGemEntry(false, "Fireball", 20),
174+
}))
175+
runCallback("OnFrame")
176+
177+
local groupsByGem = {}
178+
for _, socketGroup in ipairs(build.skillsTab.socketGroupList) do
179+
groupsByGem[socketGroup.gemList[1].nameSpec] = socketGroup
180+
end
181+
182+
assert.are.equal(2, #build.skillsTab.socketGroupList)
183+
assert.is_not_nil(groupsByGem["Dark Effigy"])
184+
assert.is_not_nil(groupsByGem.Fireball)
185+
assert.is_true(groupsByGem["Dark Effigy"].includeInFullDPS)
186+
assert.are.equal(2, groupsByGem["Dark Effigy"].mainActiveSkill)
187+
assert.is_false(groupsByGem.Fireball.enabled)
188+
end)
189+
190+
it("preserves skill part selection when reimporting items and skills", function()
191+
assertReimportPreservesSkillSubstate("Twig Focus", "Offhand", "Dark Effigy", "skillPart", 2)
192+
end)
193+
194+
it("preserves stage count when reimporting items and skills", function()
195+
assertReimportPreservesSkillSubstate("Withered Wand", "Weapon", "Flameblast", "skillStageCount", 8)
196+
end)
197+
198+
it("preserves minion skill when reimporting items and skills", function()
199+
assertReimportPreservesSkillSubstate("Linen Wraps", "Gloves", "Skeletal Sniper", "skillMinionSkill", 2)
200+
end)
201+
202+
it("preserves minion skill stat set when reimporting items and skills", function()
203+
build.skillsTab:PasteSocketGroup([[
204+
Skeletal Sniper 20/0 1
205+
]])
206+
runCallback("OnFrame")
207+
208+
local socketGroup = build.skillsTab.socketGroupList[1]
209+
local activeEffect = socketGroup.displaySkillList[1].activeEffect
210+
local grantedEffectId = activeEffect.grantedEffect.id
211+
local srcInstance = activeEffect.srcInstance
212+
srcInstance.skillMinionSkill = 2
213+
srcInstance.skillMinionSkillCalcs = 2
214+
srcInstance.skillMinionSkillStatSetIndexLookup = { [grantedEffectId] = { [2] = 3 } }
215+
srcInstance.skillMinionSkillStatSetIndexLookupCalcs = { [grantedEffectId] = { [2] = 2 } }
216+
217+
reimportSingleGem("Linen Wraps", "Gloves", "Skeletal Sniper")
218+
219+
socketGroup = build.skillsTab.socketGroupList[1]
220+
activeEffect = socketGroup.displaySkillList[1].activeEffect
221+
grantedEffectId = activeEffect.grantedEffect.id
222+
srcInstance = activeEffect.srcInstance
223+
assert.are.equal(2, srcInstance.skillMinionSkill)
224+
assert.are.equal(2, srcInstance.skillMinionSkillCalcs)
225+
assert.are.equal(3, srcInstance.skillMinionSkillStatSetIndexLookup[grantedEffectId][2])
226+
assert.are.equal(2, srcInstance.skillMinionSkillStatSetIndexLookupCalcs[grantedEffectId][2])
227+
end)
228+
229+
it("preserves active skill stat set when reimporting items and skills", function()
230+
build.skillsTab:PasteSocketGroup([[
231+
Fireball 20/0 1
232+
]])
233+
runCallback("OnFrame")
234+
235+
local socketGroup = build.skillsTab.socketGroupList[1]
236+
local activeEffect = socketGroup.displaySkillList[1].activeEffect
237+
local grantedEffectId = activeEffect.grantedEffect.id
238+
local srcInstance = activeEffect.srcInstance
239+
srcInstance.statSet = { [grantedEffectId] = 3 }
240+
srcInstance.statSetCalcs = { [grantedEffectId] = 2 }
241+
242+
reimportSingleGem("Linen Wraps", "Gloves", "Fireball")
243+
244+
socketGroup = build.skillsTab.socketGroupList[1]
245+
activeEffect = socketGroup.displaySkillList[1].activeEffect
246+
grantedEffectId = activeEffect.grantedEffect.id
247+
srcInstance = activeEffect.srcInstance
248+
assert.are.equal(3, srcInstance.statSet[grantedEffectId])
249+
assert.are.equal(2, srcInstance.statSetCalcs[grantedEffectId])
250+
end)
251+
end)

spec/System/TestItemMods_spec.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ describe("TetsItemMods", function()
246246
{range:1}(15-20)% increased Cold Damage per 1% Missing Cold Resistance, up to a maximum of 300%
247247
{range:1}(15-20)% increased Fire Damage per 1% Missing Fire Resistance, up to a maximum of 300%]])
248248
build.itemsTab:AddDisplayItem()
249-
build.skillsTab:PasteSocketGroup("Slot: Weapon 1\nFireball 20/0 Default 1\n")
249+
build.skillsTab:PasteSocketGroup("Slot: Weapon 1\nFireball 20/0 1\n")
250250
runCallback("OnFrame")
251251

252252
assert.are_not.equals(340, build.calcsTab.mainEnv.modDB:Sum("INC", "FireDamage"))

0 commit comments

Comments
 (0)