Skip to content

Commit a196919

Browse files
committed
Handle new quest_stats value sent as aggregated instead of exact lines. Add Tribal Medicine quest
1 parent 9c2bf03 commit a196919

3 files changed

Lines changed: 171 additions & 24 deletions

File tree

spec/System/TestImportTab_spec.lua

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,119 @@ describe("ImportTab", function()
4444
assert.True(importTab.controls.charSelect.list[1].detail:match("Future Ascendancy") ~= nil)
4545
end)
4646
end)
47+
48+
describe("ImportTab quest reward import", function()
49+
before_each(function()
50+
newBuild()
51+
end)
52+
53+
local function findQuest(info)
54+
for _, quest in ipairs(data.questRewards) do
55+
if quest.Info == info then
56+
return "quest" .. quest.Description .. quest.Area .. quest.Info, quest
57+
end
58+
end
59+
error("quest reward not found for Info: " .. info)
60+
end
61+
62+
local function importStats(questStats)
63+
build.importTab:ImportQuestRewardConfig(questStats)
64+
return build.configTab.input
65+
end
66+
67+
it("decomposes 0.5 rolled-up totals across the contributing quests", function()
68+
local input = importStats({
69+
"+20 to maximum Life",
70+
"+60 to [Spirit|Spirit]",
71+
"+15% to [Resistances|Cold Resistance]",
72+
"+15% to [Resistances|Fire Resistance]",
73+
"+15% to [Resistances|Lightning Resistance]",
74+
})
75+
76+
assert.is_true(input[(findQuest("Candlemass"))])
77+
assert.is_true(input[(findQuest("King In The Mists"))])
78+
assert.is_true(input[(findQuest("Ignagduk"))])
79+
assert.is_true(input[(findQuest("Beira"))])
80+
assert.is_true(input[(findQuest("Blackjaw"))])
81+
assert.is_true(input[(findQuest("Sisters of Garukhan Shrine"))])
82+
83+
local tasalioVar, tasalio = findQuest("Tasalio's Test")
84+
assert.are.equals(tasalio.Options[2], input[tasalioVar])
85+
local ngamahuVar, ngamahu = findQuest("Ngamahu's Test")
86+
assert.are.equals(ngamahu.Options[2], input[ngamahuVar])
87+
local tawhoaVar, tawhoa = findQuest("Tawhoa's Test")
88+
assert.are.equals(tawhoa.Options[2], input[tawhoaVar])
89+
90+
assert.is_false(input[(findQuest("Lythara"))])
91+
end)
92+
93+
it("sums duplicate 0.4 per-line Spirit rewards the same as the 0.5 summed total", function()
94+
local input = importStats({
95+
"+30 to [Spirit|Spirit]",
96+
"+30 to [Spirit|Spirit]",
97+
"+40 to [Spirit|Spirit]",
98+
})
99+
assert.is_true(input[(findQuest("King In The Mists"))])
100+
assert.is_true(input[(findQuest("Ignagduk"))])
101+
assert.is_true(input[(findQuest("Lythara"))])
102+
end)
103+
104+
it("ticks exactly one of the interchangeable +30 Spirit quests when only one is claimed", function()
105+
local input = importStats({ "+30 to [Spirit|Spirit]" })
106+
local king = input[(findQuest("King In The Mists"))]
107+
local ignagduk = input[(findQuest("Ignagduk"))]
108+
assert.is_false(input[(findQuest("Lythara"))])
109+
assert.are.equals(1, (king and 1 or 0) + (ignagduk and 1 or 0))
110+
end)
111+
112+
it("disambiguates Spirit total 70 to one +30 plus Lythara, not 30+30", function()
113+
local input = importStats({ "+70 to [Spirit|Spirit]" })
114+
assert.is_true(input[(findQuest("Lythara"))])
115+
local king = input[(findQuest("King In The Mists"))]
116+
local ignagduk = input[(findQuest("Ignagduk"))]
117+
assert.are.equals(1, (king and 1 or 0) + (ignagduk and 1 or 0))
118+
end)
119+
120+
it("disambiguates Spirit total 40 to Lythara alone, not a +30", function()
121+
local input = importStats({ "+40 to [Spirit|Spirit]" })
122+
assert.is_true(input[(findQuest("Lythara"))])
123+
assert.is_false(input[(findQuest("King In The Mists"))])
124+
assert.is_false(input[(findQuest("Ignagduk"))])
125+
end)
126+
127+
it("selects the correct multi-line option and leaves unclaimed option quests at None", function()
128+
local medallionVar, medallion = findQuest("Medallion")
129+
local input = importStats({
130+
"30% increased [Charm] Effect Duration",
131+
"+1 [Charm] Slot",
132+
})
133+
assert.are.equals(medallion.Options[2], input[medallionVar])
134+
assert.are.equals("None", input[(findQuest("Seven Pillars"))])
135+
assert.is_false(input[(findQuest("Beira"))])
136+
end)
137+
138+
it("credits Tribal Medicine's Kaom option, not Seven Pillars which shares the stat at 15%", function()
139+
local tribalVar, tribal = findQuest("Tribal Medicine")
140+
local input = importStats({ "30% increased Global [Armour], [Evasion] and [EnergyShield|Energy Shield]" })
141+
assert.are.equals(tribal.Options[1], input[tribalVar])
142+
assert.are.equals("None", input[(findQuest("Seven Pillars"))])
143+
end)
144+
145+
it("selects Tribal Medicine's Rakiata multi-stat option", function()
146+
local tribalVar, tribal = findQuest("Tribal Medicine")
147+
local input = importStats({
148+
"+15% of [Armour|Armour] also applies to [ElementalDamage|Elemental Damage]",
149+
"Gain [Deflect|Deflection Rating] equal to 12% of [Evasion|Evasion Rating]",
150+
"12% [FasterESRechargeStart|faster start of Energy Shield Recharge]",
151+
})
152+
assert.are.equals(tribal.Options[2], input[tribalVar])
153+
end)
154+
155+
it("decomposes Global Armour, Evasion and Energy Shield shared by Tribal Medicine (30%) and Seven Pillars (15%)", function()
156+
local tribalVar, tribal = findQuest("Tribal Medicine")
157+
local sevenVar, seven = findQuest("Seven Pillars")
158+
local input = importStats({ "45% increased Global [Armour], [Evasion] and [EnergyShield|Energy Shield]" })
159+
assert.are.equals(tribal.Options[1], input[tribalVar])
160+
assert.are.equals(seven.Options[3], input[sevenVar])
161+
end)
162+
end)

src/Classes/ImportTab.lua

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55
--
66
local ipairs = ipairs
77
local t_insert = table.insert
8-
local t_remove = table.remove
98
local b_rshift = bit.rshift
109
local band = bit.band
1110
local m_max = math.max
1211
local dkjson = require "dkjson"
1312

13+
local tradeHelpers = LoadModule("Classes/TradeHelpers")
14+
1415
local realmList = {
1516
{ label = "PoE2", id = "PoE2", realmCode = "poe2", hostName = "https://www.pathofexile.com/", profileURL = "account/view-profile/" },
1617
}
@@ -646,39 +647,57 @@ end
646647

647648
function ImportTabClass:ImportQuestRewardConfig(questStats)
648649
local configTab = self.build.configTab
649-
local statLines = {}
650+
651+
-- Reduce a stat line to a numberless key + value (e.g. "+30 to [Spirit|Spirit]" -> "+# to spirit", 30)
652+
local function statKey(text)
653+
text = escapeGGGString(text):lower():gsub("^%s+", ""):gsub("%s+$", "")
654+
return tradeHelpers.modLineTemplate(text), tradeHelpers.modLineValue(text) or 0
655+
end
656+
657+
local statTotals = {}
650658
for _, stat in ipairs(questStats) do
651-
t_insert(statLines, escapeGGGString(stat):lower())
659+
local key, value = statKey(stat)
660+
statTotals[key] = (statTotals[key] or 0) + value
661+
end
662+
663+
-- Stats shared by 3+ quests can't be split greedily (two +30 Spirit quests make 40/70 ambiguous),
664+
-- so resolve those by exact total then zero their totals.
665+
local disambiguation = {
666+
["+# to spirit"] = {
667+
[30] = { "King In The Mists" },
668+
[40] = { "Lythara" },
669+
[60] = { "King In The Mists", "Ignagduk" },
670+
[70] = { "King In The Mists", "Lythara" },
671+
[100] = { "King In The Mists", "Ignagduk", "Lythara" },
672+
},
673+
}
674+
local resolved = {}
675+
for stat, byTotal in pairs(disambiguation) do
676+
local taken = byTotal[statTotals[stat] or 0]
677+
if taken then
678+
for _, info in ipairs(taken) do resolved[info] = true end
679+
statTotals[stat] = 0
680+
end
652681
end
653682

654683
local function splitLine(text)
655684
local out = {}
656-
for line in text:gmatch("[^\r\n]+") do
657-
line = line:gsub("^%s+", ""):lower()
658-
t_insert(out, line)
685+
for line in tostring(text):gmatch("[^\r\n]+") do
686+
local key, value = statKey(line)
687+
t_insert(out, { key = key, value = value })
659688
end
660689
return out
661690
end
662691

663-
-- Ensure all required lines exist, then remove them so they can't match again
692+
-- True if the totals still hold every line of the reward; consume them so a later quest can't reclaim it
664693
local function matchQuest(requiredLines)
665-
local indices = {}
666-
for _, needed in ipairs(requiredLines) do
667-
local found
668-
for idx, line in ipairs(statLines) do
669-
if line == needed then
670-
found = idx
671-
break
672-
end
673-
end
674-
if not found then
694+
for _, line in ipairs(requiredLines) do
695+
if (statTotals[line.key] or 0) < line.value then
675696
return false
676697
end
677-
t_insert(indices, found)
678698
end
679-
table.sort(indices, function(a, b) return a > b end)
680-
for _, idx in ipairs(indices) do
681-
t_remove(statLines, idx)
699+
for _, line in ipairs(requiredLines) do
700+
statTotals[line.key] = statTotals[line.key] - line.value
682701
end
683702
return true
684703
end
@@ -688,7 +707,7 @@ function ImportTabClass:ImportQuestRewardConfig(questStats)
688707
if quest.useConfig == true then
689708
local var = "quest" .. quest.Description .. quest.Area .. quest.Info
690709
if quest.Stat then
691-
local matches = matchQuest(splitLine(quest.Stat))
710+
local matches = resolved[quest.Info] or matchQuest(splitLine(quest.Stat))
692711
if configTab.input[var] ~= matches then
693712
configTab.input[var] = matches
694713
updated = true

src/Data/QuestRewards.lua

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,18 @@ return {
157157
["AreaLevel"] = 51,
158158
["useConfig"] = true
159159
},
160+
{
161+
["Act"] = 4,
162+
["Description"] = "Act 4",
163+
["Area"] = "Eye of Hinekora",
164+
["Info"] = "Tribal Medicine",
165+
["Options"] = {
166+
"30% increased Global Armour, Evasion and Energy Shield",
167+
"+15% of Armour also applies to Elemental Damage\n\tGain Deflection Rating equal to 12% of Evasion Rating\n\t12% faster start of Energy Shield Recharge",
168+
},
169+
["AreaLevel"] = 51,
170+
["useConfig"] = true
171+
},
160172
{
161173
["Act"] = 4,
162174
["Description"] = "Act 4",
@@ -252,11 +264,11 @@ return {
252264
["Options"] = {
253265
"+5% to all Elemental Resistances",
254266
"3% increased Movement Speed",
255-
"15% increased Global Defences",
267+
"15% increased Global Armour, Evasion and Energy Shield",
256268
"20% increased Presence Area Of Effect",
257269
"12% increased Cooldown Recovery Rate",
258270
"+5 to all Attributes",
259-
"5% increased Experience Gain\n\t-5% to Elemental Resistances\n\t3% reduced Movement Speed\n\t15% reduced Global Defences\n\t20% reduced Presence Area Of Effect\n\t12% reduced Cooldown Recovery Rate\n\t5% reduced Attributes",
271+
"5% increased Experience Gain\n\t-5% to all Elemental Resistances\n\t3% reduced Movement Speed\n\t15% reduced Global Armour, Evasion and Energy Shield\n\t20% reduced Presence Area Of Effect\n\t12% reduced Cooldown Recovery Rate\n\t5% reduced Attributes",
260272
},
261273
["AreaLevel"] = 63,
262274
["useConfig"] = true

0 commit comments

Comments
 (0)