local _, addon = ... local Gemma = addon.Gemma; local BagScan = Gemma.BagScan; local SPELL_EXTRACT_GEM = 433397; local SPELLNAME_EXTRACT_GEM; local GetItemBagPosition = NarciAPI.GetItemBagPosition; local IsSocketOccupied = Gemma.IsSocketOccupied; local PickupContainerItem = C_Container.PickupContainerItem; local SocketContainerItem = C_Container.SocketContainerItem local GetItemSpell = C_Item.GetItemSpell; local GetItemGemID = C_Item.GetItemGemID; local GetItemNumSockets = C_Item.GetItemNumSockets; local GetInventoryItemLink = GetInventoryItemLink; local SocketInventoryItem = SocketInventoryItem; local ClearCursor = ClearCursor; local ClickSocketButton = ClickSocketButton; local AcceptSockets = AcceptSockets; local CloseSocketInfo = CloseSocketInfo; local GetSpellInfo = addon.TransitionAPI.GetSpellInfo; local InCombatLockdown = InCombatLockdown; local GetCVarBool = C_CVar.GetCVarBool; local After = C_Timer.After; local ipairs = ipairs; local UIParent = UIParent; local ActionButton; local EVENTS = { "SOCKET_INFO_UPDATE", "SOCKET_INFO_SUCCESS", "SOCKET_INFO_FAILURE", "SOCKET_INFO_CLOSE", "SOCKET_INFO_ACCEPT", }; local function GetSpellName() return GetSpellInfo(SPELL_EXTRACT_GEM); end addon.AddLoadingCompleteCallback(GetSpellName); local function IsUsingKeyDown() return GetCVarBool("ActionButtonUseKeyDown"); end local BLIZZARD_UI_LOADED = false; local function LoadBlizzardUI() if not BLIZZARD_UI_LOADED then if C_AddOns.IsAddOnLoaded("Blizzard_ItemSocketingUI") then BLIZZARD_UI_LOADED = true; else BLIZZARD_UI_LOADED = C_AddOns.LoadAddOn("Blizzard_ItemSocketingUI"); end end end local SocketHelper = CreateFrame("Frame"); local BagEventWatcher = CreateFrame("Frame"); do --Stop listening bag after 1.5s function BagEventWatcher:ListenBags() SocketHelper:RegisterEvent("BAG_UPDATE"); self.t = -1.5; self:SetScript("OnUpdate", self.OnUpdate); end function BagEventWatcher:OnUpdate(elapsed) self.t = self.t + elapsed; if self.t > 0 then self:UnlistenBags(); end end function BagEventWatcher:UnlistenBags() if self.t then self.t = nil; self:SetScript("OnUpdate", nil); SocketHelper:UnregisterEvent("BAG_UPDATE"); SocketHelper.pendingGemID = nil; end end end function SocketHelper:SuppressItemSocketingFrame() if not self.suppressed then self.suppressed = true; UIParent:UnregisterEvent("SOCKET_INFO_UPDATE"); --UIParent:UnregisterEvent("ADDON_ACTION_FORBIDDEN"); if ItemSocketingFrame then ItemSocketingFrame:UnregisterEvent("SOCKET_INFO_UPDATE"); ItemSocketingFrame:UnregisterEvent("SOCKET_INFO_ACCEPT"); end self:ListenEvents(true); end end function SocketHelper:UnsuppressItemSocketingFrame() CloseSocketInfo(); if self.suppressed then self:ListenEvents(false); self.suppressed = nil; UIParent:RegisterEvent("SOCKET_INFO_UPDATE"); --UIParent:RegisterEvent("ADDON_ACTION_FORBIDDEN"); if ItemSocketingFrame then ItemSocketingFrame:RegisterEvent("SOCKET_INFO_UPDATE"); ItemSocketingFrame:RegisterEvent("SOCKET_INFO_ACCEPT"); end end end function SocketHelper:ListenEvents(state) local method; if state then method = self.RegisterEvent; else method = self.UnregisterEvent; end for _, event in ipairs(EVENTS) do method(self, event); end end local function PlaceGemInSlot(gemItemID, slotID, socketIndex, customLockDuration) if SocketHelper:IsActionLocked() then return else SocketHelper:LockAction(customLockDuration or 0.25); end ClearCursor(); if not (gemItemID and slotID) then return; end local bagID, slotIndex = GetItemBagPosition(gemItemID); if not(bagID and slotIndex) then return; end SocketHelper:SuppressItemSocketingFrame(); PickupContainerItem(bagID, slotIndex); SocketInventoryItem(slotID); if IsSocketOccupied(socketIndex) then --Something went wrong. Socket isn't empty else ClickSocketButton(socketIndex); ClearCursor(); AcceptSockets(); end SocketHelper:UnsuppressItemSocketingFrame(); return true --Thank god none of the above requires hardware input :) --I jinx it :( end local function RemoveGemInSlot(arg1, arg2, arg3) if SocketHelper:IsActionLocked() then return else SocketHelper:LockAction(0); end local bag, slot, socketIndex; if arg3 then bag = arg1; slot = arg2; socketIndex = arg3; else slot = arg1; socketIndex = arg2; end ClearCursor(); if not socketIndex then return; end if not Gemma:DoesBagHaveFreeSlot() then return end SocketHelper:SuppressItemSocketingFrame(); if bag then SocketContainerItem(bag, slot); else SocketInventoryItem(slot); end --ClickSocketButton(socketIndex); end local function PlaceGemInSlot_EventDriven(gemItemID, slotID, socketIndex) ClearCursor(); if not (gemItemID and slotID) then return; end local bagID, slotIndex = GetItemBagPosition(gemItemID); if not(bagID and slotIndex) then return; end SocketHelper:SuppressItemSocketingFrame(); SocketHelper.mode = "in"; SocketHelper.socketIndex = socketIndex or 1; PickupContainerItem(bagID, slotIndex); SocketInventoryItem(slotID); end local function RemoveGemInSlot_EventDriven(slotID, socketIndex) ClearCursor(); if not (slotID and socketIndex) then return; end if not Gemma:DoesBagHaveFreeSlot() then return end SocketHelper:SuppressItemSocketingFrame(); SocketHelper.mode = "out"; SocketHelper.socketIndex = socketIndex; SocketInventoryItem(slotID); end function SocketHelper:OnUpdate(elapsed) --Throttle action frequency so it doesn't destroy existing gem self.lockTime = self.lockTime + elapsed; if self.lockTime > 0 then self.lockTime = nil; self:SetScript("OnUpdate", nil); end end function SocketHelper:LockAction(duration) if self.lockTime then return end; duration = duration or 0.5; self.lockTime = -duration; self:SetScript("OnUpdate", self.OnUpdate); end function SocketHelper:IsActionLocked() return self.lockTime ~= nil end function SocketHelper:OnEvent(event, ...) if event == "SOCKET_INFO_CLOSE" then self:UnregisterEvent(event); self:UnsuppressItemSocketingFrame(); elseif event == "SOCKET_INFO_UPDATE" then self:UnregisterEvent(event); if not self.pauseUpdate then self.pauseUpdate = true; After(0, function() self.pauseUpdate = nil; self:UnsuppressItemSocketingFrame(); end) end elseif event == "BAG_UPDATE" then --self:UnregisterEvent(event); if not self.pauseBagUpdate then self.pauseBagUpdate = true; After(0, function() self.pauseBagUpdate = nil; if self.pendingGemID then if self:PlaceGemInBestSlot(self.pendingGemID, true) then self.pendingGemID = nil; self:UnregisterEvent(event); end else end end) end end end SocketHelper:SetScript("OnEvent", SocketHelper.OnEvent); function SocketHelper:SetExtractSocketIndex(socketIndex) self.extractSocketIndex = socketIndex; end local ActionButtonMixin = {}; function ActionButtonMixin:PreClick(button) if self.parent.onClickFunc and self.parent.onClickFunc(self.parent, button) then self.showActionBlockerAfterClick = false; return end if self.socketFunc then self.socketFunc(button); end self.showActionBlockerAfterClick = true; end function ActionButtonMixin:PostClick() Gemma.MainFrame:AnchorSpinnerToButton(self.parent); if self.showActionBlockerAfterClick then Gemma.MainFrame:ShowActionBlocker(); end self:OnMouseUp(); end function ActionButtonMixin:OnMouseDown(button) if self.parent.OnMouseDown then self.parent:OnMouseDown(button); end end function ActionButtonMixin:OnMouseUp() if self.parent.OnMouseUp then self.parent:OnMouseUp(); end end function ActionButtonMixin:SetMacroText(macroText, mouseButton) self.macroText = macroText; if mouseButton == "RightButton" then self:SetAttribute("type2", "macro"); if IsUsingKeyDown() then self:RegisterForClicks("RightButtonDown"); else self:RegisterForClicks("RightButtonUp"); end elseif mouseButton == "LeftButton" then self:SetAttribute("type1", "macro"); if IsUsingKeyDown() then self:RegisterForClicks("LeftButtonDown"); else self:RegisterForClicks("LeftButtonUp"); end else self:SetAttribute("type", "macro"); if IsUsingKeyDown() then self:RegisterForClicks("LeftButtonDown", "RightButtonDown"); else self:RegisterForClicks("LeftButtonUp", "RightButtonUp"); end end self:SetAttribute("macrotext", macroText); end function ActionButtonMixin:ExtractInventoryItem(slotID, socketIndex) if not SPELLNAME_EXTRACT_GEM then SPELLNAME_EXTRACT_GEM = GetSpellName(); end self.socketFunc = function() SocketInventoryItem(slotID); end; SocketHelper:SetExtractSocketIndex(socketIndex); local macroText = string.format("/cast %s\r/run ClickSocketButton(%d)", SPELLNAME_EXTRACT_GEM, socketIndex); self:SetMacroText(macroText); end function ActionButtonMixin:ClearAction() if self.macroText then self.macroText = nil; self.socketFunc = nil; self:SetAttribute("type", nil); self:SetAttribute("type1", nil); self:SetAttribute("type2", nil); self:SetAttribute("macrotext", nil); end end function ActionButtonMixin:ClearScripts() --self:SetScript("PreClick", nil); --self:SetScript("PostClick", nil); self.socketFunc = nil; self.onEnterFunc = nil; self.onLeaveFunc = nil; end function ActionButtonMixin:Remove() if self:IsShown() then else return true end if InCombatLockdown() then return false end; self:Hide(); self:ClearAllPoints(); self:SetParent(nil); self:ClearAction(); self:ClearScripts(); return true end function ActionButtonMixin:OnShow() self:RegisterEvent("PLAYER_REGEN_DISABLED"); end function ActionButtonMixin:OnHide() self:Remove(); self:UnregisterEvent("PLAYER_REGEN_DISABLED"); end function ActionButtonMixin:OnEvent(event, ...) if event == "PLAYER_REGEN_DISABLED" then self:Remove(); end end function ActionButtonMixin:SetParentFrame(object) if self:Remove() then self.parent = object; self.onEnterFunc = object.OnEnter; self.onLeaveFunc = object.OnLeave; self:SetParent(object); self:SetFrameLevel(object:GetFrameLevel() + 4); self:SetPoint("TOPLEFT", object, "TOPLEFT", 0, 0); self:SetPoint("BOTTOMRIGHT", object, "BOTTOMRIGHT", 0, 0); local width, height = object:GetSize(); self:SetSize(width, height); self:Show(); return true end end function ActionButtonMixin:OnEnter() if self.onEnterFunc then self.onEnterFunc(self.parent, false, true); end end function ActionButtonMixin:OnLeave() if self.onLeaveFunc then self.onLeaveFunc(self.parent, nil, true); end self:Remove(); end local function CreateActionButton() local f = CreateFrame("Button", nil, UIParent, "InsecureActionButtonTemplate"); f:SetSize(40, 40); f:Hide(); Mixin(f, ActionButtonMixin); f:SetScript("PreClick", f.PreClick); f:SetScript("PostClick", f.PostClick); f:SetScript("OnShow", f.OnShow); f:SetScript("OnHide", f.OnHide); f:SetScript("OnEvent", f.OnEvent); f:SetScript("OnEnter", f.OnEnter); f:SetScript("OnLeave", f.OnLeave); f:SetScript("OnMouseDown", f.OnMouseDown); f:SetScript("OnMouseUp", f.OnMouseUp); --debug local bg = f:CreateTexture(nil, "BACKGROUND"); bg:SetAllPoints(true); --bg:SetColorTexture(1, 0, 0, 0.5); return f end do local function AcquireActionButton(parent) if not parent then return end; if InCombatLockdown() then return end; if not ActionButton then ActionButton = CreateActionButton(); end if ActionButton:SetParentFrame(parent) then return ActionButton end end Gemma.AcquireActionButton = AcquireActionButton; local function HideActionButton() if ActionButton then ActionButton:Remove(); end end Gemma.HideActionButton = HideActionButton; end --local ActionButton = CreateActionButton(); --ActionButton:ExtractInventoryItem(10, 1); --[[ function TTG() local slot = 10 local itemID = 216625; local socketIndex = 1; PlaceGemInSlot(slot, itemID, socketIndex) end --]] do local function MarcoText_UseItemOnSlot(itemID, slotID) return string.format("/stopcasting\r/use item:%s\r/use %s\r/stopspelltarget", itemID, slotID); end function ActionButtonMixin:SetWatchedSpell(spellID) Gemma.MainFrame:SetWatchedSpell(spellID); end function ActionButtonMixin:SetWatchedItem(itemID) local _, spellID = GetItemSpell(itemID); self:SetWatchedSpell(spellID); end function ActionButtonMixin:SetAction_RemoveTinker() --Engineering Tinker --Tinker Module can be removed when bags are full. Sent by Postmaster local itemID = 202087; --Tinker Removal Kit local slotID = 1; --Head local macroText = MarcoText_UseItemOnSlot(itemID, slotID); self:SetMacroText(macroText, "RightButton"); self:SetWatchedItem(itemID); end function SocketHelper:SocketInventoryItemByGemID(gemItemID) self:ClearPendingGem(); local slot, index = Gemma:GetGemInventorySlotAndIndex(gemItemID); if not (slot and index) then return end SocketHelper:SetExtractSocketIndex(index); return slot, index end function SocketHelper:ExtractGemFromInventorySlot(gemItemID) self:ClearPendingGem(); local slot, socketIndex = Gemma:GetGemInventorySlotAndIndex(gemItemID); if not (slot and socketIndex) then SocketHelper:UnsuppressItemSocketingFrame(); return end --self:SetExtractSocketIndex(socketIndex); --SocketInventoryItem(slot); --After(0, function() RemoveGemInSlot(slot, socketIndex) --end); return true, socketIndex end function SocketHelper:ExtractGemFromContainer(gemItemID) self:ClearPendingGem(); local bag, slot, socketIndex = BagScan:GetGemPositionInBagEquipment(gemItemID); if not socketIndex then SocketHelper:UnsuppressItemSocketingFrame(); return end --self:SetExtractSocketIndex(index); --SocketInventoryItem(slot); --After(0, function() self:SetPendingGem(gemItemID); RemoveGemInSlot(bag, slot, socketIndex); --end); return true, socketIndex end function SocketHelper:SocketInventorySlot(slot) self:SuppressItemSocketingFrame(); SocketInventoryItem(slot); end function SocketHelper:SocketContainerSlot(bag, slot) self:SuppressItemSocketingFrame(); SocketContainerItem(bag, slot); end function SocketHelper:SetPendingGem(gemItemID) --Gem is removed from a gear then insert into an currently equipped gear self.pendingGemID = gemItemID; BagEventWatcher:ListenBags(); end function SocketHelper:ClearPendingGem() if self.pendingGemID then self.pendingGemID = nil; BagEventWatcher:UnlistenBags(); end end function SocketHelper:PlaceGemInBestSlot(gemItemID, scanBag) local isOwned = BagScan:CanPickUpGem(gemItemID, scanBag); --GetItemCount: May be slower than BAG_UPDATE if isOwned then local slotID, socketIndex = Gemma:GetBestSlotToPlaceGem(gemItemID); if slotID and socketIndex then PlaceGemInSlot(gemItemID, slotID, socketIndex); return true else end end return false end function ActionButtonMixin:SetAction_RemovePrimordialStone(itemID) local spellID = 405805; --Pluck Out Primordial Stone self.socketFunc = function(button) SocketHelper:SuppressItemSocketingFrame(); if button == "RightButton" and SocketHelper:ExtractGemFromInventorySlot(itemID) then local macroText = "/stopspelltarget\r/click ExtraActionButton1"; self:SetMacroText(macroText, "RightButton"); elseif button == "LeftButton" then if SocketHelper:PlaceGemInBestSlot(itemID) then else if SocketHelper:ExtractGemFromContainer(itemID) then local macroText = "/stopspelltarget\r/click ExtraActionButton1"; self:SetMacroText(macroText, "LeftButton"); else self:SetMacroText(nil, "LeftButton"); end end end end; local macroText = ""; self:SetMacroText(macroText, "AnyButton"); self:SetWatchedSpell(spellID); end local function GetSpellCastMacro() if not SPELLNAME_EXTRACT_GEM then SPELLNAME_EXTRACT_GEM = GetSpellName(); end return string.format("/stopspelltarget\r/cast %s", SPELLNAME_EXTRACT_GEM); end local function GetRemoveSocketMacro(socketIndex) --For Remix Gem, Blizzard ties spellcasting to clicking socket button so removing gems caused error --So we load Blizzard_ItemSocketingUI here and /click button in the macro --Please notice three ItemSocketingSocket buttons are disabled upon "SOCKET_INFO_ACCEPT", see "ItemSocketingFrame_DisableSockets" LoadBlizzardUI(); if not SPELLNAME_EXTRACT_GEM then SPELLNAME_EXTRACT_GEM = GetSpellName(); end return string.format("/stopspelltarget\r/cast %s\r/click ItemSocketingSocket%s", SPELLNAME_EXTRACT_GEM, socketIndex); end function ActionButtonMixin:SetMacro_RemovePandariaGem_Equipped(itemID, mouseButton) local found, socketIndex = SocketHelper:ExtractGemFromInventorySlot(itemID); if found then local macroText = GetRemoveSocketMacro(socketIndex); self:SetMacroText(macroText, mouseButton); else self:SetMacroText(nil, mouseButton); end return found end function ActionButtonMixin:SetMacro_RemovePandariaGem_InBag(itemID, mouseButton) local found, socketIndex = SocketHelper:ExtractGemFromContainer(itemID); if found then local macroText = GetRemoveSocketMacro(socketIndex); self:SetMacroText(macroText, mouseButton); else self:SetMacroText(nil, mouseButton); end return found end function ActionButtonMixin:SetAction_RemovePandariaGem(itemID) local spellID = 433397; self.socketFunc = function(button) SocketHelper:SuppressItemSocketingFrame(); if button == "RightButton" then self:SetMacro_RemovePandariaGem_Equipped(itemID, button); elseif button == "LeftButton" then if SocketHelper:PlaceGemInBestSlot(itemID) then else self:SetMacro_RemovePandariaGem_InBag(itemID, button); end end end; local macroText = ""; self:SetMacroText(macroText, "AnyButton"); self:SetWatchedSpell(spellID); end function ActionButtonMixin:SetAction_RemovePandariaMetaGem(itemID) local spellID = 433397; self.socketFunc = function(button) SocketHelper:SuppressItemSocketingFrame(); if button == "RightButton" then self:SetMacro_RemovePandariaGem_Equipped(itemID, button); elseif button == "LeftButton" then local dataProvider = Gemma:GetDataProviderByName("Pandaria"); local existingGem = dataProvider:GetConflictGemItemID(itemID); if existingGem then self:SetMacro_RemovePandariaGem_Equipped(existingGem, button); SocketHelper:SetPendingGem(itemID); else if SocketHelper:PlaceGemInBestSlot(itemID) then else self:SetMacro_RemovePandariaGem_InBag(itemID, button); end end end end local macroText = ""; self:SetMacroText(macroText, "AnyButton"); self:SetWatchedSpell(spellID); end function ActionButtonMixin:SetAction_RemovePandariaPrimaryGem(itemID) local spellID = 433397; self.socketFunc = function(button) SocketHelper:SuppressItemSocketingFrame(); if button == "LeftButton" then self:SetMacro_RemovePandariaGem_Equipped(itemID, button); else self:SetMacroText(nil, "RightButton"); end end local macroText = ""; self:SetMacroText(macroText, "AnyButton"); self:SetWatchedSpell(spellID); end function ActionButtonMixin:SetAction_InsertPandariaPrimaryGem(itemID) self.socketFunc = function(button) SocketHelper:SuppressItemSocketingFrame(); if button == "LeftButton" then if SocketHelper:PlaceGemInBestSlot(itemID) then else self:SetMacro_RemovePandariaGem_InBag(itemID, button); end else self:SetMacroText(nil, "RightButton"); end end local macroText = ""; self:SetMacroText(macroText, "AnyButton"); self:SetWatchedSpell(nil); end --Loadout function ActionButtonMixin:SetAction_RemovePandariaGemOnPlayer(slotID, socketIndex) self.socketFunc = function(button) if button == "LeftButton" then SocketHelper:SocketInventorySlot(slotID); local macroText = GetRemoveSocketMacro(socketIndex); self:SetMacroText(macroText, button); else self:SetMacroText(nil, "RightButton"); end end local macroText = ""; self:SetMacroText(macroText, "AnyButton"); local spellID = 433397; self:SetWatchedSpell(spellID); end function ActionButtonMixin:SetAction_RemovePandariaGemInBag(gemItemID) self.socketFunc = function(button) if button == "LeftButton" then local bag, slot, socketIndex = BagScan:GetGemPositionInBagEquipment(gemItemID); if bag and slot then SocketHelper:SocketContainerSlot(bag, slot); local macroText = GetRemoveSocketMacro(socketIndex); self:SetMacroText(macroText, button); else self:SetMacroText(nil, "LeftButton"); end else self:SetMacroText(nil, "RightButton"); end end local macroText = ""; self:SetMacroText(macroText, "AnyButton"); local spellID = 433397; self:SetWatchedSpell(spellID); end end do --Remove All Sockets local SOCKETABLE_SLOTS = { 1, 2, 3, 5, 9, 10, 6, 7, 8, 11, 12, 13, 14, }; local function GetSlotForExtract() local itemLink; for _, slotID in ipairs(SOCKETABLE_SLOTS) do itemLink = GetInventoryItemLink("player", slotID); if itemLink and GetItemNumSockets(itemLink) > 0 then for socketIndex = 1, 3 do if GetItemGemID(itemLink, socketIndex) then return slotID end end end end end function ActionButtonMixin:SetAction_RemoveAllPandariaGems() self.socketFunc = function(button) if button == "LeftButton" then local slotID = GetSlotForExtract(); if slotID then if not SPELLNAME_EXTRACT_GEM then SPELLNAME_EXTRACT_GEM = GetSpellName(); end local macroText = string.format("/stopspelltarget\r/cast %s\r/use %s", SPELLNAME_EXTRACT_GEM, slotID); self:SetMacroText(macroText, "LeftButton"); else self:SetMacroText(nil, "LeftButton"); end else self:SetMacroText(nil, "RightButton"); end end local macroText = ""; self:SetMacroText(macroText, "AnyButton"); local spellID = 433397; self:SetWatchedSpell(spellID); end end