Compare with Previous | Blame | View Log
-- Livestock: For summoning random mounts, flying mounts, and critters. -- Concept: Khanthal (Uldum) -- Copyright (c) 2008 Scott Snowman (Recompense on Uldum(US)), scott.snowman@gmail.com -- All rights reserved. -- Namespace and saved variable tables Livestock = {} LivestockSettings = {} -- Saved variable default values and version string. local defaults = { Critters = {}, Mounts = {}, Zones = {}, Weights = {}, showcritter = 0, -- show or hide the Vanity Pets button showland = 0, -- show or hide the Land Mounts button showflying = 0, -- show or hide the Flying Mounts button showsmart = 0, -- show or hide the Smart Mounts button -- useslowland = 1, -- enable / disable seeing non-epic land mounts in the land mount menu -- useslowflight = 1, -- enable / disable seeing non-epic flying mounts in the flying mount menu scale = 1, -- scale of the Livestock buttons druidlogic = 0, -- enable / disable including Flight Forms in Smart Mount behavior (for druids only) worgenlogic = 0, -- enable / disable including Running Wild in Smart Mount behavior (worgens only) summononmove = 0, -- enable / disable automatic summoning whenever movement is initiated summonfaveonmove = 0, -- change the above setting to summon the favorite pet instead of a random pet dismisspetonmount = 0, -- dismiss any critter out when mounting or birdforming restrictautosummon = 0, -- restrict autosummoning to only occur when not PVP flagged ignorepvprestrictionininstances = 0, -- ignore the PVP restriction in an instance safeflying = 1, -- enable /disable the ability to dismount yourself in the air when calling a Livestock mounting function dismissonstealth = 0, -- enable / disable the automatic dismissal of vanity pets when you cast Stealth, Vanish, Feign Death, Shadowmeld, or Invisibility PVPdismiss = 0, -- restrict the above setting to work only when your PVP flag is enabled mountinstealth = 0, -- enable / disable the ability for Livestock mounting to break stealth effects smartcatform = 0, -- enable / disable turning into Cat Form indoors when using Smart Mounting (for druids only) favoritepet = 0, -- index for a user-defined favorite pet waterwalking = 0, -- for shamans to use Water Walking when underwater or not combatforms = 1, -- whether or not combat should switch Smart Mounting for shamans / druids crusadermount = 0, -- for paladins, toggles if Crusader Aura should trigger a Smart Mount Summon afterward donotsummoninraid = 0, -- do not automatically summon a pet if you are in a raid movingform = 0, -- check for movement to assume an instant-cast travel form indooraspects = 0, -- Smart Mounting casts AotC / AotP when inside movingaspects = 0, -- Smart Mounting casts AotC / AotP when outside and moving slowfall = 0, -- Smart Mounting casts Slow Fall / Levitate while falling and not in combat useweights = 0, -- Whether or not Smart Mounting should use weighted summons version = 1.51, -- swimming turtle fix } LIVESTOCK_VERSION = GetAddOnMetadata("Livestock", "Version"); -- Localization tables local L = LivestockLocalizations local stringstable = { -- strings to be localized in the GUI. This table is indexed by the name of the fontstring with each value being the string that should be displayed. This allows for a quick iteration to set the strings correctly. ["LivestockMenuFrame3DLabel"] = L.LIVESTOCK_FONTSTRING_3DLABEL, ["LivestockMainPreferencesFrameButtonsToggleTitle"] = L.LIVESTOCK_FONTSTRING_BUTTONSTOGGLETITLE, ["LivestockMainPreferencesFrameMacroTitle"] = L.LIVESTOCK_FONTSTRING_MACROBUTTONTITLE, --["LivestockMainPreferencesFrameOtherTitle"] = L.LIVESTOCK_FONTSTRING_OTHERTITLE, ["LivestockMainPreferencesFrameShowCrittersLabel"] = L.LIVESTOCK_FONTSTRING_SHOWCRITTERSLABEL, ["LivestockMainPreferencesFrameShowLandMountsLabel"] = L.LIVESTOCK_FONTSTRING_SHOWLANDLABEL, ["LivestockMainPreferencesFrameShowFlyingMountsLabel"] = L.LIVESTOCK_FONTSTRING_SHOWFLYINGLABEL, ["LivestockMainPreferencesFrameShowSmartMountsLabel"] = L.LIVESTOCK_FONTSTRING_SHOWSMARTLABEL, ["LivestockMainPreferencesFrameMacroText"] = L.LIVESTOCK_FONTSTRING_MACROBUTTONSTITLE, --["LivestockMainPreferencesFrameUseSlowLandText"] = L.LIVESTOCK_FONTSTRING_USESLOWLANDLABEL, --["LivestockMainPreferencesFrameUseSlowFlyingText"] = L.LIVESTOCK_FONTSTRING_USESLOWFLYINGLABEL, ["LivestockMainPreferencesFrameOpenLivestockMenuButton"] = L.LIVESTOCK_FONTSTRING_LIVESTOCKMENU, ["LivestockPetPreferencesFrameAutoSummonOnMoveText"] = L.LIVESTOCK_FONTSTRING_AUTOSUMMONONMOVELABEL, ["LivestockPetPreferencesFrameAutoSummonOnMoveFavoriteText"] = L.LIVESTOCK_FONTSTRING_AUTOSUMMONONMOVEFAVELABEL, ["LivestockPetPreferencesFrameRestrictAutoSummonOnPVPText"] = L.LIVESTOCK_FONTSTRING_RESTRICTAUTOSUMMONLABEL, ["LivestockPetPreferencesFrameIgnorePVPRestrictionInInstancesText"] = L.LIVESTOCK_FONTSTRING_IGNOREPVPRESTRICTIONININSTANCESLABEL, ["LivestockPetPreferencesFrameRestrictAutoSummonOnRaidText"] = L.LIVESTOCK_FONTSTRING_DONOTRAIDSUMMONLABEL, ["LivestockPetPreferencesFrameDismissPetOnMountText"] = L.LIVESTOCK_FONTSTRING_DISMISSPETONMOUNTLABEL, ["LivestockSmartPreferencesFrameDruidToggleText"] = L.LIVESTOCK_FONTSTRING_DRUIDTOGGLELABEL, ["LivestockSmartPreferencesFrameWorgenToggleText"] = L.LIVESTOCK_FONTSTRING_WORGENTOGGLELABEL, ["LivestockSmartPreferencesFrameOpenLivestockMenuButton"] = L.LIVESTOCK_FONTSTRING_LIVESTOCKMENU, ["LivestockCritterMenuButton"] = L.LIVESTOCK_FONTSTRING_SHOWCRITTERSLABEL, ["LivestockLandMountMenuButton"] = L.LIVESTOCK_FONTSTRING_SHOWLANDLABEL, ["LivestockFlyingMountMenuButton"] = L.LIVESTOCK_FONTSTRING_SHOWFLYINGLABEL, ["LivestockWaterMountMenuButton"] = L.LIVESTOCK_FONTSTRING_SHOWWATERLABEL, ["LivestockCritterMacroButton"] = L.LIVESTOCK_FONTSTRING_SHOWCRITTERSLABEL, ["LivestockLandMountMacroButton"] = L.LIVESTOCK_FONTSTRING_SHOWLANDLABEL, ["LivestockFlyingMountMacroButton"] = L.LIVESTOCK_FONTSTRING_SHOWFLYINGLABEL, ["LivestockComboMacroButton"] = L.LIVESTOCK_FONTSTRING_SHOWSMARTLABEL, ["LivestockSmartPreferencesFrameSafeFlightText"] = L.LIVESTOCK_FONTSTRING_SAFEFLIGHTLABEL, ["LivestockPetPreferencesFrameAutoDismissOnStealthText"] = L.LIVESTOCK_FONTSTRING_AUTODISMISSONSTEALTHLABEL, ["LivestockPetPreferencesFramePVPDismissText"] = L.LIVESTOCK_FONTSTRING_PVPDISMISSLABEL, ["LivestockSmartPreferencesFrameMountInStealthText"] = L.LIVESTOCK_FONTSTRING_MOUNTINSTEALTHLABEL, ["LivestockSmartPreferencesFrameSmartCatFormText"] = L.LIVESTOCK_FONTSTRING_SMARTCATFORMLABEL, ["LivestockSmartPreferencesFrameToggleWaterWalkingText"] = L.LIVESTOCK_FONTSTRING_WATERWALKINGLABEL, ["LivestockSmartPreferencesFrameToggleCombatFormsText"] = L.LIVESTOCK_FONTSTRING_USECOMBATFORMSLABEL, ["LivestockSmartPreferencesFrameToggleMovingFormsText"] = L.LIVESTOCK_FONTSTRING_USEMOVINGFORMSLABEL, ["LivestockSmartPreferencesFrameToggleCrusaderMountText"] = L.LIVESTOCK_FONTSTRING_CRUSADERSUMMONLABEL, ["LivestockSmartPreferencesFrameIndoorHunterAspectsText"] = L.LIVESTOCK_FONTSTRING_INDOORHUNTERASPECTSLABEL, ["LivestockSmartPreferencesFrameMovingHunterAspectsText"] = L.LIVESTOCK_FONTSTRING_MOVINGHUNTERASPECTSLABEL, ["LivestockSmartPreferencesFrameSlowFallWhileFallingText"] = L.LIVESTOCK_FONTSTRING_SLOWFALLLABEL, } local restrictSummonForTheseBuffs = { -- buffs that, when present, should prevent autosummoning from happening L.LIVESTOCK_SPELL_STEALTH, L.LIVESTOCK_SPELL_PROWL, L.LIVESTOCK_SPELL_INVISIBILITY, L.LIVESTOCK_SPELL_VANISH, L.LIVESTOCK_SPELL_CLOAKING, L.LIVESTOCK_SPELL_SHADOWMELD, L.LIVESTOCK_SPELL_FOOD, L.LIVESTOCK_SPELL_DRINK, L.LIVESTOCK_SPELL_HAUNTED, L.LIVESTOCK_SPELL_FLIGHTFORM, L.LIVESTOCK_SPELL_SWIFTFLIGHTFORM, L.LIVESTOCK_SPELL_TWILIGHTSERPENT, L.LIVESTOCK_SPELL_RUBYHARE, L.LIVESTOCK_SPELL_SAPPHIREOWL, L.LIVESTOCK_SPELL_CAMOUFLAGE, } local restrictSummonForThisEquipment = { -- equipment that, when worn, should prevent autosummoning from happening L.LIVESTOCK_EQUIPMENT_BLOODSAIL, L.LIVESTOCK_EQUIPMENT_DONCARLOS, } local hiddenBuffs = { -- buffs that indicate the player is invisible in some way L.LIVESTOCK_SPELL_STEALTH, L.LIVESTOCK_SPELL_PROWL, L.LIVESTOCK_SPELL_INVISIBILITY, L.LIVESTOCK_SPELL_VANISH, L.LIVESTOCK_SPELL_CLOAKING, L.LIVESTOCK_SPELL_SHADOWMELD, L.LIVESTOCK_SPELL_CAMOUFLAGE, } -- Variable declaration BINDING_HEADER_LIVESTOCKHEADER = "Livestock Summons" -- Keybindings menu _G["BINDING_NAME_CLICK LivestockComboButton:LeftButton"] = "Random Smart Mount" -- Keybindings _G["BINDING_NAME_CLICK LivestockLandMountsButton:LeftButton"] = "Random Land Mount" _G["BINDING_NAME_CLICK LivestockFlyingMountsButton:LeftButton"] = "Random Flying Mount" local eastereggtext, currentcritter, createdMaps local donotsummon = true local debug = false local modelcounter, class = 0 local NUMBER_OF_CRITTER_MENUS, NUMBER_OF_LAND_MENUS, NUMBER_OF_FLYING_MENUS, NUMBER_OF_WATER_MENUS = 1, 1, 1, 1 -- constants for menu appearance and building local crittersText, landText, flyingText, waterText, mountstable, temp = {}, {}, {}, {}, {}, {} -- tables that will be reused local buildtable = { -- this table contains information that we will use for building and rebuilding menus as well as hiding menus ["CRITTER"] = {token = "Critter", text = crittersText, settings = LivestockSettings.Critters}, ["LAND"] = {token = "Land", text = landText, settings = LivestockSettings.Mounts}, ["FLYING"] = { token = "Flying", text = flyingText, settings = LivestockSettings.Mounts}, ["WATER"] = { token = "Water", text = waterText, settings = LivestockSettings.Mounts}, } local sayings = { "Please... no more...", "Can I get a new master?", "Why did I sign up for this gig?", "*HRRRRKKK*", "Only you can prevent animal vomit!", "We are not amused.", "Great job, spin doctor.", "You spin me right round,\nbaby right round...", "PLEASE put the button back in the box!!", "So... dizzy...", "You expecting me to\ntake off or something?", "Animal cruelty detected.\nContacting PETA... please hold.", "Does your mother know you treat\nanimals like this?", "Don't you have quests\nto do or something?", "I think you're a bit confused. He said\n'spell rotation' NOT 'pet rotation!'", "Stop this thing, I want a refund!", "When I can see straight again, I'm\nreporting you for being mean.", "OK, OK! I'm sorry I\npeed in your backpack!", } CreateFrame("GameTooltip","LivestockTooltip",UIParent,"GameTooltipTemplate") -- Build our own tooltip to use for scanning. --Event handlers: function Livestock.OnEvent(self, event, arg1) if event == "ADDON_LOADED" and arg1 == "Livestock" then for k, v in pairs(defaults) do if not LivestockSettings[k] then if k == "version" then -- clear mounts if version detect is missing LivestockSettings.Mounts = {} end LivestockSettings[k] = v end end if LivestockSettings.version < defaults.version then LivestockSettings.Mounts = {} LivestockSettings.version = defaults.version end for k, v in pairs(LivestockSettings) do if not defaults[k] then LivestockSettings[k] = nil end end-- syncs the saved variables to match the structure of the defaults. Keys in the defaults not in the SV will be added from the defaults. Keys in the SV not in the defaults will be set to nil. currentcritter = 1 hooksecurefunc("MoveForwardStart", Livestock.MoveSummon) -- hooking forward movement to summon a critter if the player has selected the correct option. hooksecurefunc("MoveBackwardStart", Livestock.MoveSummon) -- backward movement hooksecurefunc("TurnLeftStart", Livestock.MoveSummon) -- turning left hooksecurefunc("TurnRightStart", Livestock.MoveSummon) -- turning right hooksecurefunc("ToggleAutoRun", Livestock.MoveSummon) -- auto-run hooksecurefunc("TurnOrActionStart", Livestock.MoveSummon) -- right-click hooksecurefunc("CameraOrSelectOrMoveStart", Livestock.MoveSummon) -- left-click elseif event == "PLAYER_ENTERING_WORLD" then if not createdMaps then Livestock.CreateStateMaps(LivestockComboButton) createdMaps = true end end end function Livestock.CompanionEvent(self, event, ...) if event == "PLAYER_LOGIN" then -- when the player logs in, make sure the map is set right for Smart Detection and sort out the companions if GetNumCompanions("CRITTER") == 0 then donotsummon = true end SetMapToCurrentZone() self:RegisterEvent("COMPANION_UPDATE") class = select(2, UnitClass("player")) return elseif event == "COMPANION_UPDATE" then mountfail = false critterfail = false Livestock.RenumberCompanions() if (not mountfail and not critterfail) then self:UnregisterEvent("COMPANION_UPDATE") end return elseif event == "COMPANION_LEARNED" then -- when a new companion is learned, sort out the companions again and then rebuild the three menus Livestock.RenumberCompanions() Livestock.RebuildMenu("CRITTER") Livestock.RebuildMenu("LAND") Livestock.RebuildMenu("FLYING") Livestock.RebuildMenu("WATER") return elseif event == "UNIT_SPELLCAST_SUCCEEDED" then local unit, spell, rank = ... if unit == "player" then if spell == L.LIVESTOCK_SPELL_STEALTH or spell == L.LIVESTOCK_SPELL_PROWL or spell == L.LIVESTOCK_SPELL_FEIGNDEATH or spell == L.LIVESTOCK_SPELL_INVISIBILITY or spell == L.LIVESTOCK_SPELL_VANISH or spell == L.LIVESTOCK_SPELL_SHADOWMELD or spell == L.LIVESTOCK_SPELL_CAMOUFLAGE then if LivestockSettings.dismissonstealth == 1 then if LivestockSettings.PVPdismiss == 0 then DismissCompanion("CRITTER") elseif UnitIsPVP("player") then DismissCompanion("CRITTER") end end elseif spell == L.LIVESTOCK_SPELL_CRUSADERAURA and LivestockSettings.crusadermount == 1 and not IsMounted() then if Livestock.LandOrFlying() == "FLYING" then Livestock.PickFlyingMount() else Livestock.PickLandMount() end elseif (spell == L.LIVESTOCK_SPELL_FLIGHTFORM or spell == L.LIVESTOCK_SPELL_SWIFTFLIGHTFORM) and LivestockSettings.dismisspetonmount == 1 then DismissCompanion("CRITTER") end end return elseif event == "UNIT_SPELLCAST_SENT" then local unit, spell = ... if unit == "player" then if spell == "Living Flare" then donotsummon = true end end return elseif event == "UI_INFO_MESSAGE" then if ... == "Legion Gateway Destroyed (Complete)" then donotsummon = nil end return elseif event == "UNIT_ENTERED_VEHICLE" then local arg1 = ... if arg1 == "player" and LivestockSettings.dismisspetonmount == 1 then DismissCompanion("CRITTER") end end end -- Menu creation: function Livestock.BuildMenu(kind) -- type is a string, either "CRITTER", "LAND", "FLYING", or "WATER" local token, texttable = buildtable[kind].token, buildtable[kind].text -- buildtable contains references and variables that are specific to each type of menu, indexed by the same string as the variable type local settings, mounts, MENUS = LivestockSettings.Mounts Livestock.RecycleTable(mountstable) Livestock.RecycleTable(texttable) if kind == "CRITTER" then settings = LivestockSettings.Critters NUMBER_OF_CRITTER_MENUS = math.ceil( GetNumCompanions(kind) / 30 ) -- break the number of critters into groups of 30 MENUS = NUMBER_OF_CRITTER_MENUS -- make the local MENUS equal to the constant elseif kind == "LAND" then mounts = 0 -- reset mounts to 0 so we can start counting land mounts for k in pairs(settings) do -- Check the list of mounts, pull out fast land mounts (or slow ones if the filter lets them through), increment mounts, and add the index of the mounts to mountstable if (settings[k].type == "land") then mounts = mounts + 1 tinsert(mountstable, settings[k].index) end table.sort(mountstable) -- the table indices are all jumbled because of the iteration over the settings table, so this puts them in numerical order (guaranteeing the menu is in alphabetical order) end NUMBER_OF_LAND_MENUS = math.ceil( mounts / 30 ) -- break land mounts into groups of 30 MENUS = NUMBER_OF_LAND_MENUS -- same as above elseif kind == "FLYING" then -- code is the same for flying mounts with the appropriate changes; perhaps this could be a function call? mounts = 0 for k in pairs(settings) do if (settings[k].type == "flying") then mounts = mounts + 1 tinsert(mountstable, settings[k].index) end table.sort(mountstable) end NUMBER_OF_FLYING_MENUS = math.ceil( mounts / 30) MENUS = NUMBER_OF_FLYING_MENUS -- same as above elseif kind == "WATER" then -- code is the same for water mounts with the appropriate changes; perhaps this could be a function call? mounts = 0 for k in pairs(settings) do if (settings[k].type == "water") then mounts = mounts + 1 tinsert(mountstable, settings[k].index) end table.sort(mountstable) end NUMBER_OF_WATER_MENUS = math.ceil( mounts / 30) MENUS = NUMBER_OF_WATER_MENUS -- same as above end for currentMenu = 1, MENUS do -- iterate over however many menus we're going to need local menuframe = _G["Livestock"..token.."Menu"..currentMenu] or CreateFrame("Frame","Livestock"..token.."Menu"..currentMenu, LivestockMenuFrame, "LivestockBlueFrameTemplate") -- make the background of the menu (or reference it if it already exists), naming it with the proper token and number menuframe:SetID(currentMenu) -- associate the number of the menu with the frame in a way that's unrelated to the name if currentMenu == 1 then -- if this is the first menu in a group, we need to parent it to the button that called it local header if kind == "CRITTER" then header = "LivestockCritterMenuButton" elseif kind == "LAND" then header = "LivestockLandMountMenuButton" elseif kind == "FLYING" then header = "LivestockFlyingMountMenuButton" elseif kind == "WATER" then header = "LivestockWaterMountMenuButton" end menuframe:SetPoint("TOPLEFT", header ,"BOTTOMLEFT",0,-10) -- set the first menu to appear below the associated button else local lastmenu = "Livestock"..token.."Menu"..(currentMenu-1) menuframe:SetPoint("TOPLEFT",lastmenu,"TOPRIGHT",3,0) -- set other menus to appear to the right of the previous menu end if currentMenu == 1 and MENUS > 1 then -- if there's more than 1 menu and this is the first menu, we'll have 30 animals, 1 "More", and Check / Uncheck All for 33 blocks total - about 615 high menuframe:SetHeight(615) elseif currentMenu ~= MENUS then -- the middle menus need 31 blocks total. about 580 high (30 animals, 1 More) menuframe:SetHeight(580) elseif MENUS > 1 then -- the last menu , which doesn't have check / uncheck all, depends on the number of companions. If it's a multiple of 30, we need 30 buttons. If not, it depends on how many there are if (mounts or GetNumCompanions("CRITTER")) % 30 ~= 0 then menuframe:SetHeight( ( (mounts or GetNumCompanions("CRITTER")) % 30 * 18 ) + 21 ) else menuframe:SetHeight(561) end else -- there's only one menu, so no "More", but check and uncheck are needed menuframe:SetHeight( ( ( (mounts or GetNumCompanions("CRITTER")) + 2) * 18) + 21) end local endButton -- this is the number of buttons in the menu... if currentMenu ~= MENUS then endButton = 30 -- 30 if it's not the last menu elseif MENUS > 1 then if (mounts or GetNumCompanions("CRITTER")) % 30 ~= 0 then endButton = (mounts or GetNumCompanions("CRITTER")) % 30 -- last menu will be the remainder from the other groups of 30 if its not a multiple of 30 else endButton = 30 -- if it is a multiple of 30, then there will be 30 buttons in the last menu end elseif MENUS == 1 then endButton = (mounts or GetNumCompanions("CRITTER")) -- if there's only one menu, it has the number of mounts or critters in it end local maxWidth = 100 -- default width of the buttons for button = 1, endButton do -- iterate over the buttons we need local menubutton = _G["Livestock"..token.."Menu"..currentMenu.."Button"..button] or CreateFrame("Button", "Livestock"..token.."Menu"..currentMenu.."Button"..button, menuframe, "LivestockMenuButtonTemplate") -- create or reference the button, with the appropriate name and number menubutton:Show() local text = _G["Livestock"..token.."Menu"..currentMenu.."Button"..button.."Text"] -- reference the text of the button local id, name if kind == "CRITTER" then id, name = GetCompanionInfo("CRITTER", (currentMenu - 1) * 30 + button ) elseif kind == "LAND" or kind == "FLYING" or kind == "WATER" then id, name = GetCompanionInfo("MOUNT", mountstable[(currentMenu - 1) * 30 + button] ) end -- get the ID and name of the companion for the button by mapping the position of the button in the menus(s) to the index of the companion menubutton.critterID = id menubutton.which = "toggle"..kind menubutton.name = name -- associate the info for the companion with the frame itself to be used for mousing over / clicking text:SetText( name ) tinsert(texttable, text) -- set the text of the button to reflect the name of the companion, and store the fontstring itself in a table of fontstrings local name2 = strconcat(name, "-") if name and kind == "LAND" and settings[name2] then name = strconcat(name, "-") end if name and settings[name].show == 1 then -- if the name isn't nil (Blizzard bug sometimes returns nil for companion names >:o) and the settings say this companion is selected then text:SetTextColor(1, 1, 1) -- set the color of the text to be white elseif name then text:SetTextColor(0.4, 0.4, 0.4) -- otherwise set the color to be grey elseif not name then -- debug message if the name is nil --[[local which, index if mounts then which = "MOUNT" index = mountstable[(currentMenu - 1) * 30 + button] else which = "CRITTER" index = (currentMenu - 1 ) * 30 + button end local creatureID, creatureName, creatureSpellID, icon, issummoned = GetCompanionInfo(which, index) DEFAULT_CHAT_FRAME:AddMessage("Max "..which.." is " .. (GetNumCompanions(which) or "nil")) DEFAULT_CHAT_FRAME:AddMessage(string.format("Error for "..which.." %u at menu build, ID: %s, name: %s, spellID: %s, icon: %s, summoned: %s", index, (creatureID or "nil"), (creatureName or "nil"),(creatureSpellID or "nil"), (icon or "nil"), (issummoned or "nil")))]] end menubutton:SetHeight(18) -- give the button a height if button == 1 then -- parent it to the menu's TOPLEFT corner if it's the first button, else underneath the previous button menubutton:SetPoint("TOPLEFT", menuframe, "TOPLEFT", 20, -10) else menubutton:SetPoint("TOPLEFT", _G[menuframe:GetName().."Button"..(button - 1)], "BOTTOMLEFT") end if button == endButton then -- set all the buttons to have the same width as the widest button. Do this by iterating over the buttons, getting the width of their text, and then setting the buttons' width to be the width of the text plus a cushion of 50 for i = 1, endButton do local textwidth = _G["Livestock"..token.."Menu"..currentMenu.."Button"..i.."Text"]:GetWidth() if textwidth > maxWidth then maxWidth = textwidth + 50 end end for i = 1, endButton do _G["Livestock"..token.."Menu"..currentMenu.."Button"..i]:SetWidth(maxWidth) end end menuframe:SetWidth(maxWidth + 30) -- bump up the size of the menu frame background to compensate and make sure the border and background look nice end if currentMenu ~= MENUS then -- if we're not at the last menu, we need a "More" button local moreframe = _G["Livestock"..token.."Menu"..currentMenu.."MoreButton"] or CreateFrame("Button", "Livestock"..token.."Menu"..currentMenu.."MoreButton", _G["Livestock"..token.."Menu"..currentMenu], "LivestockMenuButtonTemplate") -- reference or create the button moreframe:SetHeight(18) moreframe:SetWidth(maxWidth) -- height and width match those of the other buttons local t = _G[moreframe:GetName().."Text"] t:SetText(L.LIVESTOCK_MENU_MORE) moreframe:SetPoint("TOPLEFT", "Livestock"..token.."Menu"..currentMenu.."Button"..endButton, "BOTTOMLEFT") -- stick it underneath the last button in the menu moreframe.which = "more"..kind -- associate a string with the frame to use for mouseover end if currentMenu == 1 then -- the first menu needs "Select all / none" buttons local check = _G["Livestock"..token.."MenuCheckButton"] or CreateFrame("Button", "Livestock"..token.."MenuCheckButton", _G["Livestock"..token.."Menu1"], "LivestockMenuButtonTemplate") check.which = "check"..kind local uncheck = _G["Livestock"..token.."MenuUncheckButton"] or CreateFrame("Button", "Livestock"..token.."MenuUncheckButton", _G["Livestock"..token.."Menu1"], "LivestockMenuButtonTemplate") uncheck.which = "uncheck"..kind check:SetHeight(18) check:SetWidth(maxWidth) uncheck:SetHeight(18) uncheck:SetWidth(maxWidth) -- reference them or make them, show them, set their heights and widths, associate info with the frame, etc. _G[check:GetName().."Text"]:SetText(L.LIVESTOCK_MENU_SELECTALL) _G[check:GetName().."Text"]:SetTextColor(1, 1, 0) _G[uncheck:GetName().."Text"]:SetText(L.LIVESTOCK_MENU_SELECTNONE) _G[uncheck:GetName().."Text"]:SetTextColor(1, 1, 0) -- set their text and color (yellow to stand out) if _G["Livestock"..token.."Menu1MoreButton"] then -- if the first menu has a More button, stick the Check All button under the More button. Otherwise, stick it under the last menu button. check:SetPoint("TOPLEFT", _G["Livestock"..token.."Menu1MoreButton"], "BOTTOMLEFT") else check:SetPoint("TOPLEFT", _G["Livestock"..token.."Menu1Button"..endButton], "BOTTOMLEFT") end uncheck:SetPoint("TOPLEFT", check, "BOTTOMLEFT") -- stick the Uncheck All button under the Check All button... and we're done! end end end function Livestock.BuildMenus() -- call the build menu function on all three menus to initialize them Livestock.BuildMenu("CRITTER") Livestock.BuildMenu("LAND") Livestock.BuildMenu("FLYING") Livestock.BuildMenu("WATER") end function Livestock.RebuildMenu(kind) -- the menus change when you learn new companions, so we need to rebuild them occasionally. kind is a string as before, either "CRITTER", "LAND", "FLYING", or "WATER" for i = 1, 8 do -- iterate over 8 menus, which is definitely more than we need right now for j = 1, 30 do -- it's easiest just to assume 30 animals in each menu and break when there aren't than to see how many animals there actually are if _G["Livestock"..buildtable[kind].token.."Menu"..i.."Button"..j] then -- if the button exists, then hide it _G["Livestock"..buildtable[kind].token.."Menu"..i.."Button"..j]:Hide() else -- if it doesn't exist, we are out of animals and should stop looping break end end if _G["Livestock"..buildtable[kind].token.."Menu"..i] then _G["Livestock"..buildtable[kind].token.."Menu"..i]:Hide() -- once all the buttons are hidden, check to see if the menu exists. if it does, hide it as well. If not, stop looping. else break end end Livestock.BuildMenu(kind) -- call BuildMenu to create the new button, adjust the old ones, resize things if needed, create a new menu if needed, etc. end -- GUI functions: function Livestock.RestoreUI(self, elapsed) if InCombatLockdown() then return end -- UI elements in tables to make it easier to set up. It's not good to declare these inside a function, but the variables aren't available the right way until after the UI loads so this function needs to have the tables declared here. local buttonstable = { ["LivestockSmartButton"] = LivestockSettings.showsmart, ["LivestockCrittersButton"] = LivestockSettings.showcritter, ["LivestockLandMountsButton"] = LivestockSettings.showland, ["LivestockFlyingMountsButton"] = LivestockSettings.showflying, } local checkstable = { ["LivestockMainPreferencesFrameShowSmartMounts"] = LivestockSettings.showsmart, ["LivestockMainPreferencesFrameShowCritters"] = LivestockSettings.showcritter, ["LivestockMainPreferencesFrameShowLandMounts"] = LivestockSettings.showland, ["LivestockMainPreferencesFrameShowFlyingMounts"] = LivestockSettings.showflying, --["LivestockMainPreferencesFrameShowSlowLandMounts"] = LivestockSettings.useslowland, --["LivestockMainPreferencesFrameShowSlowFlyingMounts"] = LivestockSettings.useslowflight, ["LivestockPetPreferencesFrameToggleAutosummon"] = LivestockSettings.summononmove, ["LivestockPetPreferencesFrameToggleAutosummonFavorite"] = LivestockSettings.summonfaveonmove, ["LivestockPetPreferencesFrameRestrictAutoSummonOnPVP"] = LivestockSettings.restrictautosummon, ["LivestockPetPreferencesFrameIgnorePVPRestrictionInInstances"] = LivestockSettings.ignorepvprestrictionininstances, ["LivestockPetPreferencesFrameRestrictAutoSummonOnRaid"] = LivestockSettings.donotsummoninraid, ["LivestockPetPreferencesFrameDismissPetOnMount"] = LivestockSettings.dismisspetonmount, ["LivestockSmartPreferencesFrameToggleDruidLogic"] = LivestockSettings.druidlogic, ["LivestockSmartPreferencesFrameToggleWorgenLogic"] = LivestockSettings.worgenlogic, ["LivestockSmartPreferencesFrameToggleSafeFlying"] = LivestockSettings.safeflying, ["LivestockPetPreferencesFrameToggleDismissOnStealth"] = LivestockSettings.dismissonstealth, ["LivestockPetPreferencesFrameToggleDismissOnStealthPVPOnly"] = LivestockSettings.PVPdismiss, ["LivestockSmartPreferencesFrameToggleMountInStealth"] = LivestockSettings.mountinstealth, ["LivestockSmartPreferencesFrameToggleSmartCatForm"] = LivestockSettings.smartcatform, ["LivestockSmartPreferencesFrameToggleWaterWalking"] = LivestockSettings.waterwalking, ["LivestockSmartPreferencesFrameToggleCombatForms"] = LivestockSettings.combatforms, ["LivestockSmartPreferencesFrameToggleMovingForms"] = LivestockSettings.movingform, ["LivestockSmartPreferencesFrameToggleCrusaderMount"] = LivestockSettings.crusadermount, ["LivestockSmartPreferencesFrameIndoorHunterAspects"] = LivestockSettings.indooraspects, ["LivestockSmartPreferencesFrameMovingHunterAspects"] = LivestockSettings.movingaspects, ["LivestockSmartPreferencesFrameSlowFallWhileFalling"] = LivestockSettings.slowfall, } for k, v in pairs(buttonstable) do -- show and scale each of the buttons if its setting is saved as 1 (shown) if v == 1 then _G[k]:Show() _G[k]:SetScale(LivestockSettings.scale) end end for k, v in pairs(checkstable) do -- check each box if its setting is saved as 1 (checked) if v == 1 then _G[k]:SetChecked(true) end end for k, v in pairs(stringstable) do -- localize the fontstrings in the GUI _G[k]:SetText(v) end if LivestockSettings.summononmove == 0 then LivestockSettings.summononmove = 1 Livestock.ClickedAutoSummon() end if class ~= "DRUID" and class ~= "SHAMAN" then LivestockSmartPreferencesFrameToggleCombatFormsText:SetTextColor(0.4, 0.4, 0.4) LivestockSmartPreferencesFrameToggleMovingFormsText:SetTextColor(0.4, 0.4, 0.4) end if class ~= "DRUID" then -- Gray out the text for the druid-only options if the player isn't a druid. The disabling of the button is done in the buttons' OnLoad scripts. LivestockSmartPreferencesFrameDruidToggleText:SetTextColor(0.4, 0.4, 0.4) LivestockSmartPreferencesFrameSmartCatFormText:SetTextColor(0.4, 0.4, 0.4) end local text = format(L.LIVESTOCK_FONTSTRING_WATERWALKINGLABEL, (class == "PRIEST" and L.LIVESTOCK_SPELL_LEVITATE) or (class == "DEATHKNIGHT" and L.LIVESTOCK_SPELL_PATHOFFROST) or L.LIVESTOCK_SPELL_WATERWALKING) LivestockSmartPreferencesFrameToggleWaterWalkingText:SetText(text) if class ~= "SHAMAN" and class ~= "DEATHKNIGHT" and class ~= "PRIEST" then LivestockSmartPreferencesFrameToggleWaterWalkingText:SetTextColor(0.4, 0.4, 0.4) end if class ~= "PALADIN" then LivestockSmartPreferencesFrameToggleCrusaderMount:Disable() LivestockSmartPreferencesFrameToggleCrusaderMountText:SetTextColor(0.4, 0.4, 0.4) end local race = select(2, UnitRace("player")) if class ~= "DRUID" and class ~= "ROGUE" and class ~= "MAGE" and class ~= "HUNTER" and race ~= "NightElf" then LivestockPetPreferencesFrameAutoDismissOnStealthText:SetTextColor(0.4, 0.4, 0.4) LivestockSmartPreferencesFrameMountInStealthText:SetTextColor(0.4, 0.4, 0.4) end if (not LivestockPetPreferencesFrameToggleDismissOnStealth:IsEnabled() ) or LivestockSettings.dismissonstealth == 0 then LivestockPetPreferencesFrameToggleDismissOnStealthPVPOnly:Disable() LivestockPetPreferencesFramePVPDismissText:SetTextColor(0.4, 0.4, 0.4) end if LivestockSettings.favoritepet == 0 then LivestockPetPreferencesFrameToggleAutosummonFavorite:Disable() LivestockPetPreferencesFrameAutoSummonOnMoveFavoriteText:SetTextColor(0.4, 0.4, 0.4) end if LivestockSettings.restrictautosummon == 0 then LivestockPetPreferencesFrameIgnorePVPRestrictionInInstances:Disable() LivestockPetPreferencesFrameIgnorePVPRestrictionInInstancesText:SetTextColor(0.4, 0.4, 0.4) end if LivestockSettings.summononmove == 1 then donotsummon = nil end self:Hide() end function Livestock.HideDropdowns() -- hide all the visible dropdown menus for k in pairs(buildtable) do for j = 1, 8 do -- the number of menus may change per type, but it's certainly not more than 8 if _G["Livestock"..buildtable[k].token.."Menu"..j] and _G["Livestock"..buildtable[k].token.."Menu"..j]:IsVisible() then _G["Livestock"..buildtable[k].token.."Menu"..j]:Hide() end end end end function Livestock.HeaderButtonOnClick(token) -- when we click a main menu button, we should hide all dropdowns (so they don't overlap) and show the relevant menu, but only if it wasn't visible in the first place. This lets the buttons act as true toggles. Livestock.RenumberCompanions() Livestock.BuildMenus() local menu, visible = _G["Livestock"..token.."Menu1"] if menu and menu:IsVisible() then visible = true end Livestock.HideDropdowns() if menu and not visible then menu:Show() end end function Livestock.MenuButtonOnClick(self, button) -- self is the button clicked on, button is the mouse button used local action, companionType = strmatch(self.which, "(%l+)"), strmatch(self.which, "(%u+)") -- since the buttons come with a string attached that is of the format "actionTYPE" where action is the function of the button and TYPE is the capitalized animal type, this declaration breaks them apart into two again local settings if companionType == "CRITTER" then settings = LivestockSettings.Critters else settings = LivestockSettings.Mounts end if action == "toggle" then -- if this is a toggle type button then local name, text = self.name, _G[self:GetName().."Text"] -- name was associated with the frame to be the name of the animal inside, and text is a reference to the text string of the button if button == "RightButton" and companionType == "CRITTER" then -- Right-clicking a pet in the menu makes it the favorite pet. Livestock.MakeFavoritePet(name) return end local name2 = strconcat(name, "-") if companionType == "LAND" and settings[name2] then name = strconcat(name, "-") end settings[name].show = 1 - settings[name].show -- toggle the shown setting if settings[name].show == 1 then text:SetTextColor(1, 1, 1) else text:SetTextColor(0.4, 0.4, 0.4) -- color the fontstring according to the new setting end elseif action == "check" then -- if this is a "check all" button for k in pairs(settings) do -- if these are critters or if the mounts in the settings have the same type as the companionType, lowercased (either "land" or "flying") then toggle them on if companionType == "CRITTER" then settings[k].show = 1 end if companionType == "WATER" and settings[k].type == "water" then settings[k].show = 1 end if companionType == "LAND" and settings[k].type == "land" then settings[k].show = 1 end if companionType == "FLYING" and settings[k].type == "flying" then settings[k].show = 1 end end for _, v in ipairs(buildtable[companionType].text) do -- iterate over the fontstrings and set them all to white v:SetTextColor(1, 1, 1) end elseif action == "uncheck" then -- otherwise it should be an "uncheck all" button for k in pairs(settings) do -- if these or critters or if the mounts in the settings have the same type as the companionType, lowercased (either "land" or "flying") then toggle them on if companionType == "CRITTER" then settings[k].show = 0 end if companionType == "WATER" and settings[k].type == "water" then settings[k].show = 0 end if companionType == "LAND" and settings[k].type == "land" then settings[k].show = 0 end if companionType == "FLYING" and settings[k].type == "flying" then settings[k].show = 0 end end for _, v in ipairs(buildtable[companionType].text) do -- iterate over the fontstrings and set them all to grey v:SetTextColor(0.4, 0.4, 0.4) end end end function Livestock.MenuButtonOnDoubleClick(self) local action, companionType = strmatch(self.which, "(%l+)"), strmatch(self.which, "(%u+)") local settings = (companionType == "CRITTER" and LivestockSettings.Critters) or LivestockSettings.Mounts if action == "toggle" then local index = settings[self.name].index CallCompanion((companionType ~= "CRITTER" and "MOUNT") or "CRITTER", index) end Livestock.MenuButtonOnClick(self, "LeftButton") end function Livestock.MenuButtonOnEnter(self) -- code for mousing over a menu button. self is the menu button local which = self.which -- "which" was a string associated with the button if strsub(which, 1, 6) == "toggle" then -- if this is a toggle button, when we mouseover, we should show the 3D model if the viewer is visible local id = self.critterID -- the id of the creature was associated with the button when the menu was built if LivestockModel:IsVisible() then LivestockModel:SetCreature(id) LivestockModel.angle = 0.71 end elseif strsub(which, 1, 4) == "more" then -- if this is a more button, mousing over needs to show the next menu _G["Livestock"..buildtable[strsub(which, 5, strlen(which))].token.."Menu"..(self:GetParent():GetID() + 1)]:Show() -- find the menu associated with the button by getting its parent, adding 1 to the number of the menu, and then showing the resulting menu. The table reference looks up the right token for the name of the menu based on the end of the "which" string end end function Livestock.ClickedAutoSummon() LivestockSettings.summononmove = 1 - LivestockSettings.summononmove if LivestockSettings.summononmove == 0 then LivestockPetPreferencesFrameToggleAutosummonFavorite:Disable() LivestockPetPreferencesFrameAutoSummonOnMoveFavoriteText:SetTextColor(0.4, 0.4, 0.4) LivestockPetPreferencesFrameRestrictAutoSummonOnPVP:Disable() LivestockPetPreferencesFrameRestrictAutoSummonOnPVPText:SetTextColor(0.4, 0.4, 0.4) LivestockPetPreferencesFrameRestrictAutoSummonOnRaid:Disable() LivestockPetPreferencesFrameRestrictAutoSummonOnRaidText:SetTextColor(0.4, 0.4, 0.4) LivestockPetPreferencesFrameDismissPetOnMount:Disable() LivestockPetPreferencesFrameDismissPetOnMountText:SetTextColor(0.4, 0.4, 0.4) donotsummon = true elseif LivestockSettings.favoritepet ~= 0 then LivestockPetPreferencesFrameToggleAutosummonFavorite:Enable() LivestockPetPreferencesFrameAutoSummonOnMoveFavoriteText:SetTextColor(1, 1, 1) end if LivestockSettings.summononmove == 1 then LivestockPetPreferencesFrameRestrictAutoSummonOnPVP:Enable() LivestockPetPreferencesFrameRestrictAutoSummonOnPVPText:SetTextColor(1, 1, 1) LivestockPetPreferencesFrameRestrictAutoSummonOnRaid:Enable() LivestockPetPreferencesFrameRestrictAutoSummonOnRaidText:SetTextColor(1, 1, 1) LivestockPetPreferencesFrameDismissPetOnMount:Enable() LivestockPetPreferencesFrameDismissPetOnMountText:SetTextColor(1, 1, 1) donotsummon = nil end end function Livestock.SpinModel(self, elapsed) -- this is the OnUpdate script for the model local angle = self.angle or 0.71 modelcounter = modelcounter + elapsed if modelcounter > 0.05 then angle = angle + LivestockSpinSlider:GetValue() self:SetFacing(angle) modelcounter = 0 end self.angle = angle end -- while the frame is visible, this code uses a counter to keep track of the total time the frame was last redrawn. Every 0.05 sec, the model rotates according to the value established by the slider in the viewer function Livestock.SpinSliderChanged(self) -- easter egg text to show up when you drag the 3D model spin slider so far over it "escapes" the box if self:GetValue() > 0.623 and (not eastereggtext) then -- if the slider is dragged above the appropriate value and there's no easteregg text shown Livestock.HideDropdowns() -- hide the menus or else the cursor ends up going into one of them which changes the model eastereggtext = true LivestockEasterEgg:Show() LivestockEasterEgg:SetText(sayings[random(#sayings)]) elseif self:GetValue() < 0.623 then LivestockEasterEgg:Hide() eastereggtext = nil end end function Livestock.ScaleButtons(value) -- scale the buttons and reset them to their default positions if not LivestockCrittersButton then -- this script fires before the buttons are set, so return early if that's the case return end LivestockSettings.scale = value LivestockCrittersButton:SetScale(value) LivestockCrittersButton:ClearAllPoints() LivestockCrittersButton:SetPoint("CENTER",-50,-150) LivestockLandMountsButton:ClearAllPoints() LivestockLandMountsButton:SetScale(value) LivestockLandMountsButton:SetPoint("CENTER",0,-150) LivestockFlyingMountsButton:ClearAllPoints() LivestockFlyingMountsButton:SetScale(value) LivestockFlyingMountsButton:SetPoint("CENTER",50,-150) LivestockSmartButton:ClearAllPoints() LivestockSmartButton:SetScale(value) LivestockSmartButton:SetPoint("CENTER",0,-190) end -- Data functions function Livestock.RecycleTable(table) for k in pairs(table) do table[k] = nil end end function Livestock.CreateStateMaps(self) -- Smart mounting depends on 6 variables - mounted status, indoors/outdoors status, combat status, flyable/noflyable status, and two user settings that are relevant for druids only. -- The first four can be handled with a state map, and while they have 16 possible combinations, there are really only 6 relevant outcomes for Smart Mounting from these combinations. The following states are handled by the "smartmap" state: -- 7: In Flight Form: Druids in flight form need to check against the safe flight setting to see if they should be canceling their flight form. -- 6: Swimming: Swimming takes precedence over all other settings. Druids use Aquatic Form in this case, and shaman can select to use Water Walking. -- 1: Indoors. Should do nothing unless the player is a druid and wants to turn into a cat (or a hunter and wants to use aspect of the pact) -- 2: Mounted: Should dismount. This will get checked later against the safe flight setting to see if the player is flying and shouldn't dismount. -- 3: In Combat: Should do nothing, unless the player is a druid or a shaman or a hunter, in which case they should cast Travel Form or Ghost Wolf or AOTC if the combat forms option is selected. -- 4: Flyable area, not in combat: Should summon a random flying mount, with the possibility of druids shifting into flight forms. If a druid is in a form, they need to cancel it if they're not shifting into a flight form. -- 5: Non-flyable area, not in combat; Should summon a random land mount. If a druid is in a form, they need to cancel it. -- To handle the form canceling, there is also a "form" map which tracks the druid's current form. (Non-druids change forms as well but these form changes don't affect their ability to CallCompanion() ) -- These attributes are set at runtime to transfer settings and localization strings from tables where they can't be accessed in the secure handler to attributes of the secure button where they can be. -- Note that druid forms may change, as feral druids only have 5 forms and others can have 6. "form5" and "form6" attempt to figure out which forms are actually present to be able to cancel them effectively. self:SetAttribute("catform", LivestockSettings.smartcatform) self:SetAttribute("druidlogic", LivestockSettings.druidlogic) self:SetAttribute("worgenlogic", LivestockSettings.worgenlogic) self:SetAttribute("playerclass", select(2, UnitClass("player"))) self:SetAttribute("flightspell", GetSpellInfo(L.LIVESTOCK_SPELL_SWIFTFLIGHTFORM) or GetSpellInfo(L.LIVESTOCK_SPELL_FLIGHTFORM) or "nil") self:SetAttribute("bearspell", GetSpellInfo(L.LIVESTOCK_SPELL_DIREBEARFORM) or L.LIVESTOCK_SPELL_BEARFORM) self:SetAttribute("form5", GetSpellInfo(L.LIVESTOCK_SPELL_TREEOFLIFE) or GetSpellInfo(L.LIVESTOCK_SPELL_MOONKINFORM) or GetSpellInfo(L.LIVESTOCK_SPELL_SWIFTFLIGHTFORM) or GetSpellInfo(L.LIVESTOCK_SPELL_FLIGHTFORM) or "nil") self:SetAttribute("form6", ( GetSpellInfo(L.LIVESTOCK_SPELL_TREEOFLIFE) or GetSpellInfo(L.LIVESTOCK_SPELL_MOONKINFORM) ) and (GetSpellInfo(L.LIVESTOCK_SPELL_SWIFTFLIGHTFORM) or GetSpellInfo(L.LIVESTOCK_SPELL_FLIGHTFORM))) self:SetAttribute("sealspell", L.LIVESTOCK_SPELL_AQUATICFORM) self:SetAttribute("catspell", L.LIVESTOCK_SPELL_CATFORM) self:SetAttribute("travelspell", L.LIVESTOCK_SPELL_TRAVELFORM) self:SetAttribute("wolfspell", L.LIVESTOCK_SPELL_GHOSTWOLF) self:SetAttribute("waterwalking", (class == "SHAMAN" and L.LIVESTOCK_SPELL_WATERWALKING) or (class == "PRIEST" and L.LIVESTOCK_SPELL_LEVITATE) or (class == "DEATHKNIGHT" and L.LIVESTOCK_SPELL_PATHOFFROST)) self:SetAttribute("waterwalkingtoggle", LivestockSettings.waterwalking) self:SetAttribute("combatformstoggle", LivestockSettings.combatforms) self:SetAttribute("indooraspects", LivestockSettings.indooraspects) self:SetAttribute("aotc", L.LIVESTOCK_SPELL_CHEETAH) self:SetAttribute("safeflying", LivestockSettings.safeflying) self:SetAttribute("mountinstealth", LivestockSettings.mountinstealth) self:SetAttribute("aotp", GetSpellInfo(L.LIVESTOCK_SPELL_PACK) and L.LIVESTOCK_SPELL_PACK) self:SetAttribute("movingform", LivestockSettings.movingform) self:SetAttribute("movingaspects", LivestockSettings.movingaspects) -- Wrap the following script to occur when the attribute of the ComboButton changes. This will happen whenever the user toggles a setting, changes forms, or adjusts their indoor/outdoor, swimming, combat, flyable, or mounted status. -- Essentially, it changes the button to have different behaviors according to the classification above of the states. It also sets a flag in the button's mounttype attribute that indicates whether the PostClick handler should summon a mount or not. -- When druids change forms, the state is updated again with the same information, but the updated form info is taken into account. This allows for smart mounting to effectively cancel the form that was just cast because the type is usually set to cast the form spell that was just cast. SecureHandlerWrapScript(self, "OnAttributeChanged", self, [[ local class, catform, druidlogic, bearspell, flightspell, sealspell, catspell, travelspell, wolfspell, worgenlogic = self:GetAttribute("playerclass"), self:GetAttribute("catform"), self:GetAttribute("druidlogic"), self:GetAttribute("bearspell"), self:GetAttribute("flightspell"), self:GetAttribute("sealspell"), self:GetAttribute("catspell"), self:GetAttribute("travelspell"), self:GetAttribute("wolfspell"), self:GetAttribute("worgenlogic") local waterwalkingspell = self:GetAttribute("waterwalking") local waterwalkingtoggle = self:GetAttribute("waterwalkingtoggle") local combatformstoggle = self:GetAttribute("combatformstoggle") local safeflying = self:GetAttribute("safeflying") local mountinstealth = self:GetAttribute("mountinstealth") local indooraspects = self:GetAttribute("indooraspects") local movingform = self:GetAttribute("movingform") local movingaspects = self:GetAttribute("movingaspects") if name == "catform" or name == "state-form" or name == "druidlogic" or name == "waterwalkingtoggle" or name == "combatformstoggle" or name == "indooraspects" then self:SetAttribute("state-smartmap",self:GetAttribute("state-smartmap")) elseif name == "state-smartmap" then self:SetAttribute("type", nil) if value == 1 then if class == "DRUID" then if catform == 1 then self:SetAttribute("type", "spell") self:SetAttribute("spell", catspell) end elseif class == "HUNTER" then if indooraspects == 1 then self:SetAttribute("type", "spell") self:SetAttribute("spell", ((PlayerInGroup() and self:GetAttribute("aotp")) or self:GetAttribute("aotc"))) end elseif class == "SHAMAN" then if movingform == 1 then self:SetAttribute("type", "spell") self:SetAttribute("spell", wolfspell) end end elseif value == 2 then self:SetAttribute("type", "macro") self:SetAttribute("macrotext", "/run Livestock.Dismount()") self:SetAttribute("mounttype", nil) elseif value == 3 then if class == "DRUID" and combatformstoggle == 1 then self:SetAttribute("type", "macro") self:SetAttribute("macrotext", "/cast !"..travelspell) elseif class == "SHAMAN" and combatformstoggle == 1 then self:SetAttribute("type", "spell") self:SetAttribute("spell", wolfspell) end self:SetAttribute("mounttype", nil) elseif value == 4 then if class == "DRUID" then local spell if GetShapeshiftForm() == 1 then spell = bearspell elseif GetShapeshiftForm() == 3 then spell = catspell elseif GetShapeshiftForm() == 4 then spell = travelspell elseif GetShapeshiftForm() == 5 then spell = self:GetAttribute("form5") elseif GetShapeshiftForm() == 6 then spell = self:GetAttribute("form6") end if spell then self:SetAttribute("type", "spell") self:SetAttribute("spell", spell) end end elseif value == 5 then if class == "DRUID" then local spell if GetShapeshiftForm() == 1 then spell = bearspell elseif GetShapeshiftForm() == 3 then spell = catspell elseif GetShapeshiftForm() == 4 then spell = travelspell elseif GetShapeshiftForm() == 5 then spell = self:GetAttribute("form5") elseif GetShapeshiftForm() == 6 then spell = self:GetAttribute("form6") end self:SetAttribute("type", "spell") self:SetAttribute("spell", spell) end elseif value == 6 then if class == "DRUID" then self:SetAttribute("type", "spell") self:SetAttribute("spell", sealspell) elseif tonumber(waterwalkingtoggle) == 1 and (class == "SHAMAN" or class == "PRIEST" or class == "DEATHKNIGHT") then self:SetAttribute("type", "spell") self:SetAttribute("spell", waterwalkingspell) self:SetAttribute("unit", "player") elseif IsMounted() then self:SetAttribute("type", "macro") self:SetAttribute("macrotext", "/run Livestock.Dismount()") self:SetAttribute("mounttype", nil) end elseif value == 7 then if class == "DRUID" and safeflying == 1 then self:SetAttribute("type", nil) self:SetAttribute("spell", nil) elseif class == "DRUID" then self:SetAttribute("type", "spell") self:SetAttribute("spell", flightspell) end end end ]]) RegisterStateDriver(self, "smartmap", "[form:6] 7; [swimming] 6; [indoors, nomounted] 1; [noindoors, mounted] 2; [combat, noindoors, nomounted] 3; [nocombat, noindoors, nomounted, flyable] 4; [nocombat, noindoors, nomounted, noflyable] 5") RegisterStateDriver(self, "form", "[form:1] 1; [form:2] 2; [form:3] 3; [form:4] 4; [form:5] 5; [form:6] 6; 0") end function Livestock.RenumberCompanions() -- because the game does not distinguish between learning a new mount or a new critter, it's safest just to redo both indices for i = 1, GetNumCompanions("CRITTER") do local _, name = GetCompanionInfo("CRITTER",i) if name and not LivestockSettings.Critters[name] then -- sometimes Blizzard's code returns nil for the name. If we have a valid name but no associated data in the SV, then we need to add it. LivestockSettings.Critters[name] = { -- save the index and set the default show value to on show = 1, index = i, } elseif name then -- if there is a valid name, then make sure the index matches up because indices change when you learn a new companion LivestockSettings.Critters[name].index = i else -- debug message if the name is returned as nil critterfail = true return end end for i = 1, GetNumCompanions("MOUNT") do local _, name, spellID, _, _, mountFlags = GetCompanionInfo("MOUNT",i) if name and not LivestockSettings.Mounts[name] then -- if the name is valid and we don't have SV data for it, we need to make it local name2 = strconcat(name, "-") LivestockSettings.Mounts[name] = { index = i, } -- set the index of the mount --[[ mountFlags explanation Ground 0x01 Fly 0x02 Float 0x04 Underwater 0x08 Jump 0x10 Flying mounts = 7 Water mounts = 12 Sandstone Drake = 15 Flying carpets = 23 (they can't swim) Ground mounts = 29 (includes swimming turtles) Multi-purpose = 31 ]] if mountFlags == 7 then -- flying mount LivestockSettings.Mounts[name].type = "flying" elseif mountFlags == 31 or mountFlags == 23 or mountFlags == 15 then -- variable LivestockSettings.Mounts[name].type = "flying" LivestockSettings.Mounts[name2] = { index = i, } LivestockSettings.Mounts[name2].type = "land" LivestockSettings.Mounts[name2].show = 1 elseif mountFlags == 12 then -- water LivestockSettings.Mounts[name].type = "water" else -- otherwise, a land mount LivestockSettings.Mounts[name].type = "land" -- swimming turtle exception if spellID == 30174 or spellID == 64731 then LivestockSettings.Mounts[name].type = "water" end end LivestockSettings.Mounts[name].show = 1 elseif name then -- if we do have SV data for it, update the index. local name2 = strconcat(name, "-") LivestockSettings.Mounts[name].index = i if LivestockSettings.Mounts[name2] then LivestockSettings.Mounts[name2].index = i end else mountfail = true return end end end function Livestock.LandOrFlying() -- since [flyable] has been fixed, check for flying and CWF and fly unless you're in WG and there's a battle going on. SetMapToCurrentZone() -- make sure that the map accurately reflects the zone you're in local continent = GetCurrentMapContinent(); local mflying = GetSpellInfo(90267); -- flight master's license local flightForm = GetSpellInfo(L.LIVESTOCK_SPELL_SWIFTFLIGHTFORM) or GetSpellInfo(L.LIVESTOCK_SPELL_FLIGHTFORM); local _,_,_,expertRiding = GetAchievementInfo(890); -- Expert Riding achievement if continent == L.LIVESTOCK_CONTINENT_OUTLAND then -- if we're in Outland if expertRiding or flightForm then return "FLYING" else return "LAND" end elseif continent == L.LIVESTOCK_CONTINENT_NORTHREND and IsFlyableArea() then -- if we're in Northrend local zone, CWF = GetZoneText(), GetSpellInfo(L.LIVESTOCK_SPELL_COLDWEATHERFLYING) -- Check the zone and if we have CWF if not CWF then -- no CWF means no flying return "LAND" end local _, _, isActive = GetWorldPVPAreaInfo(1) return ((zone == L.LIVESTOCK_ZONE_WINTERGRASP and isActive == true) and "LAND") or "FLYING" -- check to see if a battle is in progress and if so, we're on a land mount. If not, flying mount.and if so, we're on a land mount. If not, flying mount. else -- we are in azeroth if IsFlyableArea() and IsUsableSpell(mflying) then -- have flight master's license and in flyable area return "FLYING" else return "LAND" -- anywhere else we should get a land mount. end end end function Livestock.SmartPreClick(self) local state = self:GetAttribute("state-smartmap") local race = select(2, UnitRace("player")) if (debug) then print(format("PREDEBUG: '%s' is mount-type, state is '%d'", self.mounttype or "nil", self:GetAttribute("state-smartmap"))) end if not InCombatLockdown() then if IsFalling() and LivestockSettings.slowfall == 1 then self:SetAttribute("type", "spell") self:SetAttribute("spell", (class == "PRIEST" and L.LIVESTOCK_SPELL_LEVITATE) or L.LIVESTOCK_SPELL_SLOWFALL) self:SetAttribute("unit", "player") return end if class == "SHAMAN" then -- Clear out Ghost Wolf from combat if it's there if not IsIndoors() and self:GetAttribute("spell") == L.LIVESTOCK_SPELL_GHOSTWOLF then self:SetAttribute("spell", nil) end elseif (class == "PRIEST" or class == "DEATHKNIGHT") and state ~= 6 then -- clear out any possible remnant of underwater spells if we're out of the water. No need to compare to the spell because these classes only ever have one spell in their Smart Button. if self:GetAttribute("spell") then self:SetAttribute("spell", nil) end elseif class == "HUNTER" then -- clear out Aspects if we're not inside if not IsIndoors() and self:GetAttribute("spell") then self:SetAttribute("spell", nil) end end end if state == 2 then -- if mounted, clear out any mounted attributes and return, which will lead to a dismount (in the Click handler) and nothing in the PostClick. Druids go to flight form if they have it. self.mounttype = nil if class == "DRUID" and not InCombatLockdown() and Livestock.LandOrFlying() == "FLYING" and LivestockSettings.druidlogic == 1 then if GetSpellInfo(L.LIVESTOCK_SPELL_SWIFTFLIGHTFORM) or GetSpellInfo(L.LIVESTOCK_SPELL_FLIGHTFORM) then self:SetAttribute("type", "spell") self:SetAttribute("spell", L.LIVESTOCK_SPELL_SWIFTFLIGHTFORM or L.LIVESTOCK_SPELL_FLIGHTFORM) end end return elseif state == 3 then -- maybe in combat if not InCombatLockdown() then self.mounttype = Livestock.LandOrFlying() if LivestockSettings.movingaspects == 1 and GetUnitSpeed("player") ~= 0 then self:SetAttribute("type", "spell") self:SetAttribute("spell", (GetRealNumPartyMembers() ~= 0 and GetSpellInfo(L.LIVESTOCK_SPELL_PACK) and L.LIVESTOCK_SPELL_PACK) or L.LIVESTOCK_SPELL_CHEETAH) self.mounttype = nil end if LivestockSettings.dismisspetonmount == 1 and self.mounttype == "FLYING" then DismissCompanion("CRITTER") end end elseif state == 4 then -- handle "flyable" areas self.mounttype = Livestock.LandOrFlying() if LivestockSettings.movingaspects == 1 and GetUnitSpeed("player") ~= 0 then self:SetAttribute("type", "spell") self:SetAttribute("spell", (GetRealNumPartyMembers() ~= 0 and GetSpellInfo(L.LIVESTOCK_SPELL_PACK) and L.LIVESTOCK_SPELL_PACK) or L.LIVESTOCK_SPELL_CHEETAH) self.mounttype = nil end if LivestockSettings.dismisspetonmount == 1 and self.mounttype == "FLYING" then DismissCompanion("CRITTER") end if class == "SHAMAN" then if LivestockSettings.movingform == 1 and GetUnitSpeed("player") ~= 0 then self:SetAttribute("type", "spell") self:SetAttribute("spell", L.LIVESTOCK_SPELL_GHOSTWOLF) self.mounttype = nil self.clearmovingform = true return end end if class == "DRUID" then if LivestockSettings.movingform == 1 and GetUnitSpeed("player") ~= 0 then if self.mounttype == "LAND" or (self.mounttype == "FLYING" and LivestockSettings.druidlogic == 0) then self:SetAttribute("type", "spell") self:SetAttribute("spell", L.LIVESTOCK_SPELL_TRAVELFORM) self.mounttype = nil self.clearmovingform = true return end end if LivestockSettings.druidlogic == 1 and self.mounttype == "FLYING" then self:SetAttribute("type", "spell") self:SetAttribute("spell", GetSpellInfo(L.LIVESTOCK_SPELL_SWIFTFLIGHTFORM) or L.LIVESTOCK_SPELL_FLIGHTFORM) self.mounttype = nil end end if self.mounttype == "LAND" and race == "Worgen" and LivestockSettings.worgenlogic == 1 and GetUnitSpeed("player") == 0 then self:SetAttribute("type", "spell") self:SetAttribute("spell", L.LIVESTOCK_SPELL_RUNNINGWILD) self.mounttype = nil end elseif state == 5 then -- if in a land area, set the button to mount a land mount or possibly cast an instant travel form spell local _, _, isActive = GetWorldPVPAreaInfo(1) if LivestockSettings.movingform == 1 and GetUnitSpeed("player") ~= 0 then self:SetAttribute("type", "spell") self:SetAttribute("spell", (class == "SHAMAN" and L.LIVESTOCK_SPELL_GHOSTWOLF) or L.LIVESTOCK_SPELL_TRAVELFORM) self.clearmovingform = true elseif LivestockSettings.movingaspects == 1 and GetUnitSpeed("player") ~= 0 then self:SetAttribute("type", "spell") self:SetAttribute("spell", (GetRealNumPartyMembers() ~= 0 and GetSpellInfo(L.LIVESTOCK_SPELL_PACK) and L.LIVESTOCK_SPELL_PACK) or L.LIVESTOCK_SPELL_CHEETAH) elseif race == "Worgen" and LivestockSettings.worgenlogic == 1 then self:SetAttribute("type", "spell") self:SetAttribute("spell", L.LIVESTOCK_SPELL_RUNNINGWILD) self.mounttype = nil elseif (GetZoneText() == L.LIVESTOCK_ZONE_WINTERGRASP and isActive == false) then self.mounttype = "FLYING" else self.mounttype = "LAND" end return elseif state == 6 then -- swimming self.mounttype = "WATER" end end function Livestock.SmartPostClick(self) if (debug) then print(format("POSTDEBUG: '%s' is mount-type, state is '%d'", self.mounttype or "nil", self:GetAttribute("state-smartmap"))) end if Recompense.CheckBuffsAgainstTable(hiddenBuffs) and LivestockSettings.mountinstealth == 0 then -- respect the setting to not mount in stealth return end if self.mounttype then if self.mounttype == "LAND" then Livestock.PickLandMount() elseif self.mounttype == "WATER" then Livestock.PickWaterMount() else Livestock.PickFlyingMount() end end self.mounttype = nil if self.clearmovingform then self.clearmovingform = nil self:SetAttribute("type", nil) self:SetAttribute("spell", nil) self:SetAttribute("macrotext", nil) end end function Livestock.NonSmartPreClick(self) local race = select(2, UnitRace("player")) if IsFlying() and LivestockSettings.safeflying == 1 then -- trying to summon a land or flying mount in the air should fail to set any attributes and not flag anything for summoning. No smush deaths. return end if IsMounted() then -- if we're mounted or in a vehicle, dismount / get out and don't set any attributes or flag anything for summoning. No dismount only to try and resummon. Dismount() return elseif UnitInVehicle("player") then VehicleExit() return elseif InCombatLockdown() then -- get out of this function if we can't set attributes, because we can't mount in combat anyway. We've already dismounted by this point. return end if ( IsStealthed() and LivestockSettings.mountinstealth == 0 ) then -- respect the setting to not mount up if you're stealthed return end if IsIndoors() then -- can't mount indoors, and this isn't smart mount return end if strfind(self:GetName(), "Flying") then -- now that dismounting is out of the way, we can assume we need to summon a mount. Attach the kind of mount needed to the button. self.typeofmount = "flying" if LivestockSettings.dismisspetonmount == 1 then DismissCompanion("CRITTER") end else self.typeofmount = "land" end if class == "SHAMAN" then local buffs, name = 0 repeat buffs = buffs + 1 name = UnitBuff("player", buffs) until not name or name == L.LIVESTOCK_SPELL_GHOSTWOLF if name then -- name will be the name of the form at this point self:SetAttribute("type", "spell") self:SetAttribute("spell",name) -- by casting the spell that we had on in the first place, we effectively remove the spell. end end if class == "DRUID" then -- see if we need to cancel a form to summon a mount. local buffs, name = 0 repeat buffs = buffs + 1 name = UnitBuff("player", buffs) until not name or name == L.LIVESTOCK_SPELL_BEARFORM or name == L.LIVESTOCK_SPELL_DIREBEARFORM or name == L.LIVESTOCK_SPELL_CATFORM or name == L.LIVESTOCK_SPELL_TRAVELFORM or name == L.LIVESTOCK_SPELL_TREEOFLIFE or name == L.LIVESTOCK_SPELL_MOONKINFORM or name == L.LIVESTOCK_SPELL_FLIGHTFORM or name == L.LIVESTOCK_SPELL_SWIFTFLIGHTFORM if name then -- name will be the name of the form at this point self:SetAttribute("type","spell") self:SetAttribute("spell",name) -- by casting the spell that we had on in the first place, we effectively remove the spell. end end if self.typeofmount == "land" and race == "Worgen" and LivestockSettings.worgenlogic == 1 and GetUnitSpeed("player") == 0 then self:SetAttribute("type", "spell") self:SetAttribute("spell", L.LIVESTOCK_SPELL_RUNNINGWILD) self.typeofmount = nil end end function Livestock.NonSmartPostClick(self) if InCombatLockdown() then -- exit setting attributes if we're in combat. Past here is mounting code, which can't be done in combat anyway. return end if self.typeofmount then -- summon the relevant mount if self.typeofmount == "land" then Livestock.PickLandMount() elseif self.typeofmount == "flying" then Livestock.PickFlyingMount() end self.typeofmount = nil -- clear the mount flag end self:SetAttribute("type", nil) -- clear the attributes of the button in case they were used self:SetAttribute("spell", nil) end function Livestock.MakeFavoritePet(name) LivestockSettings.favoritepet = name print(format(L.LIVESTOCK_INTERFACE_LISTFAVEPET, LivestockSettings.favoritepet)) LivestockPetPreferencesFrameToggleAutosummonFavorite:Enable() LivestockPetPreferencesFrameAutoSummonOnMoveFavoriteText:SetTextColor(1, 1, 1) end function Livestock.AddToZone(which, item, fromDrag) local where = (which == "zone") and GetRealZoneText() or GetSubZoneText() if item == "whatpet" or item == "whatmount" then local whatKind = (item == "whatpet" and "critter") or "mount" if LivestockSettings.Zones[where] and LivestockSettings.Zones[where][whatKind] then print(format(L.LIVESTOCK_INTERFACE_DISPLAYZONEPET, LivestockSettings.Zones[where][whatKind], whatKind == "mount" and "mount" or "pet", which, where)) return else print(format(L.LIVESTOCK_INTERFACE_NOZONEPET, (whatKind == "mount" and "mount" or "pet"), which, where)) return end elseif item == "nomount" or item == "nopet" then print(format(L.LIVESTOCK_INTERFACE_CONFIRMZONEREMOVE, item:match("no(.+)"), which, where)) if LivestockSettings.Zones[where] then LivestockSettings.Zones[where][item:match("no(.+)") == "pet" and "critter" or "mount"] = nil Livestock.UpdateZoneTexts() end return elseif (not item:match("spell:") and not fromDrag) then print(L.LIVESTOCK_INTERFACE_USEITEMLINK) return end if not fromDrag then item = item:match("%[(.+)%]") end if not (LivestockSettings.Critters[item] or LivestockSettings.Mounts[item]) then print(format(L.LIVESTOCK_INTERFACE_HAVENOTLEARNED, item)) return end local whatKind = (LivestockSettings.Critters[item] and "critter") or "mount" if not LivestockSettings.Zones[where] then LivestockSettings.Zones[where] = {} end LivestockSettings.Zones[where][whatKind] = item print(format(L.LIVESTOCK_INTERFACE_CONFIRMZONEADD, item, whatKind, which, where)) Livestock.UpdateZoneTexts(fromDrag) end -- Summon functions function Livestock.PickCritter() local shown = 0 Livestock.RecycleTable(temp) for k in pairs(LivestockSettings.Critters) do -- go throuh all the critters if LivestockSettings.Critters[k].show == 1 and not select(5, GetCompanionInfo("CRITTER",LivestockSettings.Critters[k].index)) then tinsert(temp, LivestockSettings.Critters[k].index) -- if a critter is marked as selected and it's NOT currently out in the game world, add its index to the temp table elseif LivestockSettings.Critters[k].show == 1 then shown = shown + 1 -- count the total number of critters marked as selected end end if #temp == 0 and shown ~= 1 then -- if there are no critters in the list AND the number of critters selected isn't 1, then there must be 0 critters selected. Prompt user to select some. print(L.LIVESTOCK_INTERFACE_NOCRITTERSCHECKED) elseif #temp ~= 0 then -- otherwise, there are some critters in the list. currentcritter = temp[random(#temp)] -- set the newest critter to be one of the random indices among those in the temp table. CallCompanion("CRITTER",currentcritter) -- call the critter end end function Livestock.DismissCritter() DismissCompanion("CRITTER") end function Livestock.SummonFavoritePet() if LivestockSettings.favoritepet ~= 0 then CallCompanion("CRITTER", LivestockSettings.Critters[LivestockSettings.favoritepet].index) else print(L.LIVESTOCK_INTERFACE_NOFAVEPET) end end function Livestock.PickLandMount() if IsMounted() and ( not IsFlying() or LivestockSettings.safeflying == 0 ) then -- if we're already mounted and NOT flying (no accidents, please!) then dismount and don't pick another mount Dismount() return end if UnitInVehicle("player") and not IsFlying() then VehicleExit() return end if LivestockSettings.Zones[GetSubZoneText()] and LivestockSettings.Zones[GetSubZoneText()]["mount"] then CallCompanion("MOUNT", LivestockSettings.Mounts[LivestockSettings.Zones[GetSubZoneText()]["mount"]].index) return elseif LivestockSettings.Zones[GetZoneText()] and LivestockSettings.Zones[GetZoneText()]["mount"] then CallCompanion("MOUNT", LivestockSettings.Mounts[LivestockSettings.Zones[GetZoneText()]["mount"]].index) return end Livestock.RecycleTable(temp) for k in pairs(LivestockSettings.Mounts) do -- go through the land mounts and add the ones that are selected to the temp table if (LivestockSettings.Mounts[k].type == "land") and LivestockSettings.Mounts[k].show == 1 then tinsert(temp, LivestockSettings.Mounts[k].index) end end if #temp == 0 then -- if no mounts are selected, prompt the user to select one print(L.LIVESTOCK_INTERFACE_NOLANDMOUNTSCHECKED) return end if (debug) then local number = temp[random(#temp)] local _, creatureName = GetCompanionInfo("MOUNT",number) print(format("MOUNT: '%s' is mount-index, land mount is '%s'", number, creatureName)) CallCompanion("MOUNT",number) -- call a random mount else CallCompanion("MOUNT",temp[random(#temp)]) -- call a random mount end end function Livestock.PickWaterMount() if IsMounted() and ( not IsFlying() or LivestockSettings.safeflying == 0 ) then -- if we're already mounted and NOT flying (no accidents, please!) then dismount and don't pick another mount Dismount() return end if UnitInVehicle("player") and not IsFlying() then VehicleExit() return end if LivestockSettings.Zones[GetSubZoneText()] and LivestockSettings.Zones[GetSubZoneText()]["mount"] then CallCompanion("MOUNT", LivestockSettings.Mounts[LivestockSettings.Zones[GetSubZoneText()]["mount"]].index) return elseif LivestockSettings.Zones[GetZoneText()] and LivestockSettings.Zones[GetZoneText()]["mount"] then CallCompanion("MOUNT", LivestockSettings.Mounts[LivestockSettings.Zones[GetZoneText()]["mount"]].index) return end Livestock.RecycleTable(temp) local breath = false for count = 1, MIRRORTIMER_NUMTIMERS do local timer, _, _, scale = GetMirrorTimerInfo(count) if (debug) then print(format("Timer = '%s' and value = '%s'", timer, scale)) end if timer == "BREATH" and scale == -1 then breath = true end end local spellName = UnitBuff("player", L.LIVESTOCK_SPELL_SEALEGS, nil ); if (spellName) then tinsert(temp, LivestockSettings.Mounts["Abyssal Seahorse"].index) elseif (breath == false) then Livestock.PickFlyingMount() return else for k in pairs(LivestockSettings.Mounts) do -- go through the water mounts and add the ones that are selected to the temp table if (LivestockSettings.Mounts[k].type == "water" and LivestockSettings.Mounts[k].show == 1 and k ~= "Abyssal Seahorse") then tinsert(temp, LivestockSettings.Mounts[k].index) end end end if #temp == 0 then -- if no mounts are selected, choose a land mount instead Livestock.PickLandMount() return end if (debug) then local number = temp[random(#temp)] local _, creatureName = GetCompanionInfo("MOUNT",number) print(format("MOUNT: '%s' is mount-index, land mount is '%s'", number, creatureName)) CallCompanion("MOUNT",number) -- call a random mount else CallCompanion("MOUNT",temp[random(#temp)]) -- call a random mount end end function Livestock.PickFlyingMount() if IsFlying() and LivestockSettings.safeflying == 1 then -- if we're already flying, don't do anything; if we're not and mounted, we're on the ground on a flying mount so dismount instead of summoning another mount return elseif IsMounted() then Dismount() return elseif UnitInVehicle("player") then VehicleExit() return end if LivestockSettings.Zones[GetSubZoneText()] and LivestockSettings.Zones[GetSubZoneText()]["mount"] then CallCompanion("MOUNT", LivestockSettings.Mounts[LivestockSettings.Zones[GetSubZoneText()]["mount"]].index) return elseif LivestockSettings.Zones[GetZoneText()] and LivestockSettings.Zones[GetZoneText()]["mount"] then CallCompanion("MOUNT", LivestockSettings.Mounts[LivestockSettings.Zones[GetZoneText()]["mount"]].index) return end Livestock.RecycleTable(temp) for k in pairs(LivestockSettings.Mounts) do -- go through the flying mounts and add the ones that are selected to the temp table if (LivestockSettings.Mounts[k].type == "flying") and LivestockSettings.Mounts[k].show == 1 then tinsert(temp, LivestockSettings.Mounts[k].index) end end if #temp == 0 then -- if no mounts are selected, prompt the user to select one (or pick a land mount?) Livestock.PickLandMount() return end if (debug) then local number = temp[random(#temp)] local _, creatureName = GetCompanionInfo("MOUNT",number) print(format("MOUNT: '%s' is mount-index, flying mount is '%s'", number, creatureName)) CallCompanion("MOUNT",number) -- call a random mount else CallCompanion("MOUNT",temp[random(#temp)]) -- call a random mount end end function Livestock.Dismount() if UnitIsDeadOrGhost("player") then return end if IsFlying() and LivestockSettings.safeflying == 0 then Dismount() return elseif not IsFlying() and IsMounted() then Dismount() return elseif UnitInVehicle("player") and not IsFlying() then VehicleExit() return end end function Livestock.MoveSummon() -- idea and code provided by Mikhael of Doomhammer on the WoWInterface forums, modified by Scott Snowman (author). Checks to see if you have a vanity pet out, and if you don't, it summons one (or your favorite). Used as a hook on movement functions. if LivestockSettings.summononmove == 0 or donotsummon or UnitChannelInfo("player") or UnitCastingInfo("player") or IsFalling() or Recompense.CheckBuffsAgainstTable(restrictSummonForTheseBuffs) or Recompense.CheckEquipmentAgainstTable(restrictSummonForThisEquipment) then return -- return if the flag to not summon is checked, if you're channeling or casting, or if you have a buff on you that indicates you're in a situation where you shouldn't be summoning. else if LivestockSettings.restrictautosummon == 1 and UnitIsPVP("player") == 1 then -- if the setting to ignore summoning when flagged is set, then check for PVP status. if LivestockSettings.ignorepvprestrictionininstances == 0 then -- if the PVP restriction is absolute, then return from the hook. return else -- if the option for ignoring in an instance is set, check the instance status to see if we're in a PVE instance. local inInstance, instanceType = IsInInstance() if not inInstance or instanceType == "arena" or instanceType == "pvp" then return end end end if LivestockSettings.donotsummoninraid == 1 and GetRealNumRaidMembers() > 0 then return end local anyCritterOut for i = 1, GetNumCompanions("CRITTER") do local _, _, _, _, isSummoned = GetCompanionInfo("CRITTER", i) if ( isSummoned ) then anyCritterOut = true break end end if anyCritterOut then return else local index if not UnitIsDeadOrGhost("player") and not InCombatLockdown() and not IsMounted() and not IsStealthed() then -- long conditional, only summon if the checkbox is checked (check this first for performance) and not stealthed, mounted, dead, or in combat. if (LivestockSettings.Zones[GetSubZoneText()] and LivestockSettings.Zones[GetSubZoneText()]["critter"]) or (LivestockSettings.Zones[GetZoneText()] and LivestockSettings.Zones[GetZoneText()]["critter"]) then -- check to see if we have pet data for the current zone or subzone if LivestockSettings.Zones[GetSubZoneText()] and LivestockSettings.Zones[GetSubZoneText()]["critter"] then index = LivestockSettings.Critters[LivestockSettings.Zones[GetSubZoneText()]["critter"]].index elseif LivestockSettings.Zones[GetZoneText()] and LivestockSettings.Zones[GetZoneText()]["critter"] then index = LivestockSettings.Critters[LivestockSettings.Zones[GetZoneText()]["critter"]].index end elseif LivestockSettings.summonfaveonmove == 1 then -- check to see if it's the favorite we want to summon. index = LivestockSettings.Critters[LivestockSettings.favoritepet].index -- retrieve the index of the favorite pet end if index then local _, _, _, _, isSummoned = GetCompanionInfo("CRITTER", index) if isSummoned then -- check to see if the favorite / zone pet is summoned already, and summon it if it isn't return else local sound = GetCVar("Sound_EnableSFX") SetCVar("Sound_EnableSFX", "0") -- set the sound off after retrieving the sound setting so that error messages don't sound from trying to summon the pet when you can't CallCompanion("CRITTER",index) -- summon the favorite UIErrorsFrame:Clear() -- clear any error text SetCVar("Sound_EnableSFX", sound) -- set the sound setting back to its previous value end else -- no index means no favorite or no saved info local sound = GetCVar("Sound_EnableSFX") SetCVar("Sound_EnableSFX", "0") Livestock.PickCritter() UIErrorsFrame:Clear() SetCVar("Sound_EnableSFX", sound) end end end end end -- Slash handler function Livestock.Slash(arg) local cmd, args = arg:match("(%S+)%s*(.-)$") if cmd == "reset" then -- Clear all the points of the Livestock buttons and set them back to their original locations LivestockCrittersButton:ClearAllPoints() LivestockCrittersButton:SetPoint("CENTER",-50,-150) LivestockLandMountsButton:ClearAllPoints() LivestockLandMountsButton:SetPoint("CENTER",0,-150) LivestockFlyingMountsButton:ClearAllPoints() LivestockFlyingMountsButton:SetPoint("CENTER",50,-150) LivestockSmartButton:ClearAllPoints() LivestockSmartButton:SetPoint("CENTER",0,-190) elseif cmd == "scale" then -- clear all the points of the Livestock buttons, set them back to their original locations, and then set the scale LivestockCrittersButton:ClearAllPoints() LivestockCrittersButton:SetPoint("CENTER",-50,-150) LivestockLandMountsButton:ClearAllPoints() LivestockLandMountsButton:SetPoint("CENTER",0,-150) LivestockFlyingMountsButton:ClearAllPoints() LivestockFlyingMountsButton:SetPoint("CENTER",50,-150) LivestockSmartButton:ClearAllPoints() LivestockSmartButton:SetPoint("CENTER",0,-190) Livestock.ScaleButtons(tonumber(arg:sub(7,13))) elseif cmd == "redo" then print(L.LIVESTOCK_INTERFACE_RESETSAVEDDATA) LivestockSettings.Mounts = {} Livestock.RenumberCompanions() Livestock.RebuildMenu("CRITTER") Livestock.RebuildMenu("LAND") Livestock.RebuildMenu("FLYING") Livestock.RebuildMenu("WATER") elseif cmd == "zone" or cmd == "subzone" then Livestock.AddToZone(cmd, args) elseif cmd == "prefs" then InterfaceOptionsFrame:Show() -- bring up the options frame InterfaceOptionsFrameTab2:Click() -- click on the Addons tab local button = 1 while _G["InterfaceOptionsFrameAddOnsButton"..button] do if _G["InterfaceOptionsFrameAddOnsButton"..button].element.name == "Livestock" then -- search through the buttons, find the one marked Livestock, and click it _G["InterfaceOptionsFrameAddOnsButton"..button]:Click() break end button = button + 1 end return elseif cmd == "debug" then debug = not(debug) else -- toggle the Livestock menu frame if LivestockMenuFrame:IsVisible() then LivestockMenuFrame:Hide() else LivestockMenuFrame:Show() end end end -- Companion Frame declaration and slash handler setup local companionframe = CreateFrame("Frame") companionframe:RegisterEvent("COMPANION_LEARNED") companionframe:RegisterEvent("PLAYER_LOGIN") companionframe:RegisterEvent("PLAYER_ENTERING_WORLD") companionframe:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED") companionframe:RegisterEvent("UNIT_SPELLCAST_SENT") companionframe:RegisterEvent("UNIT_ENTERED_VEHICLE") companionframe:RegisterEvent("UI_INFO_MESSAGE") companionframe:SetScript("OnEvent", Livestock.CompanionEvent) SLASH_LIVESTOCK1 = LIVESTOCK_INTERFACE_SLASHSTRING SlashCmdList["LIVESTOCK"] = Livestock.Slash