d3c8a9bf3e
Arguments might still shut be shared from the original call for custom auras. More functions need to be added to prototypes. Documentation is needed soon.
3634 lines
112 KiB
Lua
3634 lines
112 KiB
Lua
--[[ BuffTrigger2.lua
|
|
This file contains the "aura2" trigger for buffs and debuffs. It is intended to replace
|
|
the buff trigger old BuffTrigger at some future point
|
|
|
|
It registers the BuffTrigger table for the trigger type "aura2" and has the following API:
|
|
|
|
Add(data)
|
|
Adds an aura, setting up internal data structures for all buff triggers.
|
|
|
|
LoadDisplays(id)
|
|
Loads the aura ids, enabling all buff triggers in the aura.
|
|
|
|
UnloadDisplays(id)
|
|
Unloads the aura ids, disabling all buff triggers in the aura.
|
|
|
|
UnloadAll()
|
|
Unloads all auras, disabling all buff triggers.
|
|
|
|
Delete(id)
|
|
Removes all data for aura id.
|
|
|
|
Rename(oldid, newid)
|
|
Updates all data for aura oldid to use newid.
|
|
|
|
Modernize(data)
|
|
Updates all buff triggers in data.
|
|
|
|
#####################################################
|
|
# Helper functions mainly for the WeakAuras Options #
|
|
#####################################################
|
|
|
|
GetOverlayInfo(data, triggernum)
|
|
Returns a table containing all overlays. Currently there aren't any
|
|
|
|
CanHaveTooltip(data, triggernum)
|
|
Returns the type of tooltip to show for the trigger.
|
|
|
|
GetNameAndIcon(data, triggernum)
|
|
Returns the name and icon to show in the options.
|
|
|
|
GetAdditionalProperties(data, triggernum)
|
|
Returns the tooltip text for additional properties.
|
|
|
|
GetTriggerConditions(data, triggernum)
|
|
Returns the potential conditions for a trigger
|
|
]]--
|
|
if not WeakAuras.IsLibsOK() then return end
|
|
local AddonName, Private = ...
|
|
|
|
-- Lua APIs
|
|
local tinsert, wipe = table.insert, wipe
|
|
local pairs, next, type = pairs, next, type
|
|
local UnitAura = UnitAura
|
|
|
|
local WeakAuras = WeakAuras
|
|
local L = WeakAuras.L
|
|
local timer = WeakAuras.timer
|
|
local BuffTrigger = {}
|
|
local triggerInfos = {}
|
|
|
|
local watched_trigger_events = Private.watched_trigger_events
|
|
|
|
-- keyed on unit, debuffType, spellname, with a scan object value
|
|
-- scan object: id, triggernum, scanFunc
|
|
local scanFuncName = {}
|
|
local scanFuncSpellId = {}
|
|
local scanFuncGeneral = {}
|
|
|
|
-- same as above but for group triggers
|
|
local scanFuncNameGroup = {}
|
|
local scanFuncSpellIdGroup = {}
|
|
local scanFuncGeneralGroup = {}
|
|
|
|
-- Contains all scanFuncs that should be check if the existence of a unit changed
|
|
local unitExistScanFunc = {}
|
|
-- Which units exist
|
|
local existingUnits = {}
|
|
|
|
-- Loaded ScanFuncs per unit type
|
|
local groupScanFuncs = {}
|
|
--Active ScanFuncs per actual unit id
|
|
local activeGroupScanFuncs = {}
|
|
|
|
local raidMarkScanFuncs = {}
|
|
|
|
-- Multi Target tracking
|
|
local scanFuncNameMulti = {}
|
|
local scanFuncSpellIdMulti = {}
|
|
local cleanupTimerMulti = {}
|
|
|
|
-- Auras that matched, unit, index
|
|
local matchData = {}
|
|
|
|
local matchDataUpToDate = {}
|
|
|
|
local matchDataMulti = {}
|
|
-- Auras that matched, keyed on id, triggernum, kept in sync with matchData
|
|
local matchDataByTrigger = {}
|
|
|
|
local matchDataChanged = {}
|
|
|
|
local nameplateExists = {}
|
|
local unitVisible = {}
|
|
|
|
-- Returns whether a unit id exists. If it exists, the GUID is returned
|
|
-- Otherwise false
|
|
-- Work around a issue where UnitExists returns true for nameplates even
|
|
-- if the nameplate doesn't exist anymore
|
|
local function UnitExistsFixed(unit)
|
|
if #unit > 9 and unit:sub(1, 9) == "nameplate" then
|
|
return nameplateExists[unit] or false
|
|
end
|
|
return UnitExists(unit) or UnitGUID(unit) or false
|
|
end
|
|
|
|
local function UnitIsVisibleFixed(unit)
|
|
if unitVisible[unit] == nil then
|
|
unitVisible[unit] = UnitIsVisible(unit)
|
|
end
|
|
return unitVisible[unit]
|
|
end
|
|
|
|
local function UnitInSubgroupOrPlayer(unit, includePets)
|
|
if includePets == nil then
|
|
return UnitIsUnit("player", unit)
|
|
elseif includePets == "PlayersAndPets" then
|
|
return UnitIsUnit("player", unit) or UnitIsUnit("pet", unit)
|
|
elseif includePets == "PetsOnly" then
|
|
return UnitIsUnit("pet", unit)
|
|
end
|
|
end
|
|
|
|
local function GetOrCreateSubTable(base, next, ...)
|
|
if not next then
|
|
return base
|
|
end
|
|
|
|
base[next] = base[next] or {}
|
|
return GetOrCreateSubTable(base[next], ...)
|
|
end
|
|
|
|
local function GetSubTable(base, next, ...)
|
|
if not base then
|
|
return nil
|
|
end
|
|
|
|
if not next then
|
|
return base
|
|
end
|
|
|
|
return GetSubTable(base[next], ...)
|
|
end
|
|
|
|
local function IsGroupTrigger(trigger)
|
|
return trigger.unit == "group" or trigger.unit == "party" or trigger.unit == "raid"
|
|
or trigger.unit == "boss" or trigger.unit == "nameplate" or trigger.unit == "arena" or trigger.unit == "multi"
|
|
end
|
|
|
|
local function IsSingleMissing(trigger)
|
|
return not IsGroupTrigger(trigger) and trigger.matchesShowOn == "showOnMissing"
|
|
end
|
|
|
|
local function CanHaveMatchCheck(trigger)
|
|
if IsGroupTrigger(trigger) then
|
|
return true
|
|
end
|
|
if trigger.matchesShowOn == "showOnMissing" then
|
|
return false
|
|
end
|
|
if trigger.matchesShowOn == "showOnActive" or trigger.matchesShowOn == "showOnMatches" or not trigger.matchesShowOn then
|
|
return true
|
|
end
|
|
-- Always: If clones are shown
|
|
return trigger.showClones
|
|
end
|
|
|
|
local function HasMatchCount(trigger)
|
|
if IsGroupTrigger(trigger) then
|
|
return trigger.useMatch_count
|
|
else
|
|
return trigger.matchesShowOn == "showOnMatches"
|
|
end
|
|
end
|
|
|
|
local function ReferenceMatchData(id, triggernum, unit, filter, index)
|
|
local match = matchData[unit][filter][index]
|
|
local base = GetOrCreateSubTable(matchDataByTrigger, id, triggernum, unit)
|
|
|
|
base[index] = match
|
|
|
|
match.auras[id] = match.auras[id] or {}
|
|
match.auras[id][triggernum] = true
|
|
end
|
|
|
|
local function ScanMatchData(time, triggerInfo, unit, filter)
|
|
if matchData[unit] and matchData[unit][filter] then
|
|
for index, match in pairs(matchData[unit][filter]) do
|
|
if (not triggerInfo.auranames and not triggerInfo.auraspellids)
|
|
or (triggerInfo.auranames and tContains(triggerInfo.auranames, match.name))
|
|
or (triggerInfo.auraspellids and tContains(triggerInfo.auraspellids, match.spellId)) then
|
|
if triggerInfo.fetchTooltip then
|
|
matchData[unit][filter][index]:UpdateTooltip(time)
|
|
end
|
|
if not triggerInfo.scanFunc or triggerInfo.scanFunc(time, matchData[unit][filter][index]) then
|
|
local id = triggerInfo.id
|
|
local triggernum = triggerInfo.triggernum
|
|
ReferenceMatchData(id, triggernum, unit, filter, index)
|
|
matchDataChanged[id] = matchDataChanged[id] or {}
|
|
matchDataChanged[id][triggernum] = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function ReferenceMatchDataMulti(matchData, id, triggernum, destGUID)
|
|
local needToInsert = false
|
|
|
|
matchData.auras[id] = matchData.auras[id] or {}
|
|
needToInsert = not matchData.auras[id][triggernum]
|
|
matchData.auras[id][triggernum] = true
|
|
|
|
if needToInsert then
|
|
local matchDataByTriggerBase = GetOrCreateSubTable(matchDataByTrigger, id, triggernum, destGUID)
|
|
tinsert(matchDataByTriggerBase, matchData)
|
|
end
|
|
matchDataChanged[id] = matchDataChanged[id] or {}
|
|
matchDataChanged[id][triggernum] = true
|
|
end
|
|
|
|
local function MatchesTriggerInfoMulti(triggerInfo, sourceGUID)
|
|
if triggerInfo.ownOnly then
|
|
return sourceGUID == UnitGUID("player") or sourceGUID == UnitGUID("pet")
|
|
elseif triggerInfo.ownOnly == false then
|
|
return sourceGUID ~= UnitGUID("player") and sourceGUID ~= UnitGUID("pet")
|
|
else
|
|
return true
|
|
end
|
|
end
|
|
|
|
local function UpdateToolTipDataInMatchData(matchData, time)
|
|
if matchData.tooltipUpdated == time then
|
|
return
|
|
end
|
|
local changed = false
|
|
|
|
if matchData.unit and matchData.index and matchData.filter then
|
|
local tooltip, _, tooltip1, tooltip2, tooltip3, tooltip4 = WeakAuras.GetAuraTooltipInfo(matchData.unit, matchData.index, matchData.filter)
|
|
|
|
changed = matchData.tooltip ~= tooltip or matchData.tooltip1 ~= tooltip1
|
|
or matchData.tooltip2 ~= tooltip2 or matchData.tooltip3 ~= tooltip3 or matchData.tooltip4 ~= tooltip4
|
|
matchData.tooltip, matchData.tooltip1, matchData.tooltip2, matchData.tooltip3, matchData.tooltip4 = tooltip, tooltip1, tooltip2, tooltip3, tooltip4
|
|
end
|
|
|
|
matchData.tooltipUpdated = time
|
|
return changed
|
|
end
|
|
|
|
local function UpdateMatchData(time, matchDataChanged, unit, index, filter, name, icon, stacks, debuffClass, duration, expirationTime, unitCaster, isStealable, _, spellId)
|
|
if not matchData[unit] then
|
|
matchData[unit] = {}
|
|
end
|
|
if not matchData[unit][filter] then
|
|
matchData[unit][filter] = {}
|
|
end
|
|
if not matchData[unit][filter][index] then
|
|
matchData[unit][filter][index] = {
|
|
name = name,
|
|
icon = icon,
|
|
stacks = stacks,
|
|
debuffClass = debuffClass,
|
|
duration = duration,
|
|
expirationTime = expirationTime,
|
|
unitCaster = unitCaster,
|
|
casterName = unitCaster and GetUnitName(unitCaster, false) or "",
|
|
spellId = spellId,
|
|
unit = unit,
|
|
unitName = GetUnitName(unit, false) or "",
|
|
isStealable = isStealable,
|
|
time = time,
|
|
lastChanged = time,
|
|
filter = filter,
|
|
index = index,
|
|
UpdateTooltip = UpdateToolTipDataInMatchData,
|
|
auras = {}
|
|
}
|
|
return true
|
|
end
|
|
|
|
local data = matchData[unit][filter][index]
|
|
local changed = false
|
|
|
|
if data.name ~= name then
|
|
data.name = name
|
|
changed = true
|
|
end
|
|
|
|
if data.icon ~= icon then
|
|
data.icon = icon
|
|
changed = true
|
|
end
|
|
|
|
if data.stacks ~= stacks then
|
|
data.stacks = stacks
|
|
changed = true
|
|
end
|
|
|
|
if data.debuffClass ~= debuffClass then
|
|
data.debuffClass = debuffClass
|
|
changed = true
|
|
end
|
|
|
|
if data.duration ~= duration then
|
|
data.duration = duration
|
|
changed = true
|
|
end
|
|
|
|
if data.expirationTime ~= expirationTime then
|
|
data.expirationTime = expirationTime
|
|
changed = true
|
|
end
|
|
|
|
if data.unitCaster ~= unitCaster then
|
|
data.unitCaster = unitCaster
|
|
changed = true
|
|
end
|
|
|
|
local casterName = unitCaster and GetUnitName(unitCaster, false) or ""
|
|
if data.casterName ~= casterName then
|
|
data.casterName = casterName
|
|
changed = true
|
|
end
|
|
|
|
if data.spellId ~= spellId then
|
|
data.spellId = spellId
|
|
changed = true
|
|
end
|
|
|
|
if data.isStealable ~= isStealable then
|
|
data.isStealable = isStealable
|
|
changed = true
|
|
end
|
|
|
|
local unitName = GetUnitName(unit, false) or ""
|
|
if data.unitName ~= unitName then
|
|
data.unitName = unitName
|
|
changed = true
|
|
end
|
|
|
|
if data.tooltipUpdated and data.tooltipUpdated < time then
|
|
changed = data:UpdateTooltip(time) or changed
|
|
end
|
|
|
|
if changed then
|
|
data.lastChanged = time
|
|
end
|
|
|
|
if changed then
|
|
-- Tell old auras that used this match data
|
|
for id, triggerData in pairs(data.auras) do
|
|
for triggernum in pairs(triggerData) do
|
|
if matchDataByTrigger[id]
|
|
and matchDataByTrigger[id][triggernum]
|
|
and matchDataByTrigger[id][triggernum][unit]
|
|
and matchDataByTrigger[id][triggernum][unit][index]
|
|
then
|
|
matchDataByTrigger[id][triggernum][unit][index] = nil
|
|
matchDataChanged[id] = matchDataChanged[id] or {}
|
|
matchDataChanged[id][triggernum] = true
|
|
end
|
|
end
|
|
end
|
|
wipe(data.auras)
|
|
end
|
|
|
|
data.index = index
|
|
data.time = time
|
|
data.unit = unit
|
|
|
|
return changed or data.lastChanged == time
|
|
end
|
|
|
|
local function calculateNextCheck(triggerInfoRemaining, auraDataRemaining, auraDataExpirationTime, nextCheck)
|
|
if auraDataRemaining > 0 and auraDataRemaining >= triggerInfoRemaining then
|
|
if not nextCheck then
|
|
return auraDataExpirationTime - triggerInfoRemaining
|
|
else
|
|
return min(auraDataExpirationTime - triggerInfoRemaining, nextCheck)
|
|
end
|
|
end
|
|
return nextCheck
|
|
end
|
|
|
|
local function FindBestMatchData(time, id, triggernum, triggerInfo, matchedUnits)
|
|
-- Find best match
|
|
local bestMatch = nil
|
|
local matchCount = 0
|
|
local unitCount = 0
|
|
local stackCount = 0
|
|
local nextCheck
|
|
|
|
if not matchDataByTrigger[id] or not matchDataByTrigger[id][triggernum] then
|
|
return nil, 0, 0
|
|
end
|
|
|
|
for unit, unitData in pairs(matchDataByTrigger[id][triggernum]) do
|
|
local unitCounted = false
|
|
for index, auraData in pairs(unitData) do
|
|
local remCheck = true
|
|
if triggerInfo.remainingFunc and auraData.expirationTime then
|
|
if auraData.duration == 0 then
|
|
remCheck = false
|
|
else
|
|
local remaining = auraData.expirationTime - time
|
|
remCheck = triggerInfo.remainingFunc(remaining)
|
|
nextCheck = calculateNextCheck(triggerInfo.remainingCheck, remaining, auraData.expirationTime, nextCheck)
|
|
end
|
|
end
|
|
|
|
if remCheck then
|
|
matchCount = matchCount + 1
|
|
stackCount = stackCount + (auraData.stacks or 0)
|
|
matchedUnits[unit] = true
|
|
if not unitCounted then
|
|
unitCount = unitCount + 1
|
|
unitCounted = true
|
|
end
|
|
if not bestMatch or triggerInfo.compareFunc(bestMatch, auraData) then
|
|
bestMatch = auraData
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return bestMatch, matchCount, unitCount, stackCount, nextCheck
|
|
end
|
|
|
|
local function FindBestMatchDataForUnit(time, id, triggernum, triggerInfo, unit)
|
|
-- Find best match
|
|
local bestMatch = nil
|
|
local matchCount = 0
|
|
local stackCount = 0
|
|
local nextCheck
|
|
|
|
if not matchDataByTrigger[id] or not matchDataByTrigger[id][triggernum] or not matchDataByTrigger[id][triggernum][unit] then
|
|
return nil, 0
|
|
end
|
|
|
|
for index, auraData in pairs(matchDataByTrigger[id][triggernum][unit]) do
|
|
local remCheck = true
|
|
if triggerInfo.remainingFunc and auraData.expirationTime then
|
|
if auraData.expirationTime == 0 then
|
|
remCheck = false
|
|
else
|
|
local remaining = auraData.expirationTime - time
|
|
remCheck = triggerInfo.remainingFunc(remaining)
|
|
nextCheck = calculateNextCheck(triggerInfo.remainingCheck, remaining, auraData.expirationTime, nextCheck)
|
|
end
|
|
end
|
|
|
|
if remCheck then
|
|
matchCount = matchCount + 1
|
|
stackCount = stackCount + (auraData.stacks or 0)
|
|
if not bestMatch or triggerInfo.compareFunc(bestMatch, auraData) then
|
|
bestMatch = auraData
|
|
end
|
|
end
|
|
end
|
|
return bestMatch, matchCount, stackCount, nextCheck
|
|
end
|
|
|
|
local function UpdateStateWithMatch(time, bestMatch, triggerStates, cloneId, matchCount, unitCount, maxUnitCount, matchCountPerUnit, totalStacks, affected, affectedUnits, unaffected, unaffectedUnits, raidMark)
|
|
if not triggerStates[cloneId] then
|
|
triggerStates[cloneId] = {
|
|
show = true,
|
|
changed = true,
|
|
name = bestMatch.name,
|
|
icon = bestMatch.icon,
|
|
stacks = bestMatch.stacks,
|
|
debuffClass = bestMatch.debuffClass,
|
|
progressType = "timed",
|
|
duration = bestMatch.duration,
|
|
expirationTime = bestMatch.expirationTime,
|
|
unitCaster = bestMatch.unitCaster,
|
|
casterName = bestMatch.casterName,
|
|
spellId = bestMatch.spellId,
|
|
index = bestMatch.index,
|
|
filter = bestMatch.filter,
|
|
unit = bestMatch.unit,
|
|
unitName = bestMatch.unitName,
|
|
GUID = bestMatch.unit and UnitGUID(bestMatch.unit) or bestMatch.GUID,
|
|
raidMark = raidMark,
|
|
matchCount = matchCount,
|
|
unitCount = unitCount,
|
|
maxUnitCount = maxUnitCount,
|
|
matchCountPerUnit = matchCountPerUnit,
|
|
tooltip = bestMatch.tooltip,
|
|
tooltip1 = bestMatch.tooltip1,
|
|
tooltip2 = bestMatch.tooltip2,
|
|
tooltip3 = bestMatch.tooltip3,
|
|
tooltip4 = bestMatch.tooltip4,
|
|
affected = affected,
|
|
affectedUnits = affectedUnits,
|
|
unaffected = unaffected,
|
|
unaffectedUnits = unaffectedUnits,
|
|
totalStacks = totalStacks,
|
|
initialTime = time,
|
|
refreshTime = time,
|
|
active = true,
|
|
time = time,
|
|
}
|
|
return true
|
|
else
|
|
local state = triggerStates[cloneId]
|
|
local changed = false
|
|
state.time = time
|
|
|
|
if state.unit ~= bestMatch.unit then
|
|
state.unit = bestMatch.unit
|
|
changed = true
|
|
end
|
|
|
|
local GUID = bestMatch.unit and UnitGUID(bestMatch.unit) or bestMatch.GUID
|
|
if state.GUID ~= GUID then
|
|
state.GUID = GUID
|
|
changed = true
|
|
end
|
|
|
|
if state.raidMark ~= raidMark then
|
|
state.raidMark = raidMark
|
|
changed = true
|
|
end
|
|
|
|
if state.unitName ~= bestMatch.unitName then
|
|
state.unitName = bestMatch.unitName
|
|
changed = true
|
|
end
|
|
|
|
if state.show ~= true then
|
|
state.show = true
|
|
changed = true
|
|
end
|
|
|
|
if state.name ~= bestMatch.name then
|
|
state.name = bestMatch.name
|
|
changed = true
|
|
end
|
|
|
|
if state.icon ~= bestMatch.icon then
|
|
state.icon = bestMatch.icon
|
|
changed = true
|
|
end
|
|
|
|
if state.stacks ~= bestMatch.stacks then
|
|
if state.stacks and bestMatch.stacks then
|
|
if state.stacks < bestMatch.stacks then
|
|
state.stackGainTime = time
|
|
state.stackLostTime = nil
|
|
else
|
|
state.stackGainTime = nil
|
|
state.stackLostTime = time
|
|
end
|
|
end
|
|
state.stacks = bestMatch.stacks
|
|
changed = true
|
|
end
|
|
|
|
if state.debuffClass ~= bestMatch.debuffClass then
|
|
state.debuffClass = bestMatch.debuffClass
|
|
changed = true
|
|
end
|
|
|
|
if state.duration ~= bestMatch.duration then
|
|
state.duration = bestMatch.duration
|
|
changed = true
|
|
end
|
|
|
|
if not state.initialTime then
|
|
-- Only set initialTime if it wasn't set before
|
|
state.initialTime = time
|
|
changed = true
|
|
end
|
|
|
|
if state.expirationTime ~= bestMatch.expirationTime then
|
|
-- A bit fuzzy checking
|
|
if state.expirationTime and bestMatch.expirationTime and bestMatch.expirationTime - state.expirationTime > 0.2 then
|
|
state.refreshTime = time
|
|
end
|
|
state.expirationTime = bestMatch.expirationTime
|
|
changed = true
|
|
end
|
|
|
|
if state.progressType ~= "timed" then
|
|
state.progressType = "timed"
|
|
changed = true
|
|
end
|
|
|
|
if state.unitCaster ~= bestMatch.unitCaster then
|
|
state.unitCaster = bestMatch.unitCaster
|
|
state.casterName = bestMatch.casterName
|
|
changed = true
|
|
end
|
|
|
|
if state.spellId ~= bestMatch.spellId then
|
|
state.spellId = bestMatch.spellId
|
|
changed = true
|
|
end
|
|
|
|
if state.index ~= bestMatch.index then
|
|
state.index = bestMatch.index
|
|
changed = true
|
|
end
|
|
|
|
if state.filter ~= bestMatch.filter then
|
|
state.filter = bestMatch.filter
|
|
changed = true
|
|
end
|
|
|
|
if state.tooltip ~= bestMatch.tooltip then
|
|
state.tooltip = bestMatch.tooltip
|
|
changed = true
|
|
end
|
|
|
|
if state.tooltip1 ~= bestMatch.tooltip1 then
|
|
state.tooltip1 = bestMatch.tooltip1
|
|
changed = true
|
|
end
|
|
|
|
if state.tooltip2 ~= bestMatch.tooltip2 then
|
|
state.tooltip2 = bestMatch.tooltip2
|
|
changed = true
|
|
end
|
|
|
|
if state.tooltip3 ~= bestMatch.tooltip3 then
|
|
state.tooltip3 = bestMatch.tooltip3
|
|
changed = true
|
|
end
|
|
|
|
if state.tooltip4 ~= bestMatch.tooltip4 then
|
|
state.tooltip4 = bestMatch.tooltip4
|
|
changed = true
|
|
end
|
|
|
|
if state.matchCount ~= matchCount then
|
|
state.matchCount = matchCount
|
|
changed = true
|
|
end
|
|
|
|
if state.unitCount ~= unitCount then
|
|
state.unitCount = unitCount
|
|
changed = true
|
|
end
|
|
|
|
if state.maxUnitCount ~= maxUnitCount then
|
|
state.maxUnitCount = maxUnitCount
|
|
changed = true
|
|
end
|
|
|
|
if state.matchCountPerUnit ~= matchCountPerUnit then
|
|
state.matchCountPerUnit = matchCountPerUnit
|
|
changed = true
|
|
end
|
|
|
|
if state.affected ~= affected then
|
|
state.affected = affected
|
|
state.affectedUnits = affectedUnits
|
|
changed = true
|
|
end
|
|
|
|
if state.unaffected ~= unaffected then
|
|
state.unaffected = unaffected
|
|
state.unaffectedUnits = unaffectedUnits
|
|
changed = true
|
|
end
|
|
|
|
if state.active ~= true then
|
|
state.active = true
|
|
changed = true
|
|
end
|
|
|
|
if state.totalStacks ~= totalStacks then
|
|
state.totalStacks = totalStacks
|
|
changed = true
|
|
end
|
|
|
|
if changed then
|
|
state.changed = true
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
|
|
local function UpdateStateWithNoMatch(time, triggerStates, triggerInfo, cloneId, unit, matchCount, unitCount, maxUnitCount, matchCountPerUnit, totalStacks, affected, affectedUnits, unaffected, unaffectedUnits, raidMark)
|
|
local fallbackName, fallbackIcon = BuffTrigger.GetNameAndIconSimple(WeakAuras.GetData(triggerInfo.id), triggerInfo.triggernum)
|
|
if not triggerStates[cloneId] then
|
|
triggerStates[cloneId] = {
|
|
show = true,
|
|
changed = true,
|
|
progressType = 'timed',
|
|
duration = 0,
|
|
expirationTime = math.huge,
|
|
matchCount = matchCount,
|
|
unitCount = unitCount,
|
|
maxUnitCount = maxUnitCount,
|
|
matchCountPerUnit = matchCountPerUnit,
|
|
active = false,
|
|
time = time,
|
|
affected = affected,
|
|
affectedUnits = affectedUnits,
|
|
unaffected = unaffected,
|
|
unaffectedUnits = unaffectedUnits,
|
|
unit = unit,
|
|
raidMark = raidMark,
|
|
unitName = unit and GetUnitName(unit, false) or "",
|
|
destName = "",
|
|
name = fallbackName,
|
|
icon = fallbackIcon,
|
|
totalStacks = totalStacks
|
|
}
|
|
return true
|
|
else
|
|
local state = triggerStates[cloneId]
|
|
state.time = time
|
|
local changed = false
|
|
|
|
if state.show ~= true then
|
|
state.show = true
|
|
changed = true
|
|
end
|
|
|
|
if state.name ~= fallbackName then
|
|
state.name = fallbackName
|
|
changed = true
|
|
end
|
|
|
|
if state.icon ~= fallbackIcon then
|
|
state.icon = fallbackIcon
|
|
changed = true
|
|
end
|
|
|
|
if state.stacks then
|
|
state.stacks = nil
|
|
changed = true
|
|
end
|
|
|
|
if state.duration then
|
|
state.duration = nil
|
|
changed = true
|
|
end
|
|
|
|
if state.initialTime then
|
|
state.initialTime = nil
|
|
changed = true
|
|
end
|
|
if state.refreshTime then
|
|
state.refreshTime = nil
|
|
changed = true
|
|
end
|
|
|
|
if state.expirationTime ~= math.huge then
|
|
state.expirationTime = math.huge
|
|
changed = true
|
|
end
|
|
|
|
if state.progressType then
|
|
state.progressType = nil
|
|
changed = true
|
|
end
|
|
|
|
if state.unit ~= unit then
|
|
state.unit = unit
|
|
changed = true
|
|
end
|
|
|
|
local GUID = unit and UnitGUID(unit)
|
|
if state.GUID ~= GUID then
|
|
state.GUID = GUID
|
|
changed = true
|
|
end
|
|
|
|
if state.raidMark ~= raidMark then
|
|
state.raidMark = raidMark
|
|
changed = true
|
|
end
|
|
|
|
local unitName = unit and GetUnitName(unit, false) or ""
|
|
if state.unitName ~= unitName then
|
|
state.unitName = unitName
|
|
changed = true
|
|
end
|
|
|
|
if state.unitCaster then
|
|
state.unitCaster = nil
|
|
changed = true
|
|
end
|
|
|
|
if state.casterName ~= "" then
|
|
state.casterName = ""
|
|
changed = true
|
|
end
|
|
|
|
if state.spellId then
|
|
state.spellId = nil
|
|
changed = true
|
|
end
|
|
|
|
if state.index then
|
|
state.index = nil
|
|
changed = true
|
|
end
|
|
|
|
if state.tooltip or state.tooltip1 or state.tooltip2 or state.tooltip3 or state.tooltip4 then
|
|
state.tooltip, state.tooltip1, state.tooltip2, state.tooltip3, state.tooltip4 = nil, nil, nil, nil, nil
|
|
changed = true
|
|
end
|
|
|
|
if state.matchCount ~= matchCount then
|
|
state.matchCount = matchCount
|
|
changed = true
|
|
end
|
|
|
|
if state.unitCount ~= unitCount then
|
|
state.unitCount = unitCount
|
|
changed = true
|
|
end
|
|
|
|
if state.maxUnitCount ~= maxUnitCount then
|
|
state.maxUnitCount = maxUnitCount
|
|
changed = true
|
|
end
|
|
|
|
if state.matchCountPerUnit ~= matchCountPerUnit then
|
|
state.matchCountPerUnit = matchCountPerUnit
|
|
changed = true
|
|
end
|
|
|
|
if state.active then
|
|
state.active = false
|
|
changed = true
|
|
end
|
|
|
|
if state.affected ~= affected then
|
|
state.affected = affected
|
|
state.affectedUnits = affectedUnits
|
|
changed = true
|
|
end
|
|
|
|
if state.unaffected ~= unaffected then
|
|
state.unaffected = unaffected
|
|
state.unaffectedUnits = unaffectedUnits
|
|
changed = true
|
|
end
|
|
|
|
if state.totalStacks ~= totalStacks then
|
|
state.totalStacks = totalStacks
|
|
changed = true
|
|
end
|
|
|
|
if changed then
|
|
state.changed = true
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
|
|
local function RemoveState(triggerStates, cloneId)
|
|
local state = triggerStates[cloneId]
|
|
if state then
|
|
if state.show then
|
|
state.show = false
|
|
state.changed = true
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
|
|
local function GetAllUnits(unit, allUnits, includePets)
|
|
if unit == "group" then
|
|
if allUnits then
|
|
local i = 1
|
|
local raid = true
|
|
local pets = true
|
|
return function()
|
|
if raid then
|
|
if i <= 40 then
|
|
local ret
|
|
if includePets == "PlayersAndPets" then
|
|
ret = pets and WeakAuras.raidpetUnits[i] or WeakAuras.raidUnits[i]
|
|
pets = not pets
|
|
if pets then
|
|
i = i + 1
|
|
end
|
|
elseif includePets == "PetsOnly" then
|
|
ret = WeakAuras.raidpetUnits[i]
|
|
i = i + 1
|
|
else -- raid
|
|
ret = WeakAuras.raidUnits[i]
|
|
i = i + 1
|
|
end
|
|
return ret
|
|
end
|
|
if includePets and pets then
|
|
pets = not pets
|
|
return "pet"
|
|
end
|
|
raid = false
|
|
i = 1
|
|
return "player"
|
|
end
|
|
|
|
if i <= 4 then
|
|
local ret
|
|
if includePets == "PlayersAndPets" then
|
|
ret = pets and WeakAuras.partypetUnits[i] or WeakAuras.partyUnits[i]
|
|
pets = not pets
|
|
if pets then
|
|
i = i + 1
|
|
end
|
|
elseif includePets == "PetsOnly" then
|
|
ret = WeakAuras.partypetUnits[i]
|
|
i = i + 1
|
|
else -- group
|
|
ret = WeakAuras.partyUnits[i]
|
|
i = i + 1
|
|
end
|
|
return ret
|
|
end
|
|
|
|
i = 1
|
|
raid = true
|
|
end
|
|
end
|
|
|
|
-- allunits == false
|
|
if IsInRaid() then
|
|
local i = 1
|
|
local max = GetNumGroupMembers()
|
|
local pets = true
|
|
return function()
|
|
if i <= max then
|
|
local ret
|
|
if includePets == "PlayersAndPets" then
|
|
ret = pets and WeakAuras.raidpetUnits[i] or WeakAuras.raidUnits[i]
|
|
pets = not pets
|
|
if pets then
|
|
i = i + 1
|
|
end
|
|
elseif includePets == "PetsOnly" then
|
|
ret = WeakAuras.raidpetUnits[i]
|
|
i = i + 1
|
|
else -- raid
|
|
ret = WeakAuras.raidUnits[i]
|
|
i = i + 1
|
|
end
|
|
return ret
|
|
end
|
|
i = 1
|
|
end
|
|
else
|
|
local i = 0
|
|
local max = GetNumSubgroupMembers()
|
|
local pets = true
|
|
return function()
|
|
if i == 0 then
|
|
if includePets == "PlayersAndPets" then
|
|
local ret = pets and "pet" or "player"
|
|
pets = not pets
|
|
if pets then
|
|
i = i + 1
|
|
end
|
|
return ret
|
|
elseif includePets == "PetsOnly" then
|
|
i = 1
|
|
return "pet"
|
|
else -- group
|
|
i = 1
|
|
return "player"
|
|
end
|
|
else
|
|
if i <= max then
|
|
local ret
|
|
if includePets == "PlayersAndPets" then
|
|
ret = pets and WeakAuras.partypetUnits[i] or WeakAuras.partyUnits[i]
|
|
pets = not pets
|
|
if pets then
|
|
i = i + 1
|
|
end
|
|
elseif includePets == "PetsOnly" then
|
|
ret = WeakAuras.partypetUnits[i]
|
|
i = i + 1
|
|
else -- group
|
|
ret = WeakAuras.partyUnits[i]
|
|
i = i + 1
|
|
end
|
|
return ret
|
|
end
|
|
end
|
|
i = 0
|
|
end
|
|
end
|
|
elseif unit == "boss" or unit == "arena" or unit == "nameplate" then
|
|
local i = 1
|
|
local max
|
|
if unit == "boss" then
|
|
max = MAX_BOSS_FRAMES
|
|
elseif unit == "arena" then
|
|
max = 5
|
|
elseif unit == "nameplate" then
|
|
max = 100
|
|
else
|
|
return function() end
|
|
end
|
|
return function()
|
|
local ret = unit .. i
|
|
while not allUnits and not UnitExistsFixed(ret) do
|
|
i = i + 1
|
|
if i > max then
|
|
i = 1
|
|
return nil
|
|
end
|
|
ret = unit .. i
|
|
end
|
|
i = i + 1
|
|
if i > max then
|
|
i = 1
|
|
return nil
|
|
end
|
|
return ret
|
|
end
|
|
else
|
|
local toggle = false
|
|
return function()
|
|
toggle = not toggle
|
|
if toggle then
|
|
return unit
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function MaxUnitCount(triggerInfo)
|
|
if triggerInfo.groupTrigger then
|
|
return triggerInfo.maxUnitCount
|
|
else
|
|
return UnitExistsFixed(triggerInfo.unit) and 1 or 0
|
|
end
|
|
end
|
|
|
|
local function TriggerInfoApplies(triggerInfo, unit)
|
|
local controllingUnit = unit
|
|
if WeakAuras.UnitIsPet(unit) then
|
|
controllingUnit = WeakAuras.petUnitToUnit[unit]
|
|
end
|
|
|
|
|
|
if triggerInfo.ignoreSelf and UnitIsUnit("player", controllingUnit) then
|
|
return false
|
|
end
|
|
|
|
if triggerInfo.ignoreDead and UnitIsDeadOrGhost(unit) then
|
|
return false
|
|
end
|
|
|
|
if triggerInfo.ignoreDisconnected and not UnitIsConnected(unit) then
|
|
return false
|
|
end
|
|
|
|
if triggerInfo.ignoreInvisible and not UnitIsVisibleFixed(unit) then
|
|
return false
|
|
end
|
|
|
|
if triggerInfo.raidRole and not triggerInfo.raidRole[WeakAuras.UnitRaidRole(unit) or ""] then
|
|
return false
|
|
end
|
|
|
|
if triggerInfo.specId then
|
|
local spec = Private.LibGroupTalentsWrapper.SpecForUnit(controllingUnit)
|
|
if not triggerInfo.specId[spec] then
|
|
return false
|
|
end
|
|
end
|
|
|
|
if triggerInfo.hostility and WeakAuras.GetPlayerReaction(unit) ~= triggerInfo.hostility then
|
|
return false
|
|
end
|
|
|
|
if triggerInfo.unit == "group" then
|
|
local isPet = WeakAuras.UnitIsPet(unit)
|
|
if triggerInfo.includePets == "PetsOnly" and not isPet then
|
|
return false
|
|
elseif triggerInfo.includePets == nil and isPet then -- exclude pets
|
|
return false
|
|
end
|
|
end
|
|
|
|
if triggerInfo.unit == "group" and triggerInfo.groupSubType == "party" then
|
|
if IsInRaid() then
|
|
-- Filter our player/party# while in raid and keep only raid units that are correct
|
|
if not Private.multiUnitUnits.raid[unit] or not UnitInSubgroupOrPlayer(unit, triggerInfo.includePets) then
|
|
return false
|
|
end
|
|
else
|
|
if not UnitInSubgroupOrPlayer(unit, triggerInfo.includePets) then
|
|
return false
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Filter our player/party# while in raid
|
|
if (triggerInfo.unit == "group" and triggerInfo.groupSubType == "group" and IsInRaid() and not Private.multiUnitUnits.raid[unit]) then
|
|
return false
|
|
end
|
|
|
|
if triggerInfo.unit == "group" and triggerInfo.groupSubType == "raid" and not Private.multiUnitUnits.raid[unit] then
|
|
return false
|
|
end
|
|
|
|
if triggerInfo.class and not triggerInfo.class[select(2, UnitClass(controllingUnit))] then
|
|
return false
|
|
end
|
|
|
|
if triggerInfo.npcId and not triggerInfo.npcId:Check(tostring(tonumber(string.sub(UnitGUID(unit) or '', 8, 12), 16) or '')) then
|
|
return false
|
|
end
|
|
|
|
if triggerInfo.nameChecker and not triggerInfo.nameChecker:Check(WeakAuras.UnitNameWithRealm(unit)) then
|
|
return false
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
local function FormatAffectedUnaffected(triggerInfo, matchedUnits)
|
|
local affected = ""
|
|
local unaffected = ""
|
|
local affectedUnits, unaffectedUnits = {}, {}
|
|
for unit in GetAllUnits(triggerInfo.unit, nil, triggerInfo.includePets) do
|
|
if activeGroupScanFuncs[unit] and activeGroupScanFuncs[unit][triggerInfo] then
|
|
if matchedUnits[unit] then
|
|
affected = affected .. (GetUnitName(unit, false) or unit) .. ", "
|
|
tinsert(affectedUnits, unit)
|
|
else
|
|
unaffected = unaffected .. (GetUnitName(unit, false) or unit) .. ", "
|
|
tinsert(unaffectedUnits, unit)
|
|
end
|
|
end
|
|
end
|
|
unaffected = unaffected == "" and L["None"] or unaffected:sub(1, -3)
|
|
affected = affected == "" and L["None"] or affected:sub(1, -3)
|
|
|
|
return affected, affectedUnits, unaffected, unaffectedUnits
|
|
end
|
|
|
|
local recheckTriggerInfo
|
|
|
|
local function SatisfiesGroupMatchCount(triggerInfo, unitCount, maxUnitCount, matchCount)
|
|
if triggerInfo.groupCountFunc and not triggerInfo.groupCountFunc(unitCount, maxUnitCount) then
|
|
return false
|
|
end
|
|
|
|
if triggerInfo.matchCountFunc and not triggerInfo.matchCountFunc(matchCount) then
|
|
return false
|
|
end
|
|
return true
|
|
end
|
|
|
|
local function SatisfiesMatchCountPerUnit(triggerInfo, countPerUnit)
|
|
return not triggerInfo.matchPerUnitCountFunc or triggerInfo.matchPerUnitCountFunc(countPerUnit)
|
|
end
|
|
|
|
local function bestUnit(triggerInfo, bestMatch)
|
|
if bestMatch then
|
|
return bestMatch.unit
|
|
elseif not triggerInfo.groupTrigger and triggerInfo.unit then
|
|
return triggerInfo.unit
|
|
end
|
|
end
|
|
|
|
local function markForTriggerInfo(triggerInfo, unit)
|
|
if triggerInfo.fetchRaidMark then
|
|
local rt = unit and GetRaidTargetIndex(unit)
|
|
if rt then
|
|
return "{rt" .. GetRaidTargetIndex(unit) .. "}"
|
|
end
|
|
end
|
|
end
|
|
|
|
local function SortMatchDataByUnitIndex(a, b)
|
|
if a.unit and b.unit and a.unit ~= b.unit then
|
|
return a.unit < b.unit
|
|
end
|
|
if a.index and b.index and a.index ~= b.index then
|
|
return a.index < b.index
|
|
end
|
|
return a.expirationTime < b.expirationTime
|
|
end
|
|
|
|
local function UpdateTriggerState(time, id, triggernum)
|
|
local triggerStates = WeakAuras.GetTriggerStateForTrigger(id, triggernum)
|
|
local triggerInfo = triggerInfos[id][triggernum]
|
|
local updated
|
|
local nextCheck
|
|
local matchCount = 0
|
|
local totalStacks = 0
|
|
local unitCount = 0
|
|
local auraDatas = {}
|
|
local maxUnitCount = MaxUnitCount(triggerInfo)
|
|
local matchedUnits = {}
|
|
local matchCountPerUnit = {}
|
|
|
|
if triggerInfo.combineMode == "showOne" then
|
|
local bestMatch
|
|
bestMatch, matchCount, unitCount, totalStacks, nextCheck = FindBestMatchData(time, id, triggernum, triggerInfo, matchedUnits)
|
|
local cloneId = ""
|
|
|
|
local useMatch = true
|
|
if triggerInfo.unitExists ~= nil and not existingUnits[triggerInfo.unit] then
|
|
useMatch = triggerInfo.unitExists
|
|
else
|
|
useMatch = SatisfiesGroupMatchCount(triggerInfo, unitCount, maxUnitCount, matchCount)
|
|
end
|
|
|
|
if useMatch then
|
|
local affected, unaffected, affectedUnits, unaffectedUnits
|
|
if triggerInfo.useAffected then
|
|
affected, affectedUnits, unaffected, unaffectedUnits = FormatAffectedUnaffected(triggerInfo, matchedUnits)
|
|
end
|
|
|
|
local unit = bestUnit(triggerInfo, bestMatch)
|
|
local mark = markForTriggerInfo(triggerInfo, unit)
|
|
|
|
if bestMatch then
|
|
updated = UpdateStateWithMatch(time, bestMatch, triggerStates, cloneId, matchCount, unitCount, maxUnitCount, matchCount, totalStacks, affected, affectedUnits, unaffected, unaffectedUnits, mark)
|
|
else
|
|
updated = UpdateStateWithNoMatch(time, triggerStates, triggerInfo, cloneId, unit, 0, 0, maxUnitCount, 0, 0, affected, affectedUnits, unaffected, unaffectedUnits, mark)
|
|
end
|
|
else
|
|
updated = RemoveState(triggerStates, cloneId)
|
|
end
|
|
elseif triggerInfo.combineMode == "showClones" then
|
|
if matchDataByTrigger[id] and matchDataByTrigger[id][triggernum] then
|
|
for unit, unitData in pairs(matchDataByTrigger[id][triggernum]) do
|
|
local unitCounted = false
|
|
for index, auraData in pairs(unitData) do
|
|
local remCheck = true
|
|
if triggerInfo.remainingFunc and auraData.expirationTime then
|
|
if auraData.expirationTime == 0 then
|
|
remCheck = false
|
|
else
|
|
local remaining = auraData.expirationTime - time
|
|
remCheck = triggerInfo.remainingFunc(remaining)
|
|
nextCheck = calculateNextCheck(triggerInfo.remainingCheck, remaining, auraData.expirationTime, nextCheck)
|
|
end
|
|
end
|
|
|
|
if remCheck then
|
|
tinsert(auraDatas, auraData)
|
|
matchCount = matchCount + 1
|
|
totalStacks = totalStacks + (auraData.stacks or 0)
|
|
matchedUnits[unit] = true
|
|
matchCountPerUnit[unit] = (matchCountPerUnit[unit] or 0) + 1
|
|
if not unitCounted then
|
|
unitCount = unitCount + 1
|
|
unitCounted = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local useMatches = true
|
|
if triggerInfo.unitExists ~= nil and not existingUnits[triggerInfo.unit] then
|
|
useMatches = triggerInfo.unitExists
|
|
else
|
|
useMatches = SatisfiesGroupMatchCount(triggerInfo, unitCount, maxUnitCount, matchCount)
|
|
end
|
|
|
|
local cloneIds = {}
|
|
if useMatches then
|
|
table.sort(auraDatas, SortMatchDataByUnitIndex)
|
|
|
|
local affected, affectedUnits, unaffected, unaffectedUnits
|
|
if triggerInfo.useAffected then
|
|
affected, affectedUnits, unaffected, unaffectedUnits = FormatAffectedUnaffected(triggerInfo, matchedUnits)
|
|
end
|
|
|
|
local usedCloneIds = {};
|
|
for index, auraData in ipairs(auraDatas) do
|
|
local cloneId = (auraData.GUID or auraData.unit or "unknown") .. " " .. auraData.spellId
|
|
if usedCloneIds[cloneId] then
|
|
usedCloneIds[cloneId] = usedCloneIds[cloneId] + 1
|
|
cloneId = cloneId .. usedCloneIds[cloneId]
|
|
else
|
|
usedCloneIds[cloneId] = 1
|
|
end
|
|
|
|
local mark = markForTriggerInfo(triggerInfo, auraData.unit)
|
|
updated = UpdateStateWithMatch(time, auraData, triggerStates, cloneId, matchCount, unitCount, maxUnitCount,
|
|
matchCountPerUnit[auraData.unit], totalStacks, affected, affectedUnits, unaffected, unaffectedUnits, mark) or updated
|
|
cloneIds[cloneId] = true
|
|
end
|
|
|
|
if matchCount == 0 then
|
|
local unit = bestUnit(triggerInfo, nil)
|
|
local mark = markForTriggerInfo(triggerInfo, unit)
|
|
updated = UpdateStateWithNoMatch(time, triggerStates, triggerInfo, "", nil, 0, 0, maxUnitCount, 0, totalStacks,
|
|
affected, affectedUnits, unaffected, unaffectedUnits, mark) or updated
|
|
cloneIds[""] = true
|
|
end
|
|
end
|
|
|
|
for cloneId, state in pairs(triggerStates) do
|
|
if not cloneIds[cloneId] then
|
|
updated = RemoveState(triggerStates, cloneId) or updated
|
|
end
|
|
end
|
|
elseif triggerInfo.combineMode == "showPerUnit" then
|
|
local matches = {}
|
|
if matchDataByTrigger[id] and matchDataByTrigger[id][triggernum] then
|
|
for unit, unitData in pairs(matchDataByTrigger[id][triggernum]) do
|
|
local bestMatch, countPerUnit, stacks, nextCheckForMatch = FindBestMatchDataForUnit(time, id, triggernum, triggerInfo, unit)
|
|
|
|
if SatisfiesMatchCountPerUnit(triggerInfo, countPerUnit) then
|
|
matchCount = matchCount + countPerUnit
|
|
totalStacks = totalStacks + (stacks or 0)
|
|
if bestMatch then
|
|
unitCount = unitCount + 1
|
|
matchedUnits[unit] = true
|
|
end
|
|
|
|
if not nextCheck then
|
|
nextCheck = nextCheckForMatch
|
|
elseif nextCheckForMatch then
|
|
nextCheck = min(nextCheck, nextCheckForMatch)
|
|
end
|
|
matches[unit] = bestMatch
|
|
matchCountPerUnit[unit] = countPerUnit
|
|
end
|
|
end
|
|
end
|
|
|
|
local useMatches = SatisfiesGroupMatchCount(triggerInfo, unitCount, maxUnitCount, matchCount)
|
|
|
|
local cloneIds = {}
|
|
if useMatches then
|
|
local affected, affectedUnits, unaffected, unaffectedUnits
|
|
if triggerInfo.useAffected then
|
|
affected, affectedUnits, unaffected, unaffectedUnits = FormatAffectedUnaffected(triggerInfo, matchedUnits)
|
|
end
|
|
|
|
if triggerInfo.perUnitMode == "affected" then
|
|
for unit, bestMatch in pairs(matches) do
|
|
if bestMatch then
|
|
local mark = markForTriggerInfo(triggerInfo, unit)
|
|
updated = UpdateStateWithMatch(time, bestMatch, triggerStates, unit, matchCount, unitCount, maxUnitCount,
|
|
matchCountPerUnit[unit], totalStacks, affected, affectedUnits, unaffected, unaffectedUnits, mark)
|
|
or updated
|
|
cloneIds[unit] = true
|
|
end
|
|
end
|
|
else
|
|
-- state per unaffected unit
|
|
for unit in GetAllUnits(triggerInfo.unit, nil, triggerInfo.includePets) do
|
|
if activeGroupScanFuncs[unit] and activeGroupScanFuncs[unit][triggerInfo] then
|
|
local bestMatch = matches[unit]
|
|
local mark = markForTriggerInfo(triggerInfo, unit)
|
|
if bestMatch then
|
|
if triggerInfo.perUnitMode == "all" then
|
|
updated = UpdateStateWithMatch(time, bestMatch, triggerStates, unit, matchCount, unitCount, maxUnitCount,
|
|
matchCountPerUnit[unit], totalStacks, affected, affectedUnits, unaffected, unaffectedUnits, mark)
|
|
or updated
|
|
cloneIds[unit] = true
|
|
end
|
|
else
|
|
updated = UpdateStateWithNoMatch(time, triggerStates, triggerInfo, unit, unit, matchCount,
|
|
unitCount, maxUnitCount, matchCountPerUnit[unit], totalStacks,
|
|
affected, affectedUnits, unaffected, unaffectedUnits)
|
|
or updated
|
|
cloneIds[unit] = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
for cloneId, state in pairs(triggerStates) do
|
|
if not cloneIds[cloneId] then
|
|
updated = RemoveState(triggerStates, cloneId) or updated
|
|
end
|
|
end
|
|
end
|
|
|
|
if nextCheck then
|
|
if triggerInfo.nextScheduledCheck ~= nextCheck then
|
|
if triggerInfo.nextScheduledCheckHandle then
|
|
timer:CancelTimer(triggerInfo.nextScheduledCheckHandle)
|
|
end
|
|
triggerInfo.nextScheduledCheckHandle = timer:ScheduleTimer(recheckTriggerInfo, nextCheck - time, triggerInfo)
|
|
triggerInfo.nextScheduledCheck = nextCheck
|
|
end
|
|
elseif triggerInfo.nextScheduledCheckHandle then
|
|
timer:CancelTimer(triggerInfo.nextScheduledCheckHandle)
|
|
triggerInfo.nextScheduledCheckHandle = nil
|
|
triggerInfo.nextScheduledCheck = nil
|
|
end
|
|
|
|
-- if the trigger has updated then check to see if it is flagged for WatchedTrigger and send to queue if it is
|
|
if updated then
|
|
if watched_trigger_events[id] and watched_trigger_events[id][triggernum] then
|
|
Private.AddToWatchedTriggerDelay(id, triggernum)
|
|
end
|
|
end
|
|
|
|
return updated
|
|
end
|
|
|
|
recheckTriggerInfo = function(triggerInfo)
|
|
matchDataChanged[triggerInfo.id] = matchDataChanged[triggerInfo.id] or {}
|
|
matchDataChanged[triggerInfo.id][triggerInfo.triggernum] = true
|
|
triggerInfo.nextScheduledCheckHandle = nil
|
|
triggerInfo.nextScheduledCheck = nil
|
|
end
|
|
|
|
local function PrepareMatchData(unit, filter)
|
|
if not matchDataUpToDate[unit] or not matchDataUpToDate[unit][filter] then
|
|
local time = GetTime()
|
|
local index = 1
|
|
while true do
|
|
local name, rank, icon, stacks, debuffClass, duration, expirationTime, unitCaster, isStealable, _, spellId = UnitAura(unit, index, filter)
|
|
if not name then
|
|
break
|
|
end
|
|
|
|
if debuffClass == nil then
|
|
debuffClass = "none"
|
|
elseif debuffClass == "" then
|
|
debuffClass = "enrage"
|
|
else
|
|
debuffClass = string.lower(debuffClass)
|
|
end
|
|
|
|
local updatedMatchData = UpdateMatchData(time, matchDataChanged, unit, index, filter, name, icon, stacks, debuffClass, duration, expirationTime, unitCaster, isStealable, _, spellId)
|
|
index = index + 1
|
|
end
|
|
|
|
matchDataUpToDate[unit] = matchDataUpToDate[unit] or {}
|
|
matchDataUpToDate[unit][filter] = true
|
|
end
|
|
end
|
|
|
|
local function CleanUpOutdatedMatchData(removeIndex, unit, filter)
|
|
-- Figure out if any matchData is outdated
|
|
if matchData[unit] and matchData[unit][filter] then
|
|
for index = removeIndex, #matchData[unit][filter] do
|
|
local data = matchData[unit][filter][index]
|
|
if data.index >= removeIndex or not UnitExistsFixed(unit) then
|
|
matchData[unit][filter][index] = nil
|
|
for id, triggerData in pairs(data.auras) do
|
|
for triggernum in pairs(triggerData) do
|
|
matchDataByTrigger[id][triggernum][unit][index] = nil
|
|
matchDataChanged[id] = matchDataChanged[id] or {}
|
|
matchDataChanged[id][triggernum] = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function CleanUpMatchDataForUnit(unit, filter)
|
|
-- Figure out if any matchData is outdated
|
|
if matchData[unit] and matchData[unit][filter] then
|
|
for index, data in pairs(matchData[unit][filter]) do
|
|
matchData[unit][filter][index] = nil
|
|
for id, triggerData in pairs(data.auras) do
|
|
for triggernum in pairs(triggerData) do
|
|
if matchDataByTrigger[id] and matchDataByTrigger[id][triggernum]
|
|
and matchDataByTrigger[id][triggernum][unit]
|
|
and matchDataByTrigger[id][triggernum][unit][index]
|
|
then
|
|
matchDataByTrigger[id][triggernum][unit][index] = nil
|
|
matchDataChanged[id] = matchDataChanged[id] or {}
|
|
matchDataChanged[id][triggernum] = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function DeactivateScanFuncs(toDeactivate)
|
|
for unit, triggerInfosPerUnit in pairs(toDeactivate) do
|
|
for triggerInfo in pairs(triggerInfosPerUnit) do
|
|
local id = triggerInfo.id
|
|
local triggernum = triggerInfo.triggernum
|
|
local matches = GetSubTable(matchDataByTrigger, id, triggernum, unit)
|
|
if (matches) then
|
|
for index, match in pairs(matches) do
|
|
match.auras[id][triggernum] = nil
|
|
end
|
|
matchDataByTrigger[id][triggernum][unit] = nil
|
|
matchDataChanged[id] = matchDataChanged[id] or {}
|
|
matchDataChanged[id][triggernum] = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function CheckScanFuncs(scanFuncs, unit, filter, index)
|
|
if scanFuncs then
|
|
for triggerInfo in pairs(scanFuncs) do
|
|
if triggerInfo.fetchTooltip then
|
|
matchData[unit][filter][index]:UpdateTooltip(GetTime())
|
|
end
|
|
if not triggerInfo.scanFunc or triggerInfo.scanFunc(time, matchData[unit][filter][index]) then
|
|
local id = triggerInfo.id
|
|
local triggernum = triggerInfo.triggernum
|
|
ReferenceMatchData(id, triggernum, unit, filter, index)
|
|
matchDataChanged[id] = matchDataChanged[id] or {}
|
|
matchDataChanged[id][triggernum] = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function ScanUnitWithFilter(matchDataChanged, time, unit, filter,
|
|
scanFuncNameGroup, scanFuncSpellIdGroup, scanFuncGeneralGroup,
|
|
scanFuncName, scanFuncSpellId, scanFuncGeneral)
|
|
if not scanFuncName and not scanFuncSpellId and not scanFuncGeneral and not scanFuncNameGroup and not scanFuncSpellIdGroup and not scanFuncGeneralGroup then
|
|
if matchDataUpToDate[unit] then
|
|
matchDataUpToDate[unit][filter] = nil
|
|
end
|
|
CleanUpOutdatedMatchData(1, unit, filter)
|
|
return
|
|
end
|
|
|
|
local index = 1
|
|
|
|
if UnitExistsFixed(unit) then
|
|
while true do
|
|
local name, rank, icon, stacks, debuffClass, duration, expirationTime, unitCaster, isStealable, _, spellId = UnitAura(unit, index, filter)
|
|
if not name then
|
|
break
|
|
end
|
|
|
|
if debuffClass == nil then
|
|
debuffClass = "none"
|
|
elseif debuffClass == "" then
|
|
debuffClass = "enrage"
|
|
else
|
|
debuffClass = string.lower(debuffClass)
|
|
end
|
|
|
|
local updatedMatchData = UpdateMatchData(time, matchDataChanged, unit, index, filter, name, icon, stacks, debuffClass, duration, expirationTime, unitCaster, isStealable, _, spellId)
|
|
|
|
if updatedMatchData then -- Aura data changed, check against triggerInfos
|
|
CheckScanFuncs(scanFuncName and scanFuncName[name], unit, filter, index)
|
|
CheckScanFuncs(scanFuncNameGroup and scanFuncNameGroup[name], unit, filter, index)
|
|
CheckScanFuncs(scanFuncSpellId and scanFuncSpellId[spellId], unit, filter, index)
|
|
CheckScanFuncs(scanFuncSpellIdGroup and scanFuncSpellIdGroup[spellId], unit, filter, index)
|
|
CheckScanFuncs(scanFuncGeneral, unit, filter, index)
|
|
CheckScanFuncs(scanFuncGeneralGroup, unit, filter, index)
|
|
end
|
|
index = index + 1
|
|
end
|
|
end
|
|
|
|
CleanUpOutdatedMatchData(index, unit, filter)
|
|
|
|
matchDataUpToDate[unit] = matchDataUpToDate[unit] or {}
|
|
matchDataUpToDate[unit][filter] = true
|
|
end
|
|
|
|
local function UpdateStates(matchDataChanged, time)
|
|
for id, auraData in pairs(matchDataChanged) do
|
|
Private.StartProfileAura(id)
|
|
local updated = false
|
|
for triggernum in pairs(auraData) do
|
|
updated = UpdateTriggerState(time, id, triggernum) or updated
|
|
end
|
|
if updated then
|
|
Private.UpdatedTriggerState(id)
|
|
end
|
|
Private.StopProfileAura(id)
|
|
end
|
|
end
|
|
|
|
local function ScanRaidMarkScanFunc(matchDataChanged)
|
|
for id, idData in pairs(raidMarkScanFuncs) do
|
|
matchDataChanged[id] = matchDataChanged[id] or {}
|
|
for _, triggerInfo in ipairs(idData) do
|
|
matchDataChanged[id][triggerInfo.triggernum] = true
|
|
end
|
|
end
|
|
end
|
|
|
|
local function ScanGroupUnit(time, matchDataChanged, unitType, unit)
|
|
local unitExists = UnitExistsFixed(unit) == 1 and true or false
|
|
if existingUnits[unit] ~= unitExists then
|
|
existingUnits[unit] = unitExists
|
|
|
|
if unitExistScanFunc[unit] then
|
|
for id, idData in pairs(unitExistScanFunc[unit]) do
|
|
matchDataChanged[id] = matchDataChanged[id] or {}
|
|
for _, triggerInfo in ipairs(idData) do
|
|
matchDataChanged[id][triggerInfo.triggernum] = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
scanFuncName[unit] = scanFuncName[unit] or {}
|
|
scanFuncSpellId[unit] = scanFuncSpellId[unit] or {}
|
|
scanFuncGeneral[unit] = scanFuncGeneral[unit] or {}
|
|
if unitType then
|
|
scanFuncNameGroup[unit] = scanFuncNameGroup[unit] or {}
|
|
scanFuncSpellIdGroup[unit] = scanFuncSpellIdGroup[unit] or {}
|
|
scanFuncGeneralGroup[unit] = scanFuncGeneralGroup[unit] or {}
|
|
|
|
ScanUnitWithFilter(matchDataChanged, time, unit, "HELPFUL",
|
|
scanFuncNameGroup[unit]["HELPFUL"],
|
|
scanFuncSpellIdGroup[unit]["HELPFUL"],
|
|
scanFuncGeneralGroup[unit]["HELPFUL"],
|
|
scanFuncName[unit]["HELPFUL"],
|
|
scanFuncSpellId[unit]["HELPFUL"],
|
|
scanFuncGeneral[unit]["HELPFUL"])
|
|
|
|
ScanUnitWithFilter(matchDataChanged, time, unit, "HARMFUL",
|
|
scanFuncNameGroup[unit]["HARMFUL"],
|
|
scanFuncSpellIdGroup[unit]["HARMFUL"],
|
|
scanFuncGeneralGroup[unit]["HARMFUL"],
|
|
scanFuncName[unit]["HARMFUL"],
|
|
scanFuncSpellId[unit]["HARMFUL"],
|
|
scanFuncGeneral[unit]["HARMFUL"])
|
|
else
|
|
ScanUnitWithFilter(matchDataChanged, time, unit, "HELPFUL", nil, nil, nil,
|
|
scanFuncName[unit]["HELPFUL"],
|
|
scanFuncSpellId[unit]["HELPFUL"],
|
|
scanFuncGeneral[unit]["HELPFUL"])
|
|
|
|
ScanUnitWithFilter(matchDataChanged, time, unit, "HARMFUL", nil, nil, nil,
|
|
scanFuncName[unit]["HARMFUL"],
|
|
scanFuncSpellId[unit]["HARMFUL"],
|
|
scanFuncGeneral[unit]["HARMFUL"])
|
|
end
|
|
end
|
|
|
|
local function ScanUnit(time, arg1)
|
|
if not arg1 then return end
|
|
if (Private.multiUnitUnits.raid[arg1] and IsInRaid()) then
|
|
ScanGroupUnit(time, matchDataChanged, "group", arg1)
|
|
elseif (Private.multiUnitUnits.party[arg1] and not IsInRaid()) then
|
|
ScanGroupUnit(time, matchDataChanged, "group", arg1)
|
|
elseif Private.multiUnitUnits.boss[arg1] then
|
|
ScanGroupUnit(time, matchDataChanged, "boss", arg1)
|
|
elseif Private.multiUnitUnits.arena[arg1] then
|
|
ScanGroupUnit(time, matchDataChanged, "arena", arg1)
|
|
elseif arg1:sub(1, 9) == "nameplate" then
|
|
ScanGroupUnit(time, matchDataChanged, "nameplate", arg1)
|
|
else
|
|
ScanGroupUnit(time, matchDataChanged, nil, arg1)
|
|
end
|
|
end
|
|
|
|
local function AddScanFuncs(triggerInfo, filter, unit, scanFuncName, scanFuncSpellId, scanFuncGeneral)
|
|
if triggerInfo.auranames then
|
|
for _, name in ipairs(triggerInfo.auranames) do
|
|
if name ~= "" then
|
|
local base = unit and GetOrCreateSubTable(scanFuncName, unit, filter, name) or GetOrCreateSubTable(scanFuncName, filter, name)
|
|
base[triggerInfo] = true
|
|
end
|
|
end
|
|
end
|
|
|
|
if triggerInfo.auraspellids then
|
|
for _, spellId in ipairs(triggerInfo.auraspellids) do
|
|
local base = unit and GetOrCreateSubTable(scanFuncSpellId, unit, filter, spellId) or GetOrCreateSubTable(scanFuncSpellId, filter, spellId)
|
|
base[triggerInfo] = true
|
|
end
|
|
end
|
|
|
|
if not triggerInfo.auranames and not triggerInfo.auraspellids and scanFuncGeneral then
|
|
local base = GetOrCreateSubTable(scanFuncGeneral, unit, filter)
|
|
base[triggerInfo] = true
|
|
end
|
|
|
|
if unit then
|
|
PrepareMatchData(unit, filter)
|
|
ScanMatchData(GetTime(), triggerInfo, unit, filter)
|
|
end
|
|
end
|
|
|
|
local function RemoveScanFuncs(triggerInfo, filter, unit, scanFuncName, scanFuncSpellId, scanFuncGeneral)
|
|
if triggerInfo.auranames then
|
|
for _, name in ipairs(triggerInfo.auranames) do
|
|
if name ~= "" then
|
|
local base = unit and GetSubTable(scanFuncName, unit, filter, name) or GetSubTable(scanFuncName, filter, name)
|
|
if base then
|
|
base[triggerInfo] = nil
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if triggerInfo.auraspellids then
|
|
for _, spellId in ipairs(triggerInfo.auraspellids) do
|
|
local base = unit and GetSubTable(scanFuncSpellId, unit, filter, spellId) or GetSubTable(scanFuncSpellId, filter, spellId)
|
|
if base then
|
|
base[triggerInfo] = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
if not triggerInfo.auranames and not triggerInfo.auraspellids and scanFuncGeneral then
|
|
local base = GetSubTable(scanFuncGeneral, unit, filter)
|
|
if base then
|
|
base[triggerInfo] = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
local function RecheckActive(triggerInfo, unit, unitsToRemoveScan)
|
|
local isSelf, role, inParty, class
|
|
local unitExists = UnitExistsFixed(unit)
|
|
if unitExists and TriggerInfoApplies(triggerInfo, unit) then
|
|
if (not activeGroupScanFuncs[unit] or not activeGroupScanFuncs[unit][triggerInfo]) then
|
|
triggerInfo.maxUnitCount = triggerInfo.maxUnitCount + 1
|
|
if triggerInfo.debuffType == "BOTH" then
|
|
AddScanFuncs(triggerInfo, "HELPFUL", unit, scanFuncNameGroup, scanFuncSpellIdGroup, scanFuncGeneralGroup)
|
|
AddScanFuncs(triggerInfo, "HARMFUL", unit, scanFuncNameGroup, scanFuncSpellIdGroup, scanFuncGeneralGroup)
|
|
else
|
|
AddScanFuncs(triggerInfo, triggerInfo.debuffType, unit, scanFuncNameGroup, scanFuncSpellIdGroup, scanFuncGeneralGroup)
|
|
end
|
|
|
|
activeGroupScanFuncs[unit] = activeGroupScanFuncs[unit] or {}
|
|
activeGroupScanFuncs[unit][triggerInfo] = true
|
|
matchDataChanged[triggerInfo.id] = matchDataChanged[triggerInfo.id] or {}
|
|
matchDataChanged[triggerInfo.id][triggerInfo.triggernum] = true
|
|
end
|
|
else
|
|
-- Either the unit doesn't exist or the TriggerInfo no longer applies
|
|
if activeGroupScanFuncs[unit] and activeGroupScanFuncs[unit][triggerInfo] then
|
|
triggerInfo.maxUnitCount = triggerInfo.maxUnitCount - 1
|
|
if triggerInfo.debuffType == "BOTH" then
|
|
RemoveScanFuncs(triggerInfo, "HELPFUL", unit, scanFuncNameGroup, scanFuncSpellIdGroup, scanFuncGeneralGroup)
|
|
RemoveScanFuncs(triggerInfo, "HARMFUL", unit, scanFuncNameGroup, scanFuncSpellIdGroup, scanFuncGeneralGroup)
|
|
else
|
|
RemoveScanFuncs(triggerInfo, triggerInfo.debuffType, unit, scanFuncNameGroup, scanFuncSpellIdGroup, scanFuncGeneralGroup)
|
|
end
|
|
if unitsToRemoveScan then
|
|
unitsToRemoveScan[unit] = unitsToRemoveScan[unit] or {}
|
|
unitsToRemoveScan[unit][triggerInfo] = true
|
|
end
|
|
activeGroupScanFuncs[unit][triggerInfo] = nil
|
|
matchDataChanged[triggerInfo.id] = matchDataChanged[triggerInfo.id] or {}
|
|
matchDataChanged[triggerInfo.id][triggerInfo.triggernum] = true
|
|
end
|
|
end
|
|
end
|
|
|
|
local function RecheckActiveForUnitType(unitType, unit, unitsToRemoveScan)
|
|
if groupScanFuncs[unitType] then
|
|
for i, triggerInfo in ipairs(groupScanFuncs[unitType]) do
|
|
RecheckActive(triggerInfo, unit, unitsToRemoveScan)
|
|
end
|
|
end
|
|
end
|
|
|
|
local Buff2Frame = CreateFrame("Frame")
|
|
Private.frames["WeakAuras Buff2 Frame"] = Buff2Frame
|
|
|
|
local function EventHandler(frame, event, arg1, arg2, ...)
|
|
|
|
Private.StartProfileSystem("bufftrigger2")
|
|
|
|
local deactivatedTriggerInfos = {}
|
|
local unitsToRemove = {}
|
|
|
|
local time = GetTime()
|
|
if event == "PLAYER_TARGET_CHANGED" then
|
|
ScanGroupUnit(time, matchDataChanged, nil, "target")
|
|
if not UnitExistsFixed("target") then
|
|
tinsert(unitsToRemove, "target")
|
|
end
|
|
elseif event == "PLAYER_FOCUS_CHANGED" then
|
|
ScanGroupUnit(time, matchDataChanged, nil, "focus")
|
|
if not UnitExistsFixed("focus") then
|
|
tinsert(unitsToRemove, "focus")
|
|
end
|
|
elseif event == "UNIT_PET" then
|
|
local pet = WeakAuras.unitToPetUnit[arg1]
|
|
if pet then
|
|
ScanGroupUnit(time, matchDataChanged, "group", pet)
|
|
RecheckActiveForUnitType("group", pet, deactivatedTriggerInfos)
|
|
if not UnitExistsFixed(pet) then
|
|
tinsert(unitsToRemove, pet)
|
|
end
|
|
end
|
|
elseif event == "NAME_PLATE_UNIT_ADDED" then
|
|
nameplateExists[arg1] = UnitGUID(arg1)
|
|
RecheckActiveForUnitType("nameplate", arg1, deactivatedTriggerInfos)
|
|
elseif event == "NAME_PLATE_UNIT_REMOVED" then
|
|
nameplateExists[arg1] = false
|
|
RecheckActiveForUnitType("nameplate", arg1, deactivatedTriggerInfos)
|
|
tinsert(unitsToRemove, arg1)
|
|
elseif event == "UNIT_FACTION" then
|
|
if arg1:sub(1, 9) == "nameplate" then
|
|
RecheckActiveForUnitType("nameplate", arg1, deactivatedTriggerInfos)
|
|
end
|
|
elseif event == "INSTANCE_ENCOUNTER_ENGAGE_UNIT" then
|
|
for unit in GetAllUnits("boss", true) do
|
|
RecheckActiveForUnitType("boss", unit, deactivatedTriggerInfos)
|
|
if not UnitExistsFixed(unit) then
|
|
tinsert(unitsToRemove, unit)
|
|
end
|
|
end
|
|
elseif event =="ARENA_OPPONENT_UPDATE" then
|
|
for unit in GetAllUnits("arena", true) do
|
|
RecheckActiveForUnitType("arena", unit, deactivatedTriggerInfos)
|
|
if not UnitExistsFixed(unit) then
|
|
tinsert(unitsToRemove, unit)
|
|
end
|
|
end
|
|
elseif event == "PARTY_MEMBERS_CHANGED" or event == "RAID_ROSTER_UPDATE" then
|
|
for unit in GetAllUnits("group", true, "PlayersAndPets") do
|
|
RecheckActiveForUnitType("group", unit, deactivatedTriggerInfos)
|
|
local exists = UnitExistsFixed(unit)
|
|
if not exists then
|
|
tinsert(unitsToRemove, unit)
|
|
elseif exists ~= existingUnits[unit] then
|
|
ScanGroupUnit(time, matchDataChanged, "group", unit)
|
|
end
|
|
end
|
|
elseif event == "UNIT_FLAGS" or event == "UNIT_NAME_UPDATE" or event == "PLAYER_FLAGS_CHANGED" then
|
|
if Private.multiUnitUnits.group[arg1] then
|
|
RecheckActiveForUnitType("group", arg1, deactivatedTriggerInfos)
|
|
end
|
|
elseif event == "UNIT_ENTERED_VEHICLE" or event == "UNIT_EXITED_VEHICLE" then
|
|
if arg1 == "player" then
|
|
ScanGroupUnit(time, matchDataChanged, nil, "vehicle")
|
|
end
|
|
elseif event == "UNIT_AURA" then
|
|
ScanUnit(time, arg1)
|
|
elseif event == "PLAYER_ENTERING_WORLD" then
|
|
for unit in pairs(matchData) do
|
|
ScanUnit(time, unit)
|
|
if not UnitExistsFixed(unit) then
|
|
tinsert(unitsToRemove, unit)
|
|
end
|
|
end
|
|
|
|
if arg1 then
|
|
-- Initial login has a bug where the tooltip information is not available,
|
|
-- so update tooltips 2s after login
|
|
timer:ScheduleTimer(function()
|
|
for unit, matchtDataPerUnit in pairs(matchData) do
|
|
EventHandler(frame, "UNIT_AURA", unit)
|
|
end
|
|
end, 2)
|
|
end
|
|
|
|
elseif event == "RAID_TARGET_UPDATE" then
|
|
ScanRaidMarkScanFunc(matchDataChanged)
|
|
end
|
|
|
|
DeactivateScanFuncs(deactivatedTriggerInfos)
|
|
|
|
for i, unit in ipairs(unitsToRemove) do
|
|
CleanUpMatchDataForUnit(unit, "HELPFUL")
|
|
CleanUpMatchDataForUnit(unit, "HARMFUL")
|
|
matchDataUpToDate[unit] = nil
|
|
end
|
|
|
|
Private.StopProfileSystem("bufftrigger2")
|
|
end
|
|
|
|
|
|
Private.LibGroupTalentsWrapper.Register(function(unit)
|
|
Private.StartProfileSystem("bufftrigger2")
|
|
|
|
local deactivatedTriggerInfos = {}
|
|
RecheckActiveForUnitType("group", unit, deactivatedTriggerInfos)
|
|
RecheckActiveForUnitType("group", WeakAuras.unitToPetUnit[unit], deactivatedTriggerInfos)
|
|
DeactivateScanFuncs(deactivatedTriggerInfos)
|
|
|
|
Private.StopProfileSystem("bufftrigger2")
|
|
end)
|
|
|
|
Buff2Frame:RegisterEvent("UNIT_AURA")
|
|
Buff2Frame:RegisterEvent("UNIT_FACTION")
|
|
Buff2Frame:RegisterEvent("UNIT_NAME_UPDATE")
|
|
Buff2Frame:RegisterEvent("UNIT_FLAGS")
|
|
Buff2Frame:RegisterEvent("PLAYER_FLAGS_CHANGED")
|
|
Buff2Frame:RegisterEvent("UNIT_PET")
|
|
Buff2Frame:RegisterEvent("RAID_TARGET_UPDATE")
|
|
Buff2Frame:RegisterEvent("PLAYER_FOCUS_CHANGED")
|
|
Buff2Frame:RegisterEvent("ARENA_OPPONENT_UPDATE")
|
|
Buff2Frame:RegisterEvent("UNIT_ENTERED_VEHICLE")
|
|
Buff2Frame:RegisterEvent("UNIT_EXITED_VEHICLE")
|
|
Buff2Frame:RegisterEvent("PLAYER_TARGET_CHANGED")
|
|
Buff2Frame:RegisterEvent("PARTY_MEMBERS_CHANGED")
|
|
Buff2Frame:RegisterEvent("RAID_ROSTER_UPDATE")
|
|
Buff2Frame:RegisterEvent("INSTANCE_ENCOUNTER_ENGAGE_UNIT")
|
|
if WeakAuras.isAwesomeEnabled() then
|
|
Buff2Frame:RegisterEvent("NAME_PLATE_UNIT_ADDED")
|
|
Buff2Frame:RegisterEvent("NAME_PLATE_UNIT_REMOVED")
|
|
end
|
|
Buff2Frame:RegisterEvent("PLAYER_ENTERING_WORLD")
|
|
Buff2Frame:SetScript("OnEvent", EventHandler)
|
|
|
|
Buff2Frame:SetScript("OnUpdate", function()
|
|
if WeakAuras.IsPaused() then
|
|
return
|
|
end
|
|
Private.StartProfileSystem("bufftrigger2")
|
|
if next(matchDataChanged) then
|
|
local time = GetTime()
|
|
UpdateStates(matchDataChanged, time)
|
|
wipe(matchDataChanged)
|
|
end
|
|
Private.StopProfileSystem("bufftrigger2")
|
|
end)
|
|
|
|
local function UnloadAura(scanFuncName, id)
|
|
for unit, unitData in pairs(scanFuncName) do
|
|
for debuffType, debuffData in pairs(unitData) do
|
|
for name, nameData in pairs(debuffData) do
|
|
for triggerInfo in pairs(nameData) do
|
|
if triggerInfo.id == id or not id then
|
|
if triggerInfo.nextScheduledCheckHandle then
|
|
timer:CancelTimer(triggerInfo.nextScheduledCheckHandle)
|
|
triggerInfo.nextScheduledCheck = nil
|
|
triggerInfo.nextScheduledCheckHandle = nil
|
|
end
|
|
nameData[triggerInfo] = nil
|
|
end
|
|
end
|
|
if not next(nameData) then
|
|
debuffData[name] = nil
|
|
end
|
|
end
|
|
|
|
if not next(debuffData) then
|
|
unitData[debuffType] = nil
|
|
end
|
|
end
|
|
if not next(unitData) then
|
|
scanFuncName[unit] = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
local function UnloadGeneral(scanFuncGeneral, id)
|
|
for unit, unitData in pairs(scanFuncGeneral) do
|
|
for debuffType, debuffData in pairs(unitData) do
|
|
for triggerInfo in pairs(debuffData) do
|
|
if triggerInfo.id == id or not id then
|
|
if triggerInfo.nextScheduledCheckHandle then
|
|
timer:CancelTimer(triggerInfo.nextScheduledCheckHandle)
|
|
triggerInfo.nextScheduledCheck = nil
|
|
triggerInfo.nextScheduledCheckHandle = nil
|
|
end
|
|
debuffData[triggerInfo] = nil
|
|
end
|
|
end
|
|
if not next(debuffData) then
|
|
unitData[debuffType] = nil
|
|
end
|
|
end
|
|
if not next(unitData) then
|
|
scanFuncGeneral[unit] = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
function BuffTrigger.UnloadAll()
|
|
UnloadAura(scanFuncName, nil)
|
|
UnloadAura(scanFuncSpellId, nil)
|
|
UnloadGeneral(scanFuncGeneral, nil)
|
|
|
|
for unit, unitData in pairs(matchData) do
|
|
for filter, filterData in pairs(unitData) do
|
|
for index, indexData in pairs(filterData) do
|
|
wipe(indexData.auras)
|
|
end
|
|
end
|
|
end
|
|
|
|
wipe(scanFuncName)
|
|
wipe(scanFuncSpellId)
|
|
wipe(scanFuncGeneral)
|
|
wipe(scanFuncNameGroup)
|
|
wipe(scanFuncSpellIdGroup)
|
|
wipe(scanFuncGeneralGroup)
|
|
wipe(scanFuncNameMulti)
|
|
wipe(scanFuncSpellIdMulti)
|
|
wipe(unitExistScanFunc)
|
|
wipe(groupScanFuncs)
|
|
wipe(raidMarkScanFuncs)
|
|
wipe(matchDataByTrigger)
|
|
wipe(matchDataMulti)
|
|
wipe(matchDataChanged)
|
|
wipe(activeGroupScanFuncs)
|
|
end
|
|
|
|
|
|
local function LoadAura(id, triggernum, triggerInfo)
|
|
if not triggerInfo.unit then
|
|
return
|
|
end
|
|
local time = GetTime();
|
|
|
|
local unitsToCheck = {}
|
|
|
|
if triggerInfo.unit == "multi" then
|
|
if triggerInfo.debuffType == "BOTH" then
|
|
AddScanFuncs(triggerInfo, "HELPFUL", nil, scanFuncNameMulti, scanFuncSpellIdMulti, nil)
|
|
AddScanFuncs(triggerInfo, "HARMFUL", nil, scanFuncNameMulti, scanFuncSpellIdMulti, nil)
|
|
else
|
|
AddScanFuncs(triggerInfo, triggerInfo.debuffType, nil, scanFuncNameMulti, scanFuncSpellIdMulti, nil)
|
|
end
|
|
elseif triggerInfo.groupTrigger then
|
|
triggerInfo.maxUnitCount = 0
|
|
for unit in GetAllUnits(triggerInfo.unit, nil, triggerInfo.includePets) do
|
|
RecheckActive(triggerInfo, unit, unitsToCheck)
|
|
end
|
|
else
|
|
if triggerInfo.debuffType == "BOTH" then
|
|
AddScanFuncs(triggerInfo, "HELPFUL", triggerInfo.unit, scanFuncName, scanFuncSpellId, scanFuncGeneral)
|
|
AddScanFuncs(triggerInfo, "HARMFUL", triggerInfo.unit, scanFuncName, scanFuncSpellId, scanFuncGeneral)
|
|
else
|
|
AddScanFuncs(triggerInfo, triggerInfo.debuffType, triggerInfo.unit, scanFuncName, scanFuncSpellId, scanFuncGeneral)
|
|
end
|
|
unitsToCheck[triggerInfo.unit] = true
|
|
end
|
|
|
|
if triggerInfo.unitExists ~= nil then
|
|
unitExistScanFunc[triggerInfo.unit] = unitExistScanFunc[triggerInfo.unit] or {}
|
|
unitExistScanFunc[triggerInfo.unit][id] = unitExistScanFunc[triggerInfo.unit][id] or {}
|
|
tinsert(unitExistScanFunc[triggerInfo.unit][id], triggerInfo)
|
|
|
|
if existingUnits[triggerInfo.unit] == nil then
|
|
existingUnits[triggerInfo.unit] = UnitExistsFixed(triggerInfo.unit) == 1 and true or false
|
|
end
|
|
end
|
|
|
|
if triggerInfo.fetchRaidMark then
|
|
raidMarkScanFuncs[id] = raidMarkScanFuncs[id] or {}
|
|
tinsert(raidMarkScanFuncs[id], triggerInfo)
|
|
end
|
|
|
|
if triggerInfo.groupTrigger then
|
|
groupScanFuncs[triggerInfo.unit] = groupScanFuncs[triggerInfo.unit] or {}
|
|
tinsert(groupScanFuncs[triggerInfo.unit], triggerInfo)
|
|
end
|
|
|
|
matchDataChanged[id] = matchDataChanged[id] or {}
|
|
matchDataChanged[id][triggernum] = true
|
|
end
|
|
|
|
function BuffTrigger.LoadDisplays(toLoad)
|
|
for id in pairs(toLoad) do
|
|
if triggerInfos[id] then
|
|
for triggernum, triggerInfo in pairs(triggerInfos[id]) do
|
|
LoadAura(id, triggernum, triggerInfo)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
function BuffTrigger.UnloadDisplays(toUnload)
|
|
local updateGroupScanFuncs = false
|
|
for id in pairs(toUnload) do
|
|
UnloadAura(scanFuncName, id)
|
|
UnloadAura(scanFuncSpellId, id)
|
|
UnloadGeneral(scanFuncGeneral, id)
|
|
|
|
UnloadAura(scanFuncNameGroup, id)
|
|
UnloadAura(scanFuncSpellIdGroup, id)
|
|
UnloadGeneral(scanFuncGeneralGroup, id)
|
|
|
|
UnloadGeneral(scanFuncNameMulti, id)
|
|
UnloadGeneral(scanFuncSpellIdMulti, id)
|
|
|
|
for unit, unitData in pairs(unitExistScanFunc) do
|
|
unitData[id] = nil
|
|
end
|
|
|
|
raidMarkScanFuncs[id] = nil
|
|
|
|
for unit, unitData in pairs(matchData) do
|
|
for filter, filterData in pairs(unitData) do
|
|
for index, indexData in pairs(filterData) do
|
|
indexData.auras[id] = nil
|
|
end
|
|
end
|
|
end
|
|
matchDataByTrigger[id] = nil
|
|
|
|
for guid, guidData in pairs(matchDataMulti) do
|
|
for key, data in pairs(matchDataMulti[guid]) do
|
|
for source, sourceData in pairs(data) do
|
|
sourceData.auras[id] = nil
|
|
end
|
|
end
|
|
end
|
|
matchDataChanged[id] = nil
|
|
end
|
|
|
|
|
|
for unitType, funcs in pairs(groupScanFuncs) do
|
|
for i = #funcs, 1, -1 do
|
|
if toUnload[funcs[i].id] then
|
|
tremove(funcs, i)
|
|
end
|
|
end
|
|
end
|
|
|
|
for unit, unitData in pairs(activeGroupScanFuncs) do
|
|
for triggerInfo in pairs(unitData) do
|
|
if toUnload[triggerInfo.id] then
|
|
unitData[triggerInfo] = nil
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function BuffTrigger.FinishLoadUnload()
|
|
-- Nothing!
|
|
end
|
|
|
|
--- Removes all data for an aura id
|
|
-- @param id
|
|
function BuffTrigger.Delete(id)
|
|
BuffTrigger.UnloadDisplays({[id] = true})
|
|
triggerInfos[id] = nil
|
|
end
|
|
|
|
--- Updates all data for aura oldid to use newid
|
|
-- @param oldid
|
|
-- @param newid
|
|
function BuffTrigger.Rename(oldid, newid)
|
|
triggerInfos[newid] = triggerInfos[oldid]
|
|
triggerInfos[oldid] = nil
|
|
|
|
if triggerInfos[newid] then
|
|
for triggernum, triggerData in pairs(triggerInfos[newid]) do
|
|
triggerData.id = newid
|
|
end
|
|
end
|
|
|
|
matchDataByTrigger[newid] = matchDataByTrigger[oldid]
|
|
matchDataByTrigger[oldid] = nil
|
|
|
|
for unit, unitData in pairs(matchData) do
|
|
for filter, filterData in pairs(unitData) do
|
|
for index, indexData in pairs(filterData) do
|
|
indexData.auras[newid] = indexData.auras[oldid]
|
|
indexData.auras[oldid] = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
for unit, unitData in pairs(unitExistScanFunc) do
|
|
unitData[newid] = unitData[oldid]
|
|
unitData[oldid] = nil
|
|
end
|
|
raidMarkScanFuncs[newid] = raidMarkScanFuncs[oldid]
|
|
raidMarkScanFuncs[oldid] = nil
|
|
matchDataChanged[newid] = matchDataChanged[oldid]
|
|
matchDataChanged[oldid] = nil
|
|
end
|
|
|
|
local function createScanFunc(trigger)
|
|
local canHaveMatchCheck = CanHaveMatchCheck(trigger)
|
|
local isMulti = trigger.unit == "multi"
|
|
local useStacks = canHaveMatchCheck and not isMulti and trigger.useStacks
|
|
local use_stealable = canHaveMatchCheck and not isMulti and trigger.use_stealable
|
|
local use_debuffClass = canHaveMatchCheck and not isMulti and trigger.use_debuffClass
|
|
local use_tooltip = canHaveMatchCheck and not isMulti and trigger.fetchTooltip and trigger.use_tooltip
|
|
local use_tooltipValue = canHaveMatchCheck and not isMulti and trigger.fetchTooltip and trigger.use_tooltipValue
|
|
local use_total = canHaveMatchCheck and not isMulti and trigger.useTotal and trigger.total
|
|
local use_ignore_name = canHaveMatchCheck and not isMulti and trigger.useIgnoreName and trigger.ignoreAuraNames
|
|
local use_ignore_spellId = canHaveMatchCheck and not isMulti and trigger.useIgnoreExactSpellId and trigger.ignoreAuraSpellids
|
|
|
|
if not useStacks and use_stealable == nil and not use_debuffClass and trigger.ownOnly == nil
|
|
and not use_tooltip and not use_tooltipValue and not trigger.useNamePattern and not use_total
|
|
and not use_ignore_name and not use_ignore_spellId then
|
|
return nil
|
|
end
|
|
|
|
local preamble = {""}
|
|
|
|
local ret = {[=[
|
|
return function(time, matchData)
|
|
]=]}
|
|
|
|
if use_total then
|
|
local ret2 = [=[
|
|
if not(matchData.duration %s %s) then
|
|
return false
|
|
end
|
|
]=]
|
|
table.insert(ret, ret2:format(trigger.totalOperator or ">=", tonumber(trigger.total) or 0))
|
|
end
|
|
|
|
if useStacks then
|
|
local ret2 = [=[
|
|
if not(matchData.stacks %s %s) then
|
|
return false
|
|
end
|
|
]=]
|
|
table.insert(ret, ret2:format(trigger.stacksOperator or ">=", tonumber(trigger.stacks) or 0))
|
|
end
|
|
|
|
if use_stealable then
|
|
table.insert(ret, [=[
|
|
if not matchData.isStealable then
|
|
return false
|
|
end
|
|
]=])
|
|
elseif use_stealable == false then
|
|
table.insert(ret, [=[
|
|
if matchData.isStealable then
|
|
return false
|
|
end
|
|
]=])
|
|
end
|
|
if use_debuffClass then
|
|
local ret2 = [=[
|
|
local tDebuffClass = %s;
|
|
if not tDebuffClass[matchData.debuffClass] then
|
|
return false
|
|
end
|
|
]=]
|
|
table.insert(ret, ret2:format(trigger.debuffClass and type(trigger.debuffClass) == "table" and Private.SerializeTable(trigger.debuffClass) or "{}"))
|
|
end
|
|
|
|
if trigger.ownOnly then
|
|
table.insert(ret, [=[
|
|
if matchData.unitCaster ~= 'player' and matchData.unitCaster ~= 'pet' and matchData.unitCaster ~= 'vehicle' then
|
|
return false
|
|
end
|
|
]=])
|
|
elseif trigger.ownOnly == false then
|
|
table.insert(ret, [=[
|
|
if matchData.unitCaster == 'player' or matchData.unitCaster == 'pet' or matchData.unitCaster == 'vehicle' then
|
|
return false
|
|
end
|
|
]=])
|
|
end
|
|
|
|
if use_tooltip and trigger.tooltip_operator and trigger.tooltip then
|
|
if trigger.tooltip_operator == "==" then
|
|
local ret2 = [=[
|
|
if not matchData.tooltip or matchData.tooltip ~= %s then
|
|
return false
|
|
end
|
|
]=]
|
|
table.insert(ret, ret2:format(Private.QuotedString(trigger.tooltip)))
|
|
elseif trigger.tooltip_operator == "find('%s')" then
|
|
local ret2 = [=[
|
|
if not matchData.tooltip or not matchData.tooltip:find(%s, 1, true) then
|
|
return false
|
|
end
|
|
]=]
|
|
table.insert(ret, ret2:format(Private.QuotedString(trigger.tooltip)))
|
|
elseif trigger.tooltip_operator == "match('%s')" then
|
|
local ret2 = [=[
|
|
if not matchData.tooltip or not matchData.tooltip:match(%s) then
|
|
return false
|
|
end
|
|
]=]
|
|
table.insert(ret, ret2:format(Private.QuotedString(trigger.tooltip)))
|
|
end
|
|
end
|
|
|
|
if use_tooltipValue and trigger.tooltipValueNumber and trigger.tooltipValue_operator and trigger.tooltipValue then
|
|
local property = "tooltip" .. tonumber(trigger.tooltipValueNumber)
|
|
local ret2 = [=[
|
|
if not matchData.%s or not (matchData.%s %s %s) then
|
|
return false
|
|
end
|
|
]=]
|
|
ret = ret .. ret2:format(property, property, trigger.tooltipValue_operator, trigger.tooltipValue)
|
|
end
|
|
|
|
if trigger.useNamePattern and trigger.namePattern_operator and trigger.namePattern_name then
|
|
if trigger.namePattern_operator == "==" then
|
|
local ret2 = [=[
|
|
if not matchData.name == %s then
|
|
return false
|
|
end
|
|
]=]
|
|
table.insert(ret, ret2:format(Private.QuotedString(trigger.namePattern_name)))
|
|
elseif trigger.namePattern_operator == "find('%s')" then
|
|
local ret2 = [=[
|
|
if not matchData.name:find(%s, 1, true) then
|
|
return false
|
|
end
|
|
]=]
|
|
table.insert(ret, ret2:format(Private.QuotedString(trigger.namePattern_name)))
|
|
elseif trigger.namePattern_operator == "match('%s')" then
|
|
local ret2 = [=[
|
|
if not matchData.name:match(%s) then
|
|
return false
|
|
end
|
|
]=]
|
|
table.insert(ret, ret2:format(Private.QuotedString(trigger.namePattern_name)))
|
|
end
|
|
end
|
|
|
|
if use_ignore_name then
|
|
local names = {}
|
|
for index, spellName in ipairs(trigger.ignoreAuraNames) do
|
|
local spellId = WeakAuras.SafeToNumber(spellName)
|
|
local name = spellId and GetSpellInfo(spellId) or spellName
|
|
tinsert(names, name)
|
|
end
|
|
|
|
table.insert(preamble, "local ignoreNames = {\n")
|
|
for index, name in ipairs(names) do
|
|
table.insert(preamble, string.format(" [%q] = true,\n", name))
|
|
end
|
|
table.insert(preamble, "}\n")
|
|
table.insert(ret, [=[
|
|
if ignoreNames[matchData.name] then
|
|
return false
|
|
end
|
|
]=])
|
|
end
|
|
|
|
if use_ignore_spellId then
|
|
table.insert(preamble, "local ignoreSpellId = {\n")
|
|
for index, spellId in ipairs(trigger.ignoreAuraSpellids) do
|
|
local spell = WeakAuras.SafeToNumber(spellId)
|
|
if spell then
|
|
table.insert(preamble, string.format(" [%s] = true,\n", spell))
|
|
end
|
|
end
|
|
table.insert(preamble, "}\n")
|
|
table.insert(ret, [=[
|
|
if ignoreSpellId[matchData.spellId] then
|
|
return false
|
|
end
|
|
]=])
|
|
end
|
|
|
|
table.insert(ret, [=[
|
|
return true
|
|
end
|
|
]=])
|
|
|
|
local func, err = loadstring(table.concat(preamble) .. table.concat(ret))
|
|
|
|
if func then
|
|
return func()
|
|
end
|
|
end
|
|
|
|
local matchCombineFunctions = {
|
|
showHighest = function(bestMatch, auraMatch)
|
|
if bestMatch.expirationTime and auraMatch.expirationTime then
|
|
return auraMatch.expirationTime > bestMatch.expirationTime
|
|
end
|
|
return true
|
|
end,
|
|
showLowest = function(bestMatch, auraMatch)
|
|
if bestMatch.expirationTime and auraMatch.expirationTime then
|
|
return auraMatch.expirationTime < bestMatch.expirationTime
|
|
end
|
|
return false
|
|
end,
|
|
showLowestSpellId = function(bestMatch, auraMatch)
|
|
if bestMatch.spellId and auraMatch.spellId then
|
|
return auraMatch.spellId < bestMatch.spellId
|
|
end
|
|
return false
|
|
end,
|
|
showHighestSpellId = function(bestMatch, auraMatch)
|
|
if bestMatch.spellId and auraMatch.spellId then
|
|
return auraMatch.spellId > bestMatch.spellId
|
|
end
|
|
return false
|
|
end,
|
|
}
|
|
|
|
local function GreaterEqualOne(x)
|
|
return x >= 1
|
|
end
|
|
|
|
local function EqualZero(x)
|
|
return x == 0
|
|
end
|
|
|
|
--- Adds an aura, setting up internal data structures for all buff triggers.
|
|
-- @param data
|
|
function BuffTrigger.Add(data)
|
|
local id = data.id
|
|
|
|
triggerInfos[id] = nil
|
|
for triggernum, triggerData in ipairs(data.triggers) do
|
|
local trigger, untrigger = triggerData.trigger, triggerData.untrigger
|
|
if trigger.type == "aura2" then
|
|
|
|
trigger.unit = trigger.unit or "player"
|
|
trigger.debuffType = trigger.debuffType or "HELPFUL"
|
|
|
|
local combineMode = "showOne"
|
|
local perUnitMode
|
|
if not IsSingleMissing(trigger) and trigger.showClones then
|
|
if IsGroupTrigger(trigger) and trigger.combinePerUnit then
|
|
combineMode = "showPerUnit"
|
|
if trigger.unit == "multi" then
|
|
perUnitMode = "affected"
|
|
else
|
|
perUnitMode = trigger.perUnitMode or "affected"
|
|
end
|
|
else
|
|
combineMode = "showClones"
|
|
end
|
|
end
|
|
|
|
local scanFunc = createScanFunc(trigger)
|
|
|
|
local remFunc
|
|
if trigger.unit ~= "multi" and CanHaveMatchCheck(trigger) and trigger.useRem then
|
|
local remFuncStr = Private.function_strings.count:format(trigger.remOperator or ">=", tonumber(trigger.rem) or 0)
|
|
remFunc = Private.LoadFunction(remFuncStr)
|
|
end
|
|
|
|
local names
|
|
if trigger.useName and trigger.auranames then
|
|
names = {}
|
|
for index, spellName in ipairs(trigger.auranames) do
|
|
local spellId = WeakAuras.SafeToNumber(spellName)
|
|
names[index] = spellId and GetSpellInfo(spellId) or spellName
|
|
end
|
|
end
|
|
|
|
local showIfInvalidUnit
|
|
if trigger.unit ~= "player" and not IsGroupTrigger(trigger) then
|
|
showIfInvalidUnit = trigger.unitExists or false
|
|
end
|
|
local effectiveUseGroupCount = IsGroupTrigger(trigger) and trigger.useGroup_count
|
|
local groupCountFunc
|
|
if effectiveUseGroupCount then
|
|
local group_countFuncStr
|
|
local count, countType = Private.ParseNumber(trigger.group_count)
|
|
if trigger.group_countOperator and count and countType then
|
|
if countType == "whole" then
|
|
group_countFuncStr = Private.function_strings.count:format(trigger.group_countOperator, count)
|
|
else
|
|
group_countFuncStr = Private.function_strings.count_fraction:format(trigger.group_countOperator, count)
|
|
end
|
|
else
|
|
group_countFuncStr = Private.function_strings.count:format(">", 0)
|
|
end
|
|
groupCountFunc = Private.LoadFunction(group_countFuncStr)
|
|
end
|
|
|
|
local matchCountFunc
|
|
if HasMatchCount(trigger) and trigger.match_countOperator and trigger.match_count and tonumber(trigger.match_count) then
|
|
local count = tonumber(trigger.match_count)
|
|
local match_countFuncStr = Private.function_strings.count:format(trigger.match_countOperator, count)
|
|
matchCountFunc = Private.LoadFunction(match_countFuncStr)
|
|
elseif IsGroupTrigger(trigger) then
|
|
if trigger.showClones and not trigger.combinePerUnit then
|
|
matchCountFunc = GreaterEqualOne
|
|
end
|
|
elseif not IsGroupTrigger(trigger) then
|
|
if trigger.matchesShowOn == "showOnMissing" then
|
|
matchCountFunc = EqualZero
|
|
elseif trigger.matchesShowOn == "showOnActive" or not trigger.matchesShowOn then
|
|
matchCountFunc = GreaterEqualOne
|
|
end
|
|
end
|
|
|
|
local matchPerUnitCountFunc
|
|
if IsGroupTrigger(trigger) and combineMode == "showPerUnit" and perUnitMode ~= "unaffected" and trigger.useMatchPerUnit_count
|
|
and tonumber(trigger.matchPerUnit_count) and trigger.matchPerUnit_countOperator then
|
|
local count = tonumber(trigger.matchPerUnit_count)
|
|
local match_countFuncStr = Private.function_strings.count:format(trigger.matchPerUnit_countOperator, count)
|
|
matchPerUnitCountFunc = Private.LoadFunction(match_countFuncStr)
|
|
end
|
|
|
|
local groupTrigger = trigger.unit == "group" or trigger.unit == "raid" or trigger.unit == "party"
|
|
local effectiveIgnoreSelf = (groupTrigger or trigger.unit == "nameplate") and trigger.ignoreSelf
|
|
local effectiveRaidRole = groupTrigger and trigger.useRaidRole and trigger.raid_role or nil
|
|
local effectiveClass = groupTrigger and trigger.useClass and trigger.class
|
|
local effectiveSpecId = groupTrigger and trigger.useActualSpec and trigger.actualSpec or nil
|
|
local effectiveIgnoreDead = groupTrigger and trigger.ignoreDead
|
|
local effectiveIgnoreDisconnected = groupTrigger and trigger.ignoreDisconnected
|
|
local effectiveIgnoreInvisible = groupTrigger and trigger.ignoreInvisible
|
|
local effectiveHostility = (groupTrigger or trigger.unit == "nameplate") and trigger.useHostility and trigger.hostility
|
|
local effectiveNameCheck = groupTrigger and trigger.useUnitName and trigger.unitName
|
|
local effectiveNpcId = (trigger.unit == "nameplate" or trigger.unit == "boss") and trigger.useNpcId and Private.ExecEnv.ParseStringCheck(trigger.npcId)
|
|
|
|
if trigger.unit == "multi" then
|
|
BuffTrigger.InitMultiAura()
|
|
end
|
|
|
|
local auraspellids
|
|
if trigger.useExactSpellId and trigger.auraspellids then
|
|
auraspellids = {}
|
|
for _, spellIdString in ipairs(trigger.auraspellids) do
|
|
if spellIdString ~= "" then
|
|
local spellId = tonumber(spellIdString)
|
|
if spellId then
|
|
tinsert(auraspellids, spellId)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local unit
|
|
local groupSubType = "group"
|
|
if trigger.unit == "member" then
|
|
unit = trigger.specificUnit
|
|
elseif trigger.unit == "raid" then
|
|
unit = "group"
|
|
groupSubType = "raid"
|
|
elseif trigger.unit == "party" then
|
|
unit = "group"
|
|
groupSubType = "party"
|
|
else
|
|
unit = trigger.unit
|
|
end
|
|
|
|
local triggerInformation = {
|
|
auranames = names,
|
|
auraspellids = auraspellids,
|
|
unit = unit,
|
|
debuffType = trigger.debuffType,
|
|
ownOnly = trigger.ownOnly,
|
|
combineMode = combineMode,
|
|
perUnitMode = perUnitMode,
|
|
scanFunc = scanFunc,
|
|
remainingFunc = remFunc,
|
|
remainingCheck = trigger.unit ~= "multi" and CanHaveMatchCheck(trigger) and trigger.useRem and tonumber(trigger.rem) or 0,
|
|
id = id,
|
|
triggernum = triggernum,
|
|
compareFunc = matchCombineFunctions[trigger.combineMode] or matchCombineFunctions["showLowest"],
|
|
unitExists = showIfInvalidUnit,
|
|
fetchTooltip = not IsSingleMissing(trigger) and trigger.unit ~= "multi" and trigger.fetchTooltip,
|
|
fetchRaidMark = trigger.unit ~= "multi" and trigger.fetchRaidMark,
|
|
groupTrigger = IsGroupTrigger(trigger),
|
|
ignoreSelf = effectiveIgnoreSelf,
|
|
ignoreDead = effectiveIgnoreDead,
|
|
ignoreDisconnected = effectiveIgnoreDisconnected,
|
|
ignoreInvisible = effectiveIgnoreInvisible,
|
|
raidRole = effectiveRaidRole,
|
|
specId = effectiveSpecId,
|
|
groupSubType = groupSubType,
|
|
groupCountFunc = groupCountFunc,
|
|
class = effectiveClass,
|
|
hostility = effectiveHostility,
|
|
matchCountFunc = matchCountFunc,
|
|
matchPerUnitCountFunc = matchPerUnitCountFunc,
|
|
useAffected = unit == "group" and trigger.useAffected,
|
|
isMulti = trigger.unit == "multi",
|
|
nameChecker = effectiveNameCheck and Private.ExecEnv.ParseNameCheck(trigger.unitName),
|
|
includePets = trigger.use_includePets and trigger.includePets or nil,
|
|
npcId = effectiveNpcId
|
|
}
|
|
triggerInfos[id] = triggerInfos[id] or {}
|
|
triggerInfos[id][triggernum] = triggerInformation
|
|
end
|
|
end
|
|
end
|
|
|
|
--- Returns a table containing the names of all overlays
|
|
-- @param data
|
|
-- @param triggernum
|
|
function BuffTrigger.GetOverlayInfo(data, triggernum)
|
|
return {}
|
|
end
|
|
|
|
--- Returns whether the trigger can have clones.
|
|
-- @param data
|
|
-- @param triggernum
|
|
-- @return
|
|
local function CanHaveClones(data, triggernum)
|
|
local trigger = data.triggers[triggernum].trigger
|
|
if not IsSingleMissing(trigger) and trigger.showClones then
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
---Returns the type of tooltip to show for the trigger.
|
|
-- @param data
|
|
-- @param triggernum
|
|
-- @return string
|
|
function BuffTrigger.CanHaveTooltip(data, triggernum)
|
|
return "aura"
|
|
end
|
|
|
|
function BuffTrigger.SetToolTip(trigger, state)
|
|
if not state.unit or not state.index then
|
|
return false
|
|
end
|
|
if state.filter == "HELPFUL" then
|
|
GameTooltip:SetUnitBuff(state.unit, state.index, state.filter)
|
|
elseif state.filter == "HARMFUL" then
|
|
GameTooltip:SetUnitDebuff(state.unit, state.index, state.filter)
|
|
end
|
|
return true
|
|
end
|
|
|
|
|
|
function BuffTrigger.GetNameAndIconSimple(data, triggernum)
|
|
if not data then
|
|
return
|
|
end
|
|
local _, name, icon
|
|
local trigger = data.triggers[triggernum].trigger
|
|
|
|
if trigger.useName and trigger.auranames then
|
|
for index, spellName in ipairs(trigger.auranames) do
|
|
local spellId = WeakAuras.SafeToNumber(spellName)
|
|
if spellId then
|
|
name, _, icon = GetSpellInfo(spellName)
|
|
if name and icon then
|
|
return name, icon
|
|
end
|
|
elseif not tonumber(spellName) then
|
|
name, _, icon = GetSpellInfo(spellName)
|
|
if (name and icon) then
|
|
return name, icon
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if trigger.useExactSpellId and trigger.auraspellids then
|
|
for index, spellIdString in ipairs(trigger.auraspellids) do
|
|
local spellId = spellIdString ~= "" and tonumber(spellIdString)
|
|
if spellId then
|
|
name, _, icon = GetSpellInfo(spellIdString)
|
|
if name and icon then
|
|
return name, icon
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
--- Returns the name and icon to show in the options.
|
|
-- @param data
|
|
-- @param triggernum
|
|
-- @return name and icon
|
|
function BuffTrigger.GetNameAndIcon(data, triggernum)
|
|
local name, icon = BuffTrigger.GetNameAndIconSimple(data, triggernum)
|
|
if (not name or not icon and WeakAuras.spellCache) then
|
|
local trigger = data.triggers[triggernum].trigger
|
|
if trigger.useName and trigger.auranames then
|
|
for index, spellName in ipairs(trigger.auranames) do
|
|
icon = WeakAuras.spellCache.GetIcon(spellName)
|
|
if icon then
|
|
return spellName, icon
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return name, icon
|
|
end
|
|
|
|
--- Returns the tooltip text for additional properties.
|
|
-- @param data
|
|
-- @param triggernum
|
|
-- @return string of additional properties
|
|
function BuffTrigger.GetAdditionalProperties(data, triggernum)
|
|
local trigger = data.triggers[triggernum].trigger
|
|
local props = {}
|
|
props["spellId"] = L["Spell ID"]
|
|
props["debuffClass"] = L["Debuff Class"]
|
|
props["debuffClassIcon"] = L["Debuff Class Icon"]
|
|
props["unitCaster"] = L["Caster Unit"]
|
|
props["casterName"] = L["Caster Name"]
|
|
|
|
if trigger.unit ~= "multi" then
|
|
props["unit"] = L["Unit"]
|
|
end
|
|
props["unitName"] = L["Unit Name"]
|
|
props["matchCount"] = L["Match Count"]
|
|
props["matchCountPerUnit"] = L["Match Count per Unit"]
|
|
props["unitCount"] = L["Units Affected"]
|
|
props["totalStacks"] = L["Total stacks over all matches"]
|
|
|
|
if trigger.unit ~= "multi" then
|
|
props["maxUnitCount"] = L["Total Units"]
|
|
end
|
|
|
|
if not IsSingleMissing(trigger) and trigger.unit ~= "multi" and trigger.fetchTooltip then
|
|
props["tooltip"] = L["Tooltip"]
|
|
props["tooltip1"] = L["First Value of Tooltip Text"]
|
|
props["tooltip2"] = L["Second Value of Tooltip Text"]
|
|
props["tooltip3"] = L["Third Value of Tooltip Text"]
|
|
props["tooltip4"] = L["Fourth Value of Tooltip Text"]
|
|
end
|
|
|
|
if trigger.unit ~= "multi" then
|
|
props["stackGainTime"] = L["Since Stack Gain"]
|
|
props["stackLostTime"] = L["Since Stack Lost"]
|
|
props["initialTime"] = L["Since Apply"]
|
|
props["refreshTime"] = L["Since Apply/Refresh"]
|
|
end
|
|
|
|
if trigger.unit ~= "multi" and trigger.fetchRaidMark then
|
|
props["raidMark"] = L["Raid Mark"]
|
|
end
|
|
|
|
if (trigger.unit == "group" or trigger.unit == "raid" or trigger.unit == "party") and trigger.useAffected then
|
|
props["affected"] = L["Names of affected Players"]
|
|
props["unaffected"] = L["Names of unaffected Players"]
|
|
props["affectedUnits"] = L["Units of affected Players in a table format"]
|
|
props["unaffectedUnits"] = L["Units of unaffected Players in a table format"]
|
|
end
|
|
|
|
return props
|
|
end
|
|
|
|
function BuffTrigger.GetProgressSources(data, triggernum, values)
|
|
local trigger = data.triggers[triggernum].trigger
|
|
tinsert(values, {
|
|
trigger = triggernum,
|
|
property = "matchCount",
|
|
type = "number",
|
|
display = L["Match Count"]
|
|
})
|
|
tinsert(values, {
|
|
trigger = triggernum,
|
|
property = "matchCountPerUnit",
|
|
type = "number",
|
|
display = L["Match Count per Unit"]
|
|
})
|
|
tinsert(values, {
|
|
trigger = triggernum,
|
|
property = "unitCount",
|
|
type = "number",
|
|
display = L["Units Affected"],
|
|
total = trigger.unit ~= "multi" and "maxUnitCount" or nil
|
|
})
|
|
tinsert(values, {
|
|
trigger = triggernum,
|
|
property = "stacks",
|
|
type = "number",
|
|
display = L["Stacks"]
|
|
})
|
|
tinsert(values, {
|
|
trigger = triggernum,
|
|
property = "totalStacks",
|
|
type = "number",
|
|
display = L["Total stacks over all matches"]
|
|
})
|
|
if not IsSingleMissing(trigger) and trigger.unit ~= "multi" and trigger.fetchTooltip then
|
|
tinsert(values, {
|
|
trigger = triggernum,
|
|
property = "tooltip1",
|
|
type = "number",
|
|
display = L["Tooltip 1"]
|
|
})
|
|
tinsert(values, {
|
|
trigger = triggernum,
|
|
property = "tooltip2",
|
|
type = "number",
|
|
display = L["Tooltip 2"]
|
|
})
|
|
tinsert(values, {
|
|
trigger = triggernum,
|
|
property = "tooltip3",
|
|
type = "number",
|
|
display = L["Tooltip 3"]
|
|
})
|
|
end
|
|
tinsert(values, {
|
|
trigger = triggernum,
|
|
property = "expirationTime",
|
|
type = "timer",
|
|
display = L["Timed Progress"],
|
|
total = "duration",
|
|
modRate = "modRate",
|
|
paused = "paused",
|
|
remaining = "remaining"
|
|
})
|
|
tinsert(values, {
|
|
trigger = triggernum,
|
|
property = "stackGainTime",
|
|
type = "elapsedTimer",
|
|
display = L["Time since stack gain"],
|
|
})
|
|
tinsert(values, {
|
|
trigger = triggernum,
|
|
property = "stackLostTime",
|
|
type = "elapsedTimer",
|
|
display = L["Time since stack lost"],
|
|
})
|
|
tinsert(values, {
|
|
trigger = triggernum,
|
|
property = "initialTime",
|
|
type = "elapsedTimer",
|
|
display = L["Time since initial application"],
|
|
})
|
|
tinsert(values, {
|
|
trigger = triggernum,
|
|
property = "refreshTime",
|
|
type = "elapsedTimer",
|
|
display = L["Time since last refresh"],
|
|
})
|
|
end
|
|
|
|
function BuffTrigger.GetTriggerConditions(data, triggernum)
|
|
local trigger = data.triggers[triggernum].trigger
|
|
local result = {}
|
|
|
|
result["debuffClass"] = {
|
|
display = L["Debuff Type"],
|
|
type = "select",
|
|
values = Private.debuff_class_types
|
|
}
|
|
|
|
result["unitCaster"] = {
|
|
display = L["Caster Unit"],
|
|
type = "string"
|
|
}
|
|
|
|
result["nameCaster"] = {
|
|
display = L["Casters Name/Realm"],
|
|
type = "string",
|
|
preamble = function(input)
|
|
return Private.ExecEnv.ParseNameCheck(input)
|
|
end,
|
|
test = function(state, needle, op, preamble)
|
|
return state.unitCaster and preamble:Check(WeakAuras.UnitNameWithRealm(state.unitCaster))
|
|
end,
|
|
operator_types = "none",
|
|
}
|
|
|
|
result["expirationTime"] = {
|
|
display = L["Remaining Duration"],
|
|
type = "timer"
|
|
}
|
|
|
|
result["duration"] = {
|
|
display = L["Total Duration"],
|
|
type = "number"
|
|
}
|
|
|
|
result["stacks"] = {
|
|
display = L["Stacks"],
|
|
type = "number"
|
|
}
|
|
|
|
result["name"] = {
|
|
display = L["Name"],
|
|
type = "string"
|
|
}
|
|
|
|
result["spellId"] = {
|
|
display = L["Spell Id"],
|
|
type = "number",
|
|
operator_types = "only_equal"
|
|
}
|
|
|
|
result["matchCount"] = {
|
|
display = L["Total Match Count"],
|
|
type = "number"
|
|
}
|
|
|
|
result["matchCountPerUnit"] = {
|
|
display = L["Match Count per Unit"],
|
|
type = "number"
|
|
}
|
|
|
|
result["unitCount"] = {
|
|
display = L["Affected Unit Count"],
|
|
type = "number"
|
|
}
|
|
|
|
result["totalStacks"] = {
|
|
display = L["Total Stacks"],
|
|
type = "number"
|
|
}
|
|
|
|
if trigger.unit ~= "multi" then
|
|
result["maxUnitCount"] = {
|
|
display = L["Total Unit Count"],
|
|
type = "number"
|
|
}
|
|
end
|
|
|
|
if not IsGroupTrigger(trigger) and trigger.matchesShowOn == "showAlways"
|
|
or IsGroupTrigger(trigger) and trigger.showClones and trigger.unit ~= "multi" and trigger.combinePerUnit
|
|
then
|
|
result["buffed"] = {
|
|
display = L["Aura(s) Found"],
|
|
type = "bool",
|
|
test = function(state, needle)
|
|
return state and state.show and ((state.active and true or false) == (needle == 1))
|
|
end
|
|
}
|
|
end
|
|
|
|
if not IsSingleMissing(trigger) and trigger.unit ~= "multi" and trigger.fetchTooltip then
|
|
result["tooltip1"] = {
|
|
display = L["Tooltip Value 1"],
|
|
type = "number"
|
|
}
|
|
result["tooltip2"] = {
|
|
display = L["Tooltip Value 2"],
|
|
type = "number"
|
|
}
|
|
result["tooltip3"] = {
|
|
display = L["Tooltip Value 3"],
|
|
type = "number"
|
|
}
|
|
result["tooltip4"] = {
|
|
display = L["Tooltip Value 4"],
|
|
type = "number"
|
|
}
|
|
end
|
|
|
|
if trigger.unit ~= "multi" then
|
|
result["stackGainTime"] = {
|
|
display = L["Since Stack Gain"],
|
|
type = "elapsedTimer"
|
|
}
|
|
result["stackLostTime"] = {
|
|
display = L["Since Stack Lost"],
|
|
type = "elapsedTimer"
|
|
}
|
|
result["initialTime"] = {
|
|
display = L["Since Apply"],
|
|
type = "elapsedTimer"
|
|
}
|
|
result["refreshTime"] = {
|
|
display = L["Since Apply/Refresh"],
|
|
type = "elapsedTimer"
|
|
}
|
|
end
|
|
|
|
return result
|
|
end
|
|
|
|
function BuffTrigger.CreateFallbackState(data, triggernum, state)
|
|
state.show = true
|
|
state.changed = true
|
|
state.progressType = "timed"
|
|
state.duration = 0
|
|
state.expirationTime = math.huge
|
|
local name, icon = BuffTrigger.GetNameAndIconSimple(data, triggernum)
|
|
state.name = name
|
|
state.icon = icon
|
|
end
|
|
|
|
function BuffTrigger.GetName(triggerType)
|
|
if triggerType == "aura2" then
|
|
return L["Aura"]
|
|
end
|
|
end
|
|
|
|
function Private.CanConvertBuffTrigger2(trigger)
|
|
if trigger.type ~= "aura" then
|
|
return false
|
|
end
|
|
|
|
if trigger.unit == "multi" then
|
|
return true, L["Note: The available text replacements for multi triggers match the normal triggers now."]
|
|
end
|
|
|
|
if trigger.unit and trigger.hideAlone then
|
|
return false, L["Note: 'Hide Alone' is not available in the new aura tracking system. A load option can be used instead."]
|
|
end
|
|
|
|
if trigger.unit == "group" then
|
|
return true, L["Warning: Name info is now available via %affected, %unaffected. Number of affected group members via %unitCount. Some options behave differently now. This is not automatically adjusted."]
|
|
end
|
|
|
|
if trigger.fullscan then
|
|
if trigger.subcount then
|
|
return true, L["Warning: Tooltip values are now available via %tooltip1, %tooltip2, %tooltip3 instead of %s. This is not automatically adjusted."]
|
|
end
|
|
|
|
if trigger.use_name and trigger.use_spellId then
|
|
return false, L["Warning: Full Scan auras checking for both name and spell id can't be converted."]
|
|
end
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
function Private.ConvertBuffTrigger2(trigger)
|
|
if not Private.CanConvertBuffTrigger2(trigger) then
|
|
return
|
|
end
|
|
trigger.type = "aura2"
|
|
|
|
if trigger.fullscan and trigger.autoclone then
|
|
trigger.combineMatches = "showClones"
|
|
else
|
|
trigger.combineMatches = "showLowest"
|
|
end
|
|
|
|
if trigger.fullscan and trigger.use_stealable then
|
|
trigger.use_stealable = true
|
|
else
|
|
trigger.use_stealable = nil
|
|
end
|
|
|
|
if trigger.fullscan and trigger.use_debuffClass and trigger.debuffClass then
|
|
else
|
|
trigger.use_debuffClass = false
|
|
end
|
|
|
|
if trigger.fullscan and trigger.use_tooltip then
|
|
trigger.fetchTooltip = true
|
|
else
|
|
trigger.use_tooltip = false
|
|
end
|
|
|
|
if trigger.fullscan and trigger.subcount then
|
|
trigger.fetchTooltip = true
|
|
end
|
|
|
|
if trigger.fullscan and trigger.use_name then
|
|
trigger.useNamePattern = true
|
|
trigger.namePattern_operator = trigger.name_operator
|
|
trigger.namePattern_name = trigger.name
|
|
end
|
|
|
|
if trigger.fullscan then
|
|
-- Use name from fullscan
|
|
if trigger.use_name then
|
|
if trigger.name_operator == "==" then
|
|
-- Convert to normal name check
|
|
trigger.useName = true
|
|
trigger.auranames = {}
|
|
trigger.auranames[1] = trigger.name
|
|
end
|
|
end
|
|
if trigger.use_spellId then
|
|
trigger.useExactSpellId = true
|
|
trigger.auraspellids = {}
|
|
trigger.auraspellids[1] = trigger.spellId
|
|
end
|
|
else
|
|
trigger.useName = true
|
|
end
|
|
|
|
if not trigger.fullscan and trigger.unit ~= "multi" then
|
|
trigger.auranames = {}
|
|
for i = 1, 9 do
|
|
trigger.auranames[i] = trigger.spellIds[i] and tostring(trigger.spellIds[i]) or trigger.names[i]
|
|
end
|
|
end
|
|
|
|
if trigger.unit == "multi" then
|
|
-- Closest to the old behavior
|
|
trigger.showClones = true
|
|
trigger.useName = true
|
|
trigger.auranames = {}
|
|
trigger.auranames[1] = tostring(trigger.spellId) or trigger.name
|
|
end
|
|
|
|
-- debuffType is exactly the same, no need to touch it
|
|
-- remaining is exactly the same for now
|
|
-- ownOnly is exactly the same
|
|
-- unitExists is exactly the same
|
|
|
|
if trigger.useCount then
|
|
trigger.useStacks = trigger.useCount
|
|
trigger.stacksOperator = trigger.countOperator
|
|
trigger.stacks = trigger.count
|
|
end
|
|
|
|
if trigger.fullscan and trigger.autoclone then
|
|
trigger.matchesShowOn = "showOnActive"
|
|
else
|
|
trigger.matchesShowOn = trigger.buffShowOn
|
|
end
|
|
|
|
if trigger.unit == "group" then
|
|
trigger.matchesShowOn = nil
|
|
trigger.showClones = trigger.groupclone
|
|
end
|
|
|
|
if trigger.unit == "group" and not trigger.groupclone then
|
|
if trigger.name_info == "players" or trigger.name_info == "nonplayers" then
|
|
trigger.useAffected = true
|
|
end
|
|
end
|
|
|
|
if trigger.unit == "group" and trigger.group_countOperator and trigger.group_count then
|
|
trigger.useGroup_count = true
|
|
else
|
|
trigger.useGroup_count = false
|
|
end
|
|
end
|
|
|
|
-- Multi Target trigger code
|
|
local multiAuraFrame
|
|
local pendingTracks = {}
|
|
|
|
local unitToGuid = {}
|
|
local guidToUnit = {}
|
|
|
|
local function ReleaseUID(unit)
|
|
local guid = unitToGuid[unit]
|
|
if guid then
|
|
guidToUnit[guid][unit] = nil
|
|
end
|
|
end
|
|
|
|
local function SetUID(guid, unit)
|
|
ReleaseUID(unit)
|
|
|
|
unitToGuid[unit] = guid
|
|
guidToUnit[guid] = guidToUnit[guid] or {}
|
|
guidToUnit[guid][unit] = true
|
|
end
|
|
|
|
local function GetUnit(guid)
|
|
if not guidToUnit[guid] then
|
|
return nil
|
|
end
|
|
for unit in pairs(guidToUnit[guid]) do
|
|
if UnitGUID(unit) == guid then
|
|
return unit
|
|
else
|
|
guidToUnit[guid][unit] = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
local function TrackUid(unit)
|
|
local GUID = UnitGUID(unit)
|
|
if GUID then
|
|
SetUID(GUID, unit)
|
|
BuffTrigger.HandlePendingTracks(unit, GUID)
|
|
else
|
|
ReleaseUID(unit)
|
|
end
|
|
unit = unit.."target"
|
|
GUID = UnitGUID(unit)
|
|
if GUID then
|
|
SetUID(GUID, unit)
|
|
BuffTrigger.HandlePendingTracks(unit, GUID)
|
|
else
|
|
ReleaseUID(unit)
|
|
end
|
|
end
|
|
|
|
local function RemoveMatchDataMulti(base, destGUID, key, sourceGUID)
|
|
if base[key] and base[key][sourceGUID] then
|
|
for id, idData in pairs(base[key][sourceGUID].auras) do
|
|
for triggernum, triggerData in pairs(idData) do
|
|
tDeleteItem(matchDataByTrigger[id][triggernum][destGUID], base[key][sourceGUID])
|
|
if not next(matchDataByTrigger[id][triggernum][destGUID]) then
|
|
matchDataByTrigger[id][triggernum][destGUID] = nil
|
|
end
|
|
matchDataChanged[id] = matchDataChanged[id] or {}
|
|
matchDataChanged[id][triggernum] = true
|
|
end
|
|
end
|
|
base[key][sourceGUID] = nil
|
|
end
|
|
end
|
|
|
|
local function CleanUpMulti(guid)
|
|
cleanupTimerMulti[guid].handle = nil
|
|
cleanupTimerMulti[guid].nextTime = nil
|
|
local nextCheck
|
|
if matchDataMulti[guid] then
|
|
local time = GetTime()
|
|
for key, data in pairs(matchDataMulti[guid]) do
|
|
for source, sourceData in pairs(data) do
|
|
local removeAt
|
|
if sourceData.expirationTime and sourceData.expirationTime ~= math.huge then
|
|
removeAt = sourceData.expirationTime
|
|
else
|
|
removeAt = sourceData.time + 60
|
|
end
|
|
if removeAt <= time then
|
|
RemoveMatchDataMulti(matchDataMulti[guid], guid, key, source)
|
|
else
|
|
if not nextCheck then
|
|
nextCheck = removeAt
|
|
elseif (removeAt < nextCheck) then
|
|
nextCheck = removeAt
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if nextCheck then
|
|
local timeUntilNext = nextCheck - GetTime()
|
|
if timeUntilNext > 0 then
|
|
cleanupTimerMulti[guid].handle = timer:ScheduleTimer(CleanUpMulti, timeUntilNext, guid)
|
|
cleanupTimerMulti[guid].nextTime = nextCheck
|
|
end
|
|
end
|
|
end
|
|
|
|
local function ScheduleMultiCleanUp(guid, time)
|
|
cleanupTimerMulti[guid] = cleanupTimerMulti[guid] or {}
|
|
if not cleanupTimerMulti[guid].nextTime or time < cleanupTimerMulti[guid].nextTime then
|
|
if cleanupTimerMulti[guid].handle then
|
|
timer:CancelTimer(cleanupTimerMulti[guid].handle)
|
|
end
|
|
cleanupTimerMulti[guid].handle = timer:ScheduleTimer(CleanUpMulti, time - GetTime(), guid)
|
|
cleanupTimerMulti[guid].nextTime = time
|
|
end
|
|
end
|
|
|
|
local function UpdateMatchDataMulti(time, base, key, event, sourceGUID, sourceName, destGUID, destName, spellId, spellName, amount)
|
|
local updated = false
|
|
local icon = spellId and select(3, GetSpellInfo(spellId))
|
|
ScheduleMultiCleanUp(destGUID, time + 60)
|
|
if not base[key] or not base[key][sourceGUID] then
|
|
updated = true
|
|
base[key] = base[key] or {}
|
|
base[key][sourceGUID] = {
|
|
name = spellName,
|
|
icon = icon,
|
|
duration = 0,
|
|
expirationTime = math.huge,
|
|
spellId = spellId,
|
|
GUID = destGUID,
|
|
sourceGUID = sourceGUID,
|
|
unitName = destName,
|
|
casterName = sourceName,
|
|
time = time,
|
|
auras = {}
|
|
}
|
|
else
|
|
base[key][sourceGUID] = base[key][sourceGUID] or {}
|
|
local match = base[key][sourceGUID]
|
|
match.time = time
|
|
|
|
if match.name ~= spellName then
|
|
match.name = spellName
|
|
updated = true
|
|
end
|
|
|
|
if match.unitName ~= destName then
|
|
match.unitName = destName
|
|
updated = true
|
|
end
|
|
|
|
local duration, expirationTime
|
|
if event == "SPELL_AURA_APPLIED_DOSE" or event == "SPELL_AURA_REMOVED_DOSE" then
|
|
-- Shouldn't affect duration/expirationTime nor icon
|
|
duration = match.duration or 0
|
|
expirationTime = match.expirationTime or math.huge
|
|
icon = match.icon or icon
|
|
else
|
|
duration = 0
|
|
expirationTime = math.huge
|
|
end
|
|
|
|
if match.duration ~= duration then
|
|
match.duration = duration
|
|
updated = true
|
|
end
|
|
|
|
if match.expirationTime ~= expirationTime then
|
|
match.expirationTime = expirationTime
|
|
updated = true
|
|
end
|
|
|
|
if match.icon ~= icon then
|
|
match.icon = icon
|
|
updated = true
|
|
end
|
|
|
|
if match.count ~= amount then
|
|
match.count = amount
|
|
updated = true
|
|
end
|
|
|
|
if match.spellId ~= spellId then
|
|
match.spellId = spellId
|
|
updated = true
|
|
end
|
|
|
|
if match.casterName ~= sourceName then
|
|
match.casterName = sourceName
|
|
updated = true
|
|
end
|
|
end
|
|
|
|
return updated
|
|
end
|
|
|
|
local function AugmentMatchDataMultiWith(matchData, unit, name, icon, stacks, debuffClass, duration, expirationTime, unitCaster, isStealable, _, spellId)
|
|
if expirationTime == 0 then
|
|
expirationTime = math.huge
|
|
else
|
|
ScheduleMultiCleanUp(matchData.GUID, expirationTime)
|
|
end
|
|
local changed = false
|
|
if matchData.name ~= name then
|
|
matchData.name = name
|
|
changed = true
|
|
end
|
|
|
|
if matchData.icon ~= icon then
|
|
matchData.icon = icon
|
|
changed = true
|
|
end
|
|
|
|
if matchData.stacks ~= stacks then
|
|
matchData.stacks = stacks
|
|
changed = true
|
|
end
|
|
|
|
if matchData.debuffClass ~= debuffClass then
|
|
matchData.debuffClass = debuffClass
|
|
changed = true
|
|
end
|
|
|
|
if matchData.duration ~= duration then
|
|
matchData.duration = duration
|
|
changed = true
|
|
end
|
|
|
|
if matchData.expirationTime ~= expirationTime then
|
|
matchData.expirationTime = expirationTime
|
|
changed = true
|
|
end
|
|
|
|
if matchData.unitCaster ~= unitCaster then
|
|
matchData.unitCaster = unitCaster
|
|
changed = true
|
|
end
|
|
|
|
local casterName = GetUnitName(unitCaster, false) or ""
|
|
if matchData.casterName ~= casterName then
|
|
matchData.casterName = casterName
|
|
changed = true
|
|
end
|
|
|
|
local unitName = GetUnitName(unit, false) or ""
|
|
if matchData.unitName ~= unitName then
|
|
matchData.unitName = unitName
|
|
changed = true
|
|
end
|
|
|
|
if matchData.spellId ~= spellId then
|
|
matchData.spellId = name
|
|
changed = true
|
|
end
|
|
return changed
|
|
end
|
|
|
|
local function AugmentMatchDataMulti(matchData, unit, filter, sourceGUID, nameKey, spellKey)
|
|
local index = 1
|
|
while true do
|
|
local name, rank, icon, stacks, debuffClass, duration, expirationTime, unitCaster, isStealable, _, spellId = UnitAura(unit, index, filter)
|
|
if not name then
|
|
return false
|
|
end
|
|
|
|
if debuffClass == nil then
|
|
debuffClass = "none"
|
|
elseif debuffClass == "" then
|
|
debuffClass = "enrage"
|
|
else
|
|
debuffClass = string.lower(debuffClass)
|
|
end
|
|
local auraSourceGuid = unitCaster and UnitGUID(unitCaster)
|
|
if (name == nameKey or spellId == spellKey) and sourceGUID == auraSourceGuid then
|
|
local changed = AugmentMatchDataMultiWith(matchData, unit, name, icon, stacks, debuffClass, duration, expirationTime, unitCaster, isStealable, _, spellId)
|
|
return changed
|
|
end
|
|
index = index + 1
|
|
end
|
|
end
|
|
|
|
local function HandleCombatLog(scanFuncsName, scanFuncsSpellId, filter, event, sourceGUID, sourceName, destGUID, destName, spellId, spellName, amount)
|
|
local time = GetTime()
|
|
local unit = GetUnit(destGUID)
|
|
if scanFuncsName and scanFuncsName[spellName] or scanFuncsSpellId and scanFuncsSpellId[spellId] then
|
|
ScheduleMultiCleanUp(destGUID, time + 60)
|
|
matchDataMulti[destGUID] = matchDataMulti[destGUID] or {}
|
|
|
|
if scanFuncsSpellId and scanFuncsSpellId[spellId] then
|
|
local updatedSpellId = UpdateMatchDataMulti(time, matchDataMulti[destGUID], spellId, event, sourceGUID, sourceName, destGUID, destName, spellId, spellName, amount)
|
|
if unit then
|
|
updatedSpellId = AugmentMatchDataMulti(matchDataMulti[destGUID][spellId][sourceGUID], unit, filter, sourceGUID, nil, spellId) or updatedSpellId
|
|
else
|
|
pendingTracks[destGUID] = true
|
|
end
|
|
if updatedSpellId then
|
|
for triggerInfo in pairs(scanFuncsSpellId[spellId]) do
|
|
if MatchesTriggerInfoMulti(triggerInfo, sourceGUID) then
|
|
ReferenceMatchDataMulti(matchDataMulti[destGUID][spellId][sourceGUID], triggerInfo.id, triggerInfo.triggernum, destGUID)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if scanFuncsName and scanFuncsName[spellName] then
|
|
local updatedName = UpdateMatchDataMulti(time, matchDataMulti[destGUID], spellName, event, sourceGUID, sourceName, destGUID, destName, spellId, spellName, amount)
|
|
if unit then
|
|
updatedName = AugmentMatchDataMulti(matchDataMulti[destGUID][spellName][sourceGUID], unit, filter, sourceGUID, spellName, nil) or updatedName
|
|
else
|
|
pendingTracks[destGUID] = true
|
|
end
|
|
if updatedName then
|
|
for triggerInfo in pairs(scanFuncsName[spellName]) do
|
|
if MatchesTriggerInfoMulti(triggerInfo, sourceGUID) then
|
|
ReferenceMatchDataMulti(matchDataMulti[destGUID][spellName][sourceGUID], triggerInfo.id, triggerInfo.triggernum, destGUID)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function HandleCombatLogRemove(scanFuncsName, scanFuncsSpellId, sourceGUID, destGUID, spellId, spellName)
|
|
if scanFuncsName and scanFuncsName[spellName] or scanFuncsSpellId and scanFuncsSpellId[spellId] then
|
|
if matchDataMulti[destGUID] then
|
|
RemoveMatchDataMulti(matchDataMulti[destGUID], destGUID, spellId, sourceGUID)
|
|
RemoveMatchDataMulti(matchDataMulti[destGUID], destGUID, spellName, sourceGUID)
|
|
end
|
|
end
|
|
end
|
|
|
|
local function CombatLog(_, event, sourceGUID, sourceName, _, destGUID, destName, _, spellId, spellName, _, auraType, amount)
|
|
if event == "SPELL_AURA_APPLIED" or event == "SPELL_AURA_REFRESH" or event == "SPELL_AURA_APPLIED_DOSE" or event == "SPELL_AURA_REMOVED_DOSE" then
|
|
if auraType == "BUFF" then
|
|
HandleCombatLog(scanFuncNameMulti["HELPFUL"], scanFuncSpellIdMulti["HELPFUL"], "HELPFUL", event, sourceGUID, sourceName, destGUID, destName, spellId, spellName, amount)
|
|
elseif auraType == "DEBUFF" then
|
|
HandleCombatLog(scanFuncNameMulti["HARMFUL"], scanFuncSpellIdMulti["HARMFUL"], "HARMFUL", event, sourceGUID, sourceName, destGUID, destName, spellId, spellName, amount)
|
|
end
|
|
elseif event == "SPELL_AURA_REMOVED" then
|
|
if auraType == "BUFF" then
|
|
HandleCombatLogRemove(scanFuncNameMulti["HELPFUL"], scanFuncSpellIdMulti["HELPFUL"], sourceGUID, destGUID, spellId, spellName)
|
|
elseif auraType == "DEBUFF" then
|
|
HandleCombatLogRemove(scanFuncNameMulti["HARMFUL"], scanFuncSpellIdMulti["HARMFUL"], sourceGUID, destGUID, spellId, spellName)
|
|
end
|
|
end
|
|
end
|
|
|
|
local function CheckAurasMulti(base, unit, filter)
|
|
local index = 1
|
|
while true do
|
|
local name, rank, icon, stacks, debuffClass, duration, expirationTime, unitCaster, isStealable, _, spellId = UnitAura(unit, index, filter)
|
|
if not name then
|
|
return false
|
|
end
|
|
|
|
if debuffClass == nil then
|
|
debuffClass = "none"
|
|
elseif debuffClass == "" then
|
|
debuffClass = "enrage"
|
|
else
|
|
debuffClass = string.lower(debuffClass)
|
|
end
|
|
local auraCasterGUID = unitCaster and UnitGUID(unitCaster)
|
|
if base[name] and base[name][auraCasterGUID] then
|
|
local changed = AugmentMatchDataMultiWith(base[name][auraCasterGUID], unit, name, icon, stacks, debuffClass, duration, expirationTime, unitCaster, isStealable, _, spellId)
|
|
if changed then
|
|
for id, idData in pairs(base[name][auraCasterGUID].auras) do
|
|
for triggernum in pairs(idData) do
|
|
matchDataChanged[id] = matchDataChanged[id] or {}
|
|
matchDataChanged[id][triggernum] = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
if base[spellId] and base[spellId][auraCasterGUID] then
|
|
local changed = AugmentMatchDataMultiWith(base[spellId][auraCasterGUID], unit, name, icon, stacks, debuffClass, duration, expirationTime, unitCaster, isStealable, _, spellId)
|
|
if changed then
|
|
for id, idData in pairs(base[spellId][auraCasterGUID].auras) do
|
|
for triggernum in pairs(idData) do
|
|
matchDataChanged[id] = matchDataChanged[id] or {}
|
|
matchDataChanged[id][triggernum] = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
index = index + 1
|
|
end
|
|
end
|
|
|
|
function BuffTrigger.HandlePendingTracks(unit, GUID)
|
|
if pendingTracks[GUID] then
|
|
if matchDataMulti[GUID] then
|
|
CheckAurasMulti(matchDataMulti[GUID], unit, "HELPFUL")
|
|
CheckAurasMulti(matchDataMulti[GUID], unit, "HARMFUL")
|
|
end
|
|
end
|
|
end
|
|
|
|
function BuffTrigger.InitMultiAura()
|
|
if not multiAuraFrame then
|
|
multiAuraFrame = CreateFrame("frame")
|
|
multiAuraFrame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
|
|
multiAuraFrame:RegisterEvent("UNIT_TARGET")
|
|
multiAuraFrame:RegisterEvent("UNIT_AURA")
|
|
multiAuraFrame:RegisterEvent("PLAYER_TARGET_CHANGED")
|
|
multiAuraFrame:RegisterEvent("PLAYER_FOCUS_CHANGED")
|
|
if WeakAuras.isAwesomeEnabled() then
|
|
multiAuraFrame:RegisterEvent("NAME_PLATE_UNIT_ADDED")
|
|
multiAuraFrame:RegisterEvent("NAME_PLATE_UNIT_REMOVED")
|
|
end
|
|
multiAuraFrame:RegisterEvent("PLAYER_LEAVING_WORLD")
|
|
multiAuraFrame:SetScript("OnEvent", BuffTrigger.HandleMultiEvent)
|
|
Private.frames["Multi-target 2 Aura Trigger Handler"] = multiAuraFrame
|
|
end
|
|
end
|
|
|
|
function BuffTrigger.HandleMultiEvent(frame, event, ...)
|
|
local system = "bufftrigger2 - multi - " .. event
|
|
Private.StartProfileSystem(system)
|
|
if event == "COMBAT_LOG_EVENT_UNFILTERED" then
|
|
CombatLog(...)
|
|
elseif event == "UNIT_TARGET" then
|
|
TrackUid(...)
|
|
elseif event == "PLAYER_TARGET_CHANGED" then
|
|
TrackUid("target")
|
|
elseif event == "PLAYER_FOCUS_CHANGED" then
|
|
TrackUid("focus")
|
|
elseif event == "NAME_PLATE_UNIT_ADDED" then
|
|
TrackUid(...)
|
|
elseif event == "NAME_PLATE_UNIT_REMOVED" then
|
|
local unit = ...
|
|
ReleaseUID(unit)
|
|
unit = unit.."target"
|
|
ReleaseUID(unit)
|
|
elseif event == "UNIT_AURA" then
|
|
local unit = ...
|
|
if not unit then return end
|
|
local guid = UnitGUID(unit)
|
|
if matchDataMulti[guid] then
|
|
CheckAurasMulti(matchDataMulti[guid], unit, "HELPFUL")
|
|
CheckAurasMulti(matchDataMulti[guid], unit, "HARMFUL")
|
|
end
|
|
elseif event == "PLAYER_LEAVING_WORLD" then
|
|
-- Remove everything..
|
|
for GUID, GUIDData in pairs(matchDataMulti) do
|
|
for key in pairs(GUIDData) do
|
|
RemoveMatchDataMulti(GUIDData, GUID, key)
|
|
end
|
|
end
|
|
wipe(matchDataMulti)
|
|
end
|
|
Private.StopProfileSystem(system)
|
|
end
|
|
|
|
function BuffTrigger.GetTriggerDescription(data, triggernum, namestable)
|
|
local trigger = data.triggers[triggernum].trigger
|
|
if trigger.useName and trigger.auranames then
|
|
for index, name in pairs(trigger.auranames) do
|
|
if index > 10 then
|
|
tinsert(namestable, {" ", "[...]"})
|
|
break
|
|
end
|
|
|
|
local left = " "
|
|
if(index == 1) then
|
|
if(#trigger.auranames > 0) then
|
|
if(#trigger.auranames > 1) then
|
|
left = L["Auras:"]
|
|
else
|
|
left = L["Aura:"]
|
|
end
|
|
end
|
|
end
|
|
local icon
|
|
local spellId = WeakAuras.SafeToNumber(name)
|
|
if spellId then
|
|
icon = select(3, GetSpellInfo(spellId))
|
|
else
|
|
icon = WeakAuras.spellCache.GetIcon(name)
|
|
end
|
|
icon = icon or "Interface\\Icons\\INV_Misc_QuestionMark"
|
|
tinsert(namestable, {left, name, icon})
|
|
end
|
|
end
|
|
|
|
if trigger.useExactSpellId and trigger.auraspellids then
|
|
for index, spellId in pairs(trigger.auraspellids) do
|
|
if index > 10 then
|
|
tinsert(namestable, {" ", "[...]"})
|
|
break
|
|
end
|
|
|
|
local left = " "
|
|
if index == 1 then
|
|
if #trigger.auraspellids > 0 then
|
|
if #trigger.auraspellids > 1 then
|
|
left = L["Spell IDs:"]
|
|
else
|
|
left = L["Spell ID:"]
|
|
end
|
|
end
|
|
end
|
|
|
|
local icon = select(3, GetSpellInfo(spellId or 0)) or "Interface\\Icons\\INV_Misc_QuestionMark"
|
|
tinsert(namestable, {left, spellId, icon})
|
|
end
|
|
end
|
|
end
|
|
|
|
function BuffTrigger.CreateFakeStates(id, triggernum)
|
|
local allStates = WeakAuras.GetTriggerStateForTrigger(id, triggernum);
|
|
local data = WeakAuras.GetData(id)
|
|
local state = {}
|
|
BuffTrigger.CreateFallbackState(data, triggernum, state)
|
|
state.expirationTime = GetTime() + 60
|
|
state.duration = 65
|
|
state.progressType = "timed"
|
|
state.stacks = 1
|
|
allStates[""] = state
|
|
if CanHaveClones(data, triggernum) then
|
|
for i = 1, 2 do
|
|
local state = {}
|
|
BuffTrigger.CreateFallbackState(data, triggernum, state)
|
|
state.expirationTime = GetTime() + 60 + i * 20
|
|
state.duration = 100
|
|
state.progressType = "timed"
|
|
state.stacks = 1
|
|
allStates[i] = state
|
|
end
|
|
end
|
|
end
|
|
|
|
WeakAuras.RegisterTriggerSystem({"aura2"}, BuffTrigger)
|