1. local   BUTTON_HEIGHT,  ICON_SIZE,  GAP,    TEXT_OFFSET,    FONT_SIZE =
  2.     17,     14,     10, 5,      12
  3.  
  4. local f = CreateFrame("Frame", nil, UIParent)
  5. local g = CreateFrame("Frame", nil, f) -- gear sets
  6. local t = CreateFrame"Frame" -- timers
  7.  
  8. local highlight = f:CreateTexture()
  9. highlight:SetTexture"Interface\\QuestFrame\\UI-QuestTitleHighlight"
  10. highlight:SetBlendMode"ADD"
  11. highlight:SetAlpha(0)
  12.  
  13. local backdrop =  {
  14.     bgFile = "Interface\\Buttons\\WHITE8X8",
  15.     edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
  16.     edgeSize=16, tile = false, tileSize=0,
  17.     insets = { left=3, right=3, top=3, bottom=3 } }
  18.  
  19. local ShowGears, specIndex, gears, popupData
  20. local block, char, tip, tipshown, config
  21. local DEFAULT_ICON = "Interface\\Icons\\Spell_Shadow_SacrificialShield"
  22. local orgSetActiveSpecGroup = SetActiveSpecGroup
  23. local POPUP_SET_ALIAS = "ABSS_SET_ALIAS"
  24. local BLOCK, SPEC, GEAR = 0, 1, 2
  25.  
  26. local spam1 = ERR_LEARN_ABILITY_S:gsub("%.", "%."):gsub("%%s", "(.*)")
  27. local spam2 = ERR_LEARN_SPELL_S:gsub("%.", "%."):gsub("%%s", "(.*)")
  28. local spam3 = ERR_SPELL_UNLEARNED_S:gsub("%.", "%."):gsub("%%s", "(.*)")
  29.  
  30. local function SpamFilter(self, event, msg)
  31.     if strfind(msg, spam1) or strfind(msg, spam2) or strfind(msg, spam3) then return true end
  32. end
  33.  
  34.  
  35. local function OnEquipmentSetChange(oldName, newName)
  36.     for i=1, GetNumSpecializations() do
  37.         if char[i].set == oldName then char[i].set = newName end
  38.     end
  39.     f:PLAYER_TALENT_UPDATE()
  40. end
  41.  
  42. hooksecurefunc("ModifyEquipmentSet", OnEquipmentSetChange)
  43. hooksecurefunc("DeleteEquipmentSet", OnEquipmentSetChange)
  44.  
  45.  
  46. local function WearSet(arg, isGearIndex) -- defaults to talent group index
  47.     local set = type(arg) == "number" and (isGearIndex and GetEquipmentSetInfo(arg) or char[arg].set) or type(arg) == "string" and arg
  48.     if set and GetEquipmentSetInfoByName(set) then UseEquipmentSet(set) end
  49. end
  50.  
  51. local function DelayWearSet(self, elapsed)
  52.     t:SetScript("OnUpdate",nil)
  53.     ChatFrame_RemoveMessageEventFilter("CHAT_MSG_SYSTEM", SpamFilter)
  54.     WearSet(GetActiveSpecGroup())
  55. end
  56.  
  57. function SetActiveSpecGroup(...)
  58.     f:RegisterEvent"UNIT_SPELLCAST_SUCCEEDED"
  59.     f:RegisterEvent"UNIT_SPELLCAST_STOP"
  60.     ChatFrame_AddMessageEventFilter("CHAT_MSG_SYSTEM", SpamFilter)
  61.     return orgSetActiveSpecGroup(...)
  62. end
  63.  
  64. function f:UNIT_SPELLCAST_STOP(event, unit)
  65.     if unit ~= "player" then return end
  66.     f:UnregisterEvent"UNIT_SPELLCAST_SUCCEEDED"
  67.     f:UnregisterEvent"UNIT_SPELLCAST_STOP"
  68.     if event == "UNIT_SPELLCAST_SUCCEEDED" then return t:SetScript("OnUpdate", DelayWearSet) end
  69.     ChatFrame_RemoveMessageEventFilter("CHAT_MSG_SYSTEM", SpamFilter)
  70. end
  71. f.UNIT_SPELLCAST_SUCCEEDED = f.UNIT_SPELLCAST_STOP
  72.  
  73. local function GetTalentText(group)
  74.         local spec = group or GetActiveSpecGroup()
  75.         local id,name,desc,icon,back,role = GetSpecializationInfo(spec)
  76.         local finalIcon = icon or DEFAULT_ICON
  77.         return name, finalIcon
  78. end
  79.  
  80.  
  81. local hints = {
  82.     [BLOCK] =
  83. [[|cffffd100Hints [|cffffffffBlock|r|cffffd100]|r
  84. $Click|r to swap talent spec.
  85. $Right-Click|r to open talent frame.
  86. $Middle-Click|r to toggle hints.]],
  87.     [SPEC] =
  88. [[|cffffd100Hints|r
  89. $Click|r to set an alias.
  90. $Shift+Click|r to equip set.
  91. $Middle-Click|r to toggle hints.
  92. $MouseWheel|r to resize tooltip.]],
  93.     [GEAR] =
  94. [[|cffffd100Hints|r
  95. $Click|r to associate set.
  96. $Shift+Click|r to equip set.]],
  97. }
  98.  
  99. local function UpdateHints(cat)
  100.     if cat == BLOCK and (not f.onBlock or config.hideBlockHints) or cat ~= BLOCK and config.hideHints then return tip:Hide() end
  101.     local showRight = f:GetCenter() > UIParent:GetWidth()/config.scale/2 and "LEFT" or "RIGHT"
  102.     local showBelow = select(2, f:GetCenter()) > UIParent:GetHeight()/config.scale/2 and "TOP" or "BOTTOM"
  103.     tip:SetOwner(f, "ANCHOR_NONE")
  104.     tip:SetPoint(showBelow..showRight, f, (showBelow == "TOP" and "BOTTOM" or "TOP")..showRight )
  105.  
  106.     tip:AddLine(hints[cat]:gsub("%$","|cffff8020"), .2, 1, .2)
  107.     tip:Show()
  108. end
  109.  
  110.  
  111. local function Gear_OnClick(button, click)
  112.     if click == "LeftButton" and IsShiftKeyDown() then
  113.         return button.index > 0 and WearSet(button.index, true)
  114.     end
  115.     local spec = char[specIndex]
  116.     spec.set = button.index > 0 and button.gearName:GetText() or nil
  117.  
  118.     for i, b in next, gears do
  119.         local gearName = b.gearName:GetText()
  120.         if gearName == spec.set or not spec.set and gearName == NONE then b.check:Show() else b.check:Hide() end
  121.     end
  122.     f:PLAYER_TALENT_UPDATE()
  123. end
  124.  
  125. local function Spec_OnClick(button, click)
  126.     if click == "LeftButton" and IsShiftKeyDown() then
  127.         return WearSet(button.index)
  128.     elseif click == "MiddleButton" then
  129.         config.hideHints = not config.hideHints
  130.         return UpdateHints(SPEC)
  131.     end
  132.  
  133.     local data = char[button.index]
  134.     if StaticPopup_FindVisible( POPUP_SET_ALIAS, data ) then
  135.         return StaticPopup_Hide( POPUP_SET_ALIAS, data )
  136.     end
  137.  
  138.     local specName, specIcon = GetTalentText(button.index)
  139.     if not StaticPopupDialogs[POPUP_SET_ALIAS] then
  140.         local dialog = {
  141.             text = "Set an alias for %s (spec %i).\nLeave blank to remove alias.",
  142.             button3 = "Use gear set",
  143.             OnAccept = function(self, spec)
  144.                 local input = self.editBox:GetText()
  145.                 spec[popupData] = input ~= "" and input or nil
  146.                 spec.aliasIsSetName = false
  147.                 f:PLAYER_TALENT_UPDATE()
  148.             end,
  149.             OnShow = function(self, spec)
  150.                 self.editBox:SetText(spec[popupData]or"")
  151.                 self.editBox:SetFocus()
  152.             end,
  153.             OnAlt = function(self, spec)
  154.                 spec.aliasIsSetName = true
  155.                 f:PLAYER_TALENT_UPDATE()
  156.             end,
  157.             EditBoxOnEnterPressed = function(self, spec)
  158.                 local p = self:GetParent()
  159.                 StaticPopupDialogs[POPUP_SET_ALIAS].OnAccept(p, spec)
  160.                 p:Hide()
  161.             end,
  162.             EditBoxOnEscapePressed = function(self) self:GetParent():Hide() end,
  163.             multiple = true,
  164.         }
  165.         for k, v in next, StaticPopupDialogs.RENAME_GUILD do
  166.             if not dialog[k] then dialog[k] = v end
  167.         end
  168.         dialog.OnCancel, dialog.OnAlt = dialog.OnAlt, dialog.OnCancel
  169.         dialog.button3, dialog.button2 = dialog.button2, dialog.button3
  170.         StaticPopupDialogs[POPUP_SET_ALIAS] = dialog
  171.     end
  172.  
  173.     local talentPlate = (" |T%s:17:17:0:0:25:25:2:23:2:23|t %s"):format( specIcon, button.specName:GetText() )
  174.     popupData = specName
  175.     StaticPopup_Show( POPUP_SET_ALIAS, talentPlate, button.index, data)
  176. end
  177.  
  178.  
  179. local function Gear_OnEnter(b)
  180.     if not (b and b.index) then return end
  181.     highlight:SetAllPoints(b)
  182.     highlight:SetAlpha(1)
  183.     UpdateHints(GEAR)
  184. end
  185.  
  186. local function Gear_OnLeave(b)
  187.     highlight:ClearAllPoints()
  188.     tip:Hide()
  189.     if b then highlight:SetAlpha(0) end
  190.     if not (g:IsMouseOver() or f:IsMouseOver() or f.onBlock) then f:Hide() end
  191. end
  192.  
  193. local function Menu_OnEnter(b)
  194.     if not (b and b.index) then return end
  195.     highlight:SetAllPoints(b)
  196.     highlight:SetAlpha(1)
  197.     UpdateHints(SPEC)
  198.     ShowGears(b.index)
  199. end
  200.  
  201. local function Menu_OnLeave(b)
  202.     highlight:ClearAllPoints()
  203.     tip:Hide()
  204.     if b then highlight:SetAlpha(0) end
  205.     if not (g:IsMouseOver() or f:IsMouseOver() or f.onBlock) then f:Hide() end
  206. end
  207.  
  208. local function CreateTx(parent, ...)
  209.     local tx = parent:CreateTexture()
  210.     tx:SetSize( ICON_SIZE, ICON_SIZE )
  211.     tx:SetTexCoord( 2/25, 23/25, 2/25, 23/25 )
  212.     tx:SetPoint(...)
  213.     return tx
  214. end
  215.  
  216. local baseFont = GameFontNormal:GetFont()
  217.  
  218. local function CreateFS(parent, justify, ...)
  219.     local fs = parent:CreateFontString( nil, "OVERLAY", "SystemFont_Shadow_Med1" )
  220.     fs:SetJustifyH(justify)
  221.     fs:SetFont(baseFont, FONT_SIZE)
  222.     fs:SetPoint(...)
  223.     return fs
  224. end
  225.  
  226. gears = setmetatable( {}, { __index = function( table, key )
  227.     local button = CreateFrame( "Button", nil, g )
  228.     table[key] = button
  229.     button.index = key
  230.     button:RegisterForClicks"AnyUp"
  231.     button:SetScript( "OnEnter", Gear_OnEnter )
  232.     button:SetScript( "OnLeave", Gear_OnLeave )
  233.     button:SetScript( "OnClick", Gear_OnClick )
  234.  
  235.     button:SetHeight(BUTTON_HEIGHT)
  236.     button.check = CreateTx(button, "LEFT")
  237.     button.check:SetTexture"Interface\\Buttons\\UI-CheckBox-Check"
  238.     button.gearIcon = CreateTx(button, "LEFT", button.check, "RIGHT", TEXT_OFFSET, 0)
  239.     button.gearName = CreateFS(button, "LEFT", "LEFT", button.gearIcon, "RIGHT", TEXT_OFFSET, 0)
  240.  
  241.     local y = -GAP - key*BUTTON_HEIGHT
  242.     button:SetPoint("TOPLEFT", GAP, y)
  243.     button:SetPoint("TOPRIGHT", -GAP, y)
  244.     return button
  245. end } )
  246.  
  247. local function SetGearData(index, checked, gearIcon, gearName)
  248.     local button = gears[index]
  249.     if checked then button.check:Show() else button.check:Hide() end
  250.     button.gearIcon:SetTexture(gearIcon)
  251.     button.gearName:SetText(gearName)
  252.     return button, button.gearName:GetStringWidth()
  253. end
  254.  
  255. local buttons = setmetatable( {}, { __index = function( table, key )
  256.     local button = CreateFrame( "Button", nil, f )
  257.     table[key] = button
  258.     button.index = key
  259.     button:RegisterForClicks"AnyUp"
  260.     button:SetScript( "OnEnter", Menu_OnEnter )
  261.     button:SetScript( "OnLeave", Menu_OnLeave )
  262.     button:SetScript( "OnClick", Spec_OnClick)
  263.  
  264.     button:SetHeight(BUTTON_HEIGHT)
  265.     button.specIcon = CreateTx(button, "LEFT")
  266.     button.specName = CreateFS(button, "LEFT", "LEFT", ICON_SIZE + TEXT_OFFSET, 0)
  267.     button.aliasText= CreateFS(button, "LEFT", "LEFT", button.specName, "RIGHT", GAP, 0)
  268.     button.gearName = CreateFS(button, "LEFT", "RIGHT")
  269.     button.gearName:SetTextColor(1,.82,0)
  270.     button.gearIcon = CreateTx(button, "RIGHT", button.gearName, "LEFT", -TEXT_OFFSET, 0)
  271.  
  272.     local y = -GAP - (key-1)*BUTTON_HEIGHT
  273.     button:SetPoint("TOPLEFT", GAP, y)
  274.     button:SetPoint("TOPRIGHT", -GAP, y)
  275.     return button
  276. end } )
  277.  
  278.  
  279. local function SetButtonData(index, active, specIcon, specName, alias, gearIcon, gearName)
  280.     local button = buttons[index]
  281.     button.specIcon:SetTexture(specIcon)
  282.     button.specName:SetFormattedText("%s%s|r", active and "|cff19ff19" or "|cffff1919", specName)
  283.     if alias then button.aliasText:SetFormattedText("|cffffffff%s",alias) else button.aliasText:SetText"" end
  284.     button.gearIcon:SetTexture(gearIcon and "Interface\\Icons\\"..gearIcon or "")
  285.     button.gearName:SetText(gearIcon and gearName or "")
  286.     return  button,
  287.         button.specName:GetStringWidth(),
  288.         alias and button.aliasText:GetStringWidth() or 0,
  289.         gearIcon and button.gearName:GetStringWidth() or 0
  290. end
  291.  
  292.  
  293. local function UpdateTablet()
  294.     f:SetScale(config.scale)
  295.     local talentGroup = GetActiveSpecGroup()
  296.     local specC, aliasC, gearC = 0, 0, 0
  297.     local hasAlias, hasGear
  298.     local nbSpec = GetNumSpecGroups()
  299.     for i = 1, nbSpec do
  300.                 local id,specName,description,specIcon,background,role = GetSpecializationInfo(i)
  301.         local group = char[i]
  302.                 local alias,gearName,gearIcon
  303.                 if group then
  304.                   alias, gearName, gearIcon = group[specName], group.set
  305.                 end
  306.  
  307.         if gearName then
  308.             hasGear = true
  309.             gearIcon = GetEquipmentSetInfoByName(gearName or "")
  310.         end
  311.  
  312.         if alias then
  313.             hasAlias = true
  314.         end
  315.  
  316.         local button, specW, aliasW, gearW =
  317.             SetButtonData(i, i==talentGroup, specIcon, specName or "unknown", alias, gearIcon, gearName)
  318.  
  319.         if specW > specC then specC = specW end
  320.         if aliasW > aliasC then aliasC = aliasW end
  321.         if gearW > gearC then gearC = gearW end
  322.     end
  323.     local maxWidth = ICON_SIZE + TEXT_OFFSET + specC + (hasAlias and GAP + aliasC or 0) + (hasGear and GAP + ICON_SIZE + TEXT_OFFSET + gearC or 0)
  324.     for index, button in next, buttons do
  325.         if hasAlias then button.specName:SetWidth(specC) end
  326.         if hasGear then button.gearName:SetWidth(gearC) end
  327.     end
  328.     f:SetSize( GAP + maxWidth + GAP, GAP + nbSpec * BUTTON_HEIGHT + GAP )
  329.     if not (f.onBlock or f:IsMouseOver() or g:IsMouseOver()) then f:Hide() end
  330. end
  331.  
  332.  
  333. ShowGears = function(_specIndex)
  334.     if not _specIndex then return end
  335.     specIndex = _specIndex
  336.     local nbGear = GetNumEquipmentSets()
  337.     if not nbGear or nbGear == 0 then return end
  338.     g:Show()
  339.     if char[specIndex] then
  340.     local currGear = char[specIndex].set
  341.     end
  342.     local button, maxWidth = SetGearData(0, not currGear, "", NONE)
  343.     for i=1, nbGear do
  344.         local gearName, gearIcon = GetEquipmentSetInfo(i)
  345.         local button, width = SetGearData(i, currGear == gearName, gearIcon, gearName)
  346.         if width > maxWidth then maxWidth = width end
  347.     end
  348.     g:SetSize( (GAP + ICON_SIZE + TEXT_OFFSET)*2 + maxWidth, GAP + (nbGear+1) * BUTTON_HEIGHT + GAP )
  349.     for k, v in next, gears do if k>nbGear then v:Hide() else v:Show() end end
  350.  
  351.     g:ClearAllPoints()
  352.     local horiz = f:GetCenter() > UIParent:GetWidth()/config.scale/2 and "RIGHT" or "LEFT"
  353.     local verti = (f:GetPoint()=="TOP"and f:GetTop() or f:GetBottom()) > UIParent:GetHeight()*.5 and "TOP" or "BOTTOM"
  354.     g:SetPoint(verti..horiz, f, verti..(horiz=="LEFT"and"RIGHT"or"LEFT"), 0, (1-specIndex)*BUTTON_HEIGHT )
  355. end
  356.  
  357.  
  358. local tiptacBKG = { tile = false, insets = {} }
  359. -- Setup Gradient Tip (from TipTac)
  360. local function SetupGradientTip(tip,cfg)
  361.     local g = tip.ttGradient;
  362.     if not cfg.gradientTip then
  363.         return g and g:Hide()
  364.     elseif not g then
  365.         g = tip:CreateTexture()
  366.         g:SetTexture(1,1,1,1)
  367.         tip.ttGradient = g
  368.     end
  369.     g:SetGradientAlpha("VERTICAL",0,0,0,0,unpack(cfg.gradientColor))
  370.     g:SetPoint("TOPLEFT",cfg.backdropInsets,cfg.backdropInsets * -1)
  371.     g:SetPoint("BOTTOMRIGHT",tip,"TOPRIGHT",cfg.backdropInsets * -1,-36)
  372.     g:Show()
  373. end
  374.  
  375.  
  376. local function SetTabletBG(frame, cfg)
  377.     if TipTac then
  378.         frame:SetBackdrop(tiptacBKG)
  379.         frame:SetBackdropColor(unpack(cfg.tipColor))
  380.         frame:SetBackdropBorderColor(unpack(cfg.tipBorderColor))
  381.         SetupGradientTip(frame,cfg)
  382.     elseif Skinner then
  383.         Skinner:applySkin(frame)
  384.     else
  385.         frame:SetBackdrop(backdrop)
  386.         if frame.ttGradient then frame.ttGradient:Hide() end
  387.         frame:SetBackdropColor(.1, .1, .1, .85)
  388.         frame:SetBackdropBorderColor(.3, .3, .3, .9)
  389.     end
  390. end
  391.  
  392. local function AnchorTablet(frame)
  393.     f:Show()
  394.     f.isTop, f.onBlock = select(2, frame:GetCenter()) > UIParent:GetHeight() / 2, true
  395.     f:ClearAllPoints()
  396.     f:SetPoint(f.isTop and "TOP" or "BOTTOM", frame, f.isTop and "BOTTOM" or "TOP")
  397.     local cfg
  398.     if TipTac then
  399.         cfg = TipTac_Config
  400.         tiptacBKG.bgFile = cfg.tipBackdropBG
  401.         tiptacBKG.edgeFile = cfg.tipBackdropEdge
  402.         tiptacBKG.edgeSize = cfg.backdropEdgeSize
  403.         tiptacBKG.insets.left = cfg.backdropInsets
  404.         tiptacBKG.insets.right = cfg.backdropInsets
  405.         tiptacBKG.insets.top = cfg.backdropInsets
  406.         tiptacBKG.insets.bottom = cfg.backdropInsets
  407.     end
  408.     SetTabletBG(f, cfg)
  409.     SetTabletBG(g, cfg)
  410.     UpdateHints(BLOCK)
  411.     UpdateTablet()
  412. end
  413.  
  414.  
  415. block = LibStub("LibDataBroker-1.1"):NewDataObject("|cFFFFB366Ara|r SpecSwitcher", {
  416.     type = "data source",
  417.     icon = DEFAULT_ICON,
  418.     iconCoords = { .08, .92, .08, .92 },
  419.     text = "00/00/00",
  420.  
  421.     OnEnter = AnchorTablet,
  422.  
  423.     OnLeave = function()
  424.         f.onBlock = nil
  425.         tip:Hide()
  426.         if not f:IsMouseOver() then
  427.             f:Hide()
  428.         end
  429.     end,
  430.  
  431.     OnClick = function(self, button)
  432.         if button == "RightButton" then
  433.             ToggleTalentFrame()
  434.         elseif button == "MiddleButton" then
  435.             config.hideBlockHints = not config.hideBlockHints
  436.             UpdateHints(BLOCK)
  437.         else
  438.             SetActiveSpecGroup( 3 - GetActiveSpecGroup() )
  439.         end
  440.     end
  441. })
  442.  
  443.  
  444. function f:PLAYER_TALENT_UPDATE()
  445.     local curr = char[GetActiveSpecGroup()]
  446.     local spec, icon = GetTalentText()
  447.     block.icon = curr.aliasIsSetName and GetEquipmentSetInfoByName(curr.set or "") and ("Interface\\Icons\\"..GetEquipmentSetInfoByName(curr.set or "")) or icon
  448.     block.text = curr.aliasIsSetName and curr.set or curr[spec] or spec
  449.     if f:IsVisible() then UpdateTablet() end
  450. end
  451.  
  452. function f:ACTIVE_TALENT_GROUP_CHANGED()
  453.     t:SetScript("OnUpdate", DelayWearSet)
  454. end
  455.  
  456. function f:ADDON_LOADED(event, addon)
  457.     if addon ~= "Ara_Broker_SpecSwitcher" then return end
  458.     AraSpecSwitcherDBPC = AraSpecSwitcherDBPC or { {}, {} }
  459.     char = AraSpecSwitcherDBPC
  460.     AraSpecSwitcherDB = AraSpecSwitcherDB or { scale=1 }
  461.     config = AraSpecSwitcherDB
  462.  
  463.     f:SetBackdrop(backdrop)
  464.     f:SetFrameStrata"TOOLTIP"
  465.     f:SetClampedToScreen(true)
  466.  
  467.     f:SetScript( "OnEnter", Menu_OnEnter )
  468.     f:SetScript( "OnLeave", Menu_OnLeave )
  469.     f:SetScript( "OnHide", function() g:Hide() end )
  470.     f:SetScript( "OnMouseWheel", function(self, delta)
  471.         config.scale = config.scale - delta * 0.05
  472.         UpdateTablet()
  473.     end)
  474.  
  475.     g:SetBackdrop(backdrop)
  476.     g:SetFrameStrata"TOOLTIP"
  477.     g:SetFrameLevel(0)
  478.     g:SetScript( "OnEnter", Gear_OnEnter )
  479.     g:SetScript( "OnLeave", Gear_OnLeave)
  480.  
  481.     t:SetScript( "OnUpdate", function() t:SetScript("OnUpdate", nil) f:PLAYER_TALENT_UPDATE() end )
  482.     f:RegisterEvent"PLAYER_TALENT_UPDATE"
  483.     f:RegisterEvent"ACTIVE_TALENT_GROUP_CHANGED"
  484.     f:RegisterEvent"PLAYER_ENTERING_WORLD"
  485.     f:RegisterEvent"PLAYER_LEAVING_WORLD"
  486.     tip = GameTooltip
  487.  
  488.     f:UnregisterEvent(event)
  489.     f.ADDON_LOADED = nil
  490. end
  491.  
  492. function f:PLAYER_LEAVING_WORLD() f:UnregisterEvent"PLAYER_TALENT_UPDATE" end
  493. function f:PLAYER_ENTERING_WORLD() f:RegisterEvent"PLAYER_TALENT_UPDATE" end
  494.  
  495. f:SetScript( "OnEvent", function(self, event, ...) return self[event](self, event, ...) end )
  496. f:RegisterEvent"ADDON_LOADED"