Compare with Previous | Blame | View Log
local _, class = UnitClass("player") if class ~= "DEATHKNIGHT" then return end local plugin = mRunes:NewModule("Runes", "AceEvent-3.0") plugin.mRunesPlugin = true local AceGUI = LibStub("AceGUI-3.0") local LSM = LibStub("LibSharedMedia-3.0") local BD = LibStub("LibBackdrop-1.0") local FA = LibStub("LibFrameAnchorRegistry-1.0") local UIF local db -- Rune Types local RUNETYPE_BLOOD = 1; local RUNETYPE_UNHOLY = 2; local RUNETYPE_FROST = 3; local RUNETYPE_DEATH = 4; local RuneNames = { "Blood 1", "Blood 2", "Unholy 1", "Unholy 2", "Frost 1", "Frost 2", } local NrRunes = 6 local InCombat = false local RuneOnCD = true local ConfigMode = false local Update local backdrop = { tile = true, tileSize = 16, insets = {}, } plugin.DisplayName = "Runes" plugin.DisplayIcon = [[Interface\Icons\INV_Glyph_MajorDeathKnight]] plugin.DisplayOrder = 1 function plugin:OnInitialize() local defaults = { profile = { Enabled = true, HideBlizz = true, Attached = { f = "mRunesCenter", x = 0, y = 0, Point = "CENTER", RelPoint = "CENTER", }, Rune = { ["**"] = { Order = 1, } }, InternalSpacing = 2, FrameColor = {0, 0, 0, 0.5}, RuneSpacing = 1.12, Width = 330, Height = 22, RuneTexture = "HalD", RuneAlphaInCombat = 1, RuneAlphaOutCombat = 0.2, RuneAlphaActive = 0.5, RuneCooldownInset = 2, RuneCooldownAlwaysShow = false, RuneCooldownShow = true, RuneCooldownHorAlign = "LEFT", Font = "accid", FontSize = 16, FontFlags = "OUTLINE", FontColor = {1, 1, 1, 1}, BorderTexture = "None", BorderWidth = 5, BorderInsets = 0, BorderPadding = 2, BorderColor = {0, 0, 0, 1}, RuneColors = { [RUNETYPE_BLOOD] = {1.0, 0.0, 0.0, 1}, [RUNETYPE_UNHOLY] ={0.0, 0.8, 0.0, 1}, [RUNETYPE_FROST] = {0.0, 1.0, 1.0, 1}, [RUNETYPE_DEATH] = {0.8, 0.1, 1.0, 1}, } } } self.db = mRunes.db:RegisterNamespace(self:GetName(), defaults) self.db.RegisterCallback(self, "OnProfileChanged", "ProfileChanged") self.db.RegisterCallback(self, "OnProfileCopied", "ProfileChanged") self.db.RegisterCallback(self, "OnProfileReset", "ProfileChanged") db = self.db.profile self:SetEnabledState(db.Enabled) UIF = LibStub("LibGUIFactory-1.0"):GetFactory("mRunes") if not mRuneFrame then self:CreateFuneFrame() end end function plugin:OnEnable() self:ApplySettings() self.Frame:Show() self:RegisterEvent("RUNE_TYPE_UPDATE", "RuneTypeUpdate") self:RegisterEvent("RUNE_POWER_UPDATE", "RuneStateChange") self:RegisterEvent("PLAYER_ENTERING_WORLD", "EnterWorld") mRunes.RegisterCallback(self, "EnteringCombat") mRunes.RegisterCallback(self, "LeavingCombat") mRunes.RegisterCallback(self, "ToggleConfigMode") FA.RegisterCallback(self, "FrameRegistered", "ApplySettings") if UnitAffectingCombat("player") then self:EnteringCombat("EnteringCombat") else self:LeavingCombat("LeavingCombat") end end function plugin:OnDisable() self.Frame:Hide() end function plugin:ProfileChanged() db = self.db.profile self:ApplySettings() end -- ~~~~~~~~~~~~~~~~~~~~ -- Callbacks ~~~~~~~~~~ -- ~~~~~~~~~~~~~~~~~~~~ function plugin:EnteringCombat(event) mRunes.FadeIn(self.Frame, false, db.RuneAlphaInCombat) InCombat = true RuneOnCD = false end function plugin:LeavingCombat(event) mRunes.FadeOut(self.Frame, db.RuneAlphaOutCombat) InCombat = false end function plugin:EnterWorld(...) if self.Runes then for i, rune in pairs(self.Runes) do rune.Update = true self:RuneTypeUpdate("RUNE_TYPE_UPDATE", i) end end end function plugin:ToggleConfigMode(event, enable) ConfigMode = enable if enable then self.Frame.ConfigText:Show() mRunes.FadeIn(self.Frame, false, 1) else self.Frame.ConfigText:Hide() if InCombat then mRunes.FadeIn(self.Frame, true, db.RuneAlphaInCombat) elseif RuneOnCD then mRunes.FadeIn(self.Frame, true, db.RuneAlphaActive) else mRunes.FadeIn(self.Frame, true, db.RuneAlphaOutCombat) end Update(self.Frame, 0) end end -- ~~~~~~~~~~~~~~~~~~~~ -- Events ~~~~~~~~~~~~~ -- ~~~~~~~~~~~~~~~~~~~~ -- Rune type changed (unholy runes) function plugin:RuneTypeUpdate(event, runeID) self.Runes[runeID]:SetStatusBarColor(unpack(db.RuneColors[GetRuneType(runeID)])) end -- Rune Ready/Not Ready event function plugin:RuneStateChange(event, runeID, ready) if runeID == 8 or runeID == 7 then return end if ready then self.Runes[runeID]:SetValue(1) self.Runes[runeID].Update = nil self.Runes[runeID].CDText:SetText(("%.1f"):format(0)) if not db.RuneCooldownAlwaysShow then self.Runes[runeID].CDText:Hide() end else self.Runes[runeID].Update = not ready mRunes:RegisterOnUpdate(self, Update) end end Update = function(self, elapsed) self = plugin local haveCD = false local oldHaveCD = RuneOnCD for i, rune in pairs(self.Runes) do if rune.Update then local StartTime, CooldownTime, Ready = GetRuneCooldown(i) local diff = GetTime() - StartTime local remaining = CooldownTime - diff local t = diff / CooldownTime rune:SetValue(t) if remaining < 0 and not db.RuneCooldownAlwaysShow then rune.CDText:Hide() elseif db.RuneCooldownShow then rune.CDText:Show() rune.CDText:SetText(("%.1f"):format(Ready and 0 or (remaining < 0 and 0 or remaining))) end if Ready then rune.Update = nil else haveCD = true end end end if not InCombat then RuneOnCD = haveCD if RuneOnCD and (RuneOnCD ~= oldHaveCD) then local currAlpha = self.Frame:GetAlpha() if currAlpha < db.RuneAlphaActive then mRunes.FadeIn(self.Frame, false, db.RuneAlphaActive) else mRunes.FadeOut(self.Frame, db.RuneAlphaActive) end elseif not RuneOnCD and (RuneOnCD ~= oldHaveCD) then mRunes:UnregisterOnUpdate(self, Update) mRunes.FadeOut(self.Frame, db.RuneAlphaOutCombat) end end end -- ~~~~~~~~~~~~~~~~~~~~ -- Settings ~~~~~~~~~~~ -- ~~~~~~~~~~~~~~~~~~~~ local OrderedRunes = {} local function SortRunes(a, b) local adb = db.Rune[a.id] local bdb = db.Rune[b.id] if adb.Order == bdb.Order then return a.id < b.id else return adb.Order < bdb.Order end end function plugin:ApplySettings() if self.Frame then local anchor = FA:GetAnchor(db.Attached.f) self.Frame:ClearAllPoints() self.Frame:SetPoint(db.Attached.Point, anchor, db.Attached.RelPoint, db.Attached.x, db.Attached.y) self.Frame:SetWidth(db.Width) self.Frame:SetHeight(db.Height) backdrop.insets.left = db.BorderInsets backdrop.insets.right = db.BorderInsets backdrop.insets.top = db.BorderInsets backdrop.insets.bottom = db.BorderInsets backdrop.edgeFile = LSM:Fetch("border", db.BorderTexture) backdrop.edgeSize = db.BorderWidth backdrop.bgFile = LSM:Fetch("background", "Solid") self.Frame:SetBackdrop(backdrop) self.Frame:SetBackdropColor(unpack(db.FrameColor)) self.Frame:SetBackdropBorderColor(unpack(db.BorderColor)) wipe(OrderedRunes) for i, rune in pairs(self.Runes) do rune:ClearAllPoints() tinsert(OrderedRunes, rune) end table.sort(OrderedRunes, SortRunes) local RuneWidth = db.Width / 6 for i, rune in pairs(OrderedRunes) do rune:ClearAllPoints() if i == 1 then rune:SetPoint("LEFT", db.BorderPadding + db.RuneSpacing / 2, 0) rune:SetWidth(RuneWidth - db.RuneSpacing - db.BorderPadding / 3) else rune:SetPoint("LEFT", OrderedRunes[i-1], "RIGHT", db.RuneSpacing, 0) rune:SetWidth(RuneWidth - db.RuneSpacing - db.BorderPadding / 3) end rune:SetHeight(db.Height - db.BorderPadding * 2) rune:SetStatusBarTexture(LSM:Fetch("statusbar", db.RuneTexture)) rune:GetStatusBarTexture():SetHorizTile(false) rune:SetStatusBarColor(unpack(db.RuneColors[GetRuneType(rune.id)])) local leftinset, rightinset = 0, 0 if db.RuneCooldownHorAlign == "LEFT" then leftinset = db.RuneCooldownInset elseif db.RuneCooldownHorAlign == "RIGHT" then rightinset = -db.RuneCooldownInset end rune.CDText:ClearAllPoints() rune.CDText:SetPoint("TOPLEFT", leftinset, 0) rune.CDText:SetPoint("BOTTOMRIGHT", rightinset, 0) rune.CDText:SetFont(LSM:Fetch("font", db.Font), db.FontSize, db.FontFlags) rune.CDText:SetTextColor(unpack(db.FontColor)) rune.CDText:SetJustifyH(db.RuneCooldownHorAlign) rune.CDText:SetJustifyV("MIDDLE") if db.RuneCooldownAlwaysShow and db.RuneCooldownShow then rune.CDText:Show() elseif not db.RuneCooldownShow then rune.CDText:Hide() end rune.Update = true end if not ConfigMode then self.Frame:SetAlpha(InCombat and db.RuneAlphaInCombat or (RuneOnCD and db.RuneAlphaActive or db.RuneAlphaOutCombat)) end if self.Frame:GetAlpha() > 0 then self.Frame:Show() else self.Frame:Hide() end end if db.HideBlizz then RuneFrame:Hide() else RuneFrame:Show() end if not db.Enabled and self:IsEnabled() then self:Disable() elseif db.Enabled and not self:IsEnabled() then self:Enable() end FA:FrameModified("mRunes", "Runes") end -- ~~~~~~~~~~~~~~~~~~~~ -- Create Frames ~~~~~~ -- ~~~~~~~~~~~~~~~~~~~~ function plugin:CreateFuneFrame() local frame = CreateFrame("Frame", "mRunesRuneFrame", UIParent) --BD:EnhanceBackdrop(frame) FA:RegisterAnchor("mRunes", "Runes", "mRunesRunes", frame) self.Runes = {} for i = 1, NrRunes do self.Runes[i] = self:CreateRune(i, frame) end local configFrame = CreateFrame("Frame", nil, frame) configFrame:SetAllPoints() configFrame:SetFrameLevel(self.Runes[1]:GetFrameLevel() + 10) local configText = configFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") configText:SetAllPoints() configText:SetText("Runes") configText:Hide() frame.ConfigText = configText frame:Show() self.Frame = frame end function plugin:CreateRune(runeID, parent) local rune = CreateFrame("StatusBar", "mRunesRune"..runeID, parent) rune:SetMinMaxValues(0,1) rune:SetValue(1) local runeCooldown = rune:CreateFontString(nil, nil, "GameFontNormal") rune.CDText = runeCooldown rune.id = runeID rune.Update = true rune:Show() return rune end -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Options ~~~~~~~~~~~~~~~~~~~~ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ function plugin:GetOptions() return plugin.GetGUI end do local function Callback() plugin:ApplySettings() end local FontFlagList = { ["NONE"] = "None", ["OUTLINE"] = "Outline", ["THICKOUTLINE"] = "Thick Outline", ["MONOCHROME"] = "Monochrome", } local HorizontalAlignList = { ["LEFT"] = "Left", ["CENTER"] = "Center", ["RIGHT"] = "Right", } local TabSelected local LastSelectedTab function plugin:GetGUI() self = plugin local returnGroup = AceGUI:Create("SimpleGroup") returnGroup:SetFullWidth(true) returnGroup:SetLayout("Flow") returnGroup:AddChild(UIF:NewLine(10)) returnGroup:AddChild(UIF:CheckBox("Enabled", db, "Enabled", Callback, nil, 0.2)) returnGroup:AddChild(UIF:CheckBox("Hide default rune frame", db, "HideBlizz", Callback, "Hides the blizzard build in rune frame", 0.8)) local tgroup = UIF:TabGroup() returnGroup:AddChild(tgroup) tgroup:SetTabs({ {text = "Position & Size", value = "PaS"}, {text = "Colors & Alpha", value = "CaA"}, {text = "Font", value = "Font"}, {text = "Ordering", value = "Ordering"}, }) tgroup:SetCallback("OnGroupSelected", TabSelected) tgroup:SelectTab(LastSelectedTab or "PaS") return returnGroup end TabSelected = function(self, event, tab) self:ReleaseChildren() LastSelectedTab = tab if tab == "PaS" then self:AddChild(UIF:Text1("Select a frame to attach to. You can then adjust the relative positioning to that frame via X and Y as well as the anchor points.")) local t = AceGUI:Create("FrameAnchorDropdown") t:SetRelativeWidth(0.5) t:SetList(FA:GetAnchors(plugin.Frame)) t:SetLabel("Anchor") t:SetValue(db.Attached.f) t:SetCallback("OnValueChanged", function(self, event, cat, name, frame) db.Attached.f = cat Callback() end) self:AddChild(t) self:AddChild(UIF:Spacer(10)) self:AddChild(UIF:Text2("Avoid creating circle references!", nil, nil, nil, nil, nil, 0.4)) self:AddChild(UIF:NewLine()) self:AddChild(UIF:Dropdown("Point", db.Attached, "Point", Callback, mRunes:GetAnchorList(), 0.5, true)) self:AddChild(UIF:Dropdown("Relative Point", db.Attached, "RelPoint", Callback, mRunes:GetAnchorList(), 0.5, true)) self:AddChild(UIF:Slider("X", db.Attached, "x", -300, 300, 0.1, Callback, 0.5)) self:AddChild(UIF:Slider("Y", db.Attached, "y", -300, 300, 0.1, Callback, 0.5)) self:AddChild(UIF:Slider("Width", db, "Width", 5, 1000, 0.1, Callback, 0.5)) self:AddChild(UIF:Slider("Height", db, "Height", 5, 200, 0.1, Callback, 0.5)) self:AddChild(UIF:Slider("Space between Runes", db, "RuneSpacing", 0, 100, 0.1, Callback, 0.5)) elseif tab == "CaA" then local group = UIF:InlineGroup2("Rune Colors") self:AddChild(group) group:AddChild(UIF:ColorSelect("Blood", db.RuneColors, 1, Callback, true, 0.25)) group:AddChild(UIF:ColorSelect("Unholy", db.RuneColors, 2, Callback, true, 0.25)) group:AddChild(UIF:ColorSelect("Frost", db.RuneColors, 3, Callback, true, 0.25)) group:AddChild(UIF:ColorSelect("Death", db.RuneColors, 4, Callback, true, 0.25)) group = UIF:InlineGroup2("Alpha") self:AddChild(group) group:AddChild(UIF:Slider("Combat", db, "RuneAlphaInCombat", 0, 1, 0.01, Callback, 0.33)) group:AddChild(UIF:Slider("OOC (Runes on cooldown)", db, "RuneAlphaActive", 0, 1, 0.01, Callback, 0.33)) group:AddChild(UIF:Slider("OOC (No runes on cooldown)", db, "RuneAlphaOutCombat", 0, 1, 0.01, Callback, 0.33)) group = UIF:InlineGroup2("Border and Background") self:AddChild(group) group:AddChild(UIF:LSMDropdown("border", "Border texture", db, "BorderTexture", Callback, 0.5)) group:AddChild(UIF:LSMDropdown("statusbar", "Bar Texture", db, "RuneTexture", Callback, 0.5)) group:AddChild(UIF:ColorSelect("Background Color", db, "FrameColor", Callback, true, 0.5)) group:AddChild(UIF:ColorSelect("Border color", db, "BorderColor", Callback, true, 0.5)) group:AddChild(UIF:Slider("Border Width", db, "BorderWidth", 0, 50, 1, Callback, 0.33)) group:AddChild(UIF:Slider("Border Padding", db, "BorderPadding", 0, 100, 0.1, Callback, 0.33)) group:AddChild(UIF:Slider("Border Insets", db, "BorderInsets", 0, 50, 1, Callback, 0.33)) elseif tab == "Font" then self:AddChild(UIF:Text1("These settings are for the cooldown text on each rune")) self:AddChild(UIF:CheckBox("Show", db, "RuneCooldownShow", Callback, "Toggle if the cooldown text should shown", 0.5)) self:AddChild(UIF:CheckBox("Always Show", db, "RuneCooldownAlwaysShow", Callback, "Toggle if the cooldown text should always be shown, even when it is not on cooldown", 0.5)) self:AddChild(UIF:LSMDropdown("font", "Font", db, "Font", Callback, 0.5)) self:AddChild(UIF:Dropdown("Font Flags", db, "FontFlags", Callback, FontFlagList, 0.5)) self:AddChild(UIF:ColorSelect("Color", db, "FontColor", Callback, false, 0.2)) self:AddChild(UIF:Slider("Size", db, "FontSize", 2, 50, 1, Callback, 0.4)) self:AddChild(UIF:Slider("Margin", db, "RuneCooldownInset", 0, 50, 0.1, Callback, 0.4)) self:AddChild(UIF:Dropdown("Horizontal Alignment", db, "RuneCooldownHorAlign", Callback, HorizontalAlignList, 0.5)) elseif tab == "Ordering" then self:AddChild(UIF:Text1("Runes are sorted first by the value you set here, second by their rune index. So by setting setting both unholy runes to the same sort order they will still be sorted so that unholy rune 1 comes before unholy rune 2.")) for i, rune in pairs(plugin.Runes) do self:AddChild(UIF:Slider(RuneNames[rune.id], db.Rune[rune.id], "Order", 1, 6, 1, Callback, 0.5)) end end self:DoLayout() self.parent:DoLayout() end end