Compare commits

...

4 Commits

Author SHA1 Message Date
6f78aeb7fa Limit max processing per frame
This causes Breakables to check one bag at a time, then schedule an update for the next frame if too much time has gone by since the scan was started. This reduces/removes any noticeable hitch induced by Breakables when a scan runs. Further throttling is possible if this is insufficient by checking at the Slot level instead of just the Bag level, but on my main character with full 32-slot bags and the native tooltip scanning method, I rarely see more than one throttle per check, so I think this is okay for now.
2022-12-04 22:54:02 -06:00
450527d1bd Improve scanning performance on 10.0+
Use the new TooltipInfo native class to greatly improve scanning speed on clients where it's available.
2022-12-04 22:41:56 -06:00
de6924ee14 Fix microstutter when pressing modifiers
Where "modifiers" are Shift, Alt, etc.

Fixes #1
2022-12-04 21:51:29 -06:00
b517fbf9f6 Add DF Mystic items as disenchantables
Fixes #4
2022-12-01 08:36:57 -06:00
2 changed files with 231 additions and 179 deletions

View File

@ -204,6 +204,16 @@ local EnchantingProfessionId = 333
local AdditionalDisenchantableItems = {
137195, -- highmountain armor
-- dragonflight
-- specialization items (Mystics)
200939, -- Chromatic Pocketwatch
200940, -- Everflowing Inkwell
200941, -- Seal of Order
200942, -- Vibrant Emulsion
200943, -- Whispering Band
200945, -- Valiant Hammer
200946, -- Thunderous Blade
200947, -- Carving of Awakening
}
local PickLockId = 1804
@ -469,7 +479,6 @@ function Breakables:RegisterEvents()
self:RegisterEvent("PLAYER_REGEN_DISABLED", "OnEnterCombat")
self:RegisterEvent("PLAYER_REGEN_ENABLED", "OnLeaveCombat")
self:RegisterEvent("MODIFIER_STATE_CHANGED", "FindBreakables")
self:RegisterEvent("SPELLS_CHANGED", "OnSpellsChanged")
-- this will show lockboxes if the player gains a level that then enables opening that box
self:RegisterEvent("PLAYER_LEVEL_UP", "FindBreakables")
@ -486,12 +495,6 @@ function Breakables:RegisterEvents()
end
end
function Breakables:OnModifierChanged()
if showingTooltip ~= nil and not self.bCombat then
self:OnEnterBreakableButton(showingTooltip)
end
end
function Breakables:OnDisable()
self:UnregisterAllEvents()
self.frame:SetScript("OnUpdate", nil)
@ -511,7 +514,14 @@ function Breakables:OnItemReceived(event, bag)
end
end
local STATE_IDLE, STATE_SCANNING = 0, 1
local currState = STATE_IDLE
function Breakables:CheckShouldFindBreakables()
if currState == STATE_SCANNING then
self:FindBreakables()
return
end
local latestTime = -1
for i=0,#nextCheck do
if nextCheck[i] and nextCheck[i] > latestTime then
@ -520,10 +530,10 @@ function Breakables:CheckShouldFindBreakables()
end
if latestTime > 0 and latestTime <= GetTime() then
self:FindBreakables()
for i=0,#nextCheck do
nextCheck[i] = -1
end
self:FindBreakables()
end
end
@ -1116,28 +1126,30 @@ local function IgnoreFunc(self, button, isDown)
end
end
function Breakables:FindBreakables(bag)
if self.settings.hide then
return
end
do
local bagId = 0
local updatefunc
function Breakables:FindBreakables()
if self.settings.hide then
return
end
if not canBreakSomething() then
return
end
if not canBreakSomething() then
return
end
if self.bCombat then
self.bPendingUpdate = true
return
end
if self.bCombat then
self.bPendingUpdate = true
return
end
local foundBreakables = {}
local i=1
local numBreakableStacks = {}
currState = STATE_SCANNING
local foundBreakables = {}
local i=1
local numBreakableStacks = {}
for bagId=0,NUM_BAG_SLOTS do
-- this is where i tried to throttle updates...can't just yet since the full breakables list is rebuilt every time this function is called
-- consider ways of caching off the last-known state of all breakables
--if bag == nil or bag == bagId then
local maxTime = GetTimePreciseSec() + 0.01
while bagId <= NUM_BAG_SLOTS do
local found = self:FindBreakablesInBag(bagId)
for n=1,#found do
local addedToExisting = self:MergeBreakables(found[n], foundBreakables)
@ -1147,164 +1159,173 @@ function Breakables:FindBreakables(bag)
i = i + 1
end
end
--end
end
self:SortBreakables(foundBreakables)
bagId = bagId + 1
if not self.breakableButtons then
self.breakableButtons = {}
end
for i=1,#foundBreakables do
for j=1,numEligibleProfessions do
if not self.breakableButtons[j] then
self.breakableButtons[j] = {}
if maxTime < GetTimePreciseSec() then
return
end
end
if not numBreakableStacks[j] then
numBreakableStacks[j] = 0
end
bagId = 0
currState = STATE_IDLE
if (foundBreakables[i][IDX_BREAKABLETYPE] == self.buttonFrame[j].type or (foundBreakables[i][IDX_BREAKABLETYPE] == BREAKABLE_COMBINE and foundBreakables[i][IDX_COUNT] >= 10)) and numBreakableStacks[j] < self.settings.maxBreakablesToShow then
local isDisenchantable = self:BreakableIsDisenchantable(foundBreakables[i][IDX_TYPE], foundBreakables[i][IDX_LEVEL], foundBreakables[i][IDX_RARITY], foundBreakables[i][IDX_LINK])
local isLockedItem = foundBreakables[i][IDX_BREAKABLETYPE] == BREAKABLE_PICK
self:SortBreakables(foundBreakables)
if (CanDisenchant and isDisenchantable) or (CanPickLock and isLockedItem) or (foundBreakables[i][IDX_COUNT] >= 5) then
numBreakableStacks[j] = numBreakableStacks[j] + 1
local btnIdx = numBreakableStacks[j]
if not self.breakableButtons then
self.breakableButtons = {}
end
local btn = self.breakableButtons[j][btnIdx]
if not self.breakableButtons[j][btnIdx] then
self.breakableButtons[j][btnIdx] = CreateFrame("Button", "BREAKABLES_BUTTON"..j.."-"..btnIdx, self.buttonFrame[j], "SecureActionButtonTemplate")
for i=1,#foundBreakables do
for j=1,numEligibleProfessions do
if not self.breakableButtons[j] then
self.breakableButtons[j] = {}
end
btn = self.breakableButtons[j][btnIdx]
if not numBreakableStacks[j] then
numBreakableStacks[j] = 0
end
if lbfGroup then
btn.icon = btn:CreateTexture(btn:GetName().."Icon", "BACKGROUND")
end
if (foundBreakables[i][IDX_BREAKABLETYPE] == self.buttonFrame[j].type or (foundBreakables[i][IDX_BREAKABLETYPE] == BREAKABLE_COMBINE and foundBreakables[i][IDX_COUNT] >= 10)) and numBreakableStacks[j] < self.settings.maxBreakablesToShow then
local isDisenchantable = self:BreakableIsDisenchantable(foundBreakables[i][IDX_TYPE], foundBreakables[i][IDX_LEVEL], foundBreakables[i][IDX_RARITY], foundBreakables[i][IDX_LINK])
local isLockedItem = foundBreakables[i][IDX_BREAKABLETYPE] == BREAKABLE_PICK
btn:SetWidth(buttonSize)
btn:SetHeight(buttonSize)
btn:EnableMouse(true)
btn:RegisterForClicks("AnyUp", "AnyDown")
if (CanDisenchant and isDisenchantable) or (CanPickLock and isLockedItem) or (foundBreakables[i][IDX_COUNT] >= 5) then
numBreakableStacks[j] = numBreakableStacks[j] + 1
local btnIdx = numBreakableStacks[j]
btn:SetAttribute("type1", "spell")
local btn = self.breakableButtons[j][btnIdx]
if not self.breakableButtons[j][btnIdx] then
self.breakableButtons[j][btnIdx] = CreateFrame("Button", "BREAKABLES_BUTTON"..j.."-"..btnIdx, self.buttonFrame[j], "SecureActionButtonTemplate")
if not btn.text then
btn.text = btn:CreateFontString()
btn.text:SetPoint("BOTTOM", btn, "BOTTOM", 0, 2)
end
btn.text:SetFont(NumberFont_Outline_Med:GetFont(), self.settings.fontSize, "OUTLINE")
btn = self.breakableButtons[j][btnIdx]
btn:HookScript("OnClick", IgnoreFunc)
if lbfGroup then
lbfGroup:AddButton(btn)
end
end
btn.itemId = self:GetItemIdFromLink(foundBreakables[i][IDX_LINK])
local attachFrom = "LEFT"
local attachTo = "RIGHT"
if self.settings.growDirection then
if self.settings.growDirection == 1 then -- left
attachFrom = "RIGHT"
attachTo = "LEFT"
--elseif self.settings.growDirection == 2 then -- right
elseif self.settings.growDirection == 3 then -- up
attachFrom = "BOTTOM"
attachTo = "TOP"
elseif self.settings.growDirection == 4 then -- down
attachFrom = "TOP"
attachTo = "BOTTOM"
end
end
btn:ClearAllPoints()
btn:SetPoint(attachFrom, btnIdx == 1 and self.buttonFrame[j] or self.breakableButtons[j][btnIdx - 1], attachTo)
if not isDisenchantable then
local appendText = ""
if not isLockedItem then
local breakStackSize = 5
if foundBreakables[i][IDX_BREAKABLETYPE] == BREAKABLE_COMBINE then
breakStackSize = 10
if lbfGroup then
btn.icon = btn:CreateTexture(btn:GetName().."Icon", "BACKGROUND")
end
btn:SetWidth(buttonSize)
btn:SetHeight(buttonSize)
btn:EnableMouse(true)
btn:RegisterForClicks("AnyUp", "AnyDown")
btn:SetAttribute("type1", "spell")
if not btn.text then
btn.text = btn:CreateFontString()
btn.text:SetPoint("BOTTOM", btn, "BOTTOM", 0, 2)
end
btn.text:SetFont(NumberFont_Outline_Med:GetFont(), self.settings.fontSize, "OUTLINE")
btn:HookScript("OnClick", IgnoreFunc)
if lbfGroup then
lbfGroup:AddButton(btn)
end
appendText = " ("..(floor(foundBreakables[i][IDX_COUNT]/breakStackSize))..")"
end
btn.text:SetText(foundBreakables[i][IDX_COUNT] .. appendText)
end
btn.itemId = self:GetItemIdFromLink(foundBreakables[i][IDX_LINK])
local BreakableAbilityName = GetSpellInfo(self:GetSpellIdFromProfessionButton(foundBreakables[i][IDX_BREAKABLETYPE], self:GetItemIdFromLink(foundBreakables[i][IDX_LINK])))
--GetSpellInfo((foundBreakables[i][IDX_BREAKABLETYPE] == BREAKABLE_HERB and MillingId)
--or (foundBreakables[i][IDX_BREAKABLETYPE] == BREAKABLE_ORE and ProspectingId)
--or (foundBreakables[i][IDX_BREAKABLETYPE] == BREAKABLE_DE and DisenchantId)
--or PickLockId)
if BreakableAbilityName then
btn:SetAttribute("type1", "spell")
btn:SetAttribute("spell", BreakableAbilityName)
local attachFrom = "LEFT"
local attachTo = "RIGHT"
if self.settings.growDirection then
if self.settings.growDirection == 1 then -- left
attachFrom = "RIGHT"
attachTo = "LEFT"
--elseif self.settings.growDirection == 2 then -- right
elseif self.settings.growDirection == 3 then -- up
attachFrom = "BOTTOM"
attachTo = "TOP"
elseif self.settings.growDirection == 4 then -- down
attachFrom = "TOP"
attachTo = "BOTTOM"
end
end
btn:SetAttribute("target-bag", foundBreakables[i][IDX_BAG])
btn:SetAttribute("target-slot", foundBreakables[i][IDX_SLOT])
else
btn:SetAttribute("type1", "item")
btn:SetAttribute("item", "item:" .. self:GetItemIdFromLink(foundBreakables[i][IDX_LINK]))
end
btn:ClearAllPoints()
btn:SetPoint(attachFrom, btnIdx == 1 and self.buttonFrame[j] or self.breakableButtons[j][btnIdx - 1], attachTo)
if lbfGroup then
btn.icon:SetTexture(foundBreakables[i][IDX_TEXTURE])
else
btn:SetNormalTexture(foundBreakables[i][IDX_TEXTURE])
end
btn.bag = foundBreakables[i][IDX_BAG]
btn.slot = foundBreakables[i][IDX_SLOT]
if not isDisenchantable then
local appendText = ""
if not isLockedItem then
local breakStackSize = 5
if foundBreakables[i][IDX_BREAKABLETYPE] == BREAKABLE_COMBINE then
breakStackSize = 10
end
appendText = " ("..(floor(foundBreakables[i][IDX_COUNT]/breakStackSize))..")"
end
if not btn.OnEnterFunc then
btn.OnEnterFunc = function(this) self:OnEnterBreakableButton(this) end
end
if not btn.OnLeaveFunc then
btn.OnLeaveFunc = function() self:OnLeaveBreakableButton() end
end
if not btn.PostClickedFunc then
btn.PostClickedFunc = function(this) self:PostClickedBreakableButton(this) end
end
btn.text:SetText(foundBreakables[i][IDX_COUNT] .. appendText)
end
btn:SetScript("OnEnter", btn.OnEnterFunc)
btn:SetScript("OnLeave", btn.OnLeaveFunc)
btn:SetScript("PostClick", btn.PostClickedFunc)
local BreakableAbilityName = GetSpellInfo(self:GetSpellIdFromProfessionButton(foundBreakables[i][IDX_BREAKABLETYPE], self:GetItemIdFromLink(foundBreakables[i][IDX_LINK])))
--GetSpellInfo((foundBreakables[i][IDX_BREAKABLETYPE] == BREAKABLE_HERB and MillingId)
--or (foundBreakables[i][IDX_BREAKABLETYPE] == BREAKABLE_ORE and ProspectingId)
--or (foundBreakables[i][IDX_BREAKABLETYPE] == BREAKABLE_DE and DisenchantId)
--or PickLockId)
if BreakableAbilityName then
btn:SetAttribute("type1", "spell")
btn:SetAttribute("spell", BreakableAbilityName)
btn:Show()
btn:SetAttribute("target-bag", foundBreakables[i][IDX_BAG])
btn:SetAttribute("target-slot", foundBreakables[i][IDX_SLOT])
else
btn:SetAttribute("type1", "item")
btn:SetAttribute("item", "item:" .. self:GetItemIdFromLink(foundBreakables[i][IDX_LINK]))
end
if lbfGroup then
btn.icon:SetTexture(foundBreakables[i][IDX_TEXTURE])
else
btn:SetNormalTexture(foundBreakables[i][IDX_TEXTURE])
end
btn.bag = foundBreakables[i][IDX_BAG]
btn.slot = foundBreakables[i][IDX_SLOT]
if not btn.OnEnterFunc then
btn.OnEnterFunc = function(this) self:OnEnterBreakableButton(this) end
end
if not btn.OnLeaveFunc then
btn.OnLeaveFunc = function() self:OnLeaveBreakableButton() end
end
if not btn.PostClickedFunc then
btn.PostClickedFunc = function(this) self:PostClickedBreakableButton(this) end
end
btn:SetScript("OnEnter", btn.OnEnterFunc)
btn:SetScript("OnLeave", btn.OnLeaveFunc)
btn:SetScript("PostClick", btn.PostClickedFunc)
btn:Show()
end
end
end
end
end
for i=1,numEligibleProfessions do
if not numBreakableStacks[i] then
numBreakableStacks[i] = 0
end
for i=1,numEligibleProfessions do
if not numBreakableStacks[i] then
numBreakableStacks[i] = 0
end
if self.breakableButtons[i] and numBreakableStacks[i] < #self.breakableButtons[i] then
for j=numBreakableStacks[i]+1,#self.breakableButtons[i] do
self.breakableButtons[i][j]:Hide()
if self.breakableButtons[i] and numBreakableStacks[i] < #self.breakableButtons[i] then
for j=numBreakableStacks[i]+1,#self.breakableButtons[i] do
self.breakableButtons[i][j]:Hide()
end
end
if self.buttonFrame[i] then
if numBreakableStacks[i] == 0 and self.settings.hideIfNoBreakables then
self.buttonFrame[i]:Hide()
else
self.buttonFrame[i]:Show()
end
end
end
if self.buttonFrame[i] then
if numBreakableStacks[i] == 0 and self.settings.hideIfNoBreakables then
self.buttonFrame[i]:Hide()
else
self.buttonFrame[i]:Show()
end
if showingTooltip ~= nil then
self:OnEnterBreakableButton(showingTooltip)
end
end
if showingTooltip ~= nil then
self:OnEnterBreakableButton(showingTooltip)
end
end
function Breakables:OnEnterProfessionButton(btn)
@ -1379,8 +1400,45 @@ function Breakables:FindBreakablesInBag(bagId)
return foundBreakables
end
function Breakables:ScanForTooltipLine(tooltipData, ...)
if tooltipData then
for _, line in ipairs(tooltipData.lines) do
if not line then
return false
end
if not line.leftText then
return false
end
for j=1,select('#', ...) do
if line.leftText == select(j, ...) then
return true
end
end
end
return false
end
for i=1,15 do
local leftText = _G["BreakablesTooltipTextLeft"..i]
local textLine = leftText and leftText:GetText() or nil
if not textLine then
return false
end
for j=1,select('#', ...) do
if textLine == select(j, ...) then
return true
end
end
end
return false
end
function Breakables:FindBreakablesInSlot(bagId, slotId)
if not self.myTooltip then
if not C_TooltipInfo and not self.myTooltip then
self.myTooltip = CreateFrame("GameTooltip", "BreakablesTooltip", nil, "GameTooltipTemplate")
self.myTooltip:SetOwner(WorldFrame, "ANCHOR_NONE")
end
@ -1395,21 +1453,20 @@ function Breakables:FindBreakablesInSlot(bagId, slotId)
local itemName, _, itemRarity, itemLevel, _, itemType, itemSubType, _, equipSlot, itemTexture, vendorPrice = GetItemInfo(itemLink)
self.myTooltip:SetBagItem(bagId, slotId)
local tooltipData
if C_TooltipInfo then
tooltipData = C_TooltipInfo.GetBagItem(bagId, slotId)
TooltipUtil.SurfaceArgs(tooltipData)
for _, line in ipairs(tooltipData.lines) do
TooltipUtil.SurfaceArgs(line)
end
else
self.myTooltip:SetBagItem(bagId, slotId)
end
if CanDisenchant and itemRarity and itemRarity >= RARITY_UNCOMMON and itemRarity < RARITY_HEIRLOOM
and self:BreakableIsDisenchantable(itemType, itemLevel, itemRarity, itemLink, itemId) then
local i = 1
local soulbound = false
for i=1,15 do
if _G["BreakablesTooltipTextLeft"..i] then
local textLine = _G["BreakablesTooltipTextLeft"..i]:GetText()
if textLine == ITEM_SOULBOUND or textLine == ITEM_ACCOUNTBOUND or textLine == ITEM_BNETACCOUNTBOUND then
soulbound = true
break
end
end
end
local soulbound = self:ScanForTooltipLine(tooltipData, ITEM_SOULBOUND, ITEM_ACCOUNTBOUND, ITEM_BNETACCOUNTBOUND)
local isInEquipmentSet = false
if self.settings.hideEqManagerItems then
@ -1431,20 +1488,7 @@ function Breakables:FindBreakablesInSlot(bagId, slotId)
end
end
local idx = 1
local millable = false
local prospectable = false
for idx=1,5 do
if _G["BreakablesTooltipTextLeft"..idx] then
if _G["BreakablesTooltipTextLeft"..idx]:GetText() == ITEM_MILLABLE then
millable = true
break
elseif _G["BreakablesTooltipTextLeft"..idx]:GetText() == ITEM_PROSPECTABLE then
prospectable = true
break
end
end
end
local millable = self:ScanForTooltipLine(tooltipData, ITEM_MILLABLE)
if CanMill and not millable then
for i=1,#AdditionalMillableItems do
@ -1454,7 +1498,9 @@ function Breakables:FindBreakablesInSlot(bagId, slotId)
end
end
local prospectable
if CanProspect then
prospectable = self:ScanForTooltipLine(tooltipData, ITEM_PROSPECTABLE)
if not prospectable then
for i=1,#AdditionalProspectableItems do
if AdditionalProspectableItems[i] == itemId then

View File

@ -1,3 +1,9 @@
v1.9.8:
- Add Dragonflight Mystic disenchantable items.
- Fix microstutter every time a modifier key, such as Alt, was pressed.
- Improve performance when scanning for new breakables (such as when items enter/leave bags, a lockbox is clicked, etc.).
v1.9.7:
- Add Dragonflight lockboxes.