local COMPAT, CANAME, T = select(4, GetBuildInfo()), ... if T.SkipLocalActionBook then return end if T.TenEnv then T.TenEnv() end local MODERN, CI_ERA, CF_CATA = COMPAT >= 10e4, COMPAT < 2e4, COMPAT > 4e4 and COMPAT < 10e4 local EV = T.Evie local AB = T.ActionBook:compatible(2, 31) local KR = T.ActionBook:compatible("Kindred", 1,26) local RW = T.ActionBook:compatible("Rewire", 1,40) assert(EV and AB and KR and RW and 1, "Incompatible library bundle") local playerClassLocal, playerClass = UnitClass("player") local stringArgCache = {} do local empty = {} setmetatable(stringArgCache, {__index=function(t,k) if k then local at for s in k:gmatch("[^/]+") do s = s:match("^%s*(%S.-)%s*$") if s then at = at or {} at[#at + 1] = KR:UnescapeCmdOptionsValue(s) end end at = at or empty t[k] = at return at end return empty end}) end local RegisterStateConditional do local f = CreateFrame("Frame", nil, nil, "SecureHandlerAttributeTemplate") f:SetFrameRef("KR", KR:seclib()) f:Execute('KR, curValue = self:GetFrameRef("KR"), newtable()') f:SetAttribute("_onattributechanged", [[-- AB_CondDriver_OAC local state = name:match("^state%-(.+)") if state and value ~= "_" and curValue[state] ~= nil then KR:RunAttribute("UpdateStateConditional", state, value, curValue[state] or "") curValue[state] = value or false end ]]) function RegisterStateConditional(name, cond, usesExtendedConditionals) KR:SetStateConditionalValue(name, false) f:SetAttributeNoHandler("state-" .. name, "_") f:SetAttributeNoHandler("register-name", name) f:SetAttributeNoHandler("register-cond", cond) f:SetAttributeNoHandler("register-kind", not not usesExtendedConditionals) f:Execute([[-- AB_CondStateDriver_Register local name, cond = self:GetAttribute("register-name"), self:GetAttribute("register-cond") curValue[name] = curValue[name] or false if self:GetAttribute("register-kind") then KR:SetAttribute("frameref-RegisterStateDriver-frame", self) KR:RunAttribute("RegisterStateDriver", name, cond) else RegisterStateDriver(self, name, cond) end ]]) end end securecall(function() -- zone:Zone/Sub Zone local function onZoneUpdate() local cz for i=1,4 do local z = (i == 1 and GetRealZoneText or i == 2 and GetSubZoneText or i == 3 and GetZoneText or GetMinimapZoneText)() if z and z ~= "" then cz = (cz and (cz .. "/") or "") .. z:gsub("%s*[,/%[%]][[,/%[%]]%s]*", " ") end end KR:SetStateConditionalValue("zone", cz or false) end onZoneUpdate() EV.ZONE_CHANGED = onZoneUpdate EV.ZONE_CHANGED_INDOORS = onZoneUpdate EV.ZONE_CHANGED_NEW_AREA = onZoneUpdate EV.PLAYER_ENTERING_WORLD = onZoneUpdate end) securecall(function() -- me:Player Name/Class KR:SetStateConditionalValue("me", UnitName("player") .. "/" .. playerClassLocal .. "/" .. playerClass) end) securecall(function() -- spec:id/name local s, _, _, cid = nil, UnitClass("player") for i=1,MODERN and 5 or 0 do local id, name = GetSpecializationInfoForClassID(cid, i) if id and name then s = ("%s[spec:%d] %d/%d/%s"):format(s and s .. "; " or "", i, i, id, name:lower()) end end if s then RegisterStateConditional("spec", s, false) end end) securecall(function() -- form:token local map, curCnd, pending = playerClass == "DRUID" and { [GetSpellInfo(40120) or 1]="/flight", [GetSpellInfo(33943) or 1]="/flight", [GetSpellInfo(1066) or 1]="/aquatic", [GetSpellInfo(783) or 1]="/travel", [GetSpellInfo(24858) or 1]="/moon/moonkin", [GetSpellInfo(768) or 1]="/cat", [GetSpellInfo(171745) or 1]="/cat", [GetSpellInfo(5487) or 1]="/bear", [not MODERN and GetSpellInfo(9634) or 1]="/bear", [GetSpellInfo(114282) or 1]="/treant", [GetSpellInfo(210053) or 1]="/stag", } or playerClass == "WARRIOR" and { [GetSpellInfo(197690) or 1]="/defensive", [GetSpellInfo(386164) or 1]="/battle", [GetSpellInfo(386196) or 1]="/berserker", [GetSpellInfo(386208) or 1]="/defensive", [CI_ERA and GetSpellInfo(412513) or 1]="/gladiator", [GetSpellInfo(2457) or 1]="/battle", [GetSpellInfo(71) or 1]="/defensive", [GetSpellInfo(2458) or 1]="/berserker", } if map then KR:SetAliasConditional("stance", "form") local function syncForm() local s = "" for i=1,10 do local _, _, _, fsid = GetShapeshiftFormInfo(i) local name = GetSpellInfo(fsid) s = ("%s[form:%d] %d%s;"):format(s, i,i, map[name] or "") end if curCnd ~= s then RegisterStateConditional("form", s, false) end curCnd, pending = s, nil return "remove" end EV.PLAYER_LOGIN = syncForm function EV.UPDATE_SHAPESHIFT_FORMS() if InCombatLockdown() then pending = pending or EV.RegisterEvent("PLAYER_REGEN_ENABLED", syncForm) or 1 else syncForm() end end end end) securecall(function() -- instance:arena/bg/ratedbg/lfr/raid/scenario + outland/northrend/... local mapTypes = { party="dungeon", pvp="battleground/bg", ratedbg="ratedbg/rgb", none="world", [1116]="world/draenor", [1464]="world/draenor", [1191]="world/draenor/ashran/worldpvp", [974]="world/darkmoon faire", [870]="world/pandaria", [1064]="world/pandaria", [530]="world/outland", [571]="world/northrend", [1220]="world/broken isles", [1669]="world/argus", [1642]="world/bfa/zandalar", [1643]="world/bfa/kul tiras", [1718]="world/bfa/nazjatar", [2222]="world/shadowlands", [2453]="world/torghast", -- lobby [2162]="torghast", -- towers [2444]="world/dragon isles/df", [2516]="dungeon/nokhud", [2454]="world/zaralek/df", [2548]="world/emerald dream/df", [2552]="world/khaz algar/tww", [2601]="world/khaz algar/tww", [2127]="world/siren isle/tww", [2706]="world/undermine/tww", [2662]="dungeon/dawnbreaker", [2769]="raid/undermine", [2827]="hvision", [2828]="hvision", [2212]="hvision", [2213]="hvision", hvision="scenario/hvision", garrison="world/draenor/garrison", [1158]="garrison", [1331]="garrison", [1159]="garrison", [1152]="garrison", [1330]="garrison", [1153]="garrison", [1893]="island", -- The Dread Chain [1814]="island", -- Havenswood (?) [1879]="island", -- Jorundall (?) [1897]="island", -- Molten Cay [1892]="island", -- The Rotting Mire [1898]="island", -- Skittering Hollow [1813]="island", -- Un'gol Ruins [1955]="island", -- Uncharted Island (tutorial) [1882]="island", -- Verdant Wilds [1883]="island", -- Whispering Reef } local mapZoneCheck, zoneChecked = { [2512]="world/gta", [2085e6+2512]="world/dragon isles/df/gta" }, true local function syncInstance(e) local _, itype, did, _, _, _, _, imid = GetInstanceInfo() local stype = itype == "raid" and did == 7 and "/lfr" if mapZoneCheck[imid] then if e == "PLAYER_ENTERING_WORLD" and zoneChecked then zoneChecked, EV.ZONE_CHANGED_NEW_AREA = false, syncInstance end local bm = C_Map.GetBestMapForUnit("player") itype = mapZoneCheck[bm and bm*1e6 + imid or nil] or mapZoneCheck[imid] or mapTypes[imid] elseif mapTypes[imid] then itype = mapTypes[imid] elseif itype == "pvp" and MODERN and C_PvP.IsRatedBattleground() then itype = "ratedbg" elseif itype == "none" and MODERN and IsInActiveWorldPVP() then itype = "worldpvp" elseif itype == "scenario" and C_DelvesUI and C_DelvesUI.HasActiveDelve(imid) then itype = "scenario/delve" end itype = mapTypes[itype] or itype or "daze" itype = stype and (itype .. stype) or itype KR:SetStateConditionalValue("in", itype) if e == "ZONE_CHANGED_NEW_AREA" then zoneChecked = true return "remove" end end EV.PLAYER_ENTERING_WORLD = syncInstance EV.WALK_IN_DATA_UPDATE = syncInstance function EV:PLAYER_MAP_CHANGED(_old, _new) -- [11.0.2] Delve airlocks: PEW doesn't fire; GetInstanceInfo() returns stale data during PMC EV.After(0, syncInstance) end KR:SetAliasConditional("instance", "in") KR:SetStateConditionalValue("in", "daze") end) securecall(function() -- petcontrol local hasControl = (playerClass ~= "HUNTER" and playerClass ~= "WARLOCK") or UnitLevel("player") >= 10 KR:SetStateConditionalValue("petcontrol", hasControl) if not hasControl then function EV.PLAYER_LEVEL_UP(_, level) if level >= 10 then KR:SetStateConditionalValue("petcontrol", "*") return "remove" end end end end) securecall(function() -- outpost if not MODERN then KR:SetStateConditionalValue("outpost", "") return end local map, state, name = { [161676]="garrison", [161332]="garrison", [164012]="arena", [164050]="lumber yard/yard", [161767]="sanctum", [162075]="arsenal", [168499]="brewery", [168487]="brewery", [170108]="smuggling run/run", [170097]="smuggling run/run", [164222]="corral", [165803]="corral", [160240]="tankworks", [160241]="tankworks", }, false, GetSpellInfo(161691) local function syncOutpost() local ns = map[select(7, GetSpellInfo(name))] if state ~= ns then KR:SetStateConditionalValue("outpost", ns or "") state = ns end end EV.SPELLS_CHANGED = syncOutpost syncOutpost() end) securecall(function() -- level:floor local function syncLevel() KR:SetThresholdConditionalValue("level", UnitLevel("player") or 0) end syncLevel() EV.PLAYER_LEVEL_UP = syncLevel end) securecall(function() -- horde/alliance local function syncFactionGroup(e, u) if e ~= "UNIT_FACTION" or u == "player" then local fg = UnitFactionGroup("player") KR:SetStateConditionalValue("horde", fg == "Horde" and "*" or "") KR:SetStateConditionalValue("alliance", fg == "Alliance" and "*" or "") KR:SetStateConditionalValue("merc", MODERN and UnitIsMercenary("player") and "*" or "") end end syncFactionGroup() EV.PLAYER_ENTERING_WORLD, EV.UNIT_FACTION = syncFactionGroup, syncFactionGroup KR:SetAliasConditional("mercenary", "merc") end) securecall(function() -- moving KR:SetNonSecureConditional("moving", function() return GetUnitSpeed("player") > 0 end) end) securecall(function() -- falling KR:SetNonSecureConditional("falling", function() return IsFalling() end) end) securecall(function() -- ready:spell name/spell id/item name/item id KR:SetNonSecureConditional("ready", function(_name, args) local gcS, gcL = GetSpellCooldown(61304) if not args or args == "" then return gcS == 0 and gcL == 0 end local at = stringArgCache[args] local gcE = gcS and gcL and (gcS + gcL) or math.huge for i=1,#at do local rc = at[i] local cdS, cdL, _cdA = GetSpellCooldown(rc) if cdL == nil then local _, iid = C_Item.GetItemInfo(rc) iid = tonumber((iid or rc):match("item:(%d+)")) if iid then cdS, cdL, _cdA = C_Container.GetItemCooldown(iid) end end if cdL == 0 or (cdS and cdL and (cdS + cdL) <= gcE) then return true end end return false end) end) securecall(function() -- have:item name/id KR:SetNonSecureConditional("have", function(_name, args) if not args or args == "" then return false end local at, GetItemCount = stringArgCache[args], C_Item.GetItemCount for i=1,#at do if (GetItemCount(at[i]) or 0) > 0 then return true end end return false end) end) securecall(function() -- self(de)buff:name, own(de)buff:name, (de)buff:name, cleanse local conditionalFilter = { selfbuff="HELPFUL", selfdebuff="HARMFUL", ownbuff="HELPFUL PLAYER", owndebuff="HARMFUL PLAYER", buff="HELPFUL", debuff="HARMFUL", } local function countSlots(tk, ...) return select("#", ...), tk, ... end local function checkAura(name, args, target) target = (name == "selfbuff" or name == "selfdebuff") and "player" or target or "target" if not args or args == "" or not UnitExists(target) then return false end local at, query, filter = stringArgCache[args], C_UnitAuras.GetAuraSlots, conditionalFilter[name] local count, ctok, a,b,c,d,e repeat count, ctok, a,b,c,d,e = countSlots(query(target, filter, 5, ctok)) for i=1, count do local dat = C_UnitAuras.GetAuraDataBySlot(target, a) local name = dat and dat.name for j=1, name and #at or 0 do if strcmputf8i(name, at[j]) == 0 then return true end end a,b,c,d = b,c,d,e end until not ctok return false end KR:SetNonSecureConditional("selfbuff", checkAura) KR:SetNonSecureConditional("selfdebuff", checkAura) KR:SetNonSecureConditional("debuff", checkAura) KR:SetNonSecureConditional("owndebuff", checkAura) KR:SetNonSecureConditional("buff", checkAura) KR:SetNonSecureConditional("ownbuff", checkAura) KR:SetNonSecureConditional("cleanse", function(_, _, target) target = target or "target" return UnitIsFriend("player", target) and select(2,C_UnitAuras.GetAuraSlots(target, "HARMFUL RAID", 1)) ~= nil end) end) securecall(function() -- combo:count local power, powerMap = 4, {[265]=7, [267]=14, [258]=13, PALADIN=9, MONK=12} local defaultPower = powerMap[playerClass] or 4 KR:SetNonSecureConditional("combo", function(_name, args) return UnitPower("player", power) >= (tonumber(args) or 1) end) local function syncComboPower() power = powerMap[MODERN and GetSpecializationInfo(GetSpecialization() or 0)] or defaultPower end EV.PLAYER_SPECIALIZATION_CHANGED, EV.PLAYER_ENTERING_WORLD = syncComboPower, syncComboPower end) securecall(function() -- near:oid/cid local argCache, nearValue, nearGroup, holdGroup, holdExpire = {} local GROUP_HOLD_TIME = {["tww-herb-overload"]=5, ["tww-mine-overload"]=5} local typePrefix, groups = {GameObject="o", Creature="c"}, {} for k, v in pairs({ ["herb-overload"] = "o375245/o381199/o381213/o356536/o381202/o381210/o381196/o381205/o375242/o375244/o381214/o381201/o381200/o381212/o375246/o381198/o381203/o381197/o381211/o375243/o381204/o390141/o390140/o390142/o390139/o398761/o398760/o398759/o398762/o398767/o398764/o398765/o398766/o407696/o407688/o407698/o407693", ["mine-overload"] = "o381516/o375235/o375234/o381515/o381517/o375238/o375239/o381518/o381519/o375240/o390137/o390138/o407669/o407668", ["tww-herb-overload"] = "o414327/o414329/o414326/o414328/o414325/o414337/o414339/o454053/o454008/o414338/o454084/o414336/o414335/o454066/o454079/o454069/o454074/o423363/o454082/o454067/o423368/o454077/o423364/o423366/o423367/o454072/o454064/o454006/o454050/o414332/o414331/o414330", ["tww-mine-overload"] = "o413886/o413895/o413905/o430351/o430335/o430352/o413900/o413883/o413890/o413902/o413892/o413884", }) do for e in v:gmatch("[^/]+") do groups[e] = k end end local function checkNearHoldExpire() if holdGroup and holdExpire < GetTime() then holdGroup, holdExpire = nil KR:PokeConditional("near") end end KR:SetNonSecureConditional("near", function(_name, args) checkNearHoldExpire() if args == nil then return nearValue ~= nil end local ca = argCache[args] if ca == nil then ca = {} for v in args:gmatch("[^%s/][^/]*") do ca[v:match("^(.-)%s*$")] = 1 end argCache[args] = ca end return (ca[nearValue] or ca[nearGroup] or ca[holdGroup]) ~= nil end) function EV:PLAYER_SOFT_INTERACT_CHANGED(_, guid) local ct, oid if guid and not InCombatLockdown() then ct, oid = guid:match("^(%a+)%-[-%d]+%-(%d+)%-[^-]+$") ct = typePrefix[ct] oid = ct and ct .. oid or nil end if oid ~= nearValue then local ht = not oid and GROUP_HOLD_TIME[nearGroup] if ht then holdGroup, holdExpire = nearGroup, GetTime() + ht EV.After(ht+1/128, checkNearHoldExpire) elseif oid then holdGroup, holdExpire = nil end nearValue, nearGroup = oid, groups[oid] KR:PokeConditional("near") end end end) securecall(function() -- race:token local map, _, raceToken = { Scourge="Scourge/Undead/Forsaken", LightforgedDraenei="LightforgedDraenei/Lightforged", HighmountainTauren="HighmountainTauren/Highmountain", MagharOrc="MagharOrc/Maghar", ZandalariTroll="ZandalariTroll/Zandalari", DarkIronDwarf="DarkIronDwarf/DarkIron", }, UnitRace("player") KR:SetStateConditionalValue("race", map[raceToken] or raceToken) end) securecall(function() -- professions local ct, ot, syncProfInner = {}, {} local map = MODERN and { [197]="tail", [165]="lw", [164]="bs", [171]="alch", [202]="engi", [333]="ench", [755]="jc", [773]="scri", [182]="herb", [186]="mine", [393]="skin", [794]="arch", [185]="cook", [356]="fish", [20219]="nomeng", [20222]="gobeng", } map = map or { [GetSpellInfo(3908) or ""]="tail", [GetSpellInfo(2108) or ""]="lw", [GetSpellInfo(2018) or ""]="bs", [GetSpellInfo(2259) or ""]="alch", [GetSpellInfo(4036) or ""]="engi", [GetSpellInfo(7411) or ""]="ench", [GetSpellInfo(2366) or ""]="herb", [GetSpellInfo(2575) or ""]="mine", [GetSpellInfo(8613) or ""]="skin", [GetSpellInfo(2550) or ""]="cook", [GetSpellInfo(3273) or ""]="faid", [GetSpellInfo(7620) or ""]="fish", [GetSpellInfo(20221) or ""]="gobeng", [GetSpellInfo(20222) or ""]="gobeng", [GetSpellInfo(20220) or ""]="nomeng", [GetSpellInfo(20219) or ""]="nomeng", } local spellIDProfs = { [264636]="cook3", [264620]="tail3", [264626]="tail6", [271662]="fish5", [264588]="lw6", [264590]="lw7", [264479]="eng2", [264481]="eng3", [264483]="eng4", [264485]="eng5", [264488]="eng6", [264490]="eng7", [310542]="eng9", -- eng8 is in sync code, because factions } map[""]=nil syncProfInner = MODERN and function(id, ...) if id then local _1, _2, cur, _cap, ns, sofs, skid, _bonus, specIdx, _ = GetProfessionInfo(id) local et, sid = GetSpellBookItemInfo(ns == 2 and sofs and specIdx > -1 and sofs+2 or 0, "spell") local e1, e2 = map[skid], map[et == "SPELL" and sid or nil] if e1 then ct[e1] = cur end if e2 then ct[e2] = cur end end if select("#", ...) > 0 then return syncProfInner(...) end end or function() local idx, wasCollapsed for i=1,GetNumSkillLines() do local text, isHeader, isExpanded = GetSkillLineInfo(i) if isHeader and text == TRADE_SKILLS then idx, wasCollapsed = i, not isExpanded ExpandSkillHeader(i) break end end if not idx then return end local j, text, isHeader, _, curSkill = idx+1 repeat j, text, isHeader, _, curSkill = j+1, GetSkillLineInfo(j) local skey = map[text] if skey and not isHeader then ct[skey] = curSkill end until isHeader or not text if wasCollapsed then CollapseSkillHeader(idx) end end local function syncProf() ct, ot = ot, ct for k in pairs(ct) do ct[k] = nil end if MODERN then syncProfInner(GetProfessions()) else syncProfInner() end for sid, cnd in pairs(spellIDProfs) do ct[cnd] = GetSpellInfo(GetSpellInfo(sid) or "\1") and 1 or nil end ct["eng8"] = GetSpellInfo(GetSpellInfo(UnitFactionGroup("player") == "Horde" and 265807 or 264492) or "\1") and 1 or nil for k,v in pairs(ct) do if ot[k] ~= v then KR:SetThresholdConditionalValue(k, v) ot[k] = v end end for k,v in pairs(ot) do if ct[k] ~= v then KR:SetThresholdConditionalValue(k, false) ot[k] = nil end end end for _, v in pairs(map) do KR:SetThresholdConditionalValue(v, false) end for _, v in pairs(spellIDProfs) do KR:SetThresholdConditionalValue(v, false) end for alias, real in ("tailoring:tail leatherworking:lw alchemy:alch engineering:engi enchanting:ench jewelcrafting:jc blacksmithing:bs inscription:scri herbalism:herb archaeology:arch cooking:cook fishing:fish firstaid:faid mining:mine skinning:skin"):gmatch("(%a+):(%a+)") do KR:SetAliasConditional(alias, real) end EV.PLAYER_LOGIN, EV.CHAT_MSG_SKILL = syncProf, syncProf end) securecall(function() -- pet:stable id; havepet:stable id if playerClass ~= "HUNTER" then KR:SetStateConditionalValue("havepet", false) return end local pt, noPendingSync = {}, true local function syncPet(e) if InCombatLockdown() then if noPendingSync then EV.PLAYER_REGEN_ENABLED, noPendingSync = syncPet, false end return end for k in pairs(pt) do pt[k] = nil end local o, hpo for i=1,5 do local _, n, _, r = GetStablePetInfo(i) if n and r then local k = n == r and n or (n .. "/" .. r) pt[k] = (pt[k] or ("[pet:" .. n .. (n ~= r and ",pet:" .. r .. "] " or "] ") .. k)) .. "/" .. i hpo = (hpo and hpo .. "/" .. i or i) end end for k, v in pairs(pt) do o = k:match("/") and (v .. (o and "; " .. o or "")) or ((o and o .. "; " or "") .. v) end RegisterStateConditional("pet", "[nopet]; " .. (o and o .. "; 0" or " 0"), false) KR:SetStateConditionalValue("havepet", tostring(hpo or "")) noPendingSync = true return (e == "PLAYER_LOGIN" or e == "PLAYER_REGEN_ENABLED") and "remove" end KR:SetStateConditionalValue("havepet", false) EV.PLAYER_LOGIN, EV.PET_STABLE_UPDATE, EV.PET_INFO_UPDATE, EV.LOCALPLAYER_PET_RENAMED = syncPet, syncPet, syncPet, syncPet end) securecall(function() -- game:modern/remix/era/sod/cata + era/hc KR:SetStateConditionalValue("game", "daze") function EV.PLAYER_LOGIN() local s if CI_ERA then local season, st = C_Seasons.GetActiveSeason(), Enum.SeasonID s = not season and "era" or season == st.SeasonOfDiscovery and "sod" or (season == st.Fresh or season == st.FreshHardcore) and "fresh" or "era" if C_GameRules.IsHardcoreActive() then s = s and s .. "/hc" or "hc" end elseif MODERN then s = PlayerGetTimerunningSeasonID() and "remix" or "modern" else s = "cata" end KR:SetStateConditionalValue("game", s) return "remove" end end) securecall(function() -- visual local f = CreateFrame("Frame", nil, nil, "SecureFrameTemplate") f:SetAttribute("EvaluateMacroConditional", 'return false') KR:SetSecureExternalConditional("visual", f, function() return true end) end) securecall(function() -- coven:kyrian/venthyr/fae/necro KR:SetStateConditionalValue("coven", false) KR:SetStateConditionalValue("acoven80", false) KR:SetAliasConditional("covenant", "coven") KR:SetAliasConditional("acovenant80", "acoven80") if not MODERN then return end local cv, covMap = false, {"kyrian", "venthyr", "fae/nightfae", "necro/necrolord"} local c8, p8 = false, {1, 4, 3, 2} local noPendingSync, noPendingTimer, syncCovenTimer = true, true local function syncCoven(e) if InCombatLockdown() then if noPendingSync then EV.PLAYER_REGEN_ENABLED = syncCoven end return end noPendingSync = true local nv = covMap[C_Covenants.GetActiveCovenantID()] or false if nv ~= cv then cv = nv KR:SetStateConditionalValue("coven", nv) end if GetAchievementNumCriteria(15646) == 4 then local n8 = false for i=1,4 do if select(3, GetAchievementCriteriaInfo(15646, i)) then n8 = n8 and (n8 .. "/" .. covMap[p8[i]]) or covMap[p8[i]] end end if n8 ~= c8 then c8 = n8 KR:SetStateConditionalValue("acoven80", n8) end elseif noPendingTimer then noPendingTimer = false EV.After(0.25, syncCovenTimer) end return e == "PLAYER_REGEN_ENABLED" and "remove" end function syncCovenTimer() noPendingTimer = true syncCoven() end EV.COVENANT_CHOSEN, EV.COVENANT_SANCTUM_RENOWN_LEVEL_CHANGED, EV.PLAYER_ENTERING_WORLD = syncCoven, syncCoven, syncCoven end) securecall(function() -- worldhover KR:SetStateConditionalValue("worldhover", false) local wf = CreateFrame("Frame", nil, nil, "ProtectedFrameTemplate-" .. CANAME) wf:SetAllPoints(WorldFrame) wf:SetPropagateMouseMotion(true) wf:SetPropagateMouseClicks(true) wf:EnableMouse(false) wf:EnableMouseMotion(true) wf:SetFrameStrata("BACKGROUND") wf:SetFrameLevel(0) local function wfOnMotion() if not InCombatLockdown() then local nv = wf:IsMouseMotionFocus() if nv == (KR:EvaluateCmdOptions("[worldhover]") == nil) then KR:SetStateConditionalValue("worldhover", nv) end end end wf:SetScript("OnEnter", wfOnMotion) wf:SetScript("OnLeave", wfOnMotion) function EV.PLAYER_REGEN_ENABLED() wfOnMotion(wf) end SecureHandlerSetFrameRef(wf, "KR", KR:seclib()) SecureStateDriverManager:SetAttribute("setframe", wf) SecureStateDriverManager:SetAttribute("setstate", "_wrapentered 1") SecureHandlerExecute(wf, [[KR = self:GetFrameRef("KR"); self:SetAttribute("frameref-KR", nil)]]) SecureHandlerWrapScript(wf, "OnEnter", wf, 'KR:RunAttribute("UpdateStateConditional", "worldhover", "*", nil)') SecureHandlerWrapScript(wf, "OnLeave", wf, 'KR:RunAttribute("UpdateStateConditional", "worldhover", nil, "*")') end) securecall(function() -- imbuedmh, imbuedoh, imbuedrw local h = CreateFrame("Frame", nil, nil, "SecureAuraHeaderTemplate") SecureHandlerSetFrameRef(h, "KR", KR:seclib()) SecureHandlerExecute(h, [[KR = self:GetFrameRef("KR")]]) local t1 = CreateFrame("Frame", nil, h, "SecureFrameTemplate") local t2 = CreateFrame("Frame", nil, h, "SecureFrameTemplate") local t3 = nil -- MODERN has no ranged slot; BUG[Classic/2408]: SAHT ignores ranged weapon h:SetAttribute("unit", "none") h:SetAttribute("includeWeapons", -1) h:SetAttribute("filter", "NONE") for i=1, t3 and 3 or 2 do local sf = i == 1 and t1 or i == 2 and t2 or t3 h:SetAttribute("tempEnchant" .. i, sf) sf:SetAttribute('cname', i == 1 and 'imbuedmh' or i == 2 and 'imbuedoh' or 'imbuedrw') SecureHandlerWrapScript(sf, 'OnShow', h, [[KR:RunAttribute("UpdateStateConditional", self:GetAttribute("cname"), "*", nil)]]) SecureHandlerWrapScript(sf, 'OnHide', h, [[KR:RunAttribute("UpdateStateConditional", self:GetAttribute("cname"), nil, "*")]]) sf:Hide() end KR:SetStateConditionalValue("imbuedmh", false) KR:SetStateConditionalValue("imbuedoh", false) h:Show() if not MODERN then KR:SetNonSecureConditional("imbuedrw", function(_, _args) local isImbued, _expire, _charges, _enchantID = select(9, GetWeaponEnchantInfo()) return not not isImbued end) end end) securecall(function() -- bar:id (future-aware) local CMD_SWAP, CMD_SET, NUM_PAGES = SLASH_SWAPACTIONBAR1, SLASH_CHANGEACTIONBAR1, NUM_ACTIONBAR_PAGES local argCache = {} local f = CreateFrame("Button", nil, nil, "SecureActionButtonTemplate") f:SetAttribute("type", "actionbar") f:SetAttribute("typerelease", "actionbar") f:SetAttribute("pressAndHoldAction", 1) f:SetAttribute("cmd-swap", CMD_SWAP) f:SetAttribute("num-pages", NUM_PAGES) SecureHandlerSetFrameRef(f, "KR", KR:seclib()) SecureHandlerSetFrameRef(f, "RW", RW:seclib()) SecureHandlerExecute(f, [[-- AB_bar_init argCache = newtable() CMD_SWAP = self:GetAttribute("cmd-swap"), self:SetAttribute("cmd-swap", nil) NUM_PAGES = self:GetAttribute("num-pages"), self:SetAttribute("num-pages", nil) RW = self:GetFrameRef('RW'), self:SetAttribute('frameref-RW', nil) KR = self:GetFrameRef('KR'), self:SetAttribute('frameref-KR', nil) ]]) f:SetAttribute("RunSlashCmd", [=[-- AB_bar_runslash local setTo, cmd, v = nil, ... if cmd == CMD_SWAP then local a, b = v:match("(%d+)%s+(%d+)") a, b = tonumber(a), tonumber(b) if a and b and a >= 1 and b >= 1 and a <= NUM_PAGES and b <= NUM_PAGES then setTo = KR:RunAttribute("EvaluateCmdOptions", "[bar:" .. a .. "]") and b or a end else local a = tonumber(v) if a and a >= 1 and a <= NUM_PAGES then setTo = a end end if setTo then pendingValue, pendingRunID = setTo, RW:GetAttribute("PyrolysisRunID") return cmd .. " " .. v, "notified-click", setTo end ]=]) f:SetAttribute("RunSlashCmd-PreClick", [[-- AB_bar_runslash_pre local cmd, v = ... pendingValue, pendingRunID = nil self:SetAttribute("action", v) ]]) f:SetAttribute("EvaluateMacroConditional", [=[-- AB_bar_evalmc local name, cv, target, _mark, futureID = ... if name ~= "bar" or not cv or cv == "" then return end if futureID == "driver-construct" then return nil, "use-scop" elseif not pendingRunID or RW:GetAttribute("PyrolysisRunID") ~= pendingRunID then pendingValue, pendingRunID = nil return nil, "use-scop" end local am = argCache[cv] if am == nil then am = newtable() for d in cv:gmatch("%s*(%d*)[^/]*/*") do am[d ~= "" and d+0 or 0] = true end argCache[cv], am[0] = am, nil end return am[pendingValue] ~= nil ]=]) local currentFutureID, currentBarState local function hintBarCommand(slash, _unparsed, clause, _target, _, _, _, speculationID) if (clause or "") == "" then return end if slash == CMD_SWAP then local a, b = clause:match("(%d+)%s+(%d+)") a, b = tonumber(a), tonumber(b) if a and b and a >= 1 and b >= 1 and a <= NUM_PAGES and b <= NUM_PAGES then if currentFutureID ~= speculationID then currentFutureID, currentBarState = speculationID, GetActionBarPage() end currentBarState = currentBarState == a and b or a end else local a = tonumber(clause) if a and a >= 1 and a <= NUM_PAGES then currentFutureID, currentBarState = speculationID, a end end end local function hintBarCondition(_name, cv, _target, _, futureID) if not cv or cv == "" then return false end local am = argCache[cv] if am == nil then am = {} for d in cv:gmatch("%s*(%d*)[^/]*/*") do am[d ~= "" and d+0 or 0] = true end am[0] = nil argCache[cv] = next(am) and am or false end return am and am[futureID == currentFutureID and currentBarState or GetActionBarPage()] or false end if RW:IsPyrolysisActive() then RW:RegisterCommandEx(CMD_SET, RW:GetCommandFlags(CMD_SET), f) RW:RegisterCommandEx(CMD_SWAP, RW:GetCommandFlags(CMD_SWAP), f) end RW:SetCommandHint(CMD_SET, math.huge, hintBarCommand) RW:SetCommandHint(CMD_SWAP, math.huge, hintBarCommand) KR:SetSecureExternalConditional("bar", f, hintBarCondition) end) securecall(function() -- anyflyable KR:SetStateConditionalValue("blockedflyable", false) KR:SetStateConditionalValue("superflyable", false) RegisterStateConditional("anyflyable", ("[superflyable] *; [blockedflyable]; [flyable] %s;"):format(MODERN and "[advflyable] *" or "*"), 1) end) securecall(function() -- holiday:veil/brew/hend/fire/east/luna KR:SetStateConditionalValue("holiday", false) if not (MODERN or CF_CATA) then return end local events = { [235485]="veil", [235484]="veil", [235482]="veil", veil="winterveil", [235441]="brew", [235440]="brew", [235442]="brew", brew="brewfest", [235462]="hend", [235461]="hend", [235460]="hend", hend="hallowsend", [235474]="fire", [235473]="fire", [235472]="fire", fire="midsummer", [235477]="east", [235476]="east", [235475]="east", east="noblegarden", [235471]="luna", [235470]="luna", [235469]="luna", luna="lunarfestival", [235466]="love", [235467]="love", [235468]="love", love="loveair", } local function nextHolidayInfo(md, idx) local d, x = (md % 100), (idx or 0)+1 local info = C_Calendar.GetHolidayInfo((md-d)/100, d, x) return info and x or nil, info end local function toEpoch(ct) ct.day, ct.min, ct.sec = ct.monthDay, ct.minute or 0, 0 return time(ct) end local function syncHoliday() local m0, cnow, tnow = C_Calendar.GetMonthInfo(0), C_DateAndTime.GetCurrentCalendarTime(), GetTime() local ofs = (cnow.year-m0.year)*12 + cnow.month - m0.month local mnow, d = ofs == 0 and m0 or C_Calendar.GetMonthInfo(ofs), cnow.monthDay C_Calendar.SetMonth(ofs) local cv, nextTime = false for i=1,2 do for _, info in nextHolidayInfo, i == 1 and d or d < mnow.numDays and d + 1 or 101 do local ek, st, et = events[info and info.texture], info and info.startTime, info and info.endTime ek = events[ek] or ek if ek and st and C_DateAndTime.CompareCalendarTime(cnow, st) >= 0 then local nt = GetTime() + toEpoch(st) - toEpoch(cnow) + 60 nextTime = cv and nt > nextTime and nextTime or nt elseif ek and et and C_DateAndTime.CompareCalendarTime(cnow, et) > 0 then local nt = GetTime() + toEpoch(et) - toEpoch(cnow) nextTime = cv and nt > nextTime and nextTime or nt cv = cv and cv .. "/" .. ek or ek -- KR will filter out the duplicates end end end C_Calendar.SetMonth(-ofs) KR:SetStateConditionalValue("holiday", cv) if nextTime and (nextTime - tnow) < 1e5 then nextTime = tnow+15 < nextTime and tnow + 15 or nextTime local function holidayTick() local d = nextTime - GetTime() if d <= 0 then syncHoliday() else EV.After(d > 120 and 120 or d, holidayTick) end end holidayTick() end return "remove" end EV.PLAYER_LOGIN = syncHoliday end) securecall(function() -- uslot:(slot token) KR:SetStateConditionalValue("uslot", false) local state, noPendingSync, slots = "", 1, {} for tk, sk in pairs({ head="HEADSLOT", neck="NECKSLOT", shoulders="SHOULDERSLOT", shirt="SHIRTSLOT", chest="CHESTSLOT", waist="WAISTSLOT", legs="LEGSSLOT", feet="FEETSLOT", wrist="WRISTSLOT", hands="HANDSSLOT", finger1="FINGER0SLOT", finger2="FINGER1SLOT", trinket1="TRINKET0SLOT", trinket2="TRINKET1SLOT", back="BACKSLOT", tabard="TABARDSLOT", }) do local ok, slot = pcall(GetInventorySlotInfo, sk) slots[tk] = ok and slot or nil end local function syncActiveSlots() if InCombatLockdown() then return end local GetItemSpell, o = C_Item.GetItemSpell for token, i in next, slots do local link, _, sid = token and (GetInventoryItemLink("player", i) or GetInventoryItemID("player", i)) if link then _, sid = GetItemSpell(link) end if sid and not IsPassiveSpell(sid) then o = o and (o .. "/" .. token) or token end end o, noPendingSync = o or "", 1 if state ~= o then state = o KR:SetStateConditionalValue("uslot", o) end end local function syncActiveSlotsIfPending() if not noPendingSync then syncActiveSlots() end end local function cueActiveSlotsSync(e) if noPendingSync and not InCombatLockdown() then EV.After(0, syncActiveSlots) end noPendingSync = nil return e ~= "PLAYER_EQUIPMENT_CHANGED" and "remove" end EV.PLAYER_REGEN_DISABLED = syncActiveSlotsIfPending EV.PLAYER_REGEN_ENABLED = syncActiveSlotsIfPending EV.PLAYER_EQUIPMENT_CHANGED = cueActiveSlotsSync EV.PLAYER_ENTERING_WORLD = cueActiveSlotsSync EV.PLAYER_EQUIPED_SPELLS_CHANGED = cueActiveSlotsSync end) securecall(function() -- Managed role units local mh = CreateFrame("Frame", nil, nil, "SecureFrameTemplate") SecureHandlerSetFrameRef(mh, "KR", KR:seclib()) SecureHandlerExecute(mh, [=[-- MRU_Init_Manager KR, uf, ul, spare = self:GetFrameRef("KR"), newtable(), newtable(), newtable() self:SetAttribute("frameref-KR", nil) ]=]) local syncUnits = [==[-- MRU_Sync local nl, key, nj, fa = spare, %q, 1 ul[key], spare, fa = nl, ul[key], uf[key] for i=1,40 do local u = fa[i]:GetAttribute("unit") if u then nl[i] = u else for j=i,#nl do nl[j] = nil end break end end for i=1,#nl do local u = nl[i] if u ~= playerUnit then KR:RunAttribute("SetAliasUnit", key .. nj, u) nj = nj + 1 end end for i=nj,#spare do KR:RunAttribute("SetAliasUnit", key .. i, "raid42") end self:Show() ]==] local function SpawnHeader(key, ...) local h = CreateFrame("Frame", nil, nil, "SecureGroupHeaderTemplate") for i=1,40 do local c = CreateFrame("Frame", nil, h, "SecureFrameTemplate") h:SetAttribute("child" .. i, c) SecureHandlerSetFrameRef(mh, "u" .. i, c) KR:SetAliasUnit(key .. i, "raid42") end SecureHandlerExecute(mh, ([[-- MRU_SpawnHeader_Init local a, k = newtable(), %q for i=1,40 do a[i] = self:GetFrameRef("u" .. i) self:SetAttribute("frameref-u" .. i, nil) end uf[k], ul[k] = a, newtable() ]]):format(key)) local cu = CreateFrame("Frame", nil, h, "SecureFrameTemplate") SecureHandlerWrapScript(cu, "OnHide", mh, syncUnits:format(key)) h:SetAttribute("child41", cu) h:SetAttribute("template", "ImpossibleFrameTemplate") h:SetAttribute("templateType", "Frame") h:SetAttribute("showRaid", true) h:SetAttribute("showParty", true) h:SetAttribute("showPlayer", false) h:SetAttribute("groupingOrder", "1,2,3,4,5,6,7,8") h:SetAttribute("sortMethod", "NAME") for i=1, select("#", ...), 2 do local k, v = select(i, ...) h:SetAttribute(k, v) end return h end local ph = CreateFrame("Frame", nil, nil, "SecureGroupHeaderTemplate") do local c = CreateFrame("Frame", nil, ph, "SecureFrameTemplate") ph:SetAttribute("child1", c) SecureHandlerWrapScript(c, "OnAttributeChanged", mh, [=[-- MRU_Player_Change if name ~= "unit" or value == playerUnit then return end playerUnit = value for key, v in pairs(ul) do local nj = 1 for i=1,#v do local u = v[i] if u ~= playerUnit then KR:RunAttribute("SetAliasUnit", key .. nj, u) nj = nj + 1 end end KR:RunAttribute("SetAliasUnit", key .. nj, "raid42") end ]=]) ph:SetAttribute("showRaid", true) ph:SetAttribute("showParty", false) ph:SetAttribute("showPlayer", false) ph:SetAttribute("nameList", (UnitName("player"))) ph:Show() end SpawnHeader("tank", "roleFilter","TANK"):Show() SpawnHeader("mtank", "roleFilter","MAINTANK"):Show() SpawnHeader("assist", "roleFilter","MAINASSIST"):Show() SpawnHeader("healer", "roleFilter","HEALER"):Show() SpawnHeader("dps", "roleFilter","DAMAGER"):Show() end)