Skip to content

Commit eddf591

Browse files
authored
Add tree for Legacy of Phrecia (#8494)
* Add Legacy of Phrecia trees * Support import/export of tree links * Remove debugging lines * Remove temporary jewels * Restore English Harbinger node names * Fixup PoEPlanner import * Have tree conversion respect new alternate version * Change ascendancy names in the dropdown when the tree version changes * Identify imported tree version based on ascendancy name * Update class name colors in the import tab
1 parent 4a4b7a1 commit eddf591

Some content is hidden

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

43 files changed

+192492
-1051
lines changed

RELEASE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ Steps:
5050
* `skills-3.jpg`
5151
* `skills-disabled-3.jpg`.
5252
5. Run `./fix_ascendancy_positions.py`.
53-
6. Open `./src/GameVersions.lua` and update `treeVersionList` and `treeVersions`
53+
6. Open `./src/GameVersions.lua` and update `treeVersionList`, `treeVersions`, and `poePlannerVersions`. The latter can be found via https://cdn.poeplanner.com/json/versions.json
5454
according to the file's format. This is important, otherwise the JSON data converter
5555
won't trigger.
5656
7. Restart Path of Building Community. This should result in a new file `tree.lua`.

src/Classes/ImportTab.lua

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -523,19 +523,25 @@ function ImportTabClass:BuildCharacterList(league)
523523
classColor = colorCodes[charClass:upper()]
524524

525525
if classColor == nil then
526-
if (charClass == "Elementalist" or charClass == "Necromancer" or charClass == "Occultist") then
526+
if (charClass == "Elementalist" or charClass == "Necromancer" or charClass == "Occultist" or
527+
charClass == "Harbinger" or charClass == "Herald" or charClass == "Bog Shaman") then
527528
classColor = colorCodes["WITCH"]
528-
elseif (charClass == "Guardian" or charClass == "Inquisitor" or charClass == "Hierophant") then
529+
elseif (charClass == "Guardian" or charClass == "Inquisitor" or charClass == "Hierophant" or
530+
charClass == "Architect of Chaos" or charClass == "Polytheist" or charClass == "Puppeteer") then
529531
classColor = colorCodes["TEMPLAR"]
530-
elseif (charClass == "Assassin" or charClass == "Trickster" or charClass == "Saboteur") then
532+
elseif (charClass == "Assassin" or charClass == "Trickster" or charClass == "Saboteur" or
533+
charClass == "Surfcaster" or charClass == "Servant of Arakaali" or charClass == "Blind Prophet") then
531534
classColor = colorCodes["SHADOW"]
532-
elseif (charClass == "Gladiator" or charClass == "Slayer" or charClass == "Champion") then
535+
elseif (charClass == "Gladiator" or charClass == "Slayer" or charClass == "Champion" or
536+
charClass == "Gambler" or charClass == "Paladin" or charClass == "Aristocrat") then
533537
classColor = colorCodes["DUELIST"]
534-
elseif (charClass == "Raider" or charClass == "Pathfinder" or charClass == "Deadeye" or charClass == "Warden") then
538+
elseif (charClass == "Raider" or charClass == "Pathfinder" or charClass == "Deadeye" or charClass == "Warden" or
539+
charClass == "Daughter of Oshabi" or charClass == "Whisperer" or charClass == "Wildspeaker") then
535540
classColor = colorCodes["RANGER"]
536-
elseif (charClass == "Juggernaut" or charClass == "Berserker" or charClass == "Chieftain") then
541+
elseif (charClass == "Juggernaut" or charClass == "Berserker" or charClass == "Chieftain" or
542+
charClass == "Antiquarian" or charClass == "Behemoth" or charClass == "Ancestral Commander") then
537543
classColor = colorCodes["MARAUDER"]
538-
elseif (charClass == "Ascendant") then
544+
elseif (charClass == "Ascendant" or charClass == "Scavenger") then
539545
classColor = colorCodes["SCION"]
540546
end
541547
end
@@ -676,7 +682,31 @@ function ImportTabClass:ImportPassiveTreeAndJewels(json, charData)
676682
self.build.itemsTab:PopulateSlots()
677683
self.build.itemsTab:AddUndoState()
678684

679-
self.build.spec:ImportFromNodeList(charPassiveData.character, charPassiveData.ascendancy, charPassiveData.alternate_ascendancy or 0, charPassiveData.hashes, charPassiveData.skill_overrides, charPassiveData.mastery_effects or {}, latestTreeVersion .. (charData.league:match("Ruthless") and "_ruthless" or ""))
685+
-- Alternate trees don't have an identifier, so we're forced to look up something that is unique to that tree
686+
-- Hopefully this changes, because it's totally unmaintainable
687+
local function isAscendancyInTree(className, treeVersion)
688+
local classes = main.tree[treeVersion].classes
689+
for _, class in pairs(classes) do
690+
if class.name == className then
691+
return true
692+
end
693+
for i = 0, #class.classes do
694+
local ascendClass = class.classes[i]
695+
if ascendClass.name == className then
696+
return true
697+
end
698+
end
699+
end
700+
end
701+
702+
self.build.spec:ImportFromNodeList(charPassiveData.character,
703+
charPassiveData.ascendancy,
704+
charPassiveData.alternate_ascendancy or 0,
705+
charPassiveData.hashes,
706+
charPassiveData.skill_overrides,
707+
charPassiveData.mastery_effects or {},
708+
latestTreeVersion .. (charData.league:match("Ruthless") and "_ruthless" or "") .. (isAscendancyInTree(charData.class, latestTreeVersion) and "" or "_alternate")
709+
)
680710
self.build.spec:AddUndoState()
681711
self.build.characterLevel = charData.level
682712
self.build.characterLevelAutoMode = false

src/Classes/PassiveSpec.lua

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -356,56 +356,70 @@ function PassiveSpecClass:DecodePoePlannerURL(url, return_tree_version_only)
356356
return bytes:byte(start) + bytes:byte(start + 1) * 256
357357
end
358358

359-
local function translatePoepToGggTreeVersion(minor)
360-
-- Translates internal tree version to GGG version.
361-
-- Limit poeplanner tree imports to recent versions.
362-
tree_versions = { -- poeplanner ID: GGG version
363-
[31] = 24, [29] = 23, [27] = 22, [26] = 21, [25] = 20, [24] = 19, [23] = 18,
364-
}
365-
if tree_versions[minor] then
366-
return tree_versions[minor]
367-
else
368-
return -1
369-
end
370-
end
371-
372359
local b = common.base64.decode(url:gsub("^.+/",""):gsub("-","+"):gsub("_","/"))
373360
if not b or #b < 15 then
374361
return "Invalid tree link (unrecognised format)."
375362
end
376363
-- Quick debug for when we change tree versions. Print the first 20 or so bytes
377364
-- s = ""
378365
-- for i = 1, 20 do
379-
-- s = s..i..":"..string.format('%02X ', b:byte(i))
366+
-- s = s..i..":"..string.format('%02X ', b:byte(i))
380367
-- end
381368
-- print(s)
382369

383-
-- 4-7 is tree version.version
384-
major_version = byteToInt(b,4)
385-
minor_version = translatePoepToGggTreeVersion(byteToInt(b,6))
370+
--[[
371+
PoEPlanner URL format:
372+
serializationVersion: u16
373+
buildType: u8 (either normal or royale)
374+
isPoE2: u8
375+
treeBuild: TreeBuild
376+
equipmentBuild: EquipmentBuild
377+
skillBuild: SkillBuild
378+
buildConfig: BuildConfig
379+
compressedNotesLength: u16
380+
compressedNotesBytes: []u8 (gzip)
381+
382+
TreeBuild:
383+
treeSerializationVersion: u16
384+
treeVersion: u16
385+
character: u8
386+
ascendancy: u8
387+
banditChoice: u8
388+
nodeCount: u16
389+
nodeHashes: []u16
390+
clusterNodeCount: u16
391+
clusterNodeHashes: []u16
392+
ascendancyNodeCount: u16
393+
ascendancyNodeHashes: []u16
394+
selectedMasteryEffectCount: u16
395+
selectedMasteryEffects: {masteryID: u16, effectID: u16}
396+
selectedAttributeChoiceCount: u16
397+
selectedAttributeChoices: {nodeHash: u16, choice: u8} (for choice: 0: none, 1: str, 2: dex, 3: int)
398+
]]
399+
386400
-- If we only want the tree version, exit now
387-
if minor_version < 0 then
401+
if not poePlannerVersions[byteToInt(b, 7)] then
388402
return "Invalid tree version found in link."
389403
end
390404
if return_tree_version_only then
391-
return major_version.."_"..minor_version
405+
return poePlannerVersions[byteToInt(b, 7)]
392406
end
393407

394-
-- 8 is Class, 9 is Ascendancy
395-
local classId = b:byte(8)
396-
local ascendClassId = b:byte(9)
408+
-- 9 is Class, 10 is Ascendancy
409+
local classId = b:byte(9)
410+
local ascendClassId = b:byte(10)
397411
-- print("classId, ascendClassId", classId, ascendClassId)
398412

399-
-- 9 is Bandit
413+
-- 11 is Bandit
400414
-- bandit = b[9]
401415
-- print("bandit", bandit, bandit_list[bandit])
402416

403417
self:ResetNodes()
404418
self:SelectClass(classId)
405419
self:SelectAscendClass(ascendClassId)
406420

407-
-- 11 is node count
408-
idx = 11
421+
-- 12-13 is node count
422+
idx = 12
409423
local nodesCount = byteToInt(b, idx)
410424
local nodesEnd = idx + 2 + (nodesCount * 2)
411425
local nodes = b:sub(idx + 2, nodesEnd - 1)

src/Classes/TreeTab.lua

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,7 @@ function TreeTabClass:SetActiveSpec(specId)
447447
self.build.spec = curSpec
448448
self.build.buildFlag = true
449449
self.build.spec:SetWindowTitleWithBuildClass()
450+
self.build:UpdateClassDropdowns(curSpec.treeVersion)
450451
for _, slot in pairs(self.build.itemsTab.slots) do
451452
if slot.nodeId then
452453
if prevSpec then
@@ -483,10 +484,11 @@ function TreeTabClass:SetCompareSpec(specId)
483484
self.compareSpec = curSpec
484485
end
485486

486-
function TreeTabClass:ConvertToVersion(version, remove, success, ignoreRuthlessCheck)
487-
if not ignoreRuthlessCheck and self.build.spec.treeVersion:match("ruthless") and not version:match("ruthless") then
488-
if isValueInTable(treeVersionList, version.."_ruthless") then
489-
version = version.."_ruthless"
487+
function TreeTabClass:ConvertToVersion(version, remove, success, ignoreTreeSubType)
488+
local treeSubTypeCapture = self.build.spec.treeVersion:match("(_%l+_?%l*)")
489+
if not ignoreTreeSubType and treeSubTypeCapture and not version:match(treeSubTypeCapture) then
490+
if isValueInTable(treeVersionList, version..treeSubTypeCapture) then
491+
version = version..treeSubTypeCapture
490492
end
491493
end
492494
local newSpec = new("PassiveSpec", self.build, version)
@@ -543,16 +545,16 @@ function TreeTabClass:OpenSpecManagePopup()
543545
})
544546
end
545547

546-
function TreeTabClass:OpenVersionConvertPopup(version, ignoreRuthlessCheck)
548+
function TreeTabClass:OpenVersionConvertPopup(version, ignoreTreeSubType)
547549
local controls = { }
548550
controls.warningLabel = new("LabelControl", nil, {0, 20, 0, 16}, "^7Warning: some or all of the passives may be de-allocated due to changes in the tree.\n\n" ..
549551
"Convert will replace your current tree.\nCopy + Convert will backup your current tree.\n")
550552
controls.convert = new("ButtonControl", nil, {-125, 105, 100, 20}, "Convert", function()
551-
self:ConvertToVersion(version, true, false, ignoreRuthlessCheck)
553+
self:ConvertToVersion(version, true, false, ignoreTreeSubType)
552554
main:ClosePopup()
553555
end)
554556
controls.convertCopy = new("ButtonControl", nil, {0, 105, 125, 20}, "Copy + Convert", function()
555-
self:ConvertToVersion(version, false, false, ignoreRuthlessCheck)
557+
self:ConvertToVersion(version, false, false, ignoreTreeSubType)
556558
main:ClosePopup()
557559
end)
558560
controls.cancel = new("ButtonControl", nil, {125, 105, 100, 20}, "Cancel", function()
@@ -620,20 +622,20 @@ function TreeTabClass:OpenImportPopup()
620622
main:ClosePopup()
621623
end
622624
end
623-
local function validateTreeVersion(isRuthless, major, minor)
625+
local function validateTreeVersion(alternateType, major, minor)
624626
-- Take the Major and Minor version numbers and confirm it is a valid tree version. The point release is also passed in but it is not used
625627
-- Return: the passed in tree version as text or latestTreeVersion
626628
if major and minor then
627629
--need leading 0 here
628630
local newTreeVersionNum = tonumber(string.format("%d.%02d", major, minor))
629631
if newTreeVersionNum >= treeVersions[defaultTreeVersion].num and newTreeVersionNum <= treeVersions[latestTreeVersion].num then
630632
-- no leading 0 here
631-
return string.format("%s_%s", major, minor) .. (isRuthless and "_ruthless" or "")
633+
return string.format("%s_%s", major, minor) .. (alternateType and ("_" .. alternateType:gsub("-", "_")) or "")
632634
else
633635
print(string.format("Version '%d_%02d' is out of bounds", major, minor))
634636
end
635637
end
636-
return latestTreeVersion .. (isRuthless and "_ruthless" or "")
638+
return latestTreeVersion .. (alternateType and ("_" .. alternateType:gsub("-", "_")) or "")
637639
end
638640

639641
controls.nameLabel = new("LabelControl", nil, {-180, 20, 0, 16}, "Enter name for this passive tree:")
@@ -679,21 +681,23 @@ function TreeTabClass:OpenImportPopup()
679681
controls.import.enabled = true
680682
return
681683
else
682-
decodeTreeLink(treeLink, validateTreeVersion(treeLink:match("tree/ruthless"), treeLink:match(versionLookup)))
684+
decodeTreeLink(treeLink, validateTreeVersion(treeLink:match("tree/(%w+%-?%w*)"), treeLink:match(versionLookup)))
683685
end
684686
end)
685687
end
686688
elseif treeLink:match("poeplanner.com/") then
687689
decodePoePlannerTreeLink(treeLink:gsub("/%?v=.+#","/"))
688690
elseif treeLink:match("poeskilltree.com/") then
689-
local oldStyleVersionLookup = "/%?v=([0-9]+)%.([0-9]+)%.([0-9]+)%-?r?u?t?h?l?e?s?s?#"
691+
local oldStyleVersionLookup = "/%?v=([0-9]+)%.([0-9]+)%.([0-9]+)%-?%w?%-?%w?#"
690692
-- Strip the version from the tree : https://poeskilltree.com/?v=3.6.0#AAAABAMAABEtfIOFMo6-ksHfsOvu -> https://poeskilltree.com/AAAABAMAABEtfIOFMo6-ksHfsOvu
691-
decodeTreeLink(treeLink:gsub("/%?v=.+#","/"), validateTreeVersion(treeLink:match("-ruthless#"), treeLink:match(oldStyleVersionLookup)))
693+
decodeTreeLink(treeLink:gsub("/%?v=.+#","/"), validateTreeVersion(treeLink:match("%-(%w+%-?%w*)#"), treeLink:match(oldStyleVersionLookup)))
692694
else
693695
-- EG: https://www.pathofexile.com/passive-skill-tree/3.15.0/AAAABgMADI6-HwKSwQQHLJwtH9-wTLNfKoP3ES3r5AAA
694696
-- EG: https://www.pathofexile.com/fullscreen-passive-skill-tree/3.15.0/AAAABgMADAQHES0fAiycLR9Ms18qg_eOvpLB37Dr5AAA
695697
-- EG: https://www.pathofexile.com/passive-skill-tree/ruthless/AAAABgAAAAAA (Ruthless doesn't have versions)
696-
decodeTreeLink(treeLink, validateTreeVersion(treeLink:match("tree/ruthless"), treeLink:match(versionLookup)))
698+
-- EG: https://www.pathofexile.com/passive-skill-tree/ruthless-alternate/AAAABgAAAAAA
699+
-- EG: https://www.pathofexile.com/passive-skill-tree/alternate/AAAABgAAAAAA
700+
decodeTreeLink(treeLink, validateTreeVersion(treeLink:match("tree/(%w+%-?%w*)"), treeLink:match(versionLookup)))
697701
end
698702
end)
699703
controls.import.enabled = false

0 commit comments

Comments
 (0)