Skip to content

Commit ce566e4

Browse files
committed
The "Buff - Number Of" and "Debuff - Number Of" conditions now support tracking multiple units (e.g. group 1-40). (#1989)
1 parent 3b4f266 commit ce566e4

File tree

9 files changed

+134
-70
lines changed

9 files changed

+134
-70
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
## v10.0.9
2+
* The "Buff - Number Of" and "Debuff - Number Of" conditions now support tracking multiple units (e.g. `group 1-40`). (#1989)
23
* Fix #2066 - Enrage effects were not being stored properly when fetching auras for noncached units.
34
* Fix #2059 - Condition update were not happening at the proper time for spell cooldown conditions.
45
* Fix #2038 - Fix more scenarios in which talent loadout name is not available immediately after talent events fire.

Components/Core/Conditions/Categories/BuffsDebuffs.lua

Lines changed: 39 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -60,35 +60,38 @@ function Env.AuraStacksPacked(unit, name, kindKey, onlyMine)
6060
return 0
6161
end
6262

63-
function Env.AuraCount(unit, spells, filter)
63+
function Env.AuraCount(units, spells, filter)
6464
local n = 0
6565
local names = spells.Hash
6666

67-
for i = 1, huge do
68-
local buffName, _, _, _, _, _, _, _, _, id = UnitAura(unit, i, filter)
69-
if not buffName then
70-
return n
71-
elseif names[id] or names[strlowerCache[buffName]] then
72-
n = n + 1
67+
for u = 1, #units do
68+
for i = 1, huge do
69+
local buffName, _, _, _, _, _, _, _, _, id = UnitAura(units[u], i, filter)
70+
if not buffName then
71+
break
72+
elseif names[id] or names[strlowerCache[buffName]] then
73+
n = n + 1
74+
end
7375
end
7476
end
7577

7678
return n
7779
end
7880

79-
function Env.AuraCountPacked(unit, spells, kindKey, onlyMine)
80-
local auras = GetAuras(unit)
81-
local instances = auras.instances
82-
local lookup = auras.lookup
83-
local SpellsArray = spells.Array
84-
81+
function Env.AuraCountPacked(units, spells, kindKey, onlyMine)
8582
local n = 0
86-
for i = 1, #SpellsArray do
87-
for auraInstanceID, isMine in next, lookup[SpellsArray[i]] or empty do
88-
if (isMine or not onlyMine) then
89-
local instance = instances[auraInstanceID]
90-
if instance[kindKey] then
91-
n = n + 1
83+
local SpellsArray = spells.Array
84+
for u = 1, #units do
85+
local auras = GetAuras(units[u])
86+
local instances = auras.instances
87+
local lookup = auras.lookup
88+
89+
for i = 1, #SpellsArray do
90+
for auraInstanceID, isMine in next, lookup[SpellsArray[i]] or empty do
91+
if (isMine or not onlyMine) then
92+
if instances[auraInstanceID][kindKey] then
93+
n = n + 1
94+
end
9295
end
9396
end
9497
end
@@ -359,7 +362,7 @@ end
359362

360363
local function CanUsePackedAuras(c)
361364
if not GetAuras then return false end
362-
if not TMW.COMMON.Auras:RequestUnits(CNDT:GetUnit(c.Unit)) then return false end
365+
if not TMW.COMMON.Auras:RequestUnits(c.Unit) then return false end
363366
return true
364367
end
365368

@@ -578,12 +581,13 @@ end
578581
ConditionCategory:RegisterCondition(5, "BUFFNUMBER", {
579582
text = L["ICONMENU_BUFF"] .. " - " .. L["NUMAURAS"],
580583
tooltip = L["NUMAURAS_DESC"],
581-
min = 0,
582-
max = 20,
584+
range = 20,
585+
multiUnit = true,
583586
name = function(editbox)
584587
editbox:SetTexts(L["BUFFTOCHECK"], L["CNDT_MULTIPLEVALID"])
585588
end,
586589
useSUG = true,
590+
allowMultipleSUGEntires = true,
587591
check = function(check)
588592
check:SetTexts(L["ONLYCHECKMINE"], L["ONLYCHECKMINE_DESC"])
589593
end,
@@ -592,14 +596,15 @@ ConditionCategory:RegisterCondition(5, "BUFFNUMBER", {
592596
tcoords = CNDT.COMMON.standardtcoords,
593597
funcstr = function(c)
594598
if CanUsePackedAuras(c) then
595-
return [[AuraCountPacked(c.Unit, c.Spells, "isHelpful", ]] .. (tostring(c.Checked)) .. [[) c.Operator c.Level]]
599+
return [[AuraCountPacked(c.Units, c.Spells, "isHelpful", ]] .. (tostring(c.Checked)) .. [[) c.Operator c.Level]]
596600
end
597-
return [[AuraCount(c.Unit, c.Spells, "HELPFUL]] .. (c.Checked and " PLAYER" or "") .. [[") c.Operator c.Level]]
601+
return [[AuraCount(c.Units, c.Spells, "HELPFUL]] .. (c.Checked and " PLAYER" or "") .. [[") c.Operator c.Level]]
598602
end,
599603
events = function(ConditionObject, c)
604+
local _, unitSet = TMW:GetUnits(nil, c.Unit)
600605
return
601-
ConditionObject:GetUnitChangedEventString(CNDT:GetUnit(c.Unit)),
602-
ConditionObject:GenerateUnitAuraString(CNDT:GetUnit(c.Unit), TMW:GetSpells(c.Name).First, c.Checked)
606+
ConditionObject:GenerateNormalEventString(unitSet.event),
607+
ConditionObject:GenerateUnitAuraString(unitSet, TMW:GetSpells(c.Name), c.Checked)
603608
end,
604609
})
605610

@@ -843,12 +848,13 @@ end
843848
ConditionCategory:RegisterCondition(15, "DEBUFFNUMBER", {
844849
text = L["ICONMENU_DEBUFF"] .. " - " .. L["NUMAURAS"],
845850
tooltip = L["NUMAURAS_DESC"],
846-
min = 0,
847-
max = 20,
851+
range = 20,
852+
multiUnit = true,
848853
name = function(editbox)
849854
editbox:SetTexts(L["DEBUFFTOCHECK"], L["CNDT_MULTIPLEVALID"])
850855
end,
851856
useSUG = true,
857+
allowMultipleSUGEntires = true,
852858
check = function(check)
853859
check:SetTexts(L["ONLYCHECKMINE"], L["ONLYCHECKMINE_DESC"])
854860
end,
@@ -857,14 +863,15 @@ ConditionCategory:RegisterCondition(15, "DEBUFFNUMBER", {
857863
tcoords = CNDT.COMMON.standardtcoords,
858864
funcstr = function(c)
859865
if CanUsePackedAuras(c) then
860-
return [[AuraCountPacked(c.Unit, c.Spells, "isHarmful", ]] .. (tostring(c.Checked)) .. [[) c.Operator c.Level]]
866+
return [[AuraCountPacked(c.Units, c.Spells, "isHarmful", ]] .. (tostring(c.Checked)) .. [[) c.Operator c.Level]]
861867
end
862-
return [[AuraCount(c.Unit, c.Spells, "HARMFUL]] .. (c.Checked and " PLAYER" or "") .. [[") c.Operator c.Level]]
868+
return [[AuraCount(c.Units, c.Spells, "HARMFUL]] .. (c.Checked and " PLAYER" or "") .. [[") c.Operator c.Level]]
863869
end,
864870
events = function(ConditionObject, c)
871+
local _, unitSet = TMW:GetUnits(nil, c.Unit)
865872
return
866-
ConditionObject:GetUnitChangedEventString(CNDT:GetUnit(c.Unit)),
867-
ConditionObject:GenerateUnitAuraString(CNDT:GetUnit(c.Unit), TMW:GetSpells(c.Name).First, c.Checked)
873+
ConditionObject:GetUnitChangedEventString(unitSet),
874+
ConditionObject:GenerateUnitAuraString(unitSet, TMW:GetSpells(c.Name), c.Checked)
868875
end,
869876
})
870877

Components/Core/Conditions/ConditionObject.lua

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -426,40 +426,79 @@ function ConditionObject:IsUnitAuraEventRelevant(updatedAuras, spell, onlyMine)
426426
end
427427
end
428428

429+
function ConditionObject:IsTmwAuraEventRelevant(updatedAuras, spellSet, onlyMine)
430+
-- updatedAuras = { [name | id | dispelType] = mightBeMine(bool) }
431+
432+
local Hash = spellSet.Hash
433+
for identifier, mightBeMine in next, updatedAuras do
434+
if Hash[identifier] and (mightBeMine or not OnlyMine) then
435+
return true
436+
end
437+
end
438+
end
439+
429440
function ConditionObject:GenerateUnitAuraString(unit, spell, onlyMine)
441+
local unitCheck = type(unit) == "table"
442+
-- unit is a UnitSet:
443+
and ("%s[arg1]"):format(CNDT:GetTableSubstitution(unit.UnitsLookup))
444+
-- unit is a unitID:
445+
or ("arg1 == %q"):format(unit)
446+
447+
430448
if TMW.COMMON.Auras then
431449
local canUsePacked, auraEvent = TMW.COMMON.Auras:RequestUnits(unit)
432450

433451
self:RequestEvent(auraEvent)
434452
self:SetNumEventArgs(2)
435453

436-
local str = ("event == %q and arg1 == %q"):format(auraEvent, unit)
437-
-- arg2 is payload:
438-
-- If it is nil, the event is a general update for the unit
439-
-- If it is a table, its keys are names/ids of what changed
440-
-- and the values indicate if the aura might have been mine.
441-
.. " and (not arg2 or arg2["
454+
if type(spell) == "table" then
455+
-- If we were passed a spell set that only contains one spell,
456+
-- inline that spell so we can avoid the function call to IsTmwAuraEventRelevant
457+
if #spell.Array == 1 then
458+
spell = spell.First
459+
end
460+
end
461+
462+
local str = ("event == %q and %s and"):format(auraEvent, unitCheck)
463+
464+
-- arg2 is payload:
465+
-- If it is nil, the event is a general update for the unit.
466+
-- If it is a table, its keys are names/ids of what changed
467+
-- and the values indicate if the aura might have been mine.
468+
if type(spell) == "table" then
469+
-- Spell is a SpellSet
470+
str = str .. " (not arg2 or ConditionObject:IsTmwAuraEventRelevant(arg2,"
471+
.. CNDT:GetTableSubstitution(spell)
472+
.. ", "
473+
.. tostring(onlyMine)
474+
.. "))"
475+
else
476+
-- Spell is a name/id
477+
str = str .. " (not arg2 or arg2["
442478
.. (type(spell) == "string" and format("%q", spell) or tostring(spell))
443479
.. "]"
444480
.. (onlyMine and "" or " ~= nil")
445481
.. ")"
482+
end
446483

447484
return str
448485
else
449486
self:RequestEvent("UNIT_AURA")
450487
self:SetNumEventArgs(3)
451488

452-
local str = "event == 'UNIT_AURA' and arg1 == "
453-
.. format("%q", unit)
489+
local str = ("event == %q and %s"):format("UNIT_AURA", unitCheck)
490+
491+
if type(spell) == "string" then
454492
-- arg2 is isFullUpdate:
455493
-- If it is nil, the client doesn't support the new UNIT_AURA payload.
456494
-- If it is true, the event isn't about any particular aura.
457495
-- If it is false, then arg3 is `updatedAuras`.
458-
.. " and (arg2 ~= false or ConditionObject:IsUnitAuraEventRelevant(arg3,"
496+
str = str .. " and (arg2 ~= false or ConditionObject:IsUnitAuraEventRelevant(arg3,"
459497
.. (type(spell) == "string" and format("%q", spell) or tostring(spell))
460498
.. ", "
461499
.. tostring(onlyMine)
462500
.. "))"
501+
end
463502

464503
return str
465504
end
@@ -476,7 +515,13 @@ end
476515
-- ConditionObject:GenerateNormalEventString("UNIT_HEALTH", CNDT:GetUnit(c.Unit))
477516
-- end,
478517
function ConditionObject:GetUnitChangedEventString(unit)
479-
if unit == "player" then
518+
if type(unit) == "table" then
519+
-- unit is a UnitSet
520+
if not unit.allUnitsChangeOnEvent then
521+
return false
522+
end
523+
return unit.event
524+
elseif unit == "player" then
480525
-- Returning false (as a string, not a boolean) won't cause responses to any events,
481526
-- and it also won't make the ConditionObject default to being OnUpdate driven.
482527

Components/Core/Conditions/Conditions.lua

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,22 @@ CNDT.Substitutions = {
851851
end,
852852
},
853853

854+
{
855+
src = "c.Units",
856+
rep = function(conditionData, conditionSettings, name, name2)
857+
return CNDT:GetTableSubstitution(TMW:GetUnits(nil, conditionSettings.Unit), "Units")
858+
end,
859+
},
860+
{
861+
src = "c%.Unit(2?)",
862+
rep = function(conditionData, conditionSettings, name, name2)
863+
return function(two)
864+
local unit = two == "2" and conditionSettings.Name or conditionSettings.Unit
865+
return CNDT:GetConditionUnitSubstitution(unit)
866+
end
867+
end,
868+
},
869+
854870
{
855871
src = "c.Item2",
856872
rep = function(conditionData, conditionSettings, name, name2)
@@ -931,23 +947,6 @@ end
931947
-- [INTERNAL]
932948
function CNDT:DoConditionSubstitutions(conditionData, conditionSettings, funcstr)
933949
-- Substitutes all the c.XXXXX substitutions into a string.
934-
935-
for _, append in TMW:Vararg("2", "") do -- Unit2 MUST be before Unit
936-
if strfind(funcstr, "c.Unit" .. append) then
937-
-- Use CNDT:GetUnit() first to grab only the first unit
938-
-- in case the configured value is an expansion (like party1-4).
939-
local unit
940-
if append == "2" then
941-
unit = CNDT:GetUnit(conditionSettings.Name)
942-
elseif append == "" then
943-
unit = CNDT:GetUnit(conditionSettings.Unit)
944-
end
945-
if unit then
946-
funcstr = gsub(funcstr, "c.Unit" .. append, CNDT:GetConditionUnitSubstitution(unit))
947-
end
948-
end
949-
end
950-
951950
local name = conditionNameSettingProcessedCache[conditionSettings.Name]
952951
local name2 = conditionNameSettingProcessedCache[conditionSettings.Name2]
953952

Components/Core/Conditions/Config.lua

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -828,7 +828,15 @@ TMW:RegisterCallback("TMW_CNDT_GROUP_DRAWGROUP", function(event, CndtGroup, cond
828828
CndtGroup.Unit:Show()
829829

830830
-- Reset suggestion list module. This might get modified by unit conditions.
831-
TMW.SUG:EnableEditBox(CndtGroup.Unit, "units", true)
831+
TMW.SUG:EnableEditBox(CndtGroup.Unit, "units", not conditionData.multiUnit)
832+
833+
if conditionData.multiUnit then
834+
CndtGroup.Unit:SetLabel("|cFFFF5050" .. TMW.L["ICONMENU_UNITS"] .. "!|r")
835+
CndtGroup.Unit:SetTexts(TMW.L["ICONMENU_UNITS"], TMW.L["ICONMENU_UNIT_DESC"])
836+
else
837+
CndtGroup.Unit:SetLabel("|cFFFF5050" .. TMW.L["CONDITIONPANEL_UNIT"] .. "!|r")
838+
CndtGroup.Unit:SetTexts(TMW.L["CONDITIONPANEL_UNIT"], TMW.L["ICONMENU_UNIT_DESC_CONDITIONUNIT"])
839+
end
832840

833841
elseif unit == false then
834842
-- No unit, keep editbox and static text hidden

Components/Core/Conditions/Config.xml

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -146,20 +146,17 @@
146146

147147
self:SetSetting("Unit")
148148

149-
self:SetLabel("|cFFFF5050" .. TMW.L["CONDITIONPANEL_UNIT"] .. "!|r")
150-
self:SetTexts(TMW.L["CONDITIONPANEL_UNIT"], TMW.L["ICONMENU_UNIT_DESC_CONDITIONUNIT"])
151-
152-
TMW.SUG:EnableEditBox(self, "units", true)
153-
154-
149+
155150
self:CScriptAdd("ModifyValueForSave", function(self, value)
156151
return TMW:CleanString(value)
157152
end)
158153

159154
self:CScriptAdd("SettingSaved", function()
160155
local unitTable = TMW.UNITS:GetOriginalUnitTable(self:GetText())
156+
local CndtGroup = self:GetParent()
157+
local conditionData = CndtGroup:GetConditionData()
161158

162-
if #unitTable > 1 then
159+
if #unitTable > 1 and not conditionData.multiUnit then
163160
TMW.HELP:Show{
164161
code = "CNDT_UNIT_ONLYONE",
165162
icon = TMW.CI.icon,

Components/Core/Units/Units.lua

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,9 @@ local UnitSet = TMW:NewClass("UnitSet"){
250250
self.allUnitsChangeOnEvent = false
251251

252252
elseif unit:find("^mouseover") then
253-
-- There is a unit when you gain a mouseover, but there isn't one when you lose it, so we can't have events for this one.
253+
-- There is an event when you gain a mouseover, but there isn't one when you lose it, so we can't have events for this one.
254+
-- Additionally, most other UNIT_ events don't properly fire for mouseover, so saying that mouseover has events would
255+
-- cause other functions to assume they can update on unit, even when they can't.
254256
self.allUnitsChangeOnEvent = false
255257

256258
else

Localization/TellMeWhen-enUS.lua

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1742,7 +1742,11 @@ L["PARENTHESIS_TYPE_("] = "opening"
17421742
L["PARENTHESIS_TYPE_)"] = "closing"
17431743
L["NUMAURAS"] = "Number of"
17441744
L["ACTIVE"] = "%d Active"
1745-
L["NUMAURAS_DESC"] = [[This condition checks the number of an aura active - not to be confused with the number of stacks of an aura. This is for checking things like if you have both weapon enchant procs active at the same time. Use sparingly, as the process used to count the numbers is a bit CPU intensive.]]
1745+
L["NUMAURAS_DESC"] = [[This condition checks the number of an aura active - not to be confused with the number of stacks of an aura.
1746+
1747+
This can be used for checking things like if you have both weapon enchant procs active at the same time, or for checking the quantity of a buff active on your raid/group members.
1748+
1749+
This condition's inputs accept multiple units and multiple spells.]]
17461750
L["TOOLTIPSCAN"] = "Aura Variable"
17471751
L["TOOLTIPSCAN_DESC"] = "This condition type will allow you to check the first variable associated with an aura. Numbers are provided by Blizzard API and do not necessarily match numbers found on the tooltip of the aura. There is also no guarantee that a number will be obtained for an aura. In most practical cases, though, the correct number will be checked."
17481752

@@ -2745,7 +2749,7 @@ To see the correct texture now, change the first spell being checked into a Spel
27452749

27462750
L["HELP_NOUNITS"] = [[You must enter at least one unit!]]
27472751
L["HELP_NOUNIT"] = [[You must enter a unit!]]
2748-
L["HELP_ONLYONEUNIT"] = [[Conditions only accept one unit, but you have entered %d |4unit:units;.
2752+
L["HELP_ONLYONEUNIT"] = [[This condition only accepts one unit, but you have entered %d |4unit:units;.
27492753
27502754
If you need to check many units, consider using a separate icon with an Icon Shown condition to reference that icon.]]
27512755

Options/CHANGELOG.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ TMW.CHANGELOG_LASTVER="7.4.0"
44

55
TMW.CHANGELOG = [==[
66
## v10.0.9
7+
* The "Buff - Number Of" and "Debuff - Number Of" conditions now support tracking multiple units (e.g. `group 1-40`). (#1989)
78
* Fix #2066 - Enrage effects were not being stored properly when fetching auras for noncached units.
89
* Fix #2059 - Condition update were not happening at the proper time for spell cooldown conditions.
910
* Fix #2038 - Fix more scenarios in which talent loadout name is not available immediately after talent events fire.

0 commit comments

Comments
 (0)