[Feature Request] Class-based DR icons
marco-vrinssen opened this issue · 15 comments
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
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.
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.
Would it be possible to gain access to the current version of your development environment?
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.
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