diff --git a/spec/System/TestImport_spec.lua b/spec/System/TestImport_spec.lua index 2ea158148ef..3b483f0db46 100644 --- a/spec/System/TestImport_spec.lua +++ b/spec/System/TestImport_spec.lua @@ -78,6 +78,7 @@ describe("TestImport", function() typeLine = "Rawhide Gloves", inventoryId = "Gloves", ilvl = 10, + vestigial = true, properties = { }, implicitMods = { { description = "+20 to maximum Life" }, @@ -86,6 +87,7 @@ describe("TestImport", function() { description = "+10 to maximum Life", flags = { crafted = true } }, { description = "+11 to maximum Mana", flags = { fractured = true } }, { description = "+12 to Strength", flags = { mutated = true } }, + { description = "+13 to Dexterity", flags = { vestigial = true } }, }, }) @@ -100,6 +102,9 @@ describe("TestImport", function() assert.is_true(explicitMods["+10 to maximum Life"].crafted) assert.is_true(explicitMods["+11 to maximum Mana"].fractured) assert.is_true(explicitMods["+12 to Strength"].mutated) + assert.is_true(item.vestigial) + assert.is_true(explicitMods["+13 to Dexterity"].vestigial) + assert.is_truthy(item:BuildRaw():find("{vestigial}", 1, true)) end) function importAndReimportWithOldJewel(shouldDelete) diff --git a/spec/System/TestItemParse_spec.lua b/spec/System/TestItemParse_spec.lua index b221053957e..0654d3c8634 100644 --- a/spec/System/TestItemParse_spec.lua +++ b/spec/System/TestItemParse_spec.lua @@ -981,6 +981,8 @@ describe("TestAdvancedItemParse #item", function() assert.are.equals(70, item.memoryStrands) assert.are.equals(1, #item.explicitModLines) + local _, memoryStrandsCount = item:BuildRaw():gsub("Memory Strands:", "") + assert.are.equals(1, memoryStrandsCount) item:BuildAndParseRaw() assert.are.equals(70, item.memoryStrands) diff --git a/src/Assets/intangibilitybg.png b/src/Assets/intangibilitybg.png new file mode 100644 index 00000000000..c682f48c433 Binary files /dev/null and b/src/Assets/intangibilitybg.png differ diff --git a/src/Assets/memorybg.png b/src/Assets/memorybg.png new file mode 100644 index 00000000000..ae477c71697 Binary files /dev/null and b/src/Assets/memorybg.png differ diff --git a/src/Assets/memoryicon.png b/src/Assets/memoryicon.png new file mode 100644 index 00000000000..bae7de0e5d6 Binary files /dev/null and b/src/Assets/memoryicon.png differ diff --git a/src/Assets/vestigialicon.png b/src/Assets/vestigialicon.png new file mode 100644 index 00000000000..16aaaa1c0f9 Binary files /dev/null and b/src/Assets/vestigialicon.png differ diff --git a/src/Classes/ImportTab.lua b/src/Classes/ImportTab.lua index a1a9f164bb9..9176c657d5a 100644 --- a/src/Classes/ImportTab.lua +++ b/src/Classes/ImportTab.lua @@ -505,7 +505,7 @@ local ImportTabClass = newClass("ImportTab", "ControlHost", "Control", function( end) self.controls.enablePartyExportBuffs = new("CheckBoxControl", {"LEFT",self.controls.generateCode,"RIGHT"}, {100, 0, 18}, "Export Support", function(state) self.build.partyTab.enableExportBuffs = state - self.build.buildFlag = true + self.build.buildFlag = true end, "This is for party play, to export support character, it enables the exporting of auras, curses and modifiers to the enemy", false) self.controls.generateCodeOut = new("EditControl", {"TOPLEFT",self.controls.generateCodeLabel,"BOTTOMLEFT"}, {0, 8, 250, 20}, "", "Code", "%Z") self.controls.generateCodeOut.enabled = function() @@ -726,7 +726,7 @@ local ImportTabClass = newClass("ImportTab", "ControlHost", "Control", function( end end) end - + end) -- attempt to fetch the last realm's character list once per instance, if there @@ -769,7 +769,7 @@ function ImportTabClass:Save(xml) xml.attrib.importLink = self.build.importLink end -- Gets rid of erroneous, potentially infinitely nested full base64 XML stored as an import link - xml.attrib.importLink = (xml.attrib.importLink and xml.attrib.importLink:len() < 100) and xml.attrib.importLink or nil + xml.attrib.importLink = (xml.attrib.importLink and xml.attrib.importLink:len() < 100) and xml.attrib.importLink or nil end function ImportTabClass:Draw(viewPort, inputEvents) @@ -1194,8 +1194,8 @@ function ImportTabClass:ImportPassiveTreeAndJewels(charData, deleteJewels) local ruthlessSuffix = charData.league:match("Ruthless") and "_ruthless" or "" local phreciaSuffix = isAscendancyInTree(charData.class, latestTreeVersion) and "" or "_alternate" self.build.spec:ImportFromNodeList(charData.class, - nil, - nil, + nil, + nil, alternateAscendancyId, charPassives.hashes, skillOverrides, @@ -1213,14 +1213,14 @@ function ImportTabClass:ImportPassiveTreeAndJewels(charData, deleteJewels) local resistancePenaltyIndex = 3 if self.build.Act then -- Estimate resistance penalty setting based on act progression estimate if type(self.build.Act) == "string" and self.build.Act == "Endgame" then resistancePenaltyIndex = 3 - elseif type(self.build.Act) == "number" then + elseif type(self.build.Act) == "number" then if self.build.Act < 5 then resistancePenaltyIndex = 1 elseif self.build.Act > 5 and self.build.Act < 11 then resistancePenaltyIndex = 2 elseif self.build.Act > 10 then resistancePenaltyIndex = 3 end end end self.build.configTab.varControls["resistancePenalty"]:SetSel(resistancePenaltyIndex) - + local function setSelByVal(dropdown, val) for i, v in ipairs(dropdown.list) do if v.val == val then @@ -1483,7 +1483,7 @@ function ImportTabClass:ImportItemsAndSkills(charData, clearItems, clearSkills, end local rarityMap = { [0] = "NORMAL", "MAGIC", "RARE", "UNIQUE", [9] = "RELIC", [10] = "RELIC" } -local slotMap = { ["Weapon"] = "Weapon 1", ["Offhand"] = "Weapon 2", ["Weapon2"] = "Weapon 1 Swap", ["Offhand2"] = "Weapon 2 Swap", ["Helm"] = "Helmet", ["BodyArmour"] = "Body Armour", ["Gloves"] = "Gloves", ["Boots"] = "Boots", +local slotMap = { ["Weapon"] = "Weapon 1", ["Offhand"] = "Weapon 2", ["Weapon2"] = "Weapon 1 Swap", ["Offhand2"] = "Weapon 2 Swap", ["Helm"] = "Helmet", ["BodyArmour"] = "Body Armour", ["Gloves"] = "Gloves", ["Boots"] = "Boots", ["Amulet"] = "Amulet", ["Ring"] = "Ring 1", ["Ring2"] = "Ring 2", ["Ring3"] = "Ring 3", ["Belt"] = "Belt", ["BrequelGrafts"] = "Graft 1", ["BrequelGrafts2"] = "Graft 2", } function ImportTabClass:ImportItem(itemData, slotName, ignoreWeaponSwap, itemSetId) @@ -1616,6 +1616,10 @@ function ImportTabClass:ImportItem(itemData, slotName, ignoreWeaponSwap, itemSet item.baseName = "Two-Toned Boots (Evasion/Energy Shield)" item.base = self.build.data.itemBases[item.baseName] end + elseif property.name:find("Intangibility") then + item.intangibility = tonumber(property.values[1][1]:match("%d+")) + elseif property.name == "Memory Strands" then + item.memoryStrands = tonumber(property.values[1][1]) end if property.name == "Energy Shield" or property.name == "Ward" or property.name == "Armour" or property.name == "Evasion Rating" then item.armourData = item.armourData or { } @@ -1630,6 +1634,7 @@ function ImportTabClass:ImportItem(itemData, slotName, ignoreWeaponSwap, itemSet item.corrupted = itemData.corrupted item.fractured = itemData.fractured item.synthesised = itemData.synthesised + item.vestigial = itemData.vestigial if itemData.sockets and itemData.sockets[1] then item.sockets = { } for i, socket in pairs(itemData.sockets) do @@ -1681,9 +1686,10 @@ function ImportTabClass:ImportItem(itemData, slotName, ignoreWeaponSwap, itemSet if itemData.implicitMods then for _, itemMod in ipairs(itemData.implicitMods) do local modLine = itemMod.description or itemMod + local flags = itemMod.flags or itemMod for line in modLine:gmatch("[^\n]+") do local modList, extra = modLib.parseMod(line) - t_insert(item.implicitModLines, { line = line, extra = extra, mods = modList or { } }) + t_insert(item.implicitModLines, { line = line, extra = extra, mods = modList or { }, vestigial = flags.vestigial }) end end end @@ -1705,7 +1711,8 @@ function ImportTabClass:ImportItem(itemData, slotName, ignoreWeaponSwap, itemSet t_insert(item.explicitModLines, { line = line, extra = extra, mods = modList or { }, fractured = flags.fractured, crafted = flags.crafted, - mutated = flags.mutated }) + mutated = flags.mutated, + vestigial = flags.vestigial }) end end end @@ -1817,7 +1824,7 @@ function ImportTabClass:ImportSocketedItems(item, socketedItems, slotName) local normalizedBasename = sanitiseText(socketedItem.typeLine) local gemId = self.build.data.gemForBaseName[normalizedBasename:lower()] if socketedItem.hybrid then - -- Used by transfigured gems and dual-skill gems (currently just Stormbind) + -- Used by transfigured gems and dual-skill gems (currently just Stormbind) normalizedBasename = sanitiseText(socketedItem.hybrid.baseTypeName) gemId = self.build.data.gemForBaseName[normalizedBasename:lower()] if gemId and socketedItem.hybrid.isVaalGem then diff --git a/src/Classes/Item.lua b/src/Classes/Item.lua index d422ef54e55..c0d2e7a7dcd 100644 --- a/src/Classes/Item.lua +++ b/src/Classes/Item.lua @@ -91,7 +91,7 @@ local influenceInfo = itemLib.influenceInfo.all local ItemClass = newClass("Item", function(self, raw, rarity, highQuality) if raw then self:ParseRaw(sanitiseText(raw), rarity, highQuality) - end + end end) -- Reset all influence keys to false @@ -494,6 +494,7 @@ function ItemClass:ParseRaw(raw, rarity, highQuality) self.requirements.int = 0 self.baseLines = { } self.foulborn = false + self.vestigial = false self.mutatedLines = nil ---@type RareLikeUniqueDescription? self.rareLikeUnique = nil @@ -506,7 +507,7 @@ function ItemClass:ParseRaw(raw, rarity, highQuality) local linePrefix = "" local linePostfix = "" - while self.rawLines[l] do + while self.rawLines[l] do local line = self.rawLines[l] if line == "Veiled Prefix" or line == "Veiled Suffix" then self.veiled = true @@ -823,8 +824,6 @@ function ItemClass:ParseRaw(raw, rarity, highQuality) self.catalystQuality = specToNumber(specVal) elseif specName == "Intangibility" then self.intangibility = specToNumber(specVal) - elseif specName == "Memory Strands" then - -- ignored elseif specName == "Note" then self.note = specVal elseif specName == "Str" or specName == "Strength" or specName == "Dex" or specName == "Dexterity" or @@ -925,6 +924,9 @@ function ItemClass:ParseRaw(raw, rarity, highQuality) modLine.crafted = true modLine.implicit = true end + if modLine.vestigial then + self.vestigial = true + end local baseName -- items containing (#-#) lines are marked since the values won't have catalyst or mod magnitudes applied @@ -1433,7 +1435,7 @@ function ItemClass:ParseRaw(raw, rarity, highQuality) end self.affixLimit = 0 if self.crafted then - if not self.affixes then + if not self.affixes then self.crafted = false elseif self.rarity == "MAGIC" then if self.prefixes.limit or self.suffixes.limit then @@ -1646,7 +1648,7 @@ function ItemClass:NormaliseQuality() elseif not self.uniqueID and not self.corrupted and not self.split and not self.mirrored and self.quality < 20 then self.quality = 20 end - end + end end function ItemClass:GetModSpawnWeight(mod, includeTags, excludeTags, baseTags) @@ -2059,7 +2061,7 @@ function ItemClass:Craft() end t_insert(self.explicitModLines, modLine) statOrder[order] = modLine - end + end end end end @@ -2461,7 +2463,7 @@ function ItemClass:BuildModListForSlotNum(baseList, slotNum) or ((jewelData.clusterJewelSkill or jewelData.clusterJewelSmallsAreNothingness) and jewelData.clusterJewelNodeCount) or (jewelData.clusterJewelSocketCountOverride and jewelData.clusterJewelNothingnessCount) end - end + end return { unpack(modList) } end @@ -2595,7 +2597,7 @@ function ItemClass:BuildModList() -- Force the socket count to be equal to the stated number self.selectableSocketCount = socketCount local group = 0 - for i = 1, m_max(socketCount, #self.sockets) do + for i = 1, m_max(socketCount, #self.sockets) do if i > socketCount then self.sockets[i] = nil elseif not self.sockets[i] then diff --git a/src/Classes/ItemsTab.lua b/src/Classes/ItemsTab.lua index deb1fcc43f1..84938ca9655 100644 --- a/src/Classes/ItemsTab.lua +++ b/src/Classes/ItemsTab.lua @@ -17,7 +17,7 @@ local m_modf = math.modf local buySimilar = LoadModule("Classes/CompareBuySimilar") local gemTooltip = LoadModule("Classes/GemTooltip") -local rarityDropList = { +local rarityDropList = { { label = colorCodes.NORMAL.."Normal", rarity = "NORMAL" }, { label = colorCodes.MAGIC.."Magic", rarity = "MAGIC" }, { label = colorCodes.RARE.."Rare", rarity = "RARE" }, @@ -84,7 +84,7 @@ local ItemsTabClass = newClass("ItemsTab", "UndoHandler", "ControlHost", "Contro self.Control() self.build = build - + self.socketViewer = new("PassiveTreeView") self.items = { } @@ -154,7 +154,7 @@ local ItemsTabClass = newClass("ItemsTab", "UndoHandler", "ControlHost", "Contro return self.activeItemSet.useSecondWeaponSet end for i = 1, 6 do - local abyssal = new("ItemSlotControl", {"TOPLEFT",prevSlot,"BOTTOMLEFT"}, 0, 2, self, slotName.." Swap Abyssal Socket "..i, "Abyssal #"..i) + local abyssal = new("ItemSlotControl", {"TOPLEFT",prevSlot,"BOTTOMLEFT"}, 0, 2, self, slotName.." Swap Abyssal Socket "..i, "Abyssal #"..i) addSlot(abyssal) abyssal.parentSlot = swapSlot abyssal.weaponSet = 2 @@ -175,7 +175,7 @@ local ItemsTabClass = newClass("ItemsTab", "UndoHandler", "ControlHost", "Contro if slotName == "Weapon 1" or slotName == "Weapon 2" or slotName == "Helmet" or slotName == "Gloves" or slotName == "Body Armour" or slotName == "Boots" or slotName == "Belt" then -- Add Abyssal Socket slots for i = 1, 6 do - local abyssal = new("ItemSlotControl", {"TOPLEFT",prevSlot,"BOTTOMLEFT"}, 0, 2, self, slotName.." Abyssal Socket "..i, "Abyssal #"..i) + local abyssal = new("ItemSlotControl", {"TOPLEFT",prevSlot,"BOTTOMLEFT"}, 0, 2, self, slotName.." Abyssal Socket "..i, "Abyssal #"..i) addSlot(abyssal) abyssal.parentSlot = slot if slotName:match("Weapon") then @@ -299,23 +299,23 @@ local ItemsTabClass = newClass("ItemsTab", "UndoHandler", "ControlHost", "Contro self:CraftItem() end) self.controls.craftDisplayItem.shown = function() - return self.displayItem == nil + return self.displayItem == nil end self.controls.newDisplayItem = new("ButtonControl", {"TOPLEFT",self.controls.craftDisplayItem,"TOPRIGHT"}, {8, 0, 120, 20}, "Create custom...", function() self:EditDisplayItemText() end) - self.controls.displayItemTip = new("LabelControl", {"TOPLEFT",self.controls.craftDisplayItem,"BOTTOMLEFT"}, {0, 8, 100, 16}, + self.controls.displayItemTip = new("LabelControl", {"TOPLEFT",self.controls.craftDisplayItem,"BOTTOMLEFT"}, {0, 8, 100, 16}, [[^7Double-click an item from one of the lists, or copy and paste an item from in game (hover over the item and Ctrl+C) to view or edit -the item and add it to your build. You can -also clone an item within Path of Building by +the item and add it to your build. You can +also clone an item within Path of Building by copying and pasting it with Ctrl+C and Ctrl+V. -You can Control + Click an item to equip it, or -drag it onto the slot. This will also add it to +You can Control + Click an item to equip it, or +drag it onto the slot. This will also add it to your build if it's from the unique/template list. -If there's 2 slots an item can go in, +If there's 2 slots an item can go in, holding Shift will put it in the second.]]) self.controls.sharedItemList = new("SharedItemListControl", {"TOPLEFT",self.controls.craftDisplayItem, "BOTTOMLEFT"}, {0, 232, 340, 308}, self, true) @@ -368,11 +368,11 @@ holding Shift will put it in the second.]]) if not self.controls.displayItemVariant:IsShown() then return 0 end - return (28 + - (self.displayItem.hasAltVariant and 24 or 0) + - (self.displayItem.hasAltVariant2 and 24 or 0) + + return (28 + + (self.displayItem.hasAltVariant and 24 or 0) + + (self.displayItem.hasAltVariant2 and 24 or 0) + (self.displayItem.hasAltVariant3 and 24 or 0) + - (self.displayItem.hasAltVariant4 and 24 or 0) + + (self.displayItem.hasAltVariant4 and 24 or 0) + (self.displayItem.hasAltVariant5 and 24 or 0)) end}) self.controls.displayItemVersion = new("DropDownControl", {"TOPLEFT", self.controls.displayItemSectionVariant,"TOPLEFT"}, {0, 0, 300, 20}, nil, function(index, value) @@ -571,7 +571,7 @@ holding Shift will put it in the second.]]) self.controls.displayItemAddImplicit.shown = function() return self.displayItem and self.displayItem.type ~= "Tincture" and self.displayItem.type ~= "Graft" and (self.displayItem.corruptible or ((self.displayItem.type ~= "Flask" and self.displayItem.type ~= "Jewel") and - (self.displayItem.rarity == "NORMAL" or self.displayItem.rarity == "MAGIC" or self.displayItem.rarity == "RARE"))) and + (self.displayItem.rarity == "NORMAL" or self.displayItem.rarity == "MAGIC" or self.displayItem.rarity == "RARE"))) and not self.displayItem.implicitsCannotBeChanged end @@ -694,7 +694,7 @@ holding Shift will put it in the second.]]) return self.displayItem and self.displayItem.crafted and self.displayItem.clusterJewel end } - + self.controls.displayItemClusterJewelNodeCountLabel = new("LabelControl", {"TOPLEFT",self.controls.displayItemClusterJewelSkill,"BOTTOMLEFT"}, {0, 7, 0, 14}, "^7Added Passives:") self.controls.displayItemClusterJewelNodeCount = new("SliderControl", {"LEFT",self.controls.displayItemClusterJewelNodeCountLabel,"RIGHT"}, {2, 0, 150, 20}, function(val) local divVal = self.controls.displayItemClusterJewelNodeCount:GetDivVal() @@ -751,7 +751,7 @@ holding Shift will put it in the second.]]) end return false end - + if priorMod then if flipRange(priorMod, self.displayItem.affixes[drop.list[drop.selIndex].modList[index]]) then range = 1 - range @@ -889,7 +889,7 @@ holding Shift will put it in the second.]]) else mod = self.displayItem.affixes[modList[1 + round((#modList - 1) * main.defaultItemAffixQuality)]] end - + -- Adding Mod self:AddModComparisonTooltip(tooltip, mod) end @@ -1297,17 +1297,17 @@ function ItemsTabClass:Save(xml) } for _, id in ipairs(self.itemOrderList) do local item = self.items[id] - local child = { - elem = "Item", - attrib = { - id = tostring(id), - variant = item.variant and tostring(item.variant), - variantAlt = item.variantAlt and tostring(item.variantAlt), + local child = { + elem = "Item", + attrib = { + id = tostring(id), + variant = item.variant and tostring(item.variant), + variantAlt = item.variantAlt and tostring(item.variantAlt), variantAlt2 = item.variantAlt2 and tostring(item.variantAlt2), - variantAlt3 = item.variantAlt3 and tostring(item.variantAlt3), - variantAlt4 = item.variantAlt4 and tostring(item.variantAlt4), + variantAlt3 = item.variantAlt3 and tostring(item.variantAlt3), + variantAlt4 = item.variantAlt4 and tostring(item.variantAlt4), variantAlt5 = item.variantAlt5 and tostring(item.variantAlt5) - } + } } item:BuildAndParseRaw() t_insert(child, item.raw) @@ -1418,9 +1418,9 @@ function ItemsTabClass:Draw(viewPort, inputEvents) end self.x = self.x - self.controls.scrollBarH.offset self.y = self.y - self.controls.scrollBarV.offset - + for _, event in ipairs(inputEvents) do - if event.type == "KeyDown" then + if event.type == "KeyDown" then if event.key == "v" and IsKeyDown("CTRL") then local newItem = Paste() if newItem then @@ -1671,12 +1671,12 @@ function ItemsTabClass:AddItem(item, noAutoEquip, index) end end end - + -- Add it to the list local replacing = self.items[item.id] self.items[item.id] = item item:BuildModList() - + if replacing and (replacing.clusterJewel or item.clusterJewel or replacing.baseName == "Timeless Jewel") then -- We're replacing an existing item, and either the new or old one is a cluster jewel if isValueInTable(self.build.spec.jewels, item.id) then @@ -1806,7 +1806,7 @@ function ItemsTabClass:CopyAnointsAndEldritchImplicits(newItem, copyEldritchImpl return end end - + local modifiableItem = not (newItem.corrupted or newItem.mirrored) if copyEldritchImplicits and isValueInTable(eldritchBaseTypes, newItem.base.type) and isValueInTable(eldritchRarities, newItem.rarity) and (#newItem.implicitModLines == 0 or overwrite) and modifiableItem and (currentItem.cleansing or currentItem.tangle) and currentItem.implicitModLines then @@ -2089,7 +2089,7 @@ function ItemsTabClass:UpdateAffixControls() else self:UpdateAffixControl(self.controls["displayItemAffix"..i], item, "Suffix", "suffixes", i - prefixLimit) end - end + end -- The custom affixes may have had their indexes changed, so the custom control UI is also rebuilt so that it will -- reference the correct affix index. self:UpdateCustomControls() @@ -2116,7 +2116,7 @@ function ItemsTabClass:UpdateAffixControl(control, item, affixType, outputTable, end end end - if item.clusterJewel and item.clusterJewelSkill then + if item.clusterJewel and item.clusterJewelSkill then local skill = item.clusterJewel.skills[item.clusterJewelSkill] if skill then extraTags[skill.tag] = true @@ -2259,7 +2259,7 @@ function ItemsTabClass:UpdateCustomControls() local id = item.id self:CreateDisplayItemFromRaw(item:BuildRaw()) self.displayItem.id = id - end + end i = i + 1 end end @@ -2299,7 +2299,7 @@ end function ItemsTabClass:AddModComparisonTooltip(tooltip, mod) local slotName = self.displayItem:GetPrimarySlot() local newItem = new("Item", self.displayItem:BuildRaw()) - + for _, subMod in ipairs(mod) do t_insert(newItem.explicitModLines, { line = checkLineForAllocates(subMod, self.build.spec.nodes), modTags = mod.modTags, [mod.type] = true }) end @@ -2309,7 +2309,7 @@ function ItemsTabClass:AddModComparisonTooltip(tooltip, mod) local calcFunc = self.build.calcsTab:GetMiscCalculator() local outputBase = calcFunc({ repSlotName = slotName, repItem = self.displayItem }) local outputNew = calcFunc({ repSlotName = slotName, repItem = newItem }) - self.build:AddStatComparesToTooltip(tooltip, outputBase, outputNew, "\nAdding this mod will give: ") + self.build:AddStatComparesToTooltip(tooltip, outputBase, outputNew, "\nAdding this mod will give: ") end -- Returns the first slot in which the given item is equipped @@ -2541,7 +2541,7 @@ function ItemsTabClass:EditDisplayItemText(alsoAddItem) tooltip:AddLine(14, "For Normal and Magic items, the base name must be somewhere in the first line. E.g.:") tooltip:AddLine(14, "Scholar's Platinum Kris of Joy") end - end + end controls.cancel = new("ButtonControl", nil, {45, 470, 80, 20}, "Cancel", function() main:ClosePopup() end) @@ -2552,7 +2552,7 @@ end function ItemsTabClass:EnchantDisplayItem(enchantSlot) self.enchantSlot = enchantSlot or 1 - local controls = { } + local controls = { } local enchantments = self.displayItem.enchantments local haveSkills = true for _, source in ipairs(self.build.data.enchantmentSource) do @@ -2566,7 +2566,7 @@ function ItemsTabClass:EnchantDisplayItem(enchantSlot) if haveSkills then for _, socketGroup in ipairs(self.build.skillsTab.socketGroupList) do for _, gemInstance in ipairs(socketGroup.gemList) do - if gemInstance.gemData then + if gemInstance.gemData then for _, grantedEffect in ipairs(gemInstance.gemData.grantedEffectList) do if not grantedEffect.support and enchantments[grantedEffect.name] then skillsUsed[grantedEffect.name] = true @@ -2869,7 +2869,7 @@ end function ItemsTabClass:AnointDisplayItem(enchantSlot) self.anointEnchantSlot = enchantSlot or 1 - local controls = { } + local controls = { } controls.notableDB = new("NotableDBControl", {"TOPLEFT",nil,"TOPLEFT"}, {10, 60, 360, 360}, self, self.build.spec.tree.nodes, "ANOINT") local function saveLabel() @@ -2898,7 +2898,7 @@ function ItemsTabClass:AnointDisplayItem(enchantSlot) controls.save.tooltipFunc = function(tooltip) tooltip:Clear() self:AppendAnointTooltip(tooltip, controls.notableDB.selValue) - end + end controls.close = new("ButtonControl", {"TOPLEFT", controls.save, "TOPRIGHT" }, {10, 0, 80, 20}, "Cancel", function() main:ClosePopup() end) @@ -3224,7 +3224,7 @@ function ItemsTabClass:CorruptDisplayItem(modType) controls.save.tooltipFunc = function(tooltip) tooltip:Clear() self:AddItemTooltip(tooltip, corruptItem(controls.implicit1.shown), nil, false) - end + end controls.close = new("ButtonControl", { "BOTTOM", nil, "BOTTOM" }, { 45, -4, 80, 20 }, "Cancel", function() main:ClosePopup() end) @@ -3528,11 +3528,11 @@ function ItemsTabClass:AddCustomModifierToDisplayItem() controls.save.tooltipFunc = function(tooltip) tooltip:Clear() self:AddItemTooltip(tooltip, addModifier()) - end + end controls.close = new("ButtonControl", nil, {45, 75, 80, 20}, "Cancel", function() main:ClosePopup() end) - main:OpenPopup(710, 105, "Add Modifier to Item", controls, "save", sourceList[controls.source.selIndex].sourceId == "CUSTOM" and "custom") + main:OpenPopup(710, 105, "Add Modifier to Item", controls, "save", sourceList[controls.source.selIndex].sourceId == "CUSTOM" and "custom") end -- Opens the crucible modifier popup @@ -4005,11 +4005,11 @@ function ItemsTabClass:AddImplicitToDisplayItem() controls.save.tooltipFunc = function(tooltip) tooltip:Clear() self:AddItemTooltip(tooltip, addModifier()) - end + end controls.close = new("ButtonControl", nil, {45, 100, 80, 20}, "Cancel", function() main:ClosePopup() end) - main:OpenPopup(710, 130, "Add Implicit to Item", controls, "save", sourceList[controls.source.selIndex].sourceId == "CUSTOM" and "custom") + main:OpenPopup(710, 130, "Add Implicit to Item", controls, "save", sourceList[controls.source.selIndex].sourceId == "CUSTOM" and "custom") end function ItemsTabClass:AddItemSetTooltip(tooltip, itemSet) @@ -4078,9 +4078,15 @@ function ItemsTabClass:SetTooltipHeaderInfluence(tooltip, item) if item.adjudicator then addInfluence("Warlord") end + if item.vestigial then + addInfluence("Vestigial") + end if item.synthesised and not tooltip.influenceHeader1 then addInfluence("Synthesis") end + if item.memoryStrands and not tooltip.influenceHeader1 then + addInfluence("Memory") + end end if tooltip.influenceHeader1 and not tooltip.influenceHeader2 then @@ -4339,7 +4345,7 @@ function ItemsTabClass:AddItemTooltip(tooltip, item, slot, dbMode, maxWidth) end tooltip:AddLine(fontSizeBig, elemLine, "FONTIN SC") tooltip:AddLine(fontSizeBig, s_format("^x7F7F7FElemental DPS: "..colorCodes.MAGIC.."%.1f", weaponData.ElementalDPS), "FONTIN SC") - totalDamageTypes = totalDamageTypes + 1 + totalDamageTypes = totalDamageTypes + 1 end if weaponData.ChaosDPS then tooltip:AddLine(fontSizeBig, s_format("^x7F7F7FChaos Damage: "..colorCodes.CHAOS.."%d-%d "..colorCodes.MAGIC.."(%.1f DPS)", weaponData.ChaosMin, weaponData.ChaosMax, weaponData.ChaosDPS), "FONTIN SC") @@ -4421,7 +4427,7 @@ function ItemsTabClass:AddItemTooltip(tooltip, item, slot, dbMode, maxWidth) elseif base.tincture then -- Tincture-specific info local tinctureData = item.tinctureData - + if item.quality and item.quality > 0 then tooltip:AddLine(fontSizeBig, s_format("^x7F7F7FQuality: "..colorCodes.MAGIC.."+%d%%", item.quality), "FONTIN SC") end @@ -4471,26 +4477,25 @@ function ItemsTabClass:AddItemTooltip(tooltip, item, slot, dbMode, maxWidth) local line = self:GetSocketDescriptionLine(item) tooltip:AddLine(fontSizeBig, "^x7F7F7FSockets: "..line, "FONTIN SC") end - tooltip:AddSeparator(10) - - if item.memoryStrands then - tooltip:AddLine(fontSizeBig, colorCodes.CRAFTED.."Memory Strands: ^7"..item.memoryStrands, "FONTIN SC") - tooltip:AddSeparator(10) - end if item.talismanTier then tooltip:AddLine(fontSizeBig, "^x7F7F7FTalisman Tier ^xFFFFFF"..item.talismanTier, "FONTIN SC") tooltip:AddSeparator(10) end - + if item.memoryStrands then + if main.showFlavourText then tooltip:AddLine(5, "") end + tooltip:AddLine(fontSizeBig, colorCodes.MEMORY .. s_format("Memory Strands: ^7%d", item.memoryStrands), "FONTIN SC", nil, "Assets/memorybg.png") + if main.showFlavourText then tooltip:AddLine(5, "") end + end if item.intangibility then - tooltip:AddLine(fontSizeBig, colorCodes.INTANGIBILITY .. s_format("Intangibility: ^7%d%%", item.intangibility), "FONTIN SC") - tooltip:AddSeparator(10) + if main.showFlavourText then tooltip:AddLine(5, "") end + tooltip:AddLine(fontSizeBig, colorCodes.INTANGIBILITY .. s_format("Intangibility: ^7%d%%", item.intangibility), "FONTIN SC", nil, "Assets/intangibilitybg.png") + if main.showFlavourText then tooltip:AddLine(5, "") end end - + tooltip:AddSeparator(10) -- Requirements - self.build:AddRequirementsToTooltip(tooltip, item.requirements.level, - item.requirements.strMod, item.requirements.dexMod, item.requirements.intMod, + self.build:AddRequirementsToTooltip(tooltip, item.requirements.level, + item.requirements.strMod, item.requirements.dexMod, item.requirements.intMod, item.requirements.str or 0, item.requirements.dex or 0, item.requirements.int or 0) -- Modifiers @@ -4959,7 +4964,7 @@ function ItemsTabClass:AddItemTooltip(tooltip, item, slot, dbMode, maxWidth) return end - + local slots = {} local isUnique = item.rarity == "UNIQUE" or item.rarity == "RELIC" local currentSameUniqueCount = 0 @@ -4983,7 +4988,7 @@ function ItemsTabClass:AddItemTooltip(tooltip, item, slot, dbMode, maxWidth) return end - + -- either the same unique or same base type local function similar(compareItem, sameUnique) -- empty slot @@ -5022,11 +5027,11 @@ function ItemsTabClass:AddItemTooltip(tooltip, item, slot, dbMode, maxWidth) return false end table.sort(slots, sortFunc) - + for _, slotEntry in ipairs(slots) do addCompareForSlot(slotEntry.compareSlot, slotEntry.selItem, slotEntry.output) end - + end if launch.devModeAlt then diff --git a/src/Classes/Tooltip.lua b/src/Classes/Tooltip.lua index a8cb7b22ada..1906629b706 100644 --- a/src/Classes/Tooltip.lua +++ b/src/Classes/Tooltip.lua @@ -84,7 +84,7 @@ function TooltipClass:CheckForUpdate(...) end end -function TooltipClass:AddLine(size, text, font, modLine) +function TooltipClass:AddLine(size, text, font, modLine, background) if text then local fontToUse if main.showFlavourText then @@ -92,7 +92,7 @@ function TooltipClass:AddLine(size, text, font, modLine) else fontToUse = "VAR" end - for line in s_gmatch(text .. "\n", "([^\n]*)\n") do + for line in s_gmatch(text .. "\n", "([^\n]*)\n") do if line:match("^.*(Equipping)") == "Equipping" or line:match("^.*(Removing)") == "Removing" then t_insert(self.blocks, { height = size + 2}) else @@ -100,10 +100,10 @@ function TooltipClass:AddLine(size, text, font, modLine) end if self.maxWidth then for _, wrappedLine in ipairs(main:WrapString(line, size, self.maxWidth - H_PAD)) do - t_insert(self.lines, { size = size, text = wrappedLine, block = #self.blocks, font = fontToUse, center = self.center, modLine = modLine }) + t_insert(self.lines, { size = size, text = wrappedLine, block = #self.blocks, font = fontToUse, center = self.center, modLine = modLine, background = background }) end else - t_insert(self.lines, { size = size, text = line, block = #self.blocks, font = fontToUse, center = self.center, modLine = modLine }) + t_insert(self.lines, { size = size, text = line, block = #self.blocks, font = fontToUse, center = self.center, modLine = modLine, background = background }) end end end @@ -190,7 +190,7 @@ end function TooltipClass:GetDynamicSize(viewPort) local staticttW, staticttH = self:GetSize() local columns, ttH, _, extraColumnWidth = self:CalculateColumns(0, 0, staticttH, staticttW, viewPort) - + -- ensure extra column width has sensible value extraColumnWidth = (columns > 1 and extraColumnWidth > 0) and extraColumnWidth or staticttW local ttW = staticttW + (m_max(columns - 1, 0) * extraColumnWidth) @@ -293,11 +293,12 @@ function TooltipClass:CalculateColumns(ttY, ttX, ttH, ttW, viewPort) end local lineX = lineCentered and (x + ttW / 2) or (x + (H_PAD / 2)) local lineAlign = lineCentered and "CENTER_X" or "LEFT" - + local stackEntry = {lineX, y, lineAlign, data.size, font, data.text} if data.modLine and data.modLine.disabled then stackEntry.strikethrough = true end + stackEntry.background = data.background t_insert(drawStack, stackEntry) data.bounds = { x = x + (H_PAD / 2), y = y, width = ttW - H_PAD, height = data.size + 2 } y = y + data.size + 2 @@ -336,7 +337,7 @@ function TooltipClass:CalculateColumns(ttY, ttX, ttH, ttW, viewPort) -- calculate column index (origX is at least x * original widths from start) local colIndex = m_floor((origX - ttX) / ttW) + 1 - + if colIndex > 1 then local oldBaseX = ttX + ttW * (colIndex - 1) local newBaseX = ttX + ttW + extraColumnWidth * (colIndex - 2) -- `- 2` because first column is unchanged @@ -400,21 +401,23 @@ function TooltipClass:Draw(x, y, w, h, viewPort) Synthesis = "Assets/synthesisicon.png", Experimented = "Assets/experimentedicon.png", Foulborn = "Assets/breachicon.png", + Memory = "Assets/memoryicon.png", + Vestigial = "Assets/vestigialicon.png", } local headerConfigs = { - RELIC = {left="Assets/itemsheaderfoilleft.png",middle="Assets/itemsheaderfoilmiddle.png",right="Assets/itemsheaderfoilright.png",height=54,sideWidth=47,middleWidth=52,textYOffset=1,allowInfluenceIcon=true}, - UNIQUE = {left="Assets/itemsheaderuniqueleft.png",middle="Assets/itemsheaderuniquemiddle.png",right="Assets/itemsheaderuniqueright.png",height=54,sideWidth=47,middleWidth=52,textYOffset=1,allowInfluenceIcon=true}, - RARE = {left="Assets/itemsheaderrareleft.png",middle="Assets/itemsheaderraremiddle.png",right="Assets/itemsheaderrareright.png",height=54,sideWidth=47,middleWidth=52,textYOffset=1,allowInfluenceIcon=true}, - MAGIC = {left="Assets/itemsheadermagicleft.png",middle="Assets/itemsheadermagicmiddle.png",right="Assets/itemsheadermagicright.png",height=38,sideWidth=32,middleWidth=32,textYOffset=4,allowInfluenceIcon=true}, - NORMAL = {left="Assets/itemsheaderwhiteleft.png",middle="Assets/itemsheaderwhitemiddle.png",right="Assets/itemsheaderwhiteright.png",height=38,sideWidth=32,middleWidth=32,textYOffset=4,allowInfluenceIcon=true}, - GEM = {left="Assets/itemsheadergemleft.png",middle="Assets/itemsheadergemmiddle.png",right="Assets/itemsheadergemright.png",height=38,sideWidth=33,middleWidth=38,textYOffset=3}, - JEWEL = {left="Assets/jewelpassiveheaderleft.png",middle="Assets/jewelpassiveheadermiddle.png",right="Assets/jewelpassiveheaderright.png",height=38,sideWidth=33,middleWidth=38,textYOffset=3}, - NOTABLE = {left="Assets/notablepassiveheaderleft.png",middle="Assets/notablepassiveheadermiddle.png",right="Assets/notablepassiveheaderright.png",height=38,sideWidth=38,middleWidth=38,textYOffset=3}, - PASSIVE = {left="Assets/normalpassiveheaderleft.png",middle="Assets/normalpassiveheadermiddle.png",right="Assets/normalpassiveheaderright.png",height=38,sideWidth=33,middleWidth=38,textYOffset=3}, - KEYSTONE = {left="Assets/keystonepassiveheaderleft.png",middle="Assets/keystonepassiveheadermiddle.png",right="Assets/keystonepassiveheaderright.png",height=38,sideWidth=33,middleWidth=38,textYOffset=3}, - ASCENDANCY = {left="Assets/ascendancypassiveheaderleft.png",middle="Assets/ascendancypassiveheadermiddle.png",right="Assets/ascendancypassiveheaderright.png",height=38,sideWidth=33,middleWidth=38,textYOffset=3}, - MASTERY = {left="Assets/masteryheaderunallocatedleft.png",middle="Assets/masteryheaderunallocatedmiddle.png",right="Assets/masteryheaderunallocatedright.png",height=38,sideWidth=33,middleWidth=38,textYOffset=3}, - MASTERYALLOC = {left="Assets/masteryheaderallocatedleft.png",middle="Assets/masteryheaderallocatedmiddle.png",right="Assets/masteryheaderallocatedright.png",height=38,sideWidth=33,middleWidth=38,textYOffset=3}, + RELIC = {left="Assets/itemsheaderfoilleft.png",middle="Assets/itemsheaderfoilmiddle.png",right="Assets/itemsheaderfoilright.png",height=54,sideWidth=47,middleWidth=52,textYOffset=3,allowInfluenceIcon=true}, + UNIQUE = {left="Assets/itemsheaderuniqueleft.png",middle="Assets/itemsheaderuniquemiddle.png",right="Assets/itemsheaderuniqueright.png",height=54,sideWidth=47,middleWidth=52,textYOffset=3,allowInfluenceIcon=true}, + RARE = {left="Assets/itemsheaderrareleft.png",middle="Assets/itemsheaderraremiddle.png",right="Assets/itemsheaderrareright.png",height=54,sideWidth=47,middleWidth=52,textYOffset=3,allowInfluenceIcon=true}, + MAGIC = {left="Assets/itemsheadermagicleft.png",middle="Assets/itemsheadermagicmiddle.png",right="Assets/itemsheadermagicright.png",height=38,sideWidth=32,middleWidth=32,textYOffset=6,allowInfluenceIcon=true}, + NORMAL = {left="Assets/itemsheaderwhiteleft.png",middle="Assets/itemsheaderwhitemiddle.png",right="Assets/itemsheaderwhiteright.png",height=38,sideWidth=32,middleWidth=32,textYOffset=6,allowInfluenceIcon=true}, + GEM = {left="Assets/itemsheadergemleft.png",middle="Assets/itemsheadergemmiddle.png",right="Assets/itemsheadergemright.png",height=38,sideWidth=33,middleWidth=38,textYOffset=5}, + JEWEL = {left="Assets/jewelpassiveheaderleft.png",middle="Assets/jewelpassiveheadermiddle.png",right="Assets/jewelpassiveheaderright.png",height=38,sideWidth=33,middleWidth=38,textYOffset=6}, + NOTABLE = {left="Assets/notablepassiveheaderleft.png",middle="Assets/notablepassiveheadermiddle.png",right="Assets/notablepassiveheaderright.png",height=38,sideWidth=38,middleWidth=38,textYOffset=6}, + PASSIVE = {left="Assets/normalpassiveheaderleft.png",middle="Assets/normalpassiveheadermiddle.png",right="Assets/normalpassiveheaderright.png",height=38,sideWidth=33,middleWidth=38,textYOffset=6}, + KEYSTONE = {left="Assets/keystonepassiveheaderleft.png",middle="Assets/keystonepassiveheadermiddle.png",right="Assets/keystonepassiveheaderright.png",height=38,sideWidth=33,middleWidth=38,textYOffset=6}, + ASCENDANCY = {left="Assets/ascendancypassiveheaderleft.png",middle="Assets/ascendancypassiveheadermiddle.png",right="Assets/ascendancypassiveheaderright.png",height=38,sideWidth=33,middleWidth=38,textYOffset=6}, + MASTERY = {left="Assets/masteryheaderunallocatedleft.png",middle="Assets/masteryheaderunallocatedmiddle.png",right="Assets/masteryheaderunallocatedright.png",height=38,sideWidth=33,middleWidth=38,textYOffset=6}, + MASTERYALLOC = {left="Assets/masteryheaderallocatedleft.png",middle="Assets/masteryheaderallocatedmiddle.png",right="Assets/masteryheaderallocatedright.png",height=38,sideWidth=33,middleWidth=38,textYOffset=6}, } -- spell-checker: enable local config @@ -450,7 +453,7 @@ function TooltipClass:Draw(x, y, w, h, viewPort) local newX = m_max(viewPort.x, viewPort.x + viewPort.width - totalDrawWidth) local offsetX = newX - ttX ttX = newX - + for _, line in ipairs(drawStack) do if #line < 6 then -- Text element entries have 6 entries and `x` at `[2]` @@ -568,13 +571,13 @@ function TooltipClass:Draw(x, y, w, h, viewPort) -- Draw lines and images local firstSeparatorSkipped = false - for _, line in ipairs(drawStack) do + for _, line in ipairs(drawStack) do if #line < 6 then local skip = false if line[1] and type(line[1]) == "table" and line[1].isSeparator then -- Only skip first separator for items and skill gems local tooltipType = self.tooltipHeader and tostring(self.tooltipHeader):upper() or "" - if main.showFlavourText and not firstSeparatorSkipped and + if main.showFlavourText and not firstSeparatorSkipped and (tooltipType == "RELIC" or tooltipType == "UNIQUE" or tooltipType == "RARE" or tooltipType == "MAGIC" or tooltipType == "GEM") then firstSeparatorSkipped = true skip = true @@ -599,6 +602,36 @@ function TooltipClass:Draw(x, y, w, h, viewPort) end end else + -- Draw background if specified, used for gem mod lines and desecrated mods on items. + local bg = line.background + if bg and main.showFlavourText then + -- Save current draw color BEFORE drawing background image, otherwise wrapped strings print white text for later lines. + local prevR, prevG, prevB, prevA = GetDrawColor() + + if type(bg) == "string" then + if not self._bgHandles then + self._bgHandles = {} + end + if not self._bgHandles[bg] then + local h = NewImageHandle() + h:Load(bg) + self._bgHandles[bg] = h + end + bg = self._bgHandles[bg] + end + + local x = ttX + local y = line[2] - 5 + local width = ttW - 8 + local height = line[4] + 10 + SetDrawColor(1,1,1,1) + DrawImage(bg, x + 4, y, width, height) + + -- Restore color BEFORE DrawString + SetDrawColor(prevR, prevG, prevB, prevA) + end + + -- Draw text line DrawString(unpack(line)) if line.strikethrough then local textX = line[1] @@ -618,7 +651,7 @@ function TooltipClass:Draw(x, y, w, h, viewPort) -- Draw borders if type(self.color) == "string" then - SetDrawColor(self.color) + SetDrawColor(self.color) else SetDrawColor(unpack(self.color)) end diff --git a/src/Data/Global.lua b/src/Data/Global.lua index 37b46df1b87..f7d5e469e3e 100644 --- a/src/Data/Global.lua +++ b/src/Data/Global.lua @@ -61,6 +61,7 @@ colorCodes = { SPLITPERSONALITY = "^xFFD62A", VESTIGIAL = "^xCBA5F1", INTANGIBILITY = "^x9BF4BD", + MEMORY = "^xBFE2FA", } colorCodes.STRENGTH = colorCodes.MARAUDER colorCodes.DEXTERITY = colorCodes.RANGER @@ -351,7 +352,7 @@ SkillType = { Pact = 135, } -GlobalCache = { +GlobalCache = { cachedData = { MAIN = {}, CALCS = {}, CALCULATOR = {} }, }