diff --git a/Conditionals.lua b/Conditionals.lua index 03a41fa..0c9cafe 100644 --- a/Conditionals.lua +++ b/Conditionals.lua @@ -4895,7 +4895,7 @@ function CleveRoids.ValidatePlayerAuraCount(bigger, amount) end function CleveRoids.IsReactive(name) - return CleveRoids.reactiveSpells[spellName] ~= nil + return CleveRoids.reactiveSpells[name] ~= nil end -- NOTE: CleveRoids.GetActionButtonInfo is defined in Extensions/Tooltip/Generic.lua @@ -5403,6 +5403,23 @@ local function MakeTypedPowerKeyword(condKey, unitDefault, powerType) end end +local PET_HAPPINESS_STATES = { + unhappy = 1, + content = 2, + happy = 3, +} + +local function ResolvePetHappinessState(value) + local state = tonumber(value) + if state then + return state + end + if type(value) ~= "string" then + return nil + end + return PET_HAPPINESS_STATES[GetLowercaseString(value)] +end + -- A list of Conditionals and their functions to validate them CleveRoids.Keywords = { exists = function(conditionals) @@ -7222,6 +7239,39 @@ CleveRoids.Keywords = { end, conditionals, "nopet") end, + -- Hunter pet happiness: 1=unhappy, 2=content, 3=happy. + pethappiness = function(conditionals) + local happiness = GetPetHappiness() + local _, isHunterPet = HasPetUI() + if not isHunterPet or not happiness then + return false + end + + if conditionals.pethappiness == true then + return true + end + + return Multi(conditionals.pethappiness, function(requiredState) + return happiness == ResolvePetHappinessState(requiredState) + end, conditionals, "pethappiness") + end, + + nopethappiness = function(conditionals) + local happiness = GetPetHappiness() + local _, isHunterPet = HasPetUI() + if not isHunterPet or not happiness then + return true + end + + if conditionals.nopethappiness == true then + return false + end + + return NegatedMulti(conditionals.nopethappiness, function(forbiddenState) + return happiness ~= ResolvePetHappinessState(forbiddenState) + end, conditionals, "nopethappiness") + end, + -- [focus] / [nofocus] - whether a focus is set. Covers pfUI's emulated focus -- and ClassicAPI's native focus token (set via /focus or the FOCUSTARGET keybind). focus = function(conditionals) diff --git a/Core.lua b/Core.lua index 634cb26..1272139 100644 --- a/Core.lua +++ b/Core.lua @@ -164,6 +164,8 @@ local BOOLEAN_CONDITIONALS = { stopattack = true, pet = true, nopet = true, + pethappiness = true, + nopethappiness = true, focus = true, nofocus = true, bg = true, @@ -549,6 +551,7 @@ function CleveRoids.DisableAddon(reason) SlashCmdList.CLEVEROID = disabledMsg SlashCmdList.CAST = disabledMsg SlashCmdList.USE = disabledMsg + SlashCmdList.FEEDPET = disabledMsg SlashCmdList.EQUIP = disabledMsg SlashCmdList.EQUIPMH = disabledMsg SlashCmdList.EQUIPOH = disabledMsg @@ -4849,6 +4852,7 @@ CleveRoids.Frame:RegisterEvent("BAG_UPDATE_DELAYED") CleveRoids.Frame:RegisterEvent("GET_ITEM_INFO_RECEIVED") CleveRoids.Frame:RegisterEvent("PLAYER_EQUIPMENT_CHANGED") CleveRoids.Frame:RegisterEvent("UNIT_PET") +CleveRoids.Frame:RegisterEvent("UNIT_HAPPINESS") -- == STATE CHANGE EVENT REGISTRATION (for performance) == CleveRoids.Frame:RegisterEvent("PLAYER_TARGET_CHANGED") @@ -4924,6 +4928,12 @@ function CleveRoids.Frame:UNIT_PET() end end +function CleveRoids.Frame:UNIT_HAPPINESS() + if CleveRoidMacros.realtime == 0 then + CleveRoids.QueueActionUpdate() + end +end + -- Handle shutdown to prevent SuperWoW API crashes during logout function CleveRoids.Frame:PLAYER_LOGOUT() CleveRoids.isShuttingDown = true @@ -7082,6 +7092,54 @@ local function FindItemInBags(itemName) return nil end +function CleveRoids.DoFeedPet(msg) + -- Check macro stop flags + if CleveRoids.stopMacroFlag or CleveRoids.skipMacroFlag then + return false + end + + local action = function(itemName) + -- Defensive: make sure we are not in "split stack" mode and nothing is on the cursor + if type(CloseStackSplitFrame) == "function" then CloseStackSplitFrame() end + if CursorHasItem and CursorHasItem() then ClearCursor() end + + local _, isHunterPet = HasPetUI() + if not isHunterPet or not UnitExists("pet") or UnitHealth("pet") <= 0 then + return false + end + + local bag, bagSlot = FindItemInBags(itemName) + if not bag then + CleveRoids.Print("Item not found: " .. itemName) + return false + end + + local feedPet = C_Spell.GetSpellName(6991) or "Feed Pet" + CastSpellByName(feedPet) + + -- Only apply the food if Feed Pet actually entered item-targeting mode. + if SpellIsTargeting and not SpellIsTargeting() then + return false + end + + PickupContainerItem(bag, bagSlot) + return true + end + + -- PERFORMANCE: Use numeric iteration to avoid pairs() iterator allocation + local parts = CleveRoids.splitStringIgnoringQuotes(msg) + for i = 1, table.getn(parts) do + if CleveRoids.DoWithConditionals(parts[i], action, CleveRoids.FixEmptyTarget, false, action) then + -- If /firstaction was used, stop macro evaluation after first successful feed + if CleveRoids.stopOnCastFlag then + CleveRoids.stopMacroFlag = true + end + return true + end + end + return false +end + function CleveRoids.DoApply(hand, msg) local weaponSlots = { ["main"] = 16, @@ -7123,6 +7181,16 @@ function CleveRoids.DoApply(hand, msg) return handled end +SLASH_FEEDPET1 = "/feedpet" +SlashCmdList.FEEDPET = function(msg) + -- Require an item name argument + if not msg or msg == "" or string.gsub(msg, "%s+", "") == "" then + CleveRoids.Print("Usage: /feedpet [conditionals] ItemName") + return + end + CleveRoids.DoFeedPet(msg) +end + SLASH_APPLYMAIN1 = "/applymain" SlashCmdList.APPLYMAIN = function(msg) -- Require an item name argument diff --git a/Init.lua b/Init.lua index e8d9242..e254c6b 100644 --- a/Init.lua +++ b/Init.lua @@ -124,6 +124,7 @@ CleveRoids.dynamicCmds = { ["/castpet"] = true, ["/castsequence"] = true, ["/use"] = true, + ["/feedpet"] = true, ["/equip"] = true, ["/equipmh"] = true, ["/equipoh"] = true,