diff --git a/IceCore.lua b/IceCore.lua index a63d1c9..6570fe2 100644 --- a/IceCore.lua +++ b/IceCore.lua @@ -106,6 +106,11 @@ function IceCore.prototype:Enable() newBar = IceCustomBar:new() newBar.elementName = k self:AddNewDynamicModule(newBar, true) + elseif self.settings.modules[k].isCustomCounter then + local newCounter + newCounter = IceCustomCount:new() + newCounter.elementName = k + self:AddNewDynamicModule(newCounter, true) end end @@ -123,8 +128,12 @@ function IceCore.prototype:AddNewDynamicModule(module, hasSettings) module:SetDatabase(self.settings) if not hasSettings then - local numExisting = self:GetNumCustomModules(module, true) - self:RenameDynamicModule(module, "MyCustomBar"..(numExisting+1)) + local numExisting = self:GetNumCustomModules(module, true, module:GetDefaultSettings().isCustomBar) + if module:GetDefaultSettings().isCustomBar then + self:RenameDynamicModule(module, "MyCustomBar"..(numExisting+1)) + elseif module:GetDefaultSettings().isCustomCounter then + self:RenameDynamicModule(module, "MyCustomCounter"..(numExisting+1)) + end end module:Create(self.IceHUDFrame) @@ -136,13 +145,14 @@ function IceCore.prototype:AddNewDynamicModule(module, hasSettings) end -function IceCore.prototype:GetNumCustomModules(exceptMe) +function IceCore.prototype:GetNumCustomModules(exceptMe, findBars) local num = 0 local foundNum = 0 for i=0,table.getn(self.elements) do - if (self.elements[i] and self.elements[i] ~= exceptMe and self.elements[i].moduleSettings.isCustomBar) then - local str = self.elements[i].elementName:match("MyCustomBar%d+") + if (self.elements[i] and self.elements[i] ~= exceptMe and + (findBars and self.elements[i].moduleSettings.isCustomBar or self.elements[i].moduleSettings.isCustomCounter)) then + local str = self.elements[i].elementName:match("MyCustom"..(findBars and "Bar" or "Counter").."%d+") if str then foundNum = str:match("%d+") end diff --git a/IceHUD.lua b/IceHUD.lua index f10df36..986d8b4 100644 --- a/IceHUD.lua +++ b/IceHUD.lua @@ -527,6 +527,17 @@ IceHUD.options = order = 94.5 }, + customCount = { + type = 'execute', + name = 'Create custom counter', + desc = 'Creates a new customized counter', + func = function() + IceHUD.IceCore:AddNewDynamicModule(IceCustomCount:new()) + StaticPopup_Show("ICEHUD_CUSTOM_COUNTER_CREATED") + end, + order = 94.6 + }, + configMode = { type = 'toggle', name = '|cffff0000Configuration Mode|r', @@ -607,9 +618,18 @@ StaticPopupDialogs["ICEHUD_CUSTOM_BAR_CREATED"] = hideOnEscape = 0, } +StaticPopupDialogs["ICEHUD_CUSTOM_COUNTER_CREATED"] = +{ + text = "A custom counter has been created and can be configured through Module Settings => MyCustomCounter. It is highly recommended that you change the bar name of this module so that it's easier to identify.", + button1 = OKAY, + timeout = 0, + whileDead = 1, + hideOnEscape = 0, +} + StaticPopupDialogs["ICEHUD_DELETE_CUSTOM_MODULE"] = { - text = "Are you sure you want to delete this module? This will remove all settings associated with this bar and cannot be un-done.", + text = "Are you sure you want to delete this module? This will remove all settings associated with it and cannot be un-done.", button1 = "Yes", button2 = "No", timeout = 0, @@ -748,23 +768,23 @@ function IceHUD:MathRound(num, idp) return math.floor(num * mult + 0.5) / mult end -function IceHUD:GetBuffCount(unit, ability, onlyMine) - return IceHUD:GetAuraCount("HELPFUL"..(onlyMine and "|PLAYER" or ""), unit, ability) +function IceHUD:GetBuffCount(unit, ability, onlyMine, matchByName) + return IceHUD:GetAuraCount("HELPFUL", unit, ability, onlyMine, matchByName) end -function IceHUD:GetDebuffCount(unit, ability, onlyMine) - return IceHUD:GetAuraCount("HARMFUL"..(onlyMine and "|PLAYER" or ""), unit, ability) +function IceHUD:GetDebuffCount(unit, ability, onlyMine, matchByName) + return IceHUD:GetAuraCount("HARMFUL", unit, ability, onlyMine, matchByName) end -function IceHUD:GetAuraCount(auraType, unit, ability, onlyMine) +function IceHUD:GetAuraCount(auraType, unit, ability, onlyMine, matchByName) for i = 1, 40 do - local _, _, texture, applications = UnitAura(unit, i, auraType..(onlyMine and "|PLAYER" or "")) + local name, _, texture, applications = UnitAura(unit, i, auraType..(onlyMine and "|PLAYER" or "")) - if not texture then + if (not matchByName and not texture) or (matchByName and not name) then break end - if string.match(texture, ability) then + if (not matchByName and string.match(texture, ability)) or (matchByName and string.match(name, ability)) then return applications end end diff --git a/IceHUD.toc b/IceHUD.toc index 7276ef3..d9702cc 100644 --- a/IceHUD.toc +++ b/IceHUD.toc @@ -55,3 +55,4 @@ modules\RangeCheck.lua modules\MaelstromCount.lua modules\HungerForBlood.lua modules\CustomBar.lua +modules\CustomCount.lua diff --git a/modules/CustomCount.lua b/modules/CustomCount.lua new file mode 100644 index 0000000..5e6c5df --- /dev/null +++ b/modules/CustomCount.lua @@ -0,0 +1,565 @@ +local AceOO = AceLibrary("AceOO-2.0") + +IceCustomCount = AceOO.Class(IceElement) +local waterfall = AceLibrary("Waterfall-1.0") + +IceCustomCount.prototype.countSize = 20 + +local validUnits = {"player", "target"} +local buffOrDebuff = {"buff", "debuff"} + +-- Constructor -- +function IceCustomCount.prototype:init() + IceCustomCount.super.prototype.init(self, "CustomCount") + + self.scalingEnabled = true +end + + +-- OVERRIDE +function IceCustomCount.prototype:GetOptions() + local opts = IceCustomCount.super.prototype.GetOptions(self) + + opts["customHeader"] = { + type = 'header', + name = "Aura settings", + order = 20.1, + } + + opts["deleteme"] = { + type = 'execute', + name = 'Delete me', + desc = 'Deletes this custom module and all associated settings. Cannot be undone!', + func = function() + local dialog = StaticPopup_Show("ICEHUD_DELETE_CUSTOM_MODULE") + if dialog then + dialog.data = self + end + end, + order = 20.2, + } + + opts["name"] = { + type = 'text', + name = 'Counter name', + desc = 'The name of this counter (must be unique!)', + get = function() + return self.elementName + end, + set = function(v) + IceHUD.IceCore:RenameDynamicModule(self, v) + end, + disabled = function() + return not self.moduleSettings.enabled + end, + order = 20.3, + } + + opts["auraTarget"] = { + type = 'text', + validate = validUnits, + name = 'Unit to track', + desc = 'Select which unit that this bar should be looking for buffs/debuffs on', + get = function() + return self.moduleSettings.auraTarget + end, + set = function(v) + self.moduleSettings.auraTarget = v + self.unit = v + self:Redraw() + end, + disabled = function() + return not self.moduleSettings.enabled + end, + order = 20.4, + } + + opts["auraType"] = { + type = 'text', + validate = buffOrDebuff, + name = 'Buff or debuff?', + desc = 'Whether we are tracking a buff or debuff', + get = function() + return self.moduleSettings.auraType + end, + set = function(v) + self.moduleSettings.auraType = v + self:Redraw() + end, + disabled = function() + return not self.moduleSettings.enabled + end, + order = 20.5, + } + + opts["auraName"] = { + type = 'text', + name = "Aura to track", + desc = "Which buff/debuff this counter will be tracking", + get = function() + return self.moduleSettings.auraName + end, + set = function(v) + self.moduleSettings.auraName = v + self:Redraw() + end, + disabled = function() + return not self.moduleSettings.enabled + end, + order = 20.6, + } + + opts["trackOnlyMine"] = { + type = 'toggle', + name = 'Only track auras by me', + desc = 'Checking this means that only buffs or debuffs that the player applied will trigger this bar', + get = function() + return self.moduleSettings.onlyMine + end, + set = function(v) + self.moduleSettings.onlyMine = v + self:Redraw() + end, + disabled = function() + return not self.moduleSettings.enabled + end, + order = 20.7, + } + + opts["countColor"] = { + type = 'color', + name = 'Count color', + desc = 'The color for this counter', + get = function() + return self:GetCustomColor() + end, + set = function(r,g,b) + self.moduleSettings.countColor.r = r + self.moduleSettings.countColor.g = g + self.moduleSettings.countColor.b = b + self:SetCustomColor() + end, + disabled = function() + return not self.moduleSettings.enabled + end, + order = 20.8, + } + + opts["countMinColor"] = { + type = 'color', + name = 'Count minimum color', + desc = 'The minimum color for this counter (only used if Change Color is enabled)', + get = function() + return self:GetCustomMinColor() + end, + set = function(r,g,b) + self.moduleSettings.countMinColor.r = r + self.moduleSettings.countMinColor.g = g + self.moduleSettings.countMinColor.b = b + self:SetCustomColor() + end, + disabled = function() + return not self.moduleSettings.enabled or not self.moduleSettings.gradient + end, + order = 20.81, + } + + opts["maxCount"] = { + type = 'text', + name = "Maximum applications", + desc = "How many total applications of this buff/debuff can be applied. For example, only 5 sunders can ever be on a target, so this would be set to 5 for tracking Sunder.", + get = function() + return self.moduleSettings.maxCount + end, + set = function(v) + if not v or not tonumber(v) then + v = 0 + end + self.moduleSettings.maxCount = v + self:Redraw() + end, + disabled = function() + return not self.moduleSettings.enabled + end, + order = 20.9, + } + + opts["normalHeader"] = { + type = 'header', + name = "Counter look and feel", + order = 30, + } + + opts["vpos"] = { + type = "range", + name = "Vertical Position", + desc = "Vertical Position", + get = function() + return self.moduleSettings.vpos + end, + set = function(v) + self.moduleSettings.vpos = v + self:Redraw() + end, + min = -300, + max = 200, + step = 10, + disabled = function() + return not self.moduleSettings.enabled + end, + order = 31 + } + + opts["hpos"] = { + type = "range", + name = "Horizontal Position", + desc = "Horizontal Position", + get = function() + return self.moduleSettings.hpos + end, + set = function(v) + self.moduleSettings.hpos = v + self:Redraw() + end, + min = -700, + max = 700, + step = 10, + disabled = function() + return not self.moduleSettings.enabled + end, + order = 31 + } + + opts["CustomFontSize"] = { + type = "range", + name = "Font Size", + desc = "Font Size", + get = function() + return self.moduleSettings.countFontSize + end, + set = function(v) + self.moduleSettings.countFontSize = v + self:Redraw() + end, + min = 10, + max = 40, + step = 1, + disabled = function() + return not self.moduleSettings.enabled or self.moduleSettings.countMode ~= "Numeric" + end, + order = 32 + } + + opts["CustomMode"] = { + type = "text", + name = "Display Mode", + desc = "Show graphical or numeric counts", + get = function() + return self.moduleSettings.countMode + end, + set = function(v) + self.moduleSettings.countMode = v + self:CreateCustomFrame(true) + self:Redraw() + waterfall:Refresh("IceHUD") + end, + validate = { "Numeric", "Graphical Bar", "Graphical Circle", "Graphical Glow", "Graphical Clean Circle" }, + disabled = function() + return not self.moduleSettings.enabled + end, + order = 33 + } + + opts["graphicalLayout"] = { + type = 'text', + name = 'Layout', + desc = 'How the graphical counter should be displayed', + get = function() + return self.moduleSettings.graphicalLayout + end, + set = function(v) + self.moduleSettings.graphicalLayout = v + self:Redraw() + end, + disabled = function() + return not self.moduleSettings.enabled or self.moduleSettings.countMode == "Numeric" + end, + validate = {"Horizontal", "Vertical"}, + order = 33.1 + } + + opts["countGap"] = { + type = 'range', + name = 'Icon gap', + desc = 'Spacing between each icon (only works for graphical mode)', + min = 0, + max = 100, + step = 1, + get = function() + return self.moduleSettings.countGap + end, + set = function(v) + self.moduleSettings.countGap = v + self:Redraw() + end, + disabled = function() + return not self.moduleSettings.enabled or self.moduleSettings.countMode == "Numeric" + end, + order = 33.2 + } + + opts["gradient"] = { + type = "toggle", + name = "Change color", + desc = "This will fade the bars or numeric representation from the min color specified to the regular color\n\n(e.g. if the min color is yellow, the color is red, and there are 3 total applications, then the first would be yellow, second orange, and third red)", + get = function() + return self.moduleSettings.gradient + end, + set = function(v) + self.moduleSettings.gradient = v + self:Redraw() + end, + disabled = function() + return not self.moduleSettings.enabled + end, + order = 34 + } + + return opts +end + +function IceCustomCount.prototype:GetCustomColor() + return self.moduleSettings.countColor.r, self.moduleSettings.countColor.g, self.moduleSettings.countColor.b, self.moduleSettings.countColor.a +end + +function IceCustomCount.prototype:GetCustomMinColor() + return self.moduleSettings.countMinColor.r, self.moduleSettings.countMinColor.g, self.moduleSettings.countMinColor.b, self.moduleSettings.countMinColor.a +end + + +-- OVERRIDE +function IceCustomCount.prototype:GetDefaultSettings() + local defaults = IceCustomCount.super.prototype.GetDefaultSettings(self) + defaults["vpos"] = 0 + defaults["hpos"] = 0 + defaults["countFontSize"] = 20 + defaults["countMode"] = "Numeric" + defaults["gradient"] = false + defaults["usesDogTagStrings"] = false + defaults["alwaysFullAlpha"] = true + defaults["graphicalLayout"] = "Horizontal" + defaults["countGap"] = 0 + defaults["maxCount"] = 5 + defaults["auraTarget"] = "player" + defaults["auraName"] = "" + defaults["onlyMine"] = true + defaults["isCustomCounter"] = true + defaults["countMinColor"] = {r=1, g=1, b=0, a=1} + defaults["countColor"] = {r=1, g=0, b=0, a=1} + defaults["auraType"] = "buff" + return defaults +end + + +-- OVERRIDE +function IceCustomCount.prototype:Redraw() + IceCustomCount.super.prototype.Redraw(self) + + self:CreateFrame() + self:UpdateCustomCount() +end + + +-- OVERRIDE +function IceCustomCount.prototype:Enable(core) + IceCustomCount.super.prototype.Enable(self, core) + + self:RegisterEvent("UNIT_AURA", "UpdateCustomCount") + + self.unit = self.moduleSettings.auraTarget + + self:CreateCustomFrame(true) +end + + + +-- 'Protected' methods -------------------------------------------------------- + +-- OVERRIDE +function IceCustomCount.prototype:CreateFrame() + IceCustomCount.super.prototype.CreateFrame(self) + + self.frame:SetFrameStrata("BACKGROUND") + if self.moduleSettings.graphicalLayout == "Horizontal" then + self.frame:SetWidth((self.countSize + self.moduleSettings.countGap)*self.moduleSettings.maxCount) + self.frame:SetHeight(1) + else + self.frame:SetWidth(1) + self.frame:SetHeight((self.countSize + self.moduleSettings.countGap)*self.moduleSettings.maxCount) + end + self.frame:ClearAllPoints() + self.frame:SetPoint("TOP", self.parent, "BOTTOM", self.moduleSettings.hpos, self.moduleSettings.vpos) + + self:Show(true) + + self:CreateCustomFrame() +end + + + +function IceCustomCount.prototype:CreateCustomFrame(doTextureUpdate) + -- create numeric counts + self.frame.numeric = self:FontFactory(self.moduleSettings.countFontSize, nil, self.frame.numeric) + + self.frame.numeric:SetWidth(50) + self.frame.numeric:SetJustifyH("CENTER") + + self.frame.numeric:SetPoint("TOP", self.frame, "TOP", 0, 0) + self.frame.numeric:Show() + + if (not self.frame.graphicalBG) then + self.frame.graphicalBG = {} + self.frame.graphical = {} + end + + -- create backgrounds + for i = 1, self.moduleSettings.maxCount do + if (not self.frame.graphicalBG[i]) then + self.frame.graphicalBG[i] = CreateFrame("StatusBar", nil, self.frame) + end + + if doTextureUpdate then + if self.moduleSettings.countMode == "Graphical Bar" then + self.frame.graphicalBG[i]:SetStatusBarTexture(IceElement.TexturePath .. "ComboBG") + elseif self.moduleSettings.countMode == "Graphical Circle" then + self.frame.graphicalBG[i]:SetStatusBarTexture(IceElement.TexturePath .. "ComboRoundBG") + elseif self.moduleSettings.countMode == "Graphical Glow" then + self.frame.graphicalBG[i]:SetStatusBarTexture(IceElement.TexturePath .. "ComboGlowBG") + elseif self.moduleSettings.countMode == "Graphical Clean Circle" then + self.frame.graphicalBG[i]:SetStatusBarTexture(IceElement.TexturePath .. "ComboCleanCurvesBG") + end + end + + self.frame.graphicalBG[i]:SetFrameStrata("BACKGROUND") + self.frame.graphicalBG[i]:SetWidth(self.countSize) + self.frame.graphicalBG[i]:SetHeight(self.countSize) + if self.moduleSettings.graphicalLayout == "Horizontal" then + self.frame.graphicalBG[i]:SetPoint("TOPLEFT", ((i-1) * (self.countSize-5)) + (i-1) + ((i-1) * self.moduleSettings.countGap), 0) + else + self.frame.graphicalBG[i]:SetPoint("TOPLEFT", 0, -1 * (((i-1) * (self.countSize-5)) + (i-1) + ((i-1) * self.moduleSettings.countGap))) + end + self.frame.graphicalBG[i]:SetAlpha(0.15) + + self.frame.graphicalBG[i]:Hide() + end + + -- create counts + for i = 1, self.moduleSettings.maxCount do + if (not self.frame.graphical[i]) then + self.frame.graphical[i] = CreateFrame("StatusBar", nil, self.frame) + end + + if doTextureUpdate then + if self.moduleSettings.countMode == "Graphical Bar" then + self.frame.graphical[i]:SetStatusBarTexture(IceElement.TexturePath .. "Combo") + elseif self.moduleSettings.countMode == "Graphical Circle" then + self.frame.graphical[i]:SetStatusBarTexture(IceElement.TexturePath .. "ComboRound") + elseif self.moduleSettings.countMode == "Graphical Glow" then + self.frame.graphical[i]:SetStatusBarTexture(IceElement.TexturePath .. "ComboGlow") + elseif self.moduleSettings.countMode == "Graphical Clean Circle" then + self.frame.graphical[i]:SetStatusBarTexture(IceElement.TexturePath .. "ComboCleanCurves") + end + end + + self.frame.graphical[i]:SetFrameStrata("BACKGROUND") + self.frame.graphical[i]:SetAllPoints(self.frame.graphicalBG[i]) + + self.frame.graphical[i]:Hide() + end + + self:SetCustomColor() +end + + +function IceCustomCount.prototype:SetCustomColor() + for i=1, self.moduleSettings.maxCount do + self.frame.graphicalBG[i]:SetStatusBarColor(self:GetCustomColor()) + + local r, g, b = self:GetCustomColor() + if (self.moduleSettings.gradient) then + r,g,b = self:GetGradientColor(i) + end + self.frame.graphical[i]:SetStatusBarColor(r, g, b) + end +end + +function IceCustomCount.prototype:GetGradientColor(curr) + local r, g, b = self:GetCustomColor() + local mr, mg, mb = self:GetCustomMinColor() + local scale = (curr-1)/(self.moduleSettings.maxCount-1) + + if r < mr then + r = ((r-mr)*scale) + mr + else + r = ((mr-r)*scale) + r + end + + if g < mg then + g = ((g-mg)*scale) + mg + else + g = ((mg-g)*scale) + g + end + + if b < mb then + b = ((b-mb)*scale) + mb + else + b = ((mb-b)*scale) + b + end + + return r, g, b +end + + +function IceCustomCount.prototype:UpdateCustomCount(unit) + if unit and unit ~= self.unit then + return + end + + local points + if IceHUD.IceCore:IsInConfigMode() then + points = self.moduleSettings.maxCount + else + points = IceHUD:GetAuraCount(self.moduleSettings.auraType == "buff" and "HELPFUL" or "HARMFUL", + self.unit, self.moduleSettings.auraName, self.moduleSettings.onlyMine, true) + end + + if (points == 0) then + points = nil + end + + if (self.moduleSettings.countMode == "Numeric") then + local r, g, b = self:GetCustomColor() + if (self.moduleSettings.gradient and points) then + r, g, b = self:GetGradientColor(points) + end + self.frame.numeric:SetTextColor(r, g, b, 0.7) + + self.frame.numeric:SetText(points) + else + self.frame.numeric:SetText() + + for i = 1, table.getn(self.frame.graphical) do + if (points ~= nil) then + self.frame.graphicalBG[i]:Show() + else + self.frame.graphicalBG[i]:Hide() + end + + if (points ~= nil and i <= points) then + self.frame.graphical[i]:Show() + else + self.frame.graphical[i]:Hide() + end + end + end +end