Skip to content
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
4 changes: 2 additions & 2 deletions PathOfBuilding.app/Contents/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.1.2</string>
<string>0.6.0</string>
<key>CFBundleVersion</key>
<string>0.1.2</string>
<string>0.6.0</string>
</dict>
</plist>
13 changes: 13 additions & 0 deletions PathOfBuilding.app/Contents/Resources/changelog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
VERSION[0.6.0][2026/02/21]

--- 新機能 / New Features ---
* スキルジェムツールチップの日本語翻訳を大幅強化
* ジェム効果分岐ラベル(Explosion, Aftershock, Fire, Lightning, Cold等)約360件を翻訳辞書に追加
* gem_stat_descriptions全テンプレートを網羅チェックし、未翻訳145件を追加(statテンプレートカバレッジ100%達成)

--- バグ修正 / Bug Fixes ---
* ツールチップの改行時にカラーコードが消失する問題を修正(Tooltip.lua WrapString色引き継ぎ)
* calcFunc未取得時にジェムツールチップ全体が消失する問題を修正(GemSelectControl.lua)
* statSet.label / additional.name / gemFamilyの未翻訳表示を修正
* i18n.luaのデバッグログ出力を削除(translateModLine毎回のio.openによるパフォーマンス低下を解消)

VERSION[0.5.0][2026/02/20]

--- 新機能 / New Features ---
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ function CalcBreakdownClass:AddBreakdownSection(sectionData)
{ label = "Converted Damage", key = "convSrc" },
{ label = "Total", key = "total" },
{ label = "Conversion", key = "convDst" },
{ label = "Gain", key = "gainDst" },
}
}
t_insert(self.sectionList, section)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -552,43 +552,35 @@ function GemSelectClass:Draw(viewPort, noTooltip)
SetViewport()
self:DrawControls(viewPort, (noTooltip and not self.forceTooltip) and self)
if self.hoverSel then
local success, err = pcall(function()
local gemData = self.gems[self.list[self.hoverSel]]
local tooltipY = y + height + 2 + (self.hoverSel - 1) * (height - 4) - scrollBar.offset
if gemData then
self.tooltip:Clear()
local gemInstance = {
level = self.skillsTab:ProcessGemLevel(gemData),
quality = self.skillsTab.defaultGemQuality or 0,
count = 1,
enabled = true,
enableGlobal1 = true,
enableGlobal2 = true,
gemId = gemData.id,
nameSpec = gemData.name,
skillId = gemData.grantedEffectId,
displayEffect = nil,
gemData = gemData
}
self:AddGemTooltip(gemInstance)
local calcsTab = self.skillsTab.build and self.skillsTab.build.calcsTab
local calcFunc, calcBase
if calcsTab and calcsTab.miscCalculator then
calcFunc, calcBase = calcsTab:GetMiscCalculator()
end
if calcFunc then
self.tooltip:Clear()
local gemData = self.gems[self.list[self.hoverSel]]
local output = self:CalcOutputWithThisGem(calcFunc, gemData, self.skillsTab.sortGemsByDPSField == "FullDPS", nil, calcBase)
local gemInstance = {
level = self.skillsTab:ProcessGemLevel(gemData),
quality = self.skillsTab.defaultGemQuality or 0,
count = 1,
enabled = true,
enableGlobal1 = true,
enableGlobal2 = true,
gemId = gemData.id,
nameSpec = gemData.name,
skillId = gemData.grantedEffectId,
displayEffect = nil,
gemData = gemData
}
self:AddGemTooltip(gemInstance)
self.tooltip:AddSeparator(10)
self.skillsTab.build:AddStatComparesToTooltip(self.tooltip, calcBase, output, "^7" .. i18n.t("statCompare.selectingGem"))
self.tooltip:Draw(x, y + height + 2 + (self.hoverSel - 1) * (height - 4) - scrollBar.offset, width, height - 4, viewPort)
end
end)
if not success then
-- Show simple tooltip with gem name instead of crashing
self.tooltip:Clear()
local gemData = self.gems[self.list[self.hoverSel]]
if gemData then
self.tooltip:AddLine(16, colorCodes.GEM .. gemDisplayName(gemData))
end
self.tooltip:Draw(x, y + height + 2 + (self.hoverSel - 1) * (height - 4) - scrollBar.offset, width, height - 4, viewPort)
self.tooltip:Draw(x, tooltipY, width, height - 4, viewPort)
end
end
SetDrawLayer(nil, 0)
Expand All @@ -614,11 +606,7 @@ function GemSelectClass:Draw(viewPort, noTooltip)
local cursorX, cursorY = GetCursorPos()
self.tooltip:Clear()
if gemInstance and gemInstance.gemData then
local ok, err = pcall(function() self:AddGemTooltip(gemInstance) end)
if not ok then
self.tooltip:Clear()
self.tooltip:AddLine(16, colorCodes.GEM .. (gemInstance.gemData.name or "Unknown Gem"))
end
self:AddGemTooltip(gemInstance)
else
self.tooltip:AddLine(16, toolTipText)
end
Expand Down Expand Up @@ -685,7 +673,12 @@ function GemSelectClass:AddGemTooltip(gemInstance)
self.tooltip:AddLine(fontSizeBig, "^x7F7F7F" .. tagStringDisplay(gemInstance.gemData.tagString), "FONTIN SC")
end
if gemInstance.gemData.gemFamily then
self.tooltip:AddLine(fontSizeBig, "^x7F7F7F" .. i18n.t("gemTooltip.category") .. " ^7" .. gemInstance.gemData.gemFamily, "FONTIN SC")
local familyName = gemInstance.gemData.gemFamily
local translatedFamily = gemDisplayName({name = familyName})
if translatedFamily == familyName then
translatedFamily = i18n.lookup("gemTooltip.tags", familyName) or familyName
end
self.tooltip:AddLine(fontSizeBig, "^x7F7F7F" .. i18n.t("gemTooltip.category") .. " ^7" .. translatedFamily, "FONTIN SC")
end
-- Will need rework if a gem can have 2+ additional supports
self:AddGrantedEffectInfo(gemInstance, grantedEffect, true)
Expand All @@ -697,7 +690,7 @@ function GemSelectClass:AddGemTooltip(gemInstance)
if not additional.support then
if additional.name ~= "" then
self.tooltip:AddSeparator(10)
self.tooltip:AddLine(fontSizeTitle, colorCodes.GEM .. additional.name, "FONTIN SC")
self.tooltip:AddLine(fontSizeTitle, colorCodes.GEM .. gemDisplayName({name = additional.name}), "FONTIN SC")
end
self.tooltip:AddSeparator(10)
self:AddGrantedEffectInfo(gemInstance, additional)
Expand Down Expand Up @@ -881,7 +874,7 @@ function GemSelectClass:AddStatSetInfo(gemInstance, grantedEffect, statSet, noLa
local statSetLevel = statSet.levels[displayInstance.level] or statSet.levels[1] or { }
if not (index == 1 and statSet.label == grantedEffect.name) and statSet.label ~= "" and not noLabel then
self.tooltip:AddSeparator(10)
self.tooltip:AddLine(fontSizeTitle, colorCodes.GEM .. statSet.label, "FONTIN SC")
self.tooltip:AddLine(fontSizeTitle, colorCodes.GEM .. gemDisplayName({name = statSet.label}), "FONTIN SC")
self.tooltip:AddSeparator(10)
end
if statSetLevel.critChance then
Expand All @@ -902,7 +895,6 @@ function GemSelectClass:AddStatSetInfo(gemInstance, grantedEffect, statSet, noLa
for i, line in ipairs(descriptions) do
local source = (statSet.statMap and statSet.statMap[lineMap[line]]) or self.skillsTab.build.data.skillStatMap[lineMap[line]]
local bg = nil -- GemHoverModBg.png asset not available on macOS
-- Translate stat line via mod line translator as fallback
local displayLine = (i18n and i18n.translateModLine) and i18n.translateModLine(line) or line
if source then
if launch.devModeAlt then
Expand Down
37 changes: 28 additions & 9 deletions PathOfBuilding.app/Contents/Resources/src/Classes/ModStore.lua
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ local ModStoreClass = newClass("ModStore", function(self, parent)
self.conditions = { }
end)

function ModStoreClass:ScaleAddMod(mod, scale)
function ModStoreClass:ScaleAddMod(mod, scale, replace)
local unscalable = false
for _, effects in ipairs(mod) do
if effects.unscalable then
Expand Down Expand Up @@ -67,7 +67,11 @@ function ModStoreClass:ScaleAddMod(mod, scale)
subMod.value = m_modf(round(subMod.value * scale, 2))
end
end
self:AddMod(scaledMod)
if replace then
self:ReplaceModInternal(scaledMod)
else
self:AddMod(scaledMod)
end
end
end

Expand All @@ -77,12 +81,12 @@ function ModStoreClass:CopyList(modList)
end
end

function ModStoreClass:ScaleAddList(modList, scale)
function ModStoreClass:ScaleAddList(modList, scale, replace)
if scale == 1 then
self:AddList(modList)
else
for i = 1, #modList do
self:ScaleAddMod(modList[i], scale)
self:ScaleAddMod(modList[i], scale, replace)
end
end
end
Expand Down Expand Up @@ -257,14 +261,19 @@ function ModStoreClass:GetMultiplier(var, cfg, noMod)
end

function ModStoreClass:GetStat(stat, cfg)
local function isNameInBuffList(buffList, name)
for _, buff in ipairs(buffList) do
if buff.name == name then return true end
end
return false
end
if stat == "ManaReservedPercent" then
local reservedPercentMana = 0
-- Check if mana is 0 (i.e. from Blood Magic) to avoid division by 0.
local totalMana = self.actor.output["Mana"]
if totalMana == 0 then return 0 else
for _, activeSkill in ipairs(self.actor.activeSkillList) do
-- currently only checks main statSet for skill flags. rework if required
if (activeSkill.skillTypes[SkillType.Aura] and not activeSkill.activeEffect.statSet.skillFlags.disable and activeSkill.buffList and activeSkill.buffList[1] and activeSkill.buffList[1].name == cfg.skillName) then
if (activeSkill.skillTypes[SkillType.HasReservation] and not activeSkill.skillFlags.disable and activeSkill.buffList and cfg and (isNameInBuffList(activeSkill.buffList, cfg.skillName) or isNameInBuffList(activeSkill.buffList, cfg.summonSkillName))) then
local manaBase = activeSkill.skillData["ManaReservedBase"] or 0
reservedPercentMana = m_floor(manaBase / totalMana * 100)
break
Expand Down Expand Up @@ -340,10 +349,15 @@ function ModStoreClass:EvalMod(mod, cfg, globalLimits)
tag.div = GetMultiplier(self, tag.divVar, cfg)
end
local mult = m_floor(base / (tag.div or 1) + 0.0001)
if tag.noFloor then
mult = base / (tag.div or 1)
end
local limitTotal
local limitNegTotal
if tag.limit or tag.limitVar then
local limit = tag.limit or GetMultiplier(limitTarget, tag.limitVar, cfg)
if tag.limit or tag.limitVar or tag.limitStat then
local limit = tag.limit
or tag.limitVar and GetMultiplier(limitTarget, tag.limitVar, cfg)
or tag.limitStat and GetStat(limitTarget, tag.limitStat, cfg)
if tag.limitTotal then
limitTotal = limit
elseif tag.limitNegTotal then
Expand Down Expand Up @@ -628,7 +642,7 @@ function ModStoreClass:EvalMod(mod, cfg, globalLimits)
end
if tag.searchCond then
for slot, item in pairs(items) do
if (not tag.allSlots or tag.allSlots and item.type ~= "Jewel") and slot ~= itemSlot or not tag.excludeSelf then
if (not tag.allSlots or tag.allSlots and (item.type ~= "Jewel" and item.type ~= "Graft")) and slot ~= itemSlot or not tag.excludeSelf then
t_insert(matches, item:FindModifierSubstring(tag.searchCond:lower(), slot:lower()))
end
end
Expand All @@ -643,6 +657,11 @@ function ModStoreClass:EvalMod(mod, cfg, globalLimits)
t_insert(matches, item.corrupted == tag.corruptedCond)
end
end
if tag.nameCond then
for _, item in pairs(items) do
t_insert(matches, item.name and item.name:lower() == tag.nameCond:lower())
end
end

local hasItems = false
for _, item in pairs(items) do
Expand Down
11 changes: 10 additions & 1 deletion PathOfBuilding.app/Contents/Resources/src/Classes/Tooltip.lua
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,16 @@ function TooltipClass:AddLine(size, text, font, background)
self.blocks[#self.blocks].height = self.blocks[#self.blocks].height + size + 2
end
if self.maxWidth then
for _, wrappedLine in ipairs(main:WrapString(line, size, self.maxWidth - H_PAD)) do
local wrapped = main:WrapString(line, size, self.maxWidth - H_PAD)
for idx, wrappedLine in ipairs(wrapped) do
if idx > 1 then
-- Carry forward the last color code so continuation lines retain color
local prevText = wrapped[idx - 1]
local lastColor = prevText:match(".*(%^x%x%x%x%x%x%x)") or prevText:match(".*(%^%d)")
if lastColor and not wrappedLine:match("^%^") then
wrappedLine = lastColor .. wrappedLine
end
end
t_insert(self.lines, { size = size, text = wrappedLine, block = #self.blocks, font = fontToUse, center = self.center, background = background })
end
else
Expand Down
46 changes: 24 additions & 22 deletions PathOfBuilding.app/Contents/Resources/src/Classes/TreeTab.lua
Original file line number Diff line number Diff line change
Expand Up @@ -154,14 +154,15 @@ local TreeTabClass = newClass("TreeTab", "ControlHost", function(self, build)
t_insert(self.treeVersions, value)
end
self.controls.versionText = new("LabelControl", { "LEFT", self.controls.reset, "RIGHT" }, { 8, 0, 0, 16 }, "Version:")
self.controls.versionSelect = new("DropDownControl", { "LEFT", self.controls.versionText, "RIGHT" }, { 8, 0, 60, 20 }, self.treeVersions, function(index, selected)
self.controls.versionSelect = new("DropDownControl", { "LEFT", self.controls.versionText, "RIGHT" }, { 8, 0, 100, 20 }, self.treeVersions, function(index, selected)
if selected.value ~= self.build.spec.treeVersion then
self:OpenVersionConvertPopup(selected.value, true)
end
end)
self.controls.versionSelect.maxDroppedWidth = 1000
self.controls.versionSelect.enableDroppedWidth = true
self.controls.versionSelect.enableChangeBoxWidth = true
self.controls.versionSelect:CheckDroppedWidth(true)
self.controls.versionSelect.selIndex = #self.treeVersions

-- Tree Search Textbox
Expand All @@ -173,13 +174,12 @@ local TreeTabClass = newClass("TreeTab", "ControlHost", function(self, build)

self.tradeLeaguesList = { }
-- Find Timeless Jewel Button
-- Add button back if/when we figure out how to search for them again
--self.controls.findTimelessJewel = new("ButtonControl", { "LEFT", self.controls.treeSearch, "RIGHT" }, { 8, 0, 150, 20 }, "Find Timeless Jewel", function()
--self:FindTimelessJewel()
--end)
self.controls.findTimelessJewel = new("ButtonControl", { "LEFT", self.controls.treeSearch, "RIGHT" }, { 8, 0, 150, 20 }, "Find Timeless Jewel", function()
self:FindTimelessJewel()
end)

-- Show Node Power Checkbox
self.controls.treeHeatMap = new("CheckBoxControl", { "LEFT", self.controls.treeSearch, "RIGHT" }, { 130, 0, 20 }, "Show Node Power:", function(state)
self.controls.treeHeatMap = new("CheckBoxControl", { "LEFT", self.controls.findTimelessJewel, "RIGHT" }, { 130, 0, 20 }, "Show Node Power:", function(state)
self.viewer.showHeatMap = state
self.controls.treeHeatMapStatSelect.shown = state

Expand Down Expand Up @@ -549,10 +549,11 @@ function TreeTabClass:SetCompareSpec(specId)
self.compareSpec = curSpec
end

function TreeTabClass:ConvertToVersion(version, remove, success, ignoreRuthlessCheck)
if not ignoreRuthlessCheck and self.build.spec.treeVersion:match("ruthless") and not version:match("ruthless") then
if isValueInTable(treeVersionList, version.."_ruthless") then
version = version.."_ruthless"
function TreeTabClass:ConvertToVersion(version, remove, success, ignoreTreeSubType)
local treeSubTypeCapture = self.build.spec.treeVersion:match("(_%l+_?%l*)")
if not ignoreTreeSubType and treeSubTypeCapture and not version:match(treeSubTypeCapture) then
if isValueInTable(treeVersionList, version..treeSubTypeCapture) then
version = version..treeSubTypeCapture
end
end
local newSpec = new("PassiveSpec", self.build, version)
Expand Down Expand Up @@ -598,8 +599,7 @@ function TreeTabClass:OpenSpecManagePopup()
new("ButtonControl", {"LEFT", importTree, "RIGHT"}, {8, 0, 90, 20}, "Export Tree", function()
self:OpenExportPopup()
end)
importTree.enabled = false
exportTree.enabled = false


main:OpenPopup(370, 290, "Manage Passive Trees", {
new("PassiveSpecListControl", nil, {0, 50, 350, 200}, self),
Expand All @@ -611,16 +611,16 @@ function TreeTabClass:OpenSpecManagePopup()
})
end

function TreeTabClass:OpenVersionConvertPopup(version, ignoreRuthlessCheck)
function TreeTabClass:OpenVersionConvertPopup(version, ignoreTreeSubType)
local controls = { }
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" ..
"Convert will replace your current tree.\nCopy + Convert will backup your current tree.\n")
controls.convert = new("ButtonControl", nil, {-125, 105, 100, 20}, "Convert", function()
self:ConvertToVersion(version, true, false, ignoreRuthlessCheck)
self:ConvertToVersion(version, true, false, ignoreTreeSubType)
main:ClosePopup()
end)
controls.convertCopy = new("ButtonControl", nil, {0, 105, 125, 20}, "Copy + Convert", function()
self:ConvertToVersion(version, false, false, ignoreRuthlessCheck)
self:ConvertToVersion(version, false, false, ignoreTreeSubType)
main:ClosePopup()
end)
controls.cancel = new("ButtonControl", nil, {125, 105, 100, 20}, "Cancel", function()
Expand Down Expand Up @@ -688,20 +688,20 @@ function TreeTabClass:OpenImportPopup()
main:ClosePopup()
end
end
local function validateTreeVersion(isRuthless, major, minor)
local function validateTreeVersion(alternateType, 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) .. (isRuthless and "_ruthless" or "")
return string.format("%s_%s", major, minor) .. (alternateType and ("_" .. alternateType:gsub("-", "_")) or "")
else
print(string.format("Version '%d_%02d' is out of bounds", major, minor))
end
end
return latestTreeVersion .. (isRuthless and "_ruthless" or "")
return latestTreeVersion .. (alternateType and ("_" .. alternateType:gsub("-", "_")) or "")
end

controls.nameLabel = new("LabelControl", nil, {-180, 20, 0, 16}, "Enter name for this passive tree:")
Expand Down Expand Up @@ -751,7 +751,7 @@ function TreeTabClass:OpenImportPopup()
controls.import.enabled = true
return
else
decodeTreeLink(treeLink, validateTreeVersion(treeLink:match("tree/ruthless"), treeLink:match(versionLookup)))
decodeTreeLink(treeLink, validateTreeVersion(treeLink:match("tree/(%l+%-?%l*)"), treeLink:match(versionLookup)))
end
end)
end
Expand All @@ -760,12 +760,14 @@ function TreeTabClass:OpenImportPopup()
elseif treeLink:match("poeskilltree.com/") then
local oldStyleVersionLookup = "/%?v=([0-9]+)%.([0-9]+)%.([0-9]+)%-?r?u?t?h?l?e?s?s?#"
-- 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("-ruthless#"), treeLink:match(oldStyleVersionLookup)))
decodeTreeLink(treeLink:gsub("/%?v=.+#","/"), validateTreeVersion(treeLink:match("%-(%l+%-?%l*)#"), 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
-- 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)))
-- EG: https://www.pathofexile.com/passive-skill-tree/ruthless/AAAABgAAAAAA
-- EG: https://www.pathofexile.com/passive-skill-tree/ruthless-alternate/AAAABgAAAAAA
-- EG: https://www.pathofexile.com/passive-skill-tree/alternate/AAAABgAAAAAA
decodeTreeLink(treeLink, validateTreeVersion(treeLink:match("tree/(%l+%-?%l*)"), treeLink:match(versionLookup)))
end
end)
controls.import.enabled = false
Expand Down
Loading