diff --git a/PerfectRaid.lua b/PerfectRaid.lua index 28d6a2f..3281401 100644 --- a/PerfectRaid.lua +++ b/PerfectRaid.lua @@ -18,7 +18,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHsqsANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT @@ -42,6 +42,12 @@ function PerfectRaid:Initialize() profile = { headers = {}, buffs = {}, + positions = {}, + simple = { + voffset = 15, + titles = true, + }, + mode = "double", }, } @@ -67,7 +73,101 @@ function PerfectRaid:Enable() self:RegisterEvent("UNIT_MAXFOCUS", "UNIT_MAXMANA") self:RegisterEvent("UNIT_AURA") - self:CreateRaidHeaders() + local mode = self.db.profile.mode + if mode == "singleclass" then + self:CreateSingleColumn() + elseif mode == "singlegroup" then + self:CreateSingleColumn("group") + elseif mode == "double" then + self:CreateDoubleColumn() + elseif mode == "advanced" then + self:CreateRaidHeaders() + end +end + +function PerfectRaid:CreateSingleColumn(sort) + self.column = self.column or {} + local column = self.column + + local classes = {"Warrior","Priest","Druid","Shaman","Paladin","Rogue","Mage","Warlock","Hunter"} + local voffset = self.db.profile.simple.voffset * -1 + local titles = self.db.profile.simple.titles + + if sort == "group" then + classes = {"Group 1","Group 2","Group 3","Group 4","Group 5","Group 6","Group 7","Group8"} + end + + for i=1,#classes do + local filter + if sort == "group" then + filter = string.sub(classes[i], -1, -1) + else + filter = string.upper(classes[i]) + end + local title = titles and classes[i] or nil + + if not column[i] then + column[i] = self:CreateRaidFrame("PRColumn"..i, title, filter, nil, column[1]) + column[i]:Show() + else + self:ChangeRaidFrame("PRColumn"..i, title, filter, nil, column[1]) + column[i]:Show() + column[i]:ClearAllPoints() + end + end + + self:RestorePosition("PRColumn1") + + for i=2,#classes do + self.column[i].locked = true + self.column[i]:SetPoint("TOP", self.column[i-1], "BOTTOM", 0, voffset) + end + + if GetNumRaidMembers() <= 0 then + for i,frame in ipairs(column) do + frame:Hide() + end + end +end + +function PerfectRaid:CreateDoubleColumn() + self.column = self.column or {} + local column = self.column + + local voffset = self.db.profile.simple.voffset * -1 + local titles = self.db.profile.simple.titles + local classes = {"Group 1","Group 2","Group 3","Group 4","Group 5","Group 6","Group 7","Group8"} + + for i=1,#classes do + local filter = string.sub(classes[i], -1, -1) + local title = titles and classes[i] or nil + + if not column[i] then + column[i] = self:CreateRaidFrame("PRColumn"..i, title, filter, nil, column[1]) + column[i]:Show() + else + self:ChangeRaidFrame("PRColumn"..i, title, filter, nil, column[1]) + column[i]:Show() + column[i]:ClearAllPoints() + end + end + + self:RestorePosition("PRColumn1") + + for i=2,4 do + column[i].locked = true + column[i]:SetPoint("TOP", column[i-1], "BOTTOM", 0, voffset) + end + column[5]:SetPoint("TOPLEFT", column[1], "TOPRIGHT", 50, 0) + for i=6,8 do + column[i].locked = true + column[i]:SetPoint("TOP", column[i-1], "BOTTOM", 0, voffset) + end + if GetNumRaidMembers() <= 0 then + for i,frame in ipairs(column) do + frame:Hide() + end + end end function PerfectRaid:CreateRaidHeaders() @@ -103,14 +203,23 @@ function PerfectRaid:SavePosition(name) x,y = x*s,y*s - local opt = self.db.profile.headers[tonumber(string.sub(name, -1, -1))] + local opt = self.db.profile.positions[name] + if not opt then + self.db.profile.positions[name] = {} + opt = self.db.profile.positions[name] + end opt.PosX = x opt.PosY = y end function PerfectRaid:RestorePosition(name) local f = getglobal(name) - local opt = self.db.profile.headers[tonumber(string.sub(name, -1, -1))] + local opt = self.db.profile.positions[name] + if not opt then + self.db.profile.positions[name] = {} + opt = self.db.profile.positions[name] + end + local x = opt.PosX local y = opt.PosY @@ -128,30 +237,35 @@ function PerfectRaid:RestorePosition(name) f:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", x, y) end -function PerfectRaid:ChangeRaidFrame(name, title, filter, strict) +function PerfectRaid:ChangeRaidFrame(name, title, filter, strict, dragparent) local frame = getglobal(name) frame.title:SetText(title or "") frame:SetAttribute("groupFilter", filter) frame:SetAttribute("strictFiltering", strict) + frame:SetAttribute("dragparent", dragparent or frame) frame:Hide() frame:Show() end local function OnDragStart(frame) - local parent = frame:GetParent() - parent:StartMoving() + local parent = frame:GetAttribute("dragparent") + if not parent.locked then + parent:StartMoving() + PerfectRaid.moving = parent + end end local function OnDragStop(frame) - local parent = frame:GetParent() + local parent = frame:GetAttribute("dragparent") parent:StopMovingOrSizing() PerfectRaid:SavePosition(parent:GetName()) + PerfectRaid.moving = nil end -function PerfectRaid:CreateRaidFrame(name, title, filter, strict) +function PerfectRaid:CreateRaidFrame(name, title, filter, strict, dragparent) local frame = CreateFrame("Frame", name, UIParent, "SecureRaidGroupHeaderTemplate") frame.title = frame:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") - frame.title:SetPoint("BOTTOM", frame, "TOP", 0, 5) + frame.title:SetPoint("BOTTOM", frame, "TOP", 0, 3) frame.title:SetText(title or "") frame:SetAttribute("point", "TOP") frame:SetAttribute("groupFilter", filter) @@ -160,6 +274,7 @@ function PerfectRaid:CreateRaidFrame(name, title, filter, strict) frame:SetAttribute("yOffset", -2) frame:SetAttribute("sortMethod", "NAME") frame:SetAttribute("strictFiltering", strict) + frame:SetAttribute("dragparent", dragparent or frame) frame.initialConfigFunction = PerfectRaid.ConfigureButton frame:SetMovable(true) frame:SetClampedToScreen(true) @@ -201,9 +316,13 @@ function PerfectRaid:UNIT_HEALTH(event, unit) if not frames[unit] then return end local health = UnitHealth(unit) local max = UnitHealthMax(unit) + -- Hack to fix api issue + if max < health then max = health end local deficit = max - health local perc = UnitHealthMax(unit) / health local class = select(2, UnitClass(unit)) + local perc = (health/max) or 0 + if deficit > 999 then deficit = string.format("%.1fk", deficit / 1000) elseif deficit == 0 then @@ -235,7 +354,8 @@ function PerfectRaid:UNIT_HEALTH(event, unit) if unavail[unit] then for frame in pairs(frames[unit]) do local color = frame.classcolor - frame.healthbar:SetStatusBarColor(color.r, color.g, color.b) +-- frame.healthbar:SetStatusBarColor(color.r, color.g, color.b) + frame.healthbar:SetStatusBarColor(utils.GetHPSeverity(perc)) frame.healthbar:SetMinMaxValues(0, UnitHealthMax(unit)) frame.healthbar:SetValue(UnitHealth(unit)) frame:SetBackdropBorderColor(color.r, color.g, color.b) @@ -254,6 +374,7 @@ function PerfectRaid:UNIT_HEALTH(event, unit) if not range then frame:SetAlpha(0.3) else frame:SetAlpha(1.0) end local class = select(2, UnitClass(unit)) frame.healthbar:SetValue(health) + frame.healthbar:SetStatusBarColor(utils.GetHPSeverity(perc)) frame.status:SetText(deficit) end end @@ -318,6 +439,7 @@ local function OnShow(frame) self:UNIT_HEALTH(nil, unit) self:UNIT_MANA(nil, unit) + self:UNIT_AURA(nil, unit) end local function OnHide(frame) @@ -336,7 +458,7 @@ local function OnAttributeChanged(frame, name, value) end function PerfectRaid.ConfigureButton(button) - button:SetWidth(90) + button:SetWidth(160) button:SetHeight(14) -- button:SetBackdrop(GameTooltip:GetBackdrop()) button:SetBackdropColor(0.3, 0.3, 0.3) @@ -344,9 +466,12 @@ function PerfectRaid.ConfigureButton(button) ClickCastFrames[button] = true button:SetAttribute("*type1", "target") - + + local parent = button:GetParent() + button:SetAttribute("dragparent", parent:GetAttribute("dragparent")) + local bar = CreateFrame("StatusBar", nil, button) - bar:SetPoint("TOPLEFT", 0, 0) + bar:SetPoint("TOPLEFT", 70, 0) bar:SetPoint("BOTTOMRIGHT", 0, 0) bar:SetStatusBarTexture("Interface\\AddOns\\PerfectRaid\\images\\smooth") button.healthbar = bar @@ -361,6 +486,8 @@ function PerfectRaid.ConfigureButton(button) local font = button.healthbar:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") font:SetPoint("RIGHT", button.healthbar, "LEFT", -2, 0) + font:SetPoint("LEFT", button, "LEFT", 0, 0) + font:SetJustifyH("RIGHT") button.name = font local font = button.healthbar:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") @@ -383,10 +510,10 @@ end function PerfectRaid:RAID_ROSTER_UPDATE() if GetNumRaidMembers() > 0 then --PRaidPartyHeader:Hide() - for k,v in pairs(self.headers) do - v:StopMovingOrSizing() - v:Show() + if self.moving then + self.moving:StopMovingOrSizing() end + self:ShowHideTitles() else if PRaidPartyHeader then PRaidPartyHeader:Show() @@ -396,17 +523,69 @@ function PerfectRaid:RAID_ROSTER_UPDATE() tbl[k] = nil end end - - for k,v in pairs(self.headers) do - v:Hide() + if self.headers then + for k,v in pairs(self.headers) do + v.title:Hide() + end + end + if self.column then + for k,v in pairs(self.column) do + v.title:Hide() + end end end end +function PerfectRaid:ShowHideTitles() + if self.column then + for i,frame in pairs(self.column) do + local active + for i=1,frame:GetNumChildren() do + local button = frame:GetAttribute("Child"..i) + if button:IsShown() then + active = true + end + end + if not active then + frame.title:Hide() + else + frame.title:Show() + end + end + end + + if self.headers then + for i,frame in pairs(self.column) do + local active + for i=1,frame:GetNumChildren() do + local button = frame:GetAttribute("Child"..i) + if button:IsShown() then + active = true + end + end + if not active then + frame.title:Hide() + else + frame.title:Show() + end + end + end +end + +local class = select(2, UnitClass("player")) +local spells = { + DRUID = "Healing Touch", + SHAMAN = "Healing Wave", + PRIEST = "Lesser Heal", + PALADIN = "Holy Light", +} + +local rangespell = spells[class] + local elapsed = 0 function PerfectRaid.OnUpdate(frame, arg1) for unit,tbl in pairs(frames) do - local range = IsSpellInRange("Healing Touch", unit) == 1 + local range = IsSpellInRange(rangespell, unit) == 1 local alpha = range and 1.0 or 0.3 for frame in pairs(tbl) do frame:SetAlpha(alpha) @@ -419,7 +598,7 @@ local frame = CreateFrame("Frame", "PRUpdateFrame") frame:SetScript("OnUpdate", PerfectRaid.OnUpdate) local buffcache = {} - +local work = {} function PerfectRaid:UNIT_AURA(event, unit) if not frames[unit] then return end for k,v in pairs(buffcache) do buffcache[k] = nil end @@ -434,7 +613,37 @@ function PerfectRaid:UNIT_AURA(event, unit) local name,rank,texture,count = UnitDebuff(unit, i) if not name then break end buffcache[name] = (buffcache[name] or 0) + 1 - end + end + + for k,v in pairs(work) do work[k] = nil end + + local buffs = self.db.profile.buffs + for i,entry in ipairs(buffs) do + local checkcond = false + if entry.missing then + if not buffcache[entry.buffname] and not buffcache[entry.groupbuff or "nil"] then + checkcond = true + end + else + if buffcache[entry.buffname] or buffcache[entry.groupbuff or "nil"] then + checkcond = true + end + end + + local conds = self.conditions + if checkcond then + for i,name in pairs(entry.cond) do + if conds[name] and conds[name](unit) then + table.insert(work, entry.colortext) + end + end + end + end + + local status = strjoin(" ", unpack(work)) + for frame in pairs(frames[unit]) do + frame.aura:SetText(status) + end end --seterrorhandler(function(msg) PerfectRaid:Print(msg .. debugstack()) end) diff --git a/PerfectRaid.toc b/PerfectRaid.toc index 619f186..1ba7880 100644 --- a/PerfectRaid.toc +++ b/PerfectRaid.toc @@ -6,6 +6,7 @@ ## OptionalDeps: Dongle Dongle.lua +DongleUtils.lua PerfectRaid.xml PerfectRaid.lua PerfectRaidOptions.lua diff --git a/PerfectRaidOptions.lua b/PerfectRaidOptions.lua index 01a4a69..07a3587 100644 --- a/PerfectRaidOptions.lua +++ b/PerfectRaidOptions.lua @@ -29,6 +29,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------]] +local utils = DongleStub("DongleUtils") + function PerfectRaid:SkinFrame(frame) local topleft = frame:CreateTexture() topleft:SetHeight(32) topleft:SetWidth(32) @@ -427,7 +429,7 @@ function PerfectRaid:CreateOptions() button4:SetPoint("CENTER", frame, "BOTTOM", 40, 25) button3:SetScript("OnClick", function() - local edit = scrollframe.editSave + local edit = self.editSave local entry = edit or {} entry.name = edit1:GetText() if entry.name == "" then entry.name = "Unnamed" end @@ -457,38 +459,80 @@ function PerfectRaid:CreateOptions() boxes[i]:SetChecked(nil) end edit1:SetText("") - scrollframe.editSave = nil + self.editSave = nil frame:Hide() end) button2:SetScript("OnClick", function() + local list,frame,checklist + + if self.editMode == "Frames" then + list = self.db.profile.headers + frame = PREditRaid + checklist = "filters" + elseif self.editMode == "Buffs" then + list = self.db.profile.buffs + frame = PREditBuff + checklist = "cond" + end + local idx = selected.idx local offset = FauxScrollFrame_GetOffset(scrollframe) - local entry = self.db.profile.headers[offset + idx] - scrollframe.editSave = entry + local entry = list[offset + idx] + self.editSave = entry - for i=1,#boxes do - edit1:SetText(entry.name) - local box = boxes[i] - for k,v in pairs(entry.filters) do + boxes = frame.boxes + + for i,box in ipairs(boxes) do + for k,v in pairs(entry[checklist]) do if box.value == v then box:SetChecked() end end end + + if self.editMode == "Frames" then + edit1:SetText(entry.name) + elseif self.editMode == "Buffs" then + PREditBuffName:SetText(entry.buffname or "") + PREditGroupName:SetText(entry.groupbuff or "") + PREditDispText:SetText(entry.disptext or "") + PRIconButton.icon:SetTexture(entry.texture) + PRBuffCheckMissing:SetChecked(entry.missing) + PRBuffCheckDisabled:SetChecked(entry.disabled) + PRColorSelect.normalTexture:SetVertexColor(unpack(entry.color)) + end + frame:Show() end) button5:SetScript("OnClick", function() + local list + + if self.editMode == "Frames" then + list = self.db.profile.headers + elseif self.editMode == "Buffs" then + list = self.db.profile.buffs + end + local idx = selected.idx local offset = FauxScrollFrame_GetOffset(scrollframe) - local header = self.headers[offset + idx] - table.remove(self.db.profile.headers, offset + idx) - header:Hide() - header:SetAttribute("groupFilter", nil) - header:SetAttribute("nameList", nil) - self:CreateRaidHeaders() + local entry = list[offset + idx] + table.remove(list, offset + idx) + + if self.editMode == "Frames" then + local header = self.headers[offset + idx] + header:Hide() + header:SetAttribute("groupFilter", nil) + header:SetAttribute("nameList", nil) + self:CreateRaidHeaders() + elseif self.editMode == "Buffs" then + for unit in pairs(self.frames) do + self:UNIT_AURA(nil, unit) + end + end + selected = nil scrollframe.update() end) @@ -512,7 +556,7 @@ end function PerfectRaid:CreateBuffEditWindow() local frame = CreateFrame("Frame", "PREditBuff") frame:SetFrameStrata("HIGH") - frame:SetHeight(400) + frame:SetHeight(415) frame:SetWidth(350) frame:SetPoint("CENTER", 75, -25) self:SkinFrame(frame) @@ -576,21 +620,111 @@ function PerfectRaid:CreateBuffEditWindow() iconbutton:SetScript("OnLeave", function() GameTooltip:Hide() end) iconbutton:SetScript("OnClick", function() PRIconSelectFrame:Show() end) - buffname:SetScript("OnTabPressed", function() groupbuff:SetFocus() end) - groupbuff:SetScript("OnTabPressed", function() disptext:SetFocus() end) - disptext:SetScript("OnTabPressed", function() buffname:SetFocus() end) + buffname:SetScript("OnTabPressed", function() + if IsShiftKeyDown() then disptext:SetFocus() else groupbuff:SetFocus() end + end) + groupbuff:SetScript("OnTabPressed", function() + if IsShiftKeyDown() then buffname:SetFocus() else disptext:SetFocus() end + end) + disptext:SetScript("OnTabPressed", function() + if IsShiftKeyDown() then groupbuff:SetFocus() else buffname:SetFocus() end + end) frame:SetScript("OnShow", function() buffname:SetFocus() end) - function makecheck(text, value) - local box = CreateFrame("CheckButton", "Buff"..text, frame, "UICheckButtonTemplate") + -- Color Swatch + local function colorSwatchCancel() + local self = ColorPickerFrame.object; + local r, g, b = self.r, self.g, self.b; + self.normalTexture:SetVertexColor(r, g, b); + end + + local function colorSwatchColor() + local self = ColorPickerFrame.object; + local r, g, b = ColorPickerFrame:GetColorRGB(); + self.normalTexture:SetVertexColor(r, g, b); + self.color[1] = r + self.color[2] = g + self.color[3] = b + end + + local function colorSwatchOpacity() + local self = ColorPickerFrame.object; + local a = OpacitySliderFrame:GetValue(); + end + + local function colorSwatchShow(self) + local r, g, b, a; + self.color = self.color or {1,1,1} + + local color = self.color; + if ( color ) then + r, g, b, a = unpack(color); + end + + self.r, self.g, self.b = r, g, b + self.opacityFunc = colorSwatchOpacity; + self.swatchFunc = colorSwatchColor; + self.cancelFunc = colorSwatchCancel; + + ColorPickerFrame.object = self; + UIDropDownMenuButton_OpenColorPicker(self); + ColorPickerFrame:SetFrameStrata("TOOLTIP"); + ColorPickerFrame:Raise(); + end + + local function colorSwatchOnClick(self) + CloseMenus(); + colorSwatchShow(self); + end + + local function colorSwatchOnEnter(self) + self.bg:SetVertexColor(1, 0.82, 0); + end + + local function colorSwatchOnLeave(self) + self.bg:SetVertexColor(1, 1, 1); + end + + local swatch = CreateFrame("Button", "PRColorSelect", frame); + swatch:SetHeight(20) swatch:SetWidth(20) + swatch:SetPoint("LEFT", disptext, "RIGHT", 5, 0) + local bg = swatch:CreateTexture(nil, "BACKGROUND"); + local normalTexture = swatch:CreateTexture(nil, "ARTWORK"); + + normalTexture:SetTexture("Interface\\ChatFrame\\ChatFrameColorSwatch"); + normalTexture:SetAllPoints(swatch); + swatch:SetNormalTexture(normalTexture); + bg:SetTexture(1, 1, 1); + bg:SetPoint("TOPLEFT", swatch, 1, -1); + bg:SetPoint("BOTTOMRIGHT", swatch, 0, 1); + + self.color = {1,1,1,1} + if ( self.color ) then + normalTexture:SetVertexColor(unpack(self.color)) + end + + swatch.bg, swatch.normalTexture = bg, normalTexture; + swatch.object, swatch.hasAlpha = self, 1 + + swatch:SetScript("OnLeave", colorSwatchOnLeave); + swatch:SetScript("OnEnter", colorSwatchOnEnter); + swatch:SetScript("OnClick", colorSwatchOnClick); + + function makecheck(text, func, name) + name = name or text + local box = CreateFrame("CheckButton", "PRBuffCheck"..name, frame, "UICheckButtonTemplate") box.text = getglobal(box:GetName().."Text") box.text:SetText(text) + box.value = text + box.func = func return box end -- Create buff condition checkboxes. local boxes = {} + frame.boxes = boxes + boxes[1] = makecheck("Warrior", self.conditions.Warrior) boxes[1]:SetPoint("TOPLEFT", iconbutton, "BOTTOMLEFT", -85, -5) boxes[2] = makecheck("Rogue", self.conditions.Rogue) @@ -619,9 +753,12 @@ function PerfectRaid:CreateBuffEditWindow() boxes[12] = makecheck("Range", self.conditions.Range) boxes[12]:SetPoint("LEFT", boxes[11], "RIGHT", 80, 0) - local missing = makecheck("Only display if the given buff is missing") + local missing = makecheck("Only display if the given buff is missing", nil, "Missing") missing:SetPoint("TOPLEFT", boxes[10], "BOTTOMLEFT", 0, -5) + local disabled = makecheck("Disable", nil, "Disabled") + disabled:SetPoint("TOPLEFT", missing, "BOTTOMLEFT", 0, -5) + local saveicon = CreateFrame("Button", "PRButtonBuffSave", frame) self:SkinButton(saveicon) saveicon.text:SetText("Save") @@ -759,12 +896,33 @@ function PerfectRaid:CreateBuffEditWindow() PRButtonBuffSave:SetScript("OnClick", function() local list = self.db.profile.buffs - local entry = {} + local entry = self.editSave or {} entry.name = buffname:GetText() entry.desc = disptext:GetText() or iconbutton.icon:GetTexture() - table.insert(list, entry) + entry.cond = {} + for i,box in ipairs(boxes) do + if box:GetChecked() then + table.insert(entry.cond, box.value) + end + end + entry.buffname = buffname:GetText() + entry.groupbuff = groupbuff:GetText() + entry.disptext = disptext:GetText() + entry.texture = iconbutton.icon:GetTexture() + entry.missing = missing:GetChecked() + entry.disabled = disabled:GetChecked() + entry.color = swatch.color + entry.colortext = string.format("|cFF%s%s|r", utils.RGBToHex(unpack(entry.color)), entry.disptext) + + if not self.editSave then + table.insert(list, entry) + end PRButtonBuffCancel:Click() + PRScrollFrame.update() + for unit in pairs(self.frames) do + self:UNIT_AURA("UNIT_AURA", unit) + end end) PRButtonBuffCancel:SetScript("OnClick", function() @@ -775,6 +933,7 @@ function PerfectRaid:CreateBuffEditWindow() groupbuff:SetText("") disptext:SetText("") iconbutton.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark") + swatch.normalTexture:SetVertexColor(1,1,1) end missing:SetChecked(false) end)