BattleGroundEnemies

BattleGroundEnemies

6M Downloads

[Feature Request] Class-based DR icons

marco-vrinssen opened this issue · 15 comments

commented

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
commented

Hey, on my current dev version i implemented the option to select an icon for each dr category (stun, fear) . But i also like your approach, it would basically automate that selection. I could add an button for it like "set icon based on my class" i feel like that could work well and still offer the way to customize.

commented

Thanks so much for handling the request, I’m really happy to hear the news! I think enabling the assignment of user-defined individual Spell IDs to the different DR categories makes a lot of sense—actually, it’s even better than what I suggested and is more consistent with the behavior of other arena/battleground addons like Gladius. At the time, I thought class-based icons might be simpler to maintain and implement, especially since I’m new to addon development. It could still be helpful for newer players who don’t want to think about their CC abilities and can just click a button, but overall, I think the way you’ve done it is really great.

commented

Would it be possible to gain access to the current version of your development environment? ☺️

commented

It would be possible to push the new branch i created for that, but i am working on some features that aren't finished yet, i wanna rework the way modules are moved – basically like a editmode. There is still quite some work to do there. But sure, if you are interested in that i can push it to GitHub.

commented

So i published the dev branch https://github.com/BullseiWoWAddons/BattleGroundEnemies/tree/dev
its nowhere near complete but i made quite some progress today, it should be working fine in pvp but the editing mode side of things needs work. If you want to check the new editing view enter
/run BattleGroundEnemies.EditMode.EditModeManager:OpenEditmode() into the chat
once you select a system when the config menu is open you should see it change the current selected option if you select on of the blue frames.
Things i am currently working on

  • make modules more static in edit mode, just like in blizz editmode so its not that jumpy (don't create new fake events every second instead show a predefined number of buffs, trinkets, etc)
  • rework some of the modules, for example merge class, spec and highest priority into one module. (not sure if thats the best way to to, and how to name it then ... )
  • fix the whole snappy system in the editmode
  • reorder the settings menu
  • make more options global to minimize complexity (for example the dr icons don't need to be specific to player size and faction)
  • and probably much more i just didn't think about yet...

If you got time to check it out feedback is highly appreciated even tho its still pretty early