Skip to content

[Feature Request] Class-based DR icons #48

@marco-vrinssen

Description

@marco-vrinssen

Hey, I would like to suggest the implementation of class-specific Diminishing Returns (DR) icons. This can be especially relevant for new players to create a quicker association for using CC abilities. For now, I have attached a first suggestion on how this could be achieved, but I believe that making this a toggleable option in the settings menu would provide more flexibility for different user preferences.

Thank you for your dedication to improving this addon.

Reference:


local AddonName, Data = ...
local BattleGroundEnemies = BattleGroundEnemies
local L = Data.L

local LSM = LibStub("LibSharedMedia-3.0")

local GetSpellTexture = C_Spell and C_Spell.GetSpellTexture or GetSpellTexture

local CreateFrame = CreateFrame
local BackdropTemplateMixin = BackdropTemplateMixin
local GameTooltip = GameTooltip

local IsClassic = WOW_PROJECT_ID == WOW_PROJECT_CLASSIC

local DRList = LibStub("DRList-1.0")

local defaultSettings = {
    Enabled = true,
    Parent = "Button",
    ActivePoints = 1,
    DisplayType = "Frame",
    IconSize = 20,
    Cooldown = {
        ShowNumber = true,
        FontSize = 12,
        FontOutline = "OUTLINE",
        EnableShadow = false,
        DrawSwipe = false,
        ShadowColor = {0, 0, 0, 1},
    },
    Container = {
        UseButtonHeightAsSize = true,
        IconSize = 15,
        IconsPerRow = 10,
        HorizontalGrowDirection = "rightwards",
        HorizontalSpacing = 2,
        VerticalGrowdirection = "downwards",
        VerticalSpacing = 1,
    },
    Filtering_Enabled = false,
    Filtering_Filterlist = {},
}

local options = function(location)
    return {
        ContainerSettings = {
            type = "group",
            name = L.ContainerSettings,
            order = 1,
            get = function(option)
                return Data.GetOption(location.Container, option)
            end,
            set = function(option, ...)
                return Data.SetOption(location.Container, option, ...)
            end,
            args = Data.AddContainerSettings(location.Container),
        },
        DisplayType = {
            type = "select",
            name = L.DisplayType,
            desc = L.DrTracking_DisplayType_Desc,
            values = Data.DisplayType,
            order = 2
        },
        CooldownTextSettings = {
            type = "group",
            name = L.Countdowntext,
            get = function(option)
                return Data.GetOption(location.Cooldown, option)
            end,
            set = function(option, ...)
                return Data.SetOption(location.Cooldown, option, ...)
            end,
            order = 3,
            args = Data.AddCooldownSettings(location.Cooldown)
        },
        Fake1 = Data.AddVerticalSpacing(6),
        FilteringSettings = {
            type = "group",
            name = FILTER,
            order = 4,
            args = {
                Filtering_Enabled = {
                    type = "toggle",
                    name = L.Filtering_Enabled,
                    desc = L.DrTrackingFiltering_Enabled_Desc,
                    width = 'normal',
                    order = 1
                },
                Filtering_Filterlist = {
                    type = "multiselect",
                    name = L.Filtering_Filterlist,
                    desc = L.DrTrackingFiltering_Filterlist_Desc,
                    disabled = function() return not location.Filtering_Enabled end,
                    get = function(option, key)
                        return location.Filtering_Filterlist[key]
                    end,
                    set = function(option, key, state)
                        location.Filtering_Filterlist[key] = state or nil
                    end,
                    values = Data.DrCategorys,
                    order = 2
                }
            }
        }
    }
end

local dRstates = {
    [1] = { 0, 1, 0, 1}, --green (next cc in DR time will be only half duration)
    [2] = { 1, 1, 0, 1}, --yellow (next cc in DR time will be only 1/4 duration)
    [3] = { 1, 0, 0, 1}, --red (next cc in DR time will not apply, player is immune)
}

local function drFrameUpdateStatusBorder(drFrame)
    drFrame:SetBackdropBorderColor(unpack(dRstates[drFrame:GetStatus()]))
end

local function drFrameUpdateStatusText(drFrame)
    drFrame.Cooldown.Text:SetTextColor(unpack(dRstates[drFrame:GetStatus()]))
end

local flags = {
    HasDynamicSize = true
}

local dRTracking = BattleGroundEnemies:NewButtonModule({
    moduleName = "DRTracking",
    localizedModuleName = L.DRTracking,
    flags = flags,
    defaultSettings = defaultSettings,
    options = options,
    events = {"AuraRemoved"},
    enabledInThisExpansion = true
})

local _, playerClass = UnitClass("player")

if playerClass == "DEATHKNIGHT" then
    drClassIcons = {
        stun = GetSpellTexture(108194),         -- Asphyxiate
        disorient = GetSpellTexture(207167),    -- Blinding Sleet
        incapacitate = GetSpellTexture(108194), -- Asphyxiate
        knockback = GetSpellTexture(132469),    -- Typhoon
        root = GetSpellTexture(45524),          -- Chains of Ice
        silence = GetSpellTexture(47476),       -- Strangulate
        disarm = GetSpellTexture(236077)        -- Disarm
    }
elseif playerClass == "DEMONHUNTER" then
    drClassIcons = {
        stun = GetSpellTexture(211881),         -- Fel Eruption
        disorient = GetSpellTexture(207684),    -- Sigil of Misery
        incapacitate = GetSpellTexture(217832), -- Imprison
        knockback = GetSpellTexture(132469),    -- Typhoon
        root = GetSpellTexture(202138),         -- Sigil of Chains
        silence = GetSpellTexture(204490),      -- Sigil of Silence
        disarm = GetSpellTexture(236077)        -- Disarm
    }
elseif playerClass == "DRUID" then
    drClassIcons = {
        stun = GetSpellTexture(5211),           -- Mighty Bash
        disorient = GetSpellTexture(33786),     -- Cyclone
        incapacitate = GetSpellTexture(99),     -- Incapacitating Roar
        knockback = GetSpellTexture(132469),    -- Typhoon
        root = GetSpellTexture(339),            -- Entangling Roots
        silence = GetSpellTexture(15487),       -- Silence
        disarm = GetSpellTexture(236077)        -- Disarm
    }
elseif playerClass == "EVOKER" then
    drClassIcons = {
        stun = GetSpellTexture(357210),         -- Deep Breath
        disorient = GetSpellTexture(360806),    -- Sleep Walk
        incapacitate = GetSpellTexture(6770),   -- Sap
        knockback = GetSpellTexture(357214),    -- Wing Buffet
        root = GetSpellTexture(358385),         -- Landslide
        silence = GetSpellTexture(351338),      -- Quell
        disarm = GetSpellTexture(236077)        -- Disarm
    }
elseif playerClass == "HUNTER" then
    drClassIcons = {
        stun = GetSpellTexture(19577),          -- Intimidation
        disorient = GetSpellTexture(2094),      -- Blind
        incapacitate = GetSpellTexture(187650), -- Freezing Trap
        knockback = GetSpellTexture(13813),     -- Explosive Trap
        root = GetSpellTexture(162487),         -- Steel Trap
        silence = GetSpellTexture(147362),      -- Counter Shot
        disarm = GetSpellTexture(202900)        -- Scorpid Sting
    }
elseif playerClass == "MAGE" then
    drClassIcons = {
        stun = GetSpellTexture(389794),         -- Dragon's Breath
        disorient = GetSpellTexture(31661),     -- Dragon's Breath
        incapacitate = GetSpellTexture(118),    -- Polymorph
        knockback = GetSpellTexture(157981),    -- Blast Wave
        root = GetSpellTexture(122),            -- Frost Nova
        silence = GetSpellTexture(2139),        -- Counterspell
        disarm = GetSpellTexture(236077)        -- Disarm
    }
elseif playerClass == "MONK" then
    drClassIcons = {
        stun = GetSpellTexture(119381),         -- Leg Sweep
        disorient = GetSpellTexture(198898),    -- Song of Chi-Ji
        incapacitate = GetSpellTexture(115078), -- Paralysis
        knockback = GetSpellTexture(116844),    -- Ring of Peace
        root = GetSpellTexture(343731),         -- Disable
        silence = GetSpellTexture(116705),      -- Spear Hand Strike
        disarm = GetSpellTexture(233759)        -- Grapple Weapon
    }
elseif playerClass == "PALADIN" then
    drClassIcons = {
        stun = GetSpellTexture(853),            -- Hammer of Justice
        disorient = GetSpellTexture(115750),    -- Blinding Light
        incapacitate = GetSpellTexture(20066),  -- Repentance
        knockback = GetSpellTexture(132469),    -- Typhoon
        root = GetSpellTexture(339),            -- Entangling Roots
        silence = GetSpellTexture(96231),       -- Rebuke
        disarm = GetSpellTexture(236077)        -- Disarm
    }
elseif playerClass == "PRIEST" then
    drClassIcons = {
        stun = GetSpellTexture(64044),          -- Psychic Horror
        disorient = GetSpellTexture(8122),      -- Psychic Scream
        incapacitate = GetSpellTexture(88625),  -- Holy Word: Chastise
        knockback = GetSpellTexture(132469),    -- Typhoon
        root = GetSpellTexture(108920),         -- Void Tendrils
        silence = GetSpellTexture(15487),       -- Silence
        disarm = GetSpellTexture(236077)        -- Disarm
    }
elseif playerClass == "ROGUE" then
    drClassIcons = {
        stun = GetSpellTexture(408),            -- Kidney Shot
        disorient = GetSpellTexture(2094),      -- Blind
        incapacitate = GetSpellTexture(6770),   -- Sap
        knockback = GetSpellTexture(132469),    -- Typhoon
        root = GetSpellTexture(339),            -- Entangling Roots
        silence = GetSpellTexture(1766),        -- Kick
        disarm = GetSpellTexture(207777)        -- Dismantle
    }
elseif playerClass == "SHAMAN" then
    drClassIcons = {
        stun = GetSpellTexture(305483),         -- Capacitor Totem
        disorient = GetSpellTexture(2094),      -- Blind
        incapacitate = GetSpellTexture(51514),  -- Hex
        knockback = GetSpellTexture(51490),     -- Thunderstorm
        root = GetSpellTexture(64695),          -- Earthgrab Totem
        silence = GetSpellTexture(57994),       -- Wind Shear
        disarm = GetSpellTexture(236077)        -- Disarm
    }
elseif playerClass == "WARLOCK" then
    drClassIcons = {
        stun = GetSpellTexture(30283),          -- Shadowfury
        disorient = GetSpellTexture(5782),      -- Fear
        incapacitate = GetSpellTexture(6789),   -- Mortal Coil
        knockback = GetSpellTexture(132469),    -- Typhoon
        root = GetSpellTexture(51485),          -- Earthgrab Totem
        silence = GetSpellTexture(19647),       -- Spell Lock
        disarm = GetSpellTexture(236077)        -- Disarm
    }
elseif playerClass == "WARRIOR" then
    drClassIcons = {
        stun = GetSpellTexture(107570),         -- Storm Bolt
        disorient = GetSpellTexture(5246),      -- Intimidating Shout
        incapacitate = GetSpellTexture(6770),   -- Sap
        knockback = GetSpellTexture(132469),    -- Typhoon
        root = GetSpellTexture(6343),           -- Thunder Clap
        silence = GetSpellTexture(6552),        -- Pummel
        disarm = GetSpellTexture(236077)        -- Disarm
    }
end

local function createNewDrFrame(playerButton, container)
    local drFrame = CreateFrame("Frame", nil, container, BackdropTemplateMixin and "BackdropTemplate")
    drFrame.Cooldown = BattleGroundEnemies.MyCreateCooldown(drFrame)

    drFrame.Cooldown:SetScript("OnCooldownDone", function()
        drFrame:Remove()
    end)
    drFrame:HookScript("OnEnter", function(self)
        BattleGroundEnemies:ShowTooltip(self, function()
            if IsClassic then return end
            GameTooltip:SetSpellByID(self.spellId)
        end)
    end)

    drFrame:HookScript("OnLeave", function(self)
        if GameTooltip:IsOwned(self) then
            GameTooltip:Hide()
        end
    end)

    drFrame.Container = container

    drFrame.ApplyChildFrameSettings = function(self)
        self.Cooldown:ApplyCooldownSettings(container.config.Cooldown, false)
        self:SetDisplayType()
    end

    drFrame.GetStatus = function(self)
        local status = self.input.status
        status = (math.min(status, 3))
        return status
    end

    drFrame.SetDisplayType = function(self)
        if container.config.DisplayType == "Frame" then
            self.SetStatus = drFrameUpdateStatusBorder
        else
            self.SetStatus = drFrameUpdateStatusText
        end

        self.Cooldown.Text:SetTextColor(1, 1, 1, 1)
        self:SetBackdropBorderColor(0, 0, 0, 0)
        if self.input and self.input.status ~= 0 then self:SetStatus() end
    end

    drFrame:SetBackdrop({
        bgFile = "Interface/Buttons/WHITE8X8", --drawlayer "BACKGROUND"
        edgeFile = 'Interface/Buttons/WHITE8X8', --drawlayer "BORDER"
        edgeSize = 1
    })

    drFrame:SetBackdropColor(0, 0, 0, 0)
    drFrame:SetBackdropBorderColor(0, 0, 0, 0)

    drFrame.Icon = drFrame:CreateTexture(nil, "BORDER", nil, -1) -- -1 to make it behind the SetBackdrop bg
    drFrame.Icon:SetAllPoints()

    drFrame:ApplyChildFrameSettings()

    drFrame:Hide()
    return drFrame
end

local function setupDrFrame(container, drFrame, drDetails)
    drFrame:SetStatus()

    drFrame.spellId = drDetails.spellId
    
    local drCat = drDetails.drCat
    if drClassIcons and drClassIcons[drCat] then
        drFrame.Icon:SetTexture(drClassIcons[drCat])
    else
        drFrame.Icon:SetTexture(GetSpellTexture(drDetails.spellId))
    end

    local duration = DRList:GetResetTime(drCat)
    drFrame.Cooldown:SetCooldown(drDetails.startTime, duration)
end

function dRTracking:AttachToPlayerButton(playerButton)
    local container = BattleGroundEnemies:NewContainer(playerButton, createNewDrFrame, setupDrFrame)

    function container:AuraRemoved(spellId, spellName)
        local config = self.config

        local drCat = DRList:GetCategoryBySpellID(spellId)

        if not drCat then return end

        local drTrackingEnabled = not config.Filtering_Enabled or config.Filtering_Filterlist[drCat]

        if drTrackingEnabled then
            local input = self:FindInputByAttribute("drCat", drCat)
            if input then
                input = self:UpdateInput(input, {spellId = spellId})
            else
                input = self:NewInput({
                    drCat = drCat,
                    spellId = spellId
                })
            end

            input.status = (input.status or 0) + 1
            
            input.startTime = GetTime()
            self:Display()
        end
    end

    playerButton.DRTracking = container
end

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions