local function zButtonPet_GetRoot() return MTH_ZH_GetSavedRoot() end local root = zButtonPet_GetRoot() -- Initialize with defaults only if not already configured if not root["zButtonPet"] then root["zButtonPet"] = {} root["zButtonPet"]["pet"] = {} root["zButtonPet"]["pet"]["happiness"] = nil root["zButtonPet"]["pet"]["status"] = nil root["zButtonPet"]["pet"]["dead"] = nil root["zButtonPet"]["spells"] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} root["zButtonPet"]["rows"] = 1 root["zButtonPet"]["horizontal"] = nil root["zButtonPet"]["vertical"] = nil root["zButtonPet"]["firstbutton"] = "RIGHT" root["zButtonPet"]["enabled"] = 1 root["zButtonPet"]["tooltip"] = 1 root["zButtonPet"]["parent"] = {} root["zButtonPet"]["parent"]["size"] = 36 root["zButtonPet"]["parent"]["hide"] = nil root["zButtonPet"]["parent"]["circle"] = 1 root["zButtonPet"]["children"] = {} root["zButtonPet"]["children"]["size"] = 36 root["zButtonPet"]["children"]["hideonclick"] = 1 end root["zButtonPet"]["food"] = {} ZHunterMod_Pet_Spells = { ZHUNTER_PET_EYES, ZHUNTER_PET_DISMISS, ZHUNTER_PET_MEND, ZHUNTER_PET_FEED, ZHUNTER_PET_CALL, ZHUNTER_PET_REVIVE, ZHUNTER_PET_LORE, ZHUNTER_PET_TRAINING, ZHUNTER_PET_TAMING, ZHUNTER_PET_POSITION } local ZHUNTER_PET_MAX = table.getn(ZHunterMod_Pet_Spells) local zButtonPet_SetButtons local zButtonPet_LastRefreshAt = 0 local zButtonPet_MinRefreshInterval = 0.15 local zButtonPet_MinCombatRefreshInterval = 0.30 local zButtonPet_RuntimeSpellMap = nil local zButtonPet_LastStateKey = nil local function zButtonPet_GetSaved() local currentRoot = zButtonPet_GetRoot() if not currentRoot["zButtonPet"] then currentRoot["zButtonPet"] = {} end return currentRoot["zButtonPet"] end local function zButtonPet_EnsureConfig() local saved = zButtonPet_GetSaved() saved["count"] = nil if not saved["pet"] then saved["pet"] = {} end if saved["pet"]["happiness"] == nil then saved["pet"]["happiness"] = nil end if saved["pet"]["status"] == nil then saved["pet"]["status"] = nil end if saved["pet"]["dead"] == nil then saved["pet"]["dead"] = nil end if not saved["parent"] then saved["parent"] = {} end if saved["parent"]["size"] == nil then saved["parent"]["size"] = 36 end if saved["parent"]["hide"] == nil then saved["parent"]["hide"] = nil end if saved["parent"]["circle"] == nil then saved["parent"]["circle"] = 1 end if saved["enabled"] == nil then saved["enabled"] = 1 end if saved["tooltip"] == nil then saved["tooltip"] = 1 end if saved["rows"] == nil then saved["rows"] = 1 end if saved["horizontal"] == nil then saved["horizontal"] = nil end if saved["vertical"] == nil then saved["vertical"] = nil end if saved["firstbutton"] == nil then saved["firstbutton"] = "RIGHT" end if not saved["children"] then saved["children"] = {} end if saved["children"]["size"] == nil then saved["children"]["size"] = 36 end if saved["children"]["hideonclick"] == nil then saved["children"]["hideonclick"] = 1 end if saved["food"] == nil then saved["food"] = {} end if not saved["spells"] then saved["spells"] = {} end if not saved["visible"] then saved["visible"] = {} end for i = 1, ZHUNTER_PET_MAX do if not tonumber(saved["spells"][i]) then saved["spells"][i] = i end if saved["visible"][i] == nil then saved["visible"][i] = 1 end end end local function zButtonPet_ApplyRuntimeSettings() if not zButtonPet then return end local saved = zButtonPet_GetSaved() zButtonPet.tooltip = saved["tooltip"] and true or false zButtonPet.hideonclick = saved["children"] and saved["children"]["hideonclick"] and true or false end local function zButtonPet_RebuildRuntimeSpellMap() if not zButtonPet or not zButtonPet.count then zButtonPet_RuntimeSpellMap = {} return zButtonPet_RuntimeSpellMap end local spells = {} for i=1, zButtonPet.count do local button = getglobal(zButtonPet.name..i) if button and button.id then local spellName = GetSpellName(button.id, "spell") if spellName and spellName ~= "" then spells[spellName] = button.id end end end zButtonPet_RuntimeSpellMap = spells return spells end local function zButtonPet_GetPreferredDefaultParentId(spells) if zButtonPet1 and zButtonPet1.id then return zButtonPet1.id end if zButtonPet and zButtonPet.count then for i = 1, zButtonPet.count do local child = getglobal("zButtonPet" .. i) if child and child.id then return child.id end end end return spells[ZHUNTER_PET_EYES] or spells[ZHUNTER_PET_POSITION] or spells[ZHUNTER_PET_DISMISS] end function zButtonPet_OnLoad() this:RegisterEvent("VARIABLES_LOADED") end function zButtonPet_OnEvent() if event == "VARIABLES_LOADED" then if not zButtonPet then return end if UnitClass("player") ~= ZHUNTER_HUNTER then zButtonPet:UnregisterAllEvents() zButtonPet:Hide() return end zButtonPet.customSetButtons = zButtonPet_SetButtons zButtonPet_CreateButtons() zButtonPet.beforeclick = zButtonPetAdjustment_BeforeClick zButtonPet.afterclick = zButtonPet_AfterClick zButtonPetAdjustment = CreateFrame("Frame", "zButtonPetAdjustment") zButtonPetAdjustment:RegisterEvent("UNIT_HEALTH") zButtonPetAdjustment:RegisterEvent("UNIT_HAPPINESS") zButtonPetAdjustment:RegisterEvent("UNIT_PET") zButtonPetAdjustment:RegisterEvent("PET_BAR_UPDATE") zButtonPetAdjustment:RegisterEvent("PLAYER_ENTERING_WORLD") zButtonPetAdjustment:RegisterEvent("SPELLS_CHANGED") zButtonPetAdjustment:RegisterEvent("LEARNED_SPELL_IN_TAB") zButtonPetAdjustment:SetScript("OnEvent", zButtonPetAdjustment_OnEvent) zButtonPet_SetupSizeAndPosition() zButtonPetAdjustment_OnEvent() end end function zButtonPet_CreateButtons() zButtonPet_EnsureConfig() local saved = zButtonPet_GetSaved() ZSpellButton_CreateChildren(zButtonPet, "zButtonPet", ZHUNTER_PET_MAX) local info = {} local infoIndex = 1 for i=1, table.getn(ZHunterMod_Pet_Spells) do if not tonumber(saved["spells"][i]) then info = ZHunterMod_Pet_Spells saved["spells"] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} break end local spellIndex = saved["spells"][i] if saved["visible"][spellIndex] ~= false then info[infoIndex] = ZHunterMod_Pet_Spells[spellIndex] infoIndex = infoIndex + 1 end end zButtonPet.found = ZSpellButton_SetButtons(zButtonPet, info) zButtonPet_ApplyRuntimeSettings() zButtonPet_RebuildRuntimeSpellMap() zButtonPet_LastStateKey = nil end function zButtonPet_SetupSizeAndPosition() local saved = zButtonPet_GetSaved() if saved["enabled"] == false or saved["enabled"] == 0 then if zButtonPetAdjustment and zButtonPetAdjustment.SetScript then zButtonPetAdjustment:SetScript("OnEvent", nil) end if zButtonPet and zButtonPet.Hide then zButtonPet:Hide() end return end if zButtonPetAdjustment and zButtonPetAdjustment.SetScript then zButtonPetAdjustment:SetScript("OnEvent", zButtonPetAdjustment_OnEvent) end local displayCount = zButtonPet.found or ZHUNTER_PET_MAX if displayCount < 0 then displayCount = 0 end ZSpellButton_SetSize(zButtonPet, saved["parent"]["size"]) ZSpellButton_SetSize(zButtonPet, saved["children"]["size"], 1) ZSpellButton_SetExpandDirection(zButtonPet, saved["firstbutton"]) ZSpellButton_ArrangeChildren(zButtonPet, saved["rows"], displayCount, saved["horizontal"], saved["vertical"]) end function zButtonPet_Reset() local currentRoot = zButtonPet_GetRoot() currentRoot["zButtonPet"] = {} local saved = zButtonPet_GetSaved() saved["pet"] = {} saved["pet"]["happiness"] = nil saved["pet"]["status"] = nil saved["pet"]["dead"] = nil saved["spells"] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} saved["rows"] = 1 saved["horizontal"] = nil saved["vertical"] = nil saved["firstbutton"] = "RIGHT" saved["enabled"] = 1 saved["tooltip"] = 1 saved["parent"] = {} saved["parent"]["size"] = 36 saved["parent"]["hide"] = nil saved["parent"]["circle"] = 1 saved["children"] = {} saved["children"]["size"] = 36 saved["children"]["hideonclick"] = 1 saved["visible"] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1} zButtonPet_EnsureConfig() end function IsPetDead() local saved = zButtonPet_GetSaved() return saved["pet"] and saved["pet"]["dead"] end local function zButtonPet_GetCorePetInfo() if type(MTH_GetCurrentPetInfo) == "function" then local info = MTH_GetCurrentPetInfo() if type(info) == "table" then return info end end return nil end local function zButtonPet_RefreshSavedPetState(saved, corePetInfo) local hasLivePet = false if type(UnitExists) == "function" then hasLivePet = UnitExists("pet") and true or false elseif type(corePetInfo) == "table" and corePetInfo.liveExists ~= nil then hasLivePet = corePetInfo.liveExists and true or false end if not hasLivePet then saved["pet"]["status"] = nil saved["pet"]["dead"] = nil saved["pet"]["happiness"] = nil return false end local currentHealth = (corePetInfo and tonumber(corePetInfo.health)) or (type(UnitHealth) == "function" and UnitHealth("pet")) or nil local currentHealthMax = (corePetInfo and tonumber(corePetInfo.healthMax)) or (type(UnitHealthMax) == "function" and UnitHealthMax("pet")) or nil local isDead = (corePetInfo and corePetInfo.dead) and true or false if not isDead and type(UnitIsDead) == "function" then isDead = UnitIsDead("pet") and true or false end if isDead then saved["pet"]["dead"] = 1 else saved["pet"]["dead"] = nil end local happiness = corePetInfo and corePetInfo.happiness or nil if happiness == nil and type(GetPetHappiness) == "function" then happiness = GetPetHappiness() end saved["pet"]["happiness"] = happiness if isDead then saved["pet"]["status"] = 1 elseif currentHealth and currentHealthMax and currentHealthMax > 0 then if (currentHealth / currentHealthMax) > 0.75 then saved["pet"]["status"] = 2 else saved["pet"]["status"] = 1 end else saved["pet"]["status"] = 2 end return true end local function zButtonPet_FindSpellIdByName(spellName) if not spellName or spellName == "" then return nil end local lookup = {} local foundAny = nil if type(GetNumSpellTabs) == "function" and type(GetSpellTabInfo) == "function" then local numTabs = GetNumSpellTabs() or 0 for tabIndex = 1, numTabs do local _, _, offset, numSpells = GetSpellTabInfo(tabIndex) offset = tonumber(offset) or 0 numSpells = tonumber(numSpells) or 0 if numSpells > 0 then for spellIndex = (offset + 1), (offset + numSpells) do local spellNameAtIndex = GetSpellName(spellIndex, "spell") if spellNameAtIndex then lookup[spellNameAtIndex] = spellIndex foundAny = 1 end end end end end if not foundAny then local maxSpells = tonumber((_G and _G["MAX_SPELLS"]) or nil) or 1024 local nilStreak = 0 for spellIndex = 1, maxSpells do local spellNameAtIndex = GetSpellName(spellIndex, "spell") if spellNameAtIndex then lookup[spellNameAtIndex] = spellIndex nilStreak = 0 else nilStreak = nilStreak + 1 if nilStreak >= 30 then break end end end end if spellName == ZHUNTER_PET_POSITION then if lookup[spellName] then return lookup[spellName] end for currentName, currentId in lookup do if string.find(currentName, "Take Position", 1, true) or string.find(currentName, "Position", 1, true) or string.find(currentName, "Stay", 1, true) then return currentId end end return nil end return lookup[spellName] end zButtonPet_SetButtons = function(parent, spells) if not (parent and parent.count and parent.name and spells) then return 0 end local info = {} for i = 1, table.getn(spells) do local spellName = spells[i] local spellId = zButtonPet_FindSpellIdByName(spellName) if spellId then table.insert(info, spellId) end end for i = 1, parent.count do local child = getglobal(parent.name..i) if child then child:Hide() child.id = nil child.icon = nil child.isspell = nil end end parent.id = nil parent.isspell = nil local foundCount = 0 for i = 1, table.getn(info) do if i > parent.count then break end local button = getglobal(parent.name..i) if button then button.id = info[i] button.isspell = 1 ZSpellButton_UpdateButton(button) button:Show() if i == 1 then parent.id = info[i] parent.isspell = 1 ZSpellButton_UpdateButton(parent) parent:Enable() end foundCount = foundCount + 1 end end return foundCount end function zButtonPetAdjustment_BeforeClick() if CursorHasItem() then DropItemOnUnit("pet") return 1 end end local function zButtonPet_RefreshParentSpell() if not zButtonPet or not zButtonPet.count then return end zButtonPet_EnsureConfig() local saved = zButtonPet_GetSaved() local corePetInfo = zButtonPet_GetCorePetInfo() local hasCurrentPet = nil local hasLivePet = nil if type(corePetInfo) == "table" and corePetInfo.exists ~= nil then hasCurrentPet = corePetInfo.exists and true or false end if type(corePetInfo) == "table" and corePetInfo.liveExists ~= nil then hasLivePet = corePetInfo.liveExists and true or false end zButtonPet_RefreshSavedPetState(saved, corePetInfo) local status, happiness, dead local spells = zButtonPet_RuntimeSpellMap if not spells then spells = zButtonPet_RebuildRuntimeSpellMap() end local name local choiceId = zButtonPet_GetPreferredDefaultParentId(spells) status = saved["pet"]["status"] happiness = saved["pet"]["happiness"] dead = saved["pet"]["dead"] local id = zButtonPet.id name = nil if hasCurrentPet == false and hasLivePet == false then name = ZHUNTER_PET_TAMING elseif not status then name = ZHUNTER_PET_CALL elseif dead then name = ZHUNTER_PET_REVIVE elseif status == 1 then name = ZHUNTER_PET_MEND elseif happiness ~= 3 then name = ZHUNTER_PET_FEED elseif choiceId then id = choiceId end if name and spells[name] then id = spells[name] elseif name then local fallbackId = zButtonPet_FindSpellIdByName(name) if fallbackId then spells[name] = fallbackId id = fallbackId end end local isOwned = GameTooltip and GameTooltip.IsOwned and GameTooltip:IsOwned(zButtonPet) local stateKey = tostring(hasCurrentPet and 1 or 0) ..":"..tostring(hasLivePet and 1 or 0) ..":"..tostring(status or "") ..":"..tostring(happiness or "") ..":"..tostring(dead or 0) ..":"..tostring(id or 0) if zButtonPet.id == id and zButtonPet_LastStateKey == stateKey and not isOwned then return end zButtonPet_LastStateKey = stateKey zButtonPet.id = id ZSpellButton_UpdateButton(zButtonPet) ZSpellButton_UpdateCooldown(zButtonPet) if isOwned then ZSpellButtonParent_OnEnter(zButtonPet) end end function zButtonPet_AfterClick() zButtonPet_RefreshParentSpell() end function zButtonPet_RefreshLiveState(_liveState, _source) zButtonPet_RefreshParentSpell() end function zButtonPetAdjustment_OnEvent() if not zButtonPet or not zButtonPet.count then return end if (event == "UNIT_HEALTH" or event == "UNIT_HAPPINESS") and arg1 and arg1 ~= "pet" then return end if event == "UNIT_PET" and arg1 and arg1 ~= "player" then return end if event == "SPELLS_CHANGED" or event == "LEARNED_SPELL_IN_TAB" then zButtonPet_CreateButtons() zButtonPet_SetupSizeAndPosition() end if event == "UNIT_HEALTH" or event == "UNIT_HAPPINESS" or event == "PET_BAR_UPDATE" then local now = GetTime and GetTime() or 0 local minInterval = zButtonPet_MinRefreshInterval if UnitAffectingCombat and UnitAffectingCombat("player") then minInterval = zButtonPet_MinCombatRefreshInterval end if now > 0 and zButtonPet_LastRefreshAt > 0 and (now - zButtonPet_LastRefreshAt) < minInterval then return end zButtonPet_LastRefreshAt = now end zButtonPet_RefreshParentSpell() end function zButtonPet_KeyBinding(index) if MTH_ZH_IsModuleEnabled and not MTH_ZH_IsModuleEnabled() then return end local button if index then button = getglobal("zButtonPet"..index) else button = zButtonPet end if button.id then CastSpell(button.id, "spell") if zButtonPet.hideonclick then if type(ZSpellButton_SetChildrenExpanded) == "function" then ZSpellButton_SetChildrenExpanded(zButtonPet, false) elseif zButtonPet.children then zButtonPet.children:Hide() end end end end function zButtonPet_FeedPet() end local function zButtonPet_DebugVerify() if not zButtonPet then MTH_ZH_Print("zPet: button frame is not ready yet.") return end zButtonPet_CreateButtons() zButtonPet_RefreshParentSpell() MTH_ZH_Print("zPet verify: expected spell availability") for i = 1, table.getn(ZHunterMod_Pet_Spells) do local spellName = ZHunterMod_Pet_Spells[i] local spellId = zButtonPet_FindSpellIdByName(spellName) if spellId then MTH_ZH_Print(" + "..spellName.." (id "..spellId..")") else MTH_ZH_Print(" - "..spellName.." (missing)") end end if zButtonPet.id then local parentName = GetSpellName(zButtonPet.id, "spell") or "" MTH_ZH_Print("zPet parent: "..parentName.." (id "..zButtonPet.id..")") else MTH_ZH_Print("zPet parent: ") end for i = 1, (zButtonPet.count or 0) do local button = getglobal("zButtonPet"..i) if button and button.id then local name = GetSpellName(button.id, "spell") or "" MTH_ZH_Print(" slot "..i..": "..name.." (id "..button.id..")") end end end SLASH_zButtonPet1 = "/ZPet" SlashCmdList["zButtonPet"] = function(msg) if MTH_ZH_HandleDisabledSlash and MTH_ZH_HandleDisabledSlash("Pet button is disabled while module 'zhunter' is disabled.") then return end msg = (msg and string.lower(msg)) or "" if msg == "reset" then zButtonPet_Reset() zButtonPet:ClearAllPoints() zButtonPet:SetPoint("CENTER", UIParent, "CENTER", 0, 60) elseif msg == "options" then MTH_OpenOptions("Pet") elseif msg == "verify" then zButtonPet_DebugVerify() else MTH_ZH_Print("Possible Commands: \"options\", \"reset\", \"verify\"") end end