Skip to content

Commit

Permalink
The "Buff - Number Of" and "Debuff - Number Of" conditions now suppor…
Browse files Browse the repository at this point in the history
…t tracking multiple units (e.g. `group 1-40`). (#1989)
  • Loading branch information
ascott18 committed Feb 27, 2023
1 parent 3b4f266 commit ce566e4
Show file tree
Hide file tree
Showing 9 changed files with 134 additions and 70 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
## v10.0.9
* The "Buff - Number Of" and "Debuff - Number Of" conditions now support tracking multiple units (e.g. `group 1-40`). (#1989)
* Fix #2066 - Enrage effects were not being stored properly when fetching auras for noncached units.
* Fix #2059 - Condition update were not happening at the proper time for spell cooldown conditions.
* Fix #2038 - Fix more scenarios in which talent loadout name is not available immediately after talent events fire.
Expand Down
71 changes: 39 additions & 32 deletions Components/Core/Conditions/Categories/BuffsDebuffs.lua
Original file line number Diff line number Diff line change
Expand Up @@ -60,35 +60,38 @@ function Env.AuraStacksPacked(unit, name, kindKey, onlyMine)
return 0
end

function Env.AuraCount(unit, spells, filter)
function Env.AuraCount(units, spells, filter)
local n = 0
local names = spells.Hash

for i = 1, huge do
local buffName, _, _, _, _, _, _, _, _, id = UnitAura(unit, i, filter)
if not buffName then
return n
elseif names[id] or names[strlowerCache[buffName]] then
n = n + 1
for u = 1, #units do
for i = 1, huge do
local buffName, _, _, _, _, _, _, _, _, id = UnitAura(units[u], i, filter)
if not buffName then
break
elseif names[id] or names[strlowerCache[buffName]] then
n = n + 1
end
end
end

return n
end

function Env.AuraCountPacked(unit, spells, kindKey, onlyMine)
local auras = GetAuras(unit)
local instances = auras.instances
local lookup = auras.lookup
local SpellsArray = spells.Array

function Env.AuraCountPacked(units, spells, kindKey, onlyMine)
local n = 0
for i = 1, #SpellsArray do
for auraInstanceID, isMine in next, lookup[SpellsArray[i]] or empty do
if (isMine or not onlyMine) then
local instance = instances[auraInstanceID]
if instance[kindKey] then
n = n + 1
local SpellsArray = spells.Array
for u = 1, #units do
local auras = GetAuras(units[u])
local instances = auras.instances
local lookup = auras.lookup

for i = 1, #SpellsArray do
for auraInstanceID, isMine in next, lookup[SpellsArray[i]] or empty do
if (isMine or not onlyMine) then
if instances[auraInstanceID][kindKey] then
n = n + 1
end
end
end
end
Expand Down Expand Up @@ -359,7 +362,7 @@ end

local function CanUsePackedAuras(c)
if not GetAuras then return false end
if not TMW.COMMON.Auras:RequestUnits(CNDT:GetUnit(c.Unit)) then return false end
if not TMW.COMMON.Auras:RequestUnits(c.Unit) then return false end
return true
end

Expand Down Expand Up @@ -578,12 +581,13 @@ end
ConditionCategory:RegisterCondition(5, "BUFFNUMBER", {
text = L["ICONMENU_BUFF"] .. " - " .. L["NUMAURAS"],
tooltip = L["NUMAURAS_DESC"],
min = 0,
max = 20,
range = 20,
multiUnit = true,
name = function(editbox)
editbox:SetTexts(L["BUFFTOCHECK"], L["CNDT_MULTIPLEVALID"])
end,
useSUG = true,
allowMultipleSUGEntires = true,
check = function(check)
check:SetTexts(L["ONLYCHECKMINE"], L["ONLYCHECKMINE_DESC"])
end,
Expand All @@ -592,14 +596,15 @@ ConditionCategory:RegisterCondition(5, "BUFFNUMBER", {
tcoords = CNDT.COMMON.standardtcoords,
funcstr = function(c)
if CanUsePackedAuras(c) then
return [[AuraCountPacked(c.Unit, c.Spells, "isHelpful", ]] .. (tostring(c.Checked)) .. [[) c.Operator c.Level]]
return [[AuraCountPacked(c.Units, c.Spells, "isHelpful", ]] .. (tostring(c.Checked)) .. [[) c.Operator c.Level]]
end
return [[AuraCount(c.Unit, c.Spells, "HELPFUL]] .. (c.Checked and " PLAYER" or "") .. [[") c.Operator c.Level]]
return [[AuraCount(c.Units, c.Spells, "HELPFUL]] .. (c.Checked and " PLAYER" or "") .. [[") c.Operator c.Level]]
end,
events = function(ConditionObject, c)
local _, unitSet = TMW:GetUnits(nil, c.Unit)
return
ConditionObject:GetUnitChangedEventString(CNDT:GetUnit(c.Unit)),
ConditionObject:GenerateUnitAuraString(CNDT:GetUnit(c.Unit), TMW:GetSpells(c.Name).First, c.Checked)
ConditionObject:GenerateNormalEventString(unitSet.event),
ConditionObject:GenerateUnitAuraString(unitSet, TMW:GetSpells(c.Name), c.Checked)
end,
})

Expand Down Expand Up @@ -843,12 +848,13 @@ end
ConditionCategory:RegisterCondition(15, "DEBUFFNUMBER", {
text = L["ICONMENU_DEBUFF"] .. " - " .. L["NUMAURAS"],
tooltip = L["NUMAURAS_DESC"],
min = 0,
max = 20,
range = 20,
multiUnit = true,
name = function(editbox)
editbox:SetTexts(L["DEBUFFTOCHECK"], L["CNDT_MULTIPLEVALID"])
end,
useSUG = true,
allowMultipleSUGEntires = true,
check = function(check)
check:SetTexts(L["ONLYCHECKMINE"], L["ONLYCHECKMINE_DESC"])
end,
Expand All @@ -857,14 +863,15 @@ ConditionCategory:RegisterCondition(15, "DEBUFFNUMBER", {
tcoords = CNDT.COMMON.standardtcoords,
funcstr = function(c)
if CanUsePackedAuras(c) then
return [[AuraCountPacked(c.Unit, c.Spells, "isHarmful", ]] .. (tostring(c.Checked)) .. [[) c.Operator c.Level]]
return [[AuraCountPacked(c.Units, c.Spells, "isHarmful", ]] .. (tostring(c.Checked)) .. [[) c.Operator c.Level]]
end
return [[AuraCount(c.Unit, c.Spells, "HARMFUL]] .. (c.Checked and " PLAYER" or "") .. [[") c.Operator c.Level]]
return [[AuraCount(c.Units, c.Spells, "HARMFUL]] .. (c.Checked and " PLAYER" or "") .. [[") c.Operator c.Level]]
end,
events = function(ConditionObject, c)
local _, unitSet = TMW:GetUnits(nil, c.Unit)
return
ConditionObject:GetUnitChangedEventString(CNDT:GetUnit(c.Unit)),
ConditionObject:GenerateUnitAuraString(CNDT:GetUnit(c.Unit), TMW:GetSpells(c.Name).First, c.Checked)
ConditionObject:GetUnitChangedEventString(unitSet),
ConditionObject:GenerateUnitAuraString(unitSet, TMW:GetSpells(c.Name), c.Checked)
end,
})

Expand Down
65 changes: 55 additions & 10 deletions Components/Core/Conditions/ConditionObject.lua
Original file line number Diff line number Diff line change
Expand Up @@ -426,40 +426,79 @@ function ConditionObject:IsUnitAuraEventRelevant(updatedAuras, spell, onlyMine)
end
end

function ConditionObject:IsTmwAuraEventRelevant(updatedAuras, spellSet, onlyMine)
-- updatedAuras = { [name | id | dispelType] = mightBeMine(bool) }

local Hash = spellSet.Hash
for identifier, mightBeMine in next, updatedAuras do
if Hash[identifier] and (mightBeMine or not OnlyMine) then
return true
end
end
end

function ConditionObject:GenerateUnitAuraString(unit, spell, onlyMine)
local unitCheck = type(unit) == "table"
-- unit is a UnitSet:
and ("%s[arg1]"):format(CNDT:GetTableSubstitution(unit.UnitsLookup))
-- unit is a unitID:
or ("arg1 == %q"):format(unit)


if TMW.COMMON.Auras then
local canUsePacked, auraEvent = TMW.COMMON.Auras:RequestUnits(unit)

self:RequestEvent(auraEvent)
self:SetNumEventArgs(2)

local str = ("event == %q and arg1 == %q"):format(auraEvent, unit)
-- arg2 is payload:
-- If it is nil, the event is a general update for the unit
-- If it is a table, its keys are names/ids of what changed
-- and the values indicate if the aura might have been mine.
.. " and (not arg2 or arg2["
if type(spell) == "table" then
-- If we were passed a spell set that only contains one spell,
-- inline that spell so we can avoid the function call to IsTmwAuraEventRelevant
if #spell.Array == 1 then
spell = spell.First
end
end

local str = ("event == %q and %s and"):format(auraEvent, unitCheck)

-- arg2 is payload:
-- If it is nil, the event is a general update for the unit.
-- If it is a table, its keys are names/ids of what changed
-- and the values indicate if the aura might have been mine.
if type(spell) == "table" then
-- Spell is a SpellSet
str = str .. " (not arg2 or ConditionObject:IsTmwAuraEventRelevant(arg2,"
.. CNDT:GetTableSubstitution(spell)
.. ", "
.. tostring(onlyMine)
.. "))"
else
-- Spell is a name/id
str = str .. " (not arg2 or arg2["
.. (type(spell) == "string" and format("%q", spell) or tostring(spell))
.. "]"
.. (onlyMine and "" or " ~= nil")
.. ")"
end

return str
else
self:RequestEvent("UNIT_AURA")
self:SetNumEventArgs(3)

local str = "event == 'UNIT_AURA' and arg1 == "
.. format("%q", unit)
local str = ("event == %q and %s"):format("UNIT_AURA", unitCheck)

if type(spell) == "string" then
-- arg2 is isFullUpdate:
-- If it is nil, the client doesn't support the new UNIT_AURA payload.
-- If it is true, the event isn't about any particular aura.
-- If it is false, then arg3 is `updatedAuras`.
.. " and (arg2 ~= false or ConditionObject:IsUnitAuraEventRelevant(arg3,"
str = str .. " and (arg2 ~= false or ConditionObject:IsUnitAuraEventRelevant(arg3,"
.. (type(spell) == "string" and format("%q", spell) or tostring(spell))
.. ", "
.. tostring(onlyMine)
.. "))"
end

return str
end
Expand All @@ -476,7 +515,13 @@ end
-- ConditionObject:GenerateNormalEventString("UNIT_HEALTH", CNDT:GetUnit(c.Unit))
-- end,
function ConditionObject:GetUnitChangedEventString(unit)
if unit == "player" then
if type(unit) == "table" then
-- unit is a UnitSet
if not unit.allUnitsChangeOnEvent then
return false
end
return unit.event
elseif unit == "player" then
-- Returning false (as a string, not a boolean) won't cause responses to any events,
-- and it also won't make the ConditionObject default to being OnUpdate driven.

Expand Down
33 changes: 16 additions & 17 deletions Components/Core/Conditions/Conditions.lua
Original file line number Diff line number Diff line change
Expand Up @@ -851,6 +851,22 @@ CNDT.Substitutions = {
end,
},

{
src = "c.Units",
rep = function(conditionData, conditionSettings, name, name2)
return CNDT:GetTableSubstitution(TMW:GetUnits(nil, conditionSettings.Unit), "Units")
end,
},
{
src = "c%.Unit(2?)",
rep = function(conditionData, conditionSettings, name, name2)
return function(two)
local unit = two == "2" and conditionSettings.Name or conditionSettings.Unit
return CNDT:GetConditionUnitSubstitution(unit)
end
end,
},

{
src = "c.Item2",
rep = function(conditionData, conditionSettings, name, name2)
Expand Down Expand Up @@ -931,23 +947,6 @@ end
-- [INTERNAL]
function CNDT:DoConditionSubstitutions(conditionData, conditionSettings, funcstr)
-- Substitutes all the c.XXXXX substitutions into a string.

for _, append in TMW:Vararg("2", "") do -- Unit2 MUST be before Unit
if strfind(funcstr, "c.Unit" .. append) then
-- Use CNDT:GetUnit() first to grab only the first unit
-- in case the configured value is an expansion (like party1-4).
local unit
if append == "2" then
unit = CNDT:GetUnit(conditionSettings.Name)
elseif append == "" then
unit = CNDT:GetUnit(conditionSettings.Unit)
end
if unit then
funcstr = gsub(funcstr, "c.Unit" .. append, CNDT:GetConditionUnitSubstitution(unit))
end
end
end

local name = conditionNameSettingProcessedCache[conditionSettings.Name]
local name2 = conditionNameSettingProcessedCache[conditionSettings.Name2]

Expand Down
10 changes: 9 additions & 1 deletion Components/Core/Conditions/Config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -828,7 +828,15 @@ TMW:RegisterCallback("TMW_CNDT_GROUP_DRAWGROUP", function(event, CndtGroup, cond
CndtGroup.Unit:Show()

-- Reset suggestion list module. This might get modified by unit conditions.
TMW.SUG:EnableEditBox(CndtGroup.Unit, "units", true)
TMW.SUG:EnableEditBox(CndtGroup.Unit, "units", not conditionData.multiUnit)

if conditionData.multiUnit then
CndtGroup.Unit:SetLabel("|cFFFF5050" .. TMW.L["ICONMENU_UNITS"] .. "!|r")
CndtGroup.Unit:SetTexts(TMW.L["ICONMENU_UNITS"], TMW.L["ICONMENU_UNIT_DESC"])
else
CndtGroup.Unit:SetLabel("|cFFFF5050" .. TMW.L["CONDITIONPANEL_UNIT"] .. "!|r")
CndtGroup.Unit:SetTexts(TMW.L["CONDITIONPANEL_UNIT"], TMW.L["ICONMENU_UNIT_DESC_CONDITIONUNIT"])
end

elseif unit == false then
-- No unit, keep editbox and static text hidden
Expand Down
11 changes: 4 additions & 7 deletions Components/Core/Conditions/Config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -146,20 +146,17 @@

self:SetSetting("Unit")

self:SetLabel("|cFFFF5050" .. TMW.L["CONDITIONPANEL_UNIT"] .. "!|r")
self:SetTexts(TMW.L["CONDITIONPANEL_UNIT"], TMW.L["ICONMENU_UNIT_DESC_CONDITIONUNIT"])

TMW.SUG:EnableEditBox(self, "units", true)



self:CScriptAdd("ModifyValueForSave", function(self, value)
return TMW:CleanString(value)
end)

self:CScriptAdd("SettingSaved", function()
local unitTable = TMW.UNITS:GetOriginalUnitTable(self:GetText())
local CndtGroup = self:GetParent()
local conditionData = CndtGroup:GetConditionData()

if #unitTable > 1 then
if #unitTable > 1 and not conditionData.multiUnit then
TMW.HELP:Show{
code = "CNDT_UNIT_ONLYONE",
icon = TMW.CI.icon,
Expand Down
4 changes: 3 additions & 1 deletion Components/Core/Units/Units.lua
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,9 @@ local UnitSet = TMW:NewClass("UnitSet"){
self.allUnitsChangeOnEvent = false

elseif unit:find("^mouseover") then
-- 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.
-- 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.
-- Additionally, most other UNIT_ events don't properly fire for mouseover, so saying that mouseover has events would
-- cause other functions to assume they can update on unit, even when they can't.
self.allUnitsChangeOnEvent = false

else
Expand Down
8 changes: 6 additions & 2 deletions Localization/TellMeWhen-enUS.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1742,7 +1742,11 @@ L["PARENTHESIS_TYPE_("] = "opening"
L["PARENTHESIS_TYPE_)"] = "closing"
L["NUMAURAS"] = "Number of"
L["ACTIVE"] = "%d Active"
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.]]
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 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.
This condition's inputs accept multiple units and multiple spells.]]
L["TOOLTIPSCAN"] = "Aura Variable"
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."

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

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

Expand Down
1 change: 1 addition & 0 deletions Options/CHANGELOG.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ TMW.CHANGELOG_LASTVER="7.4.0"

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

0 comments on commit ce566e4

Please sign in to comment.