Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Ruthless tree #6367

Merged
merged 9 commits into from
Aug 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Classes/ImportTab.lua
Original file line number Diff line number Diff line change
Expand Up @@ -602,7 +602,7 @@ function ImportTabClass:ImportPassiveTreeAndJewels(json, charData)
end
end
end
self.build.spec:ImportFromNodeList(charData.classId, charData.ascendancyClass, charPassiveData.hashes, charPassiveData.mastery_effects or {}, latestTreeVersion)
self.build.spec:ImportFromNodeList(charData.classId, charData.ascendancyClass, charPassiveData.hashes, charPassiveData.mastery_effects or {}, latestTreeVersion .. (charData.league:match("Ruthless") and "_ruthless" or ""))
self.build.spec:AddUndoState()
self.build.characterLevel = charData.level
self.build.characterLevelAutoMode = false
Expand Down
10 changes: 8 additions & 2 deletions src/Classes/ModStore.lua
Original file line number Diff line number Diff line change
Expand Up @@ -390,13 +390,19 @@ function ModStoreClass:EvalMod(mod, cfg)
end
elseif tag.type == "PercentStat" then
local base
local target = self
-- This functions similar to the above tagTypes in regard to which actor to use, but for PerStat
-- if the actor is 'parent', we don't want to return if we're already using 'parent', just keep using 'self'
if tag.actor and self.actor[tag.actor] then
target = self.actor[tag.actor].modDB
end
if tag.statList then
base = 0
for _, stat in ipairs(tag.statList) do
base = base + self:GetStat(stat, cfg)
base = base + target:GetStat(stat, cfg)
end
else
base = self:GetStat(tag.stat, cfg)
base = target:GetStat(tag.stat, cfg)
end
local mult = base * (tag.percent and tag.percent / 100 or 1)
local limitTotal
Expand Down
40 changes: 22 additions & 18 deletions src/Classes/TreeTab.lua
Original file line number Diff line number Diff line change
Expand Up @@ -132,15 +132,12 @@ local TreeTabClass = newClass("TreeTab", "ControlHost", function(self, build)
end
self.treeVersions = { }
for _, num in ipairs(treeVersionList) do
if not num:find("^2") then
local vers = num:gsub("%_", ".")
t_insert(self.treeVersions, vers)
end
t_insert(self.treeVersions, treeVersions[num].display)
end
self.controls.versionText = new("LabelControl", { "LEFT", self.controls.export, "RIGHT" }, 8, 0, 0, 16, "Version:")
self.controls.versionSelect = new("DropDownControl", { "LEFT", self.controls.versionText, "RIGHT" }, 8, 0, 55, 20, self.treeVersions, function(index, value)
self.controls.versionSelect = new("DropDownControl", { "LEFT", self.controls.versionText, "RIGHT" }, 8, 0, 100, 20, self.treeVersions, function(index, value)
if value ~= self.build.spec.treeVersion then
convertToVersion(value:gsub("%.", "_"))
convertToVersion(value:gsub("[%(%)]", ""):gsub("[%.%s]", "_"))
end
end)
self.controls.versionSelect.maxDroppedWidth = 1000
Expand Down Expand Up @@ -199,8 +196,14 @@ local TreeTabClass = newClass("TreeTab", "ControlHost", function(self, build)
self.controls.specConvertText.shown = function()
return self.showConvert
end
self.controls.specConvert = new("ButtonControl", { "LEFT", self.controls.specConvertText, "RIGHT" }, 8, 0, 120, 20, "^2Convert to "..treeVersions[latestTreeVersion].display, function()
convertToVersion(latestTreeVersion)
local function getLatestTreeVersion()
return latestTreeVersion .. (self.specList[self.activeSpec].treeVersion:match("^" .. latestTreeVersion .. "(.*)") or "")
end
local function buildConvertButtonLabel()
return "^2Convert to "..treeVersions[getLatestTreeVersion()].display
end
self.controls.specConvert = new("ButtonControl", { "LEFT", self.controls.specConvertText, "RIGHT" }, 8, 0, function() return DrawStringWidth(16, "VAR", buildConvertButtonLabel()) + 20 end, 20, buildConvertButtonLabel, function()
convertToVersion(getLatestTreeVersion())
end)
self.jumpToNode = false
self.jumpToX = 0
Expand Down Expand Up @@ -232,15 +235,15 @@ function TreeTabClass:Draw(viewPort, inputEvents)

-- Determine positions if one line of controls doesn't fit in the screen width
local twoLineHeight = 24
if viewPort.width >= 1168 + (self.isComparing and 198 or 0) + (self.viewer.showHeatMap and 316 or 0) then
if viewPort.width >= 1336 + (self.isComparing and 198 or 0) + (self.viewer.showHeatMap and 316 or 0) then
twoLineHeight = 0
self.controls.findTimelessJewel:SetAnchor("LEFT", self.controls.treeSearch, "RIGHT", 8, 0)
self.controls.treeSearch:SetAnchor("LEFT", self.controls.versionSelect, "RIGHT", 8, 0)
if self.controls.powerReportList then
self.controls.powerReportList:SetAnchor("TOPLEFT", self.controls.specSelect, "BOTTOMLEFT", 0, self.controls.specSelect.height + 4)
self.controls.allocatedNodeToggle:SetAnchor("TOPLEFT", self.controls.powerReportList, "TOPRIGHT", 8, 0)
end
else
self.controls.findTimelessJewel:SetAnchor("TOPLEFT", self.controls.specSelect, "BOTTOMLEFT", 0, 4)
self.controls.treeSearch:SetAnchor("TOPLEFT", self.controls.specSelect, "BOTTOMLEFT", 0, 4)
if self.controls.powerReportList then
self.controls.powerReportList:SetAnchor("TOPLEFT", self.controls.findTimelessJewel, "BOTTOMLEFT", 0, self.controls.treeHeatMap.y + self.controls.treeHeatMap.height + 4)
self.controls.allocatedNodeToggle:SetAnchor("TOPLEFT", self.controls.powerReportList, "TOPRIGHT", -76, -44)
Expand Down Expand Up @@ -382,7 +385,7 @@ function TreeTabClass:SetActiveSpec(specId)
end
end
end
self.showConvert = curSpec.treeVersion ~= latestTreeVersion
self.showConvert = not curSpec.treeVersion:match("^" .. latestTreeVersion)
if self.build.itemsTab.itemOrderList[1] then
-- Update item slots if items have been loaded already
self.build.itemsTab:PopulateSlots()
Expand Down Expand Up @@ -434,20 +437,20 @@ function TreeTabClass:OpenImportPopup()
main:ClosePopup()
end
end
local function validateTreeVersion(major, minor)
local function validateTreeVersion(isRuthless, major, minor)
-- 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
-- Return: the passed in tree version as text or latestTreeVersion
if major and minor then
--need leading 0 here
local newTreeVersionNum = tonumber(string.format("%d.%02d", major, minor))
if newTreeVersionNum >= treeVersions[defaultTreeVersion].num and newTreeVersionNum <= treeVersions[latestTreeVersion].num then
-- no leading 0 here
return string.format("%s_%s", major, minor)
return string.format("%s_%s", major, minor) .. isRuthless and "_ruthless" or ""
else
print(string.format("Version '%d_%02d' is out of bounds", major, minor))
end
end
return latestTreeVersion
return latestTreeVersion .. (isRuthless and "_ruthless" or "")
end

controls.editLabel = new("LabelControl", nil, 0, 20, 0, 16, "Enter passive tree link:")
Expand Down Expand Up @@ -487,18 +490,19 @@ function TreeTabClass:OpenImportPopup()
controls.import.enabled = true
return
else
decodeTreeLink(treeLink, validateTreeVersion(treeLink:match(versionLookup)))
decodeTreeLink(treeLink, validateTreeVersion(treeLink:match("tree/ruthless"), treeLink:match(versionLookup)))
end
end)
end
elseif treeLink:match("poeskilltree.com/") then
local oldStyleVersionLookup = "/%?v=([0-9]+)%.([0-9]+)%.([0-9]+)#"
-- Strip the version from the tree : https://poeskilltree.com/?v=3.6.0#AAAABAMAABEtfIOFMo6-ksHfsOvu -> https://poeskilltree.com/AAAABAMAABEtfIOFMo6-ksHfsOvu
decodeTreeLink(treeLink:gsub("/%?v=.+#","/"), validateTreeVersion(treeLink:match(oldStyleVersionLookup)))
decodeTreeLink(treeLink:gsub("/%?v=.+#","/"), validateTreeVersion(treeLink:match("tree/ruthless"), treeLink:match(oldStyleVersionLookup)))
else
-- EG: https://www.pathofexile.com/passive-skill-tree/3.15.0/AAAABgMADI6-HwKSwQQHLJwtH9-wTLNfKoP3ES3r5AAA
-- EG: https://www.pathofexile.com/fullscreen-passive-skill-tree/3.15.0/AAAABgMADAQHES0fAiycLR9Ms18qg_eOvpLB37Dr5AAA
decodeTreeLink(treeLink, validateTreeVersion(treeLink:match(versionLookup)))
-- EG: https://www.pathofexile.com/passive-skill-tree/ruthless/AAAABgAAAAAA (Ruthless doesn't have versions)
decodeTreeLink(treeLink, validateTreeVersion(treeLink:match("tree/ruthless"), treeLink:match(versionLookup)))
end
end)
controls.cancel = new("ButtonControl", nil, 45, 80, 80, 20, "Cancel", function()
Expand Down
7 changes: 6 additions & 1 deletion src/GameVersions.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ liveTargetVersion = "3_0"
-- Skill tree versions
---Added for convenient indexing of skill tree versions.
---@type string[]
treeVersionList = { "2_6", "3_6", "3_7", "3_8", "3_9", "3_10", "3_11", "3_12", "3_13", "3_14", "3_15", "3_16", "3_17", "3_18", "3_19", "3_20", "3_21", "3_22", }
treeVersionList = { "2_6", "3_6", "3_7", "3_8", "3_9", "3_10", "3_11", "3_12", "3_13", "3_14", "3_15", "3_16", "3_17", "3_18", "3_19", "3_20", "3_21", "3_22_ruthless", "3_22", }
--- Always points to the latest skill tree version.
latestTreeVersion = treeVersionList[#treeVersionList]
---Tree version where multiple skill trees per build were introduced to PoBC.
Expand Down Expand Up @@ -100,6 +100,11 @@ treeVersions = {
num = 3.21,
url = "https://www.pathofexile.com/passive-skill-tree/3.21.0/",
},
["3_22_ruthless"] = {
display = "3.22 (ruthless)",
num = 3.22,
url = "https://www.pathofexile.com/passive-skill-tree/ruthless/",
},
["3_22"] = {
display = "3.22",
num = 3.22,
Expand Down
22 changes: 22 additions & 0 deletions src/Modules/ModParser.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1815,6 +1815,9 @@ local specialModList = {
["totems explode on death, dealing (%d+)%% of their life as (.+) damage"] = function(amount, _, type) -- Crucible weapon mod
return explodeFunc(100, amount, type)
end,
["nearby corpses explode when you warcry, dealing (%d+)%% of their life as (.+) damage"] = function(amount, _, type) -- Ruthless Berserker node
return explodeFunc(100, amount, type)
end,
-- Keystones
["(%d+) rage regenerated for every (%d+) mana regeneration per second"] = function(num, _, div) return {
mod("RageRegen", "BASE", num, {type = "PerStat", stat = "ManaRegen", div = tonumber(div) }) ,
Expand Down Expand Up @@ -2097,6 +2100,7 @@ local specialModList = {
flag("Condition:CanGainRage", { type = "ActorCondition", actor = "enemy", var = "PinnacleBoss" }),
},
["inherent effects from having rage are tripled"] = { mod("Multiplier:RageEffect", "BASE", 2) },
["inherent effects from having rage are doubled"] = { mod("Multiplier:RageEffect", "BASE", 1) },
["cannot be stunned while you have at least (%d+) rage"] = function(num) return { flag("StunImmune", { type = "MultiplierThreshold", var = "Rage", threshold = num }) } end,
["lose ([%d%.]+)%% of life per second per rage while you are not losing rage"] = function(num) return { mod("LifeDegen", "BASE", 1, { type = "PercentStat", stat = "Life", percent = num }, { type = "Multiplier", var = "Rage" }) } end,
["if you've warcried recently, you and nearby allies have (%d+)%% increased attack speed"] = function(num) return { mod("ExtraAura", "LIST", { mod = mod("Speed", "INC", num, nil, ModFlag.Attack) }, { type = "Condition", var = "UsedWarcryRecently" }) } end,
Expand Down Expand Up @@ -2302,6 +2306,9 @@ local specialModList = {
["attack damage is lucky if you[' ]h?a?ve blocked in the past (%d+) seconds"] = {
flag("LuckyHits", nil, ModFlag.Attack, { type = "Condition", var = "BlockedRecently" })
},
["attack damage while dual wielding is lucky if you[' ]h?a?ve blocked in the past (%d+) seconds"] = {
flag("LuckyHits", nil, ModFlag.Attack, { type = "Condition", var = "BlockedRecently" }, { type = "Condition", var = "DualWielding" })
},
["hits ignore enemy monster physical damage reduction if you[' ]h?a?ve blocked in the past (%d+) seconds"] = {
flag("IgnoreEnemyPhysicalDamageReduction", { type = "Condition", var = "BlockedRecently" })
},
Expand All @@ -2311,6 +2318,7 @@ local specialModList = {
} end,
-- Guardian
["grants armour equal to (%d+)%% of your reserved life to you and nearby allies"] = function(num) return { mod("GrantReservedLifeAsAura", "LIST", { mod = mod("Armour", "BASE", num / 100) }) } end,
["grants armour equal to (%d+)%% of your reserved mana to you and nearby allies"] = function(num) return { mod("GrantReservedManaAsAura", "LIST", { mod = mod("Armour", "BASE", num / 100) }) } end,
["grants maximum energy shield equal to (%d+)%% of your reserved mana to you and nearby allies"] = function(num) return { mod("GrantReservedManaAsAura", "LIST", { mod = mod("EnergyShield", "BASE", num / 100) }) } end,
["warcries cost no mana"] = { mod("ManaCost", "MORE", -100, nil, 0, KeywordFlag.Warcry) },
["%+(%d+)%% chance to block attack damage for %d seconds? every %d seconds"] = function(num) return { mod("BlockChance", "BASE", num, { type = "Condition", var = "BastionOfHopeActive" }) } end,
Expand Down Expand Up @@ -2395,6 +2403,11 @@ local specialModList = {
["regenerate (%d+)%% of mana over 2 seconds when you consume a corpse"] = function(num) return { mod("ManaRegen", "BASE", 1, { type = "PercentStat", stat = "Mana", percent = num / 2 }, { type = "Condition", var = "ConsumedCorpseInPast2Sec" }) } end,
["corpses you spawn have (%d+)%% increased maximum life"] = function(num) return { mod("CorpseLife", "INC", num) } end,
["corpses you spawn have (%d+)%% reduced maximum life"] = function(num) return { mod("CorpseLife", "INC", -num) } end,
["minions gain added physical damage equal to (%d+)%% of maximum energy shield on your equipped helmet"] = function(num) return {
mod("MinionModifier", "LIST", { mod = mod("PhysicalMin", "BASE", 1, { type = "PercentStat", stat = "EnergyShieldOnHelmet", actor = "parent", percent = num }) }),
mod("MinionModifier", "LIST", { mod = mod("PhysicalMax", "BASE", 1, { type = "PercentStat", stat = "EnergyShieldOnHelmet", actor = "parent", percent = num }) }),

} end,
-- Occultist
["when you kill an enemy, for each curse on that enemy, gain (%d+)%% of non%-chaos damage as extra chaos damage for 4 seconds"] = function(num) return {
mod("NonChaosDamageGainAsChaos", "BASE", num, { type = "Condition", var = "KilledRecently" }, { type = "Multiplier", var = "CurseOnEnemy" }),
Expand Down Expand Up @@ -2437,6 +2450,11 @@ local specialModList = {
mod("EnemyModifier", "LIST", { mod = mod("ColdExposure", "BASE", -num) }, { type = "Condition", var = "Phasing" }),
mod("EnemyModifier", "LIST", { mod = mod("LightningExposure", "BASE", -num) }, { type = "Condition", var = "Phasing" }),
} end,
["nearby enemies have fire, cold and lightning exposure while you have phasing"] = {
mod("EnemyModifier", "LIST", { mod = mod("FireExposure", "BASE", -10) }, { type = "Condition", var = "Phasing" }),
mod("EnemyModifier", "LIST", { mod = mod("ColdExposure", "BASE", -10) }, { type = "Condition", var = "Phasing" }),
mod("EnemyModifier", "LIST", { mod = mod("LightningExposure", "BASE", -10) }, { type = "Condition", var = "Phasing" }),
},
-- Saboteur
["hits have (%d+)%% chance to deal (%d+)%% more area damage"] = function (num, _, more) return {
mod("Damage", "MORE", (num*more/100), nil, bor(ModFlag.Area, ModFlag.Hit))
Expand All @@ -2458,6 +2476,7 @@ local specialModList = {
["gain (%d+)%% increased movement speed for 20 seconds when you kill an enemy"] = function(num) return { mod("MovementSpeed", "INC", num, { type = "Condition", var = "KilledRecently" }) } end,
["gain (%d+)%% increased attack speed for 20 seconds when you kill a rare or unique enemy"] = function(num) return { mod("Speed", "INC", num, nil, ModFlag.Attack, 0, { type = "Condition", var = "KilledUniqueEnemy" }) } end,
["kill enemies that have (%d+)%% or lower life when hit by your skills"] = function(num) return { mod("CullPercent", "MAX", num) } end,
["you are unaffected by bleeding while leeching"] = { mod("SelfBleedEffect", "MORE", -100, { type = "Condition", var = "Leeching" }) },
-- Trickster
["(%d+)%% chance to gain (%d+)%% of non%-chaos damage with hits as extra chaos damage"] = function(num, _, perc) return { mod("NonChaosDamageGainAsChaos", "BASE", num / 100 * tonumber(perc)) } end,
["movement skills cost no mana"] = { mod("ManaCost", "MORE", -100, nil, 0, KeywordFlag.Movement) },
Expand Down Expand Up @@ -3297,6 +3316,9 @@ local specialModList = {
mod("ExtraAura", "LIST", { mod = flag("Condition:ArcaneSurge")}, { type = "Condition", var = "UsedWarcryRecently" }),
mod("ArcaneSurgeEffect", "INC", num, { type = "PerStat", stat = "WarcryPower", div = tonumber(div), globalLimit = tonumber(limit), globalLimitKey = "Brinerot Flag"}, { type = "Condition", var = "UsedWarcryRecently" }),
} end,
["gain arcane surge after spending a total of (%d+) mana"] = function(num) return {
mod("ExtraAura", "LIST", { mod = flag("Condition:ArcaneSurge")}, { type = "MultiplierThreshold", var = "ManaSpentRecently", threshold = num }),
} end,
["gain onslaught for (%d+) seconds on hit while at maximum frenzy charges"] = { flag("Onslaught", { type = "StatThreshold", stat = "FrenzyCharges", thresholdStat = "FrenzyChargesMax" }, { type = "Condition", var = "HitRecently" }) },
["enemies in your chilling areas take (%d+)%% increased lightning damage"] = function(num) return { mod("EnemyModifier", "LIST", { mod = mod("LightningDamageTaken", "INC", num) }, { type = "ActorCondition", actor = "enemy", var = "InChillingArea" }) } end,
["warcries count as having (%d+) additional nearby enemies"] = function(num) return {
Expand Down
Binary file added src/TreeData/3_22_ruthless/ascendancy-3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/TreeData/3_22_ruthless/background-3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/TreeData/3_22_ruthless/frame-3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/TreeData/3_22_ruthless/jewel-3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/TreeData/3_22_ruthless/jewel-radius.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/TreeData/3_22_ruthless/line-3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/TreeData/3_22_ruthless/mastery-3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/TreeData/3_22_ruthless/skills-3.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/TreeData/3_22_ruthless/skills-disabled-3.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading