Compare commits

...

27 Commits

Author SHA1 Message Date
26156517ec Update TOC 2024-01-16 17:25:50 -06:00
402c88b82d Update TOC for Classic-era 2023-12-10 18:59:19 -06:00
e559af59ec Update TOC 2023-11-07 16:38:29 -06:00
4d6efe8842 Update TOC 2023-10-12 07:53:56 -05:00
0322faf629 Update TOCs 2023-09-08 10:29:37 -05:00
4c816fc5ed Add confirmation dialog when ignoring breakables 2023-08-02 17:54:32 -05:00
bc3632d177 Update description to explain ignore feature 2023-08-02 08:57:59 -05:00
713fbf5f16 Update TOC 2023-07-11 16:51:28 -05:00
902adfa2c8 Update TOC for Wrath Classic 2023-07-04 10:22:01 -05:00
e0a5f0b97d Update TOC, add icon 2023-05-03 18:20:16 -05:00
4d93c119a2 Update TOC 2023-04-01 15:00:21 -05:00
c7a8cf4838 Update TOCs 2023-01-25 13:39:01 -06:00
ba61a08f05 Add detection of profession tools 2023-01-15 16:32:30 -06:00
7396b7189a Add detection of profession gear 2023-01-13 23:03:27 -06:00
d5a1f01749 Add Dragonflight mystics, for real
Fixes #4, fixes #6
2023-01-01 17:27:22 -06:00
9ac6b125a3 Fix reported issue with Breakables not updating
If a throttle happens, we lose the items that we already processed. So, effectively, we only display whatever we can find in the final update (i.e. things located toward the end of bags) on machines that can't process all bags in one go.

Fixes #5
2022-12-12 20:50:03 -06:00
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
b08c8c49d2 Fix lockbox skill level detection 2022-11-30 08:15:57 -06:00
cd34b60c36 Update changelog 2022-11-29 23:37:23 -06:00
4fc455150f Search for pickables when leveling up 2022-11-29 23:26:33 -06:00
fb982f7f9d Don't show pickables if level is too low
If the player's level is too low to open a locked box, this suppresses the box from being displayed (unless the player enables the new option to show it anyway).
2022-11-29 23:24:47 -06:00
cba6773d1c Add Dragonflight lockbox 2022-11-28 23:09:54 -06:00
5286afb4cd Fix typos, add more cmd feedback 2022-11-25 22:35:39 -06:00
d69da1a7d5 Fix lockboxes in 10.0.2
I failed to understand that GetContainerItemInfo isn't a 1:1 mapping to the new native version, so this adds a shim to make it work.
2022-11-16 20:33:58 -06:00
5 changed files with 555 additions and 318 deletions

View File

@ -20,7 +20,14 @@ if C_Container then
GetContainerNumSlots = C_Container.GetContainerNumSlots
end
if C_Container.GetContainerItemInfo then
GetContainerItemInfo = C_Container.GetContainerItemInfo
GetContainerItemInfo = function(bagId, slotId)
local info = C_Container.GetContainerItemInfo(bagId, slotId)
if not info then
return nil
end
return info.iconFileID, info.stackCount
end
end
if C_Container.GetContainerItemLink then
GetContainerItemLink = C_Container.GetContainerItemLink
@ -192,11 +199,22 @@ local OreCombineItems = {
local DisenchantId = 13262
local DisenchantTypes = {babbleInv["Armor"], babbleInv["Weapon"]}
local DisenchantEquipSlots = {"INVTYPE_PROFESSION_GEAR", "INVTYPE_PROFESSION_TOOL"}
local CanDisenchant = false
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
@ -234,6 +252,8 @@ local PickableItems = {
180533, -- kyrian
180522, -- night fae
186161, -- stygian lockbox, 9.1.0
-- dragonflight
190954, -- serevite lockbox
}
local CanPickLock = false
@ -255,6 +275,7 @@ local IDX_BREAKABLETYPE = 9
local IDX_SOULBOUND = 10
local IDX_NAME = 11
local IDX_RARITY = 12
local IDX_EQUIPSLOT = 13
local BREAKABLE_HERB = 1
local BREAKABLE_ORE = 2
@ -460,8 +481,9 @@ 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")
if ShouldHookTradeskillUpdate then
self:RegisterEvent("TRADE_SKILL_UPDATE", "OnTradeSkillUpdate")
@ -475,12 +497,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)
@ -500,7 +516,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
@ -509,10 +532,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
@ -644,7 +667,7 @@ function Breakables:GetOptions()
set = function(info, v)
self.settings.hide = v
if info.uiType == "cmd" then
print("|cff33ff99Breakables|r: set |cffffff78maxBreakables|r to " .. tostring(self.settings.hide))
print("|cff33ff99Breakables|r: set |cffffff78hideAlways|r to " .. tostring(self.settings.hide))
end
self:ToggleButtonFrameVisibility(not v)
if not v then
@ -663,7 +686,7 @@ function Breakables:GetOptions()
set = function(info, v)
self.settings.hideIfNoBreakables = v
if info.uiType == "cmd" then
print("|cff33ff99Breakables|r: set |cffffff78hideIfNoBreakables|r to " .. tostring(self.settings.hideIfNoBreakables))
print("|cff33ff99Breakables|r: set |cffffff78hideNoBreakables|r to " .. tostring(self.settings.hideIfNoBreakables))
end
self:FindBreakables()
end,
@ -752,6 +775,9 @@ function Breakables:GetOptions()
set = function(info, v)
self.settings.growDirection = v
self:FindBreakables()
if info.uiType == "cmd" then
print("|cff33ff99Breakables|r: set |cffffff78growDirection|r to " .. tostring(self.settings.growDirection))
end
end,
order = 7,
},
@ -785,6 +811,25 @@ function Breakables:GetOptions()
end,
order = 9,
},
showNonUnlockableItems = {
type = 'toggle',
name = L['Show high-level lockboxes'],
desc = L['If checked, a lockbox that is too high level for the player to pick will still be shown in the list, otherwise it will be hidden.'],
get = function(info)
return self.settings.showNonUnlockableItems
end,
set = function(info, v)
self.settings.showNonUnlockableItems = v
self:FindBreakables()
if info.uiType == "cmd" then
print("|cff33ff99Breakables|r: set |cffffff78showNonUnlockableItems|r to " .. tostring(self.settings.showNonUnlockableItems))
end
end,
hidden = function()
return not CanPickLock or not C_TooltipInfo
end,
order = 10,
},
ignoreList = {
type = 'multiselect',
name = L["Ignore list"],
@ -848,10 +893,13 @@ function Breakables:GetOptions()
type = "execute",
name = L["Reset placement"],
desc = L["Resets where the buttons are placed on the screen to the default location."],
func = function()
func = function(info)
self.settings.buttonFrameLeft = self.defaults.profile.buttonFrameLeft
self.settings.buttonFrameTop = self.defaults.profile.buttonFrameTop
self:CreateButtonFrame()
if info.uiType == "cmd" then
print("|cff33ff99Breakables|r: reset placement of button")
end
end,
order = 30,
},
@ -1072,36 +1120,65 @@ function Breakables:OnMouseUp(frame)
self.settings.buttonFrameTop[frameNum] = frame:GetTop()
end
local function IgnoreFunc(self, button, isDown)
if button == "RightButton" and isDown and not InCombatLockdown() then
Breakables.settings.ignoreList[self.itemId] = true
StaticPopupDialogs["BREAKABLES_CONFIRM_IGNORE"] = {
text = L["This will add the chosen item to the ignore list so it no longer appears as breakable. Items can be removed from the ignore list in the Breakables settings.\n\nWould you like to ignore this item?"],
button1 = YES,
OnShow = function(self)
self:SetFrameStrata("TOOLTIP")
end,
OnHide = function(self)
self:SetFrameStrata("DIALOG")
end,
OnAccept = function(self, data)
Breakables.settings.ignoreList[data] = true
Breakables:FindBreakables()
LibStub("AceConfigRegistry-3.0"):NotifyChange("Breakables")
end,
button2 = NO,
timeout = 0,
whileDead = 1,
hideOnEscape = 0
}
local function IgnoreFunc(self, button, isDown)
if button == "RightButton" and isDown and not InCombatLockdown() then
local dlg = StaticPopup_Show("BREAKABLES_CONFIRM_IGNORE")
if dlg then
dlg.data = self.itemId
end
end
end
function Breakables:FindBreakables(bag)
if self.settings.hide then
return
end
if not canBreakSomething() then
return
end
if self.bCombat then
self.bPendingUpdate = true
return
end
do
local bagId = 0
local updatefunc
local foundBreakables = {}
local i=1
local numBreakableStacks = {}
function Breakables:FindBreakables()
if self.settings.hide then
return
end
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
if not canBreakSomething() then
return
end
if self.bCombat then
self.bPendingUpdate = true
return
end
if currState ~= STATE_SCANNING then
local count = #foundBreakables
for i=0, count do
foundBreakables[i]=nil
end
end
currState = STATE_SCANNING
local i=#foundBreakables + 1
local numBreakableStacks = {}
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)
@ -1111,164 +1188,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], nil, foundBreakables[i][IDX_EQUIPSLOT])
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)
@ -1343,13 +1429,50 @@ 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
local texture, itemCount, locked, quality, readable = GetContainerItemInfo(bagId, slotId)
local texture, itemCount = GetContainerItemInfo(bagId, slotId)
if texture then
local itemLink = GetContainerItemLink(bagId, slotId)
local itemId = self:GetItemIdFromLink(itemLink)
@ -1359,21 +1482,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
and self:BreakableIsDisenchantable(itemType, itemLevel, itemRarity, itemLink, itemId, equipSlot) then
local soulbound = self:ScanForTooltipLine(tooltipData, ITEM_SOULBOUND, ITEM_ACCOUNTBOUND, ITEM_BNETACCOUNTBOUND)
local isInEquipmentSet = false
if self.settings.hideEqManagerItems then
@ -1388,27 +1510,14 @@ function Breakables:FindBreakablesInSlot(bagId, slotId)
local shouldHideThisItem = (self.settings.hideEqManagerItems and isInEquipmentSet) or (self.settings.hideTabards and isTabard)
or equipSlot == nil or (equipSlot == "" and not IsArtifactRelicItem(itemLink))
if (not soulbound or self.settings.showSoulbound) and not shouldHideThisItem then
return {itemLink, itemCount, itemType, itemTexture, bagId, slotId, itemSubType, itemLevel, BREAKABLE_DE, soulbound, itemName, itemRarity}
if self:IsForcedDisenchantable(itemId) or ((not soulbound or self.settings.showSoulbound) and not shouldHideThisItem) then
return {itemLink, itemCount, itemType, itemTexture, bagId, slotId, itemSubType, itemLevel, BREAKABLE_DE, soulbound, itemName, itemRarity, equipSlot}
else
return nil
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
@ -1418,7 +1527,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
@ -1437,11 +1548,11 @@ function Breakables:FindBreakablesInSlot(bagId, slotId)
if CanMill --[[and (itemSubType == MillingItemSubType or itemSubType == MillingItemSecondarySubType)]] then
if millable then
return {itemLink, itemCount, itemType, itemTexture, bagId, slotId, itemSubType, itemLevel, BREAKABLE_HERB, false, itemName, itemRarity}
return {itemLink, itemCount, itemType, itemTexture, bagId, slotId, itemSubType, itemLevel, BREAKABLE_HERB, false, itemName, itemRarity, equipSlot}
else
for i=1,#HerbCombineItems do
if HerbCombineItems[i] == itemId then
return {itemLink, itemCount, itemType, itemTexture, bagId, slotId, itemSubType, itemLevel, BREAKABLE_COMBINE, false, itemName, itemRarity}
return {itemLink, itemCount, itemType, itemTexture, bagId, slotId, itemSubType, itemLevel, BREAKABLE_COMBINE, false, itemName, itemRarity, equipSlot}
end
end
end
@ -1449,24 +1560,45 @@ function Breakables:FindBreakablesInSlot(bagId, slotId)
if CanProspect --[[and itemSubType == ProspectingItemSubType]] then
if prospectable then
return {itemLink, itemCount, itemType, itemTexture, bagId, slotId, itemSubType, itemLevel, BREAKABLE_ORE, false, itemName, itemRarity}
return {itemLink, itemCount, itemType, itemTexture, bagId, slotId, itemSubType, itemLevel, BREAKABLE_ORE, false, itemName, itemRarity, equipSlot}
else
for i=1,#OreCombineItems do
if OreCombineItems[i] == itemId then
return {itemLink, itemCount, itemType, itemTexture, bagId, slotId, itemSubType, itemLevel, BREAKABLE_COMBINE, false, itemName, itemRarity}
return {itemLink, itemCount, itemType, itemTexture, bagId, slotId, itemSubType, itemLevel, BREAKABLE_COMBINE, false, itemName, itemRarity, equipSlot}
end
end
end
end
if CanPickLock and self:ItemIsPickable(itemId) and self:ItemIsLocked(bagId, slotId) then
return {itemLink, itemCount, itemType, itemTexture, bagId, slotId, itemSubType, itemLevel, BREAKABLE_PICK, false, itemName, itemRarity}
if CanPickLock and self:ItemIsPickable(itemId) and self:ItemIsLocked(bagId, slotId) and self:PlayerHasSkillToPickItem(bagId, slotId) then
return {itemLink, itemCount, itemType, itemTexture, bagId, slotId, itemSubType, itemLevel, BREAKABLE_PICK, false, itemName, itemRarity, equipSlot}
end
end
return nil
end
function Breakables:PlayerHasSkillToPickItem(bagId, slotId)
if not C_TooltipInfo or self.settings.showNonUnlockableItems then
return true
end
local tooltipData = C_TooltipInfo.GetBagItem(bagId, slotId)
if not tooltipData then
return true
end
TooltipUtil.SurfaceArgs(tooltipData)
for _, line in ipairs(tooltipData.lines) do
TooltipUtil.SurfaceArgs(line)
if line.leftText == LOCKED then
return not (line.leftColor and line.leftColor.r == 1 and line.leftColor.g < 0.2 and line.leftColor.b < 0.2)
end
end
return true
end
function Breakables:ItemIsPickable(itemId)
for i=1,#PickableItems do
if PickableItems[i] == itemId then
@ -1573,118 +1705,124 @@ function Breakables:SortBreakables(foundBreakables)
end
end
function Breakables:BreakableIsDisenchantable(itemType, itemLevel, itemRarity, itemLink, itemId)
for i=1,#DisenchantTypes do
if DisenchantTypes[i] == itemType or IsArtifactRelicItem(itemLink) then
-- bfa+ no longer has skill level requirements for disenchanting
if IgnoreEnchantingSkillLevelForDisenchant then
return true
end
if self.settings.ignoreEnchantingSkillLevel then
return true
end
-- if we couldn't figure out the player's enchanting skill level, err on the side of showing stuff
if self.EnchantingLevel == 0 then
return true
end
-- account for WoD and higher no longer needing specific ilvl. numbers from http://wow.gamepedia.com/Item_level
if (itemRarity == RARITY_UNCOMMON and itemLevel >= 483)
or (itemRarity == RARITY_RARE and itemLevel >= 515)
or (itemRarity == RARITY_EPIC and itemLevel >= 640) then
return true
end
-- this is awful. is there an easier way? taken from www.wowpedia.org/Disenchanting
if itemRarity == RARITY_UNCOMMON then
if itemLevel <= 20 then
return self.EnchantingLevel >= 1
elseif itemLevel <= 25 then
return self.EnchantingLevel >= 25
elseif itemLevel <= 30 then
return self.EnchantingLevel >= 50
elseif itemLevel <= 35 then
return self.EnchantingLevel >= 75
elseif itemLevel <= 40 then
return self.EnchantingLevel >= 100
elseif itemLevel <= 45 then
return self.EnchantingLevel >= 125
elseif itemLevel <= 50 then
return self.EnchantingLevel >= 150
elseif itemLevel <= 55 then
return self.EnchantingLevel >= 175
elseif itemLevel <= 60 then
return self.EnchantingLevel >= 200
elseif itemLevel <= 99 then
return self.EnchantingLevel >= 225
elseif itemLevel <= 120 then
return self.EnchantingLevel >= 275
elseif itemLevel <= 150 then
return self.EnchantingLevel >= 325
elseif itemLevel <= 182 then
return self.EnchantingLevel >= 350
elseif itemLevel <= 318 then
return self.EnchantingLevel >= 425
elseif itemLevel <= 437 then
return self.EnchantingLevel >= 475
else
return self.EnchantingLevel >= 475
end
elseif itemRarity == RARITY_RARE then
if itemLevel <= 25 then
return self.EnchantingLevel >= 25
elseif itemLevel <= 30 then
return self.EnchantingLevel >= 50
elseif itemLevel <= 35 then
return self.EnchantingLevel >= 75
elseif itemLevel <= 40 then
return self.EnchantingLevel >= 100
elseif itemLevel <= 45 then
return self.EnchantingLevel >= 125
elseif itemLevel <= 50 then
return self.EnchantingLevel >= 150
elseif itemLevel <= 55 then
return self.EnchantingLevel >= 175
elseif itemLevel <= 60 then
return self.EnchantingLevel >= 200
elseif itemLevel <= 97 then
return self.EnchantingLevel >= 225
elseif itemLevel <= 115 then
return self.EnchantingLevel >= 275
elseif itemLevel <= 200 then
return self.EnchantingLevel >= 325
elseif itemLevel <= 346 then
return self.EnchantingLevel >= 450
elseif itemLevel <= 424 then
return self.EnchantingLevel >= 525
elseif itemLevel <= 463 then
return self.EnchantingLevel >= 550
else
return self.EnchantingLevel >= 550
end
elseif itemRarity == RARITY_EPIC then
if itemLevel <= 95 then
return self.EnchantingLevel >= 225
elseif itemLevel <= 164 then
return self.EnchantingLevel >= 300
elseif itemLevel <= 277 then
return self.EnchantingLevel >= 375
elseif itemLevel <= 416 then
return self.EnchantingLevel >= 475
elseif itemLevel <= 575 then
return self.EnchantingLevel >= 575
else
return self.EnchantingLevel >= 575
end
else
return false
end
return true
end
function Breakables:BreakableIsDisenchantable(itemType, itemLevel, itemRarity, itemLink, itemId, equipSlot)
if not itemId and itemLink then
itemId = self:GetItemIdFromLink(itemLink)
end
if self:IsDisenchantableItemType(itemType) or IsArtifactRelicItem(itemLink) or self:IsDisenchantableEquipSlot(equipSlot) then
-- bfa+ no longer has skill level requirements for disenchanting
if IgnoreEnchantingSkillLevelForDisenchant then
return true
end
if self.settings.ignoreEnchantingSkillLevel then
return true
end
-- if we couldn't figure out the player's enchanting skill level, err on the side of showing stuff
if self.EnchantingLevel == 0 then
return true
end
-- account for WoD and higher no longer needing specific ilvl. numbers from http://wow.gamepedia.com/Item_level
if (itemRarity == RARITY_UNCOMMON and itemLevel >= 483)
or (itemRarity == RARITY_RARE and itemLevel >= 515)
or (itemRarity == RARITY_EPIC and itemLevel >= 640) then
return true
end
-- this is awful. is there an easier way? taken from www.wowpedia.org/Disenchanting
if itemRarity == RARITY_UNCOMMON then
if itemLevel <= 20 then
return self.EnchantingLevel >= 1
elseif itemLevel <= 25 then
return self.EnchantingLevel >= 25
elseif itemLevel <= 30 then
return self.EnchantingLevel >= 50
elseif itemLevel <= 35 then
return self.EnchantingLevel >= 75
elseif itemLevel <= 40 then
return self.EnchantingLevel >= 100
elseif itemLevel <= 45 then
return self.EnchantingLevel >= 125
elseif itemLevel <= 50 then
return self.EnchantingLevel >= 150
elseif itemLevel <= 55 then
return self.EnchantingLevel >= 175
elseif itemLevel <= 60 then
return self.EnchantingLevel >= 200
elseif itemLevel <= 99 then
return self.EnchantingLevel >= 225
elseif itemLevel <= 120 then
return self.EnchantingLevel >= 275
elseif itemLevel <= 150 then
return self.EnchantingLevel >= 325
elseif itemLevel <= 182 then
return self.EnchantingLevel >= 350
elseif itemLevel <= 318 then
return self.EnchantingLevel >= 425
elseif itemLevel <= 437 then
return self.EnchantingLevel >= 475
else
return self.EnchantingLevel >= 475
end
elseif itemRarity == RARITY_RARE then
if itemLevel <= 25 then
return self.EnchantingLevel >= 25
elseif itemLevel <= 30 then
return self.EnchantingLevel >= 50
elseif itemLevel <= 35 then
return self.EnchantingLevel >= 75
elseif itemLevel <= 40 then
return self.EnchantingLevel >= 100
elseif itemLevel <= 45 then
return self.EnchantingLevel >= 125
elseif itemLevel <= 50 then
return self.EnchantingLevel >= 150
elseif itemLevel <= 55 then
return self.EnchantingLevel >= 175
elseif itemLevel <= 60 then
return self.EnchantingLevel >= 200
elseif itemLevel <= 97 then
return self.EnchantingLevel >= 225
elseif itemLevel <= 115 then
return self.EnchantingLevel >= 275
elseif itemLevel <= 200 then
return self.EnchantingLevel >= 325
elseif itemLevel <= 346 then
return self.EnchantingLevel >= 450
elseif itemLevel <= 424 then
return self.EnchantingLevel >= 525
elseif itemLevel <= 463 then
return self.EnchantingLevel >= 550
else
return self.EnchantingLevel >= 550
end
elseif itemRarity == RARITY_EPIC then
if itemLevel <= 95 then
return self.EnchantingLevel >= 225
elseif itemLevel <= 164 then
return self.EnchantingLevel >= 300
elseif itemLevel <= 277 then
return self.EnchantingLevel >= 375
elseif itemLevel <= 416 then
return self.EnchantingLevel >= 475
elseif itemLevel <= 575 then
return self.EnchantingLevel >= 575
else
return self.EnchantingLevel >= 575
end
else
return false
end
return true
end
return self:IsForcedDisenchantable(itemId)
end
function Breakables:IsForcedDisenchantable(itemId)
for i=1,#AdditionalDisenchantableItems do
if AdditionalDisenchantableItems[i] == itemId then
return true
@ -1693,3 +1831,23 @@ function Breakables:BreakableIsDisenchantable(itemType, itemLevel, itemRarity, i
return false
end
function Breakables:IsDisenchantableItemType(itemType)
for i=1,#DisenchantTypes do
if DisenchantTypes[i] == itemType then
return true
end
end
return false
end
function Breakables:IsDisenchantableEquipSlot(equipSlot)
for i=1,#DisenchantEquipSlots do
if DisenchantEquipSlots[i] == equipSlot then
return true
end
end
return false
end

View File

@ -1,8 +1,8 @@
## Interface: 100002
## Interface-Retail: 100002
## Interface-Classic: 11403
## Interface: 100205
## Interface-Retail: 100205
## Interface-Classic: 11500
## Interface-BCC: 20504
## Interface-Wrath: 30400
## Interface-Wrath: 30403
## Author: Parnic
## Name: Breakables
## Title: Breakables |cff7fff7f-Ace3-|r
@ -14,6 +14,7 @@
# ## Version: @project-version@
#@end-non-debug@
## SavedVariables: BreakablesDB
## IconTexture: Interface\Icons\ability_warrior_sunder
## OptionalDeps: Ace3, LibBabble-Inventory-3.0, Masque
## X-Curse-Project-ID: 16629
## X-WoWI-ID: 17929

View File

@ -1,3 +1,79 @@
v1.9.23:
- Update Dragonflight TOC for 10.2.5
v1.9.22:
- Update Classic-era TOC for 1.15.0
v1.9.21:
- Update Dragonflight TOC for 10.2.0
v1.9.20:
- Update Wrath-Classic TOC for 3.4.3
v1.9.19:
- Update Dragonflight TOC for 10.1.7
- Update Classic-era TOC for 1.14.4
v1.9.18:
- Added a confirmation dialog when ignoring breakables, and updated the description to mention the ignore feature.
v1.9.17:
- Update Dragonflight TOC for 10.1.5
v1.9.16:
- Update TOC for Wrath Classic 3.4.2
v1.9.15:
- Updated TOCs for 10.1.0
- Added icon for 10.1.0
v1.9.14:
- Updated TOCs for 10.0.7
v1.9.13:
- Updated TOCs for 10.0.5
v1.9.12:
- Add detection of Dragonflight profession tools as disenchantable.
v1.9.11:
- Add detection of Dragonflight profession gear as disenchantable.
v1.9.10:
- Add Dragonflight Mystic disenchantable items. (Fixed)
v1.9.9
- Fix issue with not updating or showing items in some cases.
v1.9.8:
- 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.
- Hide lockboxes that can't be picked because the player's level is too low, where we can detect it (10.0+, toggleable with an option).
v1.9.6:
- Fix lockboxes in 10.0.2
v1.9.5:
- Fix another error from 10.0.2

View File

@ -9,7 +9,7 @@ local L = LibStub("AceLocale-3.0"):NewLocale("Breakables", "enUS", true, debug)
--@debug@
L["Welcome"] = [[Thanks for using |cff33ff99Breakables|r! Use |cffffff78/brk|r to open this menu or |cffffff78/breakables|r to access the same options on the command line.
Hold shift and drag the profession button to move the breakables bar around. You can also click directly on any breakable item button to break it without having to click the profession button first.
Hold shift and drag the profession button to move the breakables bar around. You can also click directly on any breakable item button to break it without having to click the profession button first. Right-clicking a breakable button will add that item to the ignore list so it won't show up again. The ignore list can be managed from the configuration panel.
If you have any feature requests or problems, please email |cff33ff99breakables@parnic.com|r or visit the |cffffff78curse.com|r or |cffffff78wowinterface.com|r page and leave a comment.]]
--@end-debug@

View File

@ -1,12 +1,14 @@
### **Description**
# Breakables
## **Description**
Displays a bar on screen that allows quick access to enchanting, jewelcrafting, and inscription professions by presenting a Disenchant/Prospect/Mill button and all of the breakable items you have for that profession next to it. Also displays a bar for any locked junkboxes in your inventory if you're a Rogue. This allows one-click access for breaking down items instead of finding the item in your bag, clicking the appropriate profession/skill button, and clicking the item for each and every item you want to break. For prospecting and milling, you will see the number of items you have alongside the number of times you can break it. In the prospecting screenshot on the right, the player has 169 total Saronite Ore in his bags which will allow for 33 total prospects.
### **Usage**
## **Usage**
By default, if you have the appropriate profession and items in your inventory, a bar will appear with the profession ability followed by any items that are eligible for breaking. You can hold shift to drag the bar around if you want to move it. Clicking the profession button will activate that ability (disenchant/prospect/mill) and clicking an item will break it. You don't have to click the profession button first as simply clicking the item will automatically break it down.
By default, if you have the appropriate profession and items in your inventory, a bar will appear with the profession ability followed by any items that are eligible for breaking. You can hold shift to drag the bar around if you want to move it. Clicking the profession button will activate that ability (disenchant/prospect/mill) and clicking an item will break it. You don't have to click the profession button first as simply clicking the item will automatically break it down. Right-clicking an item will place that item on the ignore list so it won't show up in the list again. The ignore list can be managed in the addon's configuration panel.
### **Configuration**
## **Configuration**
Typing */brk* will open the configuration settings (or you can get to it from Blizzard's Interface options) which consist of:
@ -14,6 +16,6 @@ Typing */brk* will open the configuration settings (or you can get to it from Bl
* **Max number to display**: this controls the highest number of items that you will see next to your profession button. If this is 5 but you have 10 breakable items in your bags, you will only see 5 at a time.
* **Show soulbound items**: aimed at enchanters, this controls whether or not you will see items that are soulbound as breakable items or not.
### **Known issues**
## **Known issues**
* If you have more than 5 of a breakable item but split into stacks all smaller than 5, the game will say you do not have enough items to break. The default UI now has a built-in button to compress stacks that should solve this issue.