i think thats something for dmg meters , also its up to anyone to decide which amount of dmg is FAIL
Not many meters show this sort of thing. Secondly, not suggesting it needs to post a 'fail' alert just flagging that this sort of feedback is pretty central to these fights. Bossmods don't seem to want to focus on these aspects, damage meter can't (the data they need to present is simply too specific). Seems like a pretty reasonable area for a 'fail' type lib to consider contributing.
(ie. the entire intent of libfail is when something screws up you know who caused it - eg. facetanking tramples. Similarly, when your portal doesn't die and it dumps a 'damage done' list and that $##$#$ hunter doesn't appear on it you can nail them then and there)
Not suggesting it has to be done within libfail, but if not, where else? Asking users to install a large laundry list of mods for each of these types of encounters doesn't seem a great solution either.
Considering a generic system, IMO you could try to detect who has done no damage to a target (or who hasn't healed someone), though it would 1) require to detect starting and ending point in time, 2) consider a "no damage threshold". But depending the chosen raid strat, maybe some raid members are not intended to deal damages to a precise target or to heal only the tank so this kind of fail would not be pertinent.
Edit: on second though it also requires the code to know the role of every member in term of tank/healer/damager dealer, which could be hard to guess.
regarding your edit, ensidiafails already does determine the players role.
first tanks were excluded with a simpel if health > 45000 = tank , doesnt fail, then it used LibGroupTalents to know the players role, but still, i think we can get fails like that without LibFail
LibFail-1.0...failed (that always makes me giggle ;)) on a Deathbringer Saurfang fight in ICC25.
r182 (the released-in-last-seven-hours r184 has no changes related to Saurfang), RecountFailBot r25, USEnglish client/server. Sometime during the fight (may or may not have been when I died to Mark of the Fallen Champion, almost certain did not die to a Blood Beast):
["message"] = {
"LibFail-1.0-999 (RecountFailBot):1893: attempt to compare number with table\nLibFail-1.0-999 (RecountFailBot):695: in function <...dOns\\RecountFailBot\\Libs\\LibFail-1.0\\LibFail-1.0.lua:680>\n\nLocals:|r\nself = <unnamed> {\n 0 = <userdata>\n}\nevent = \"COMBAT_LOG_EVENT_UNFILTERED\"\n_ = 1263181888.725\netype = \"SPELL_HEAL\"\nlib = <table> {\n SindragosaSingleBeacon = 0\n SPELL_INTERRUPT = <function> defined @Interface\\AddOns\\RecountFailBot\\Libs\\LibFail-1.0\\LibFail-1.0.lua:2025\n COUNCIL_RUNE_THRESHOLD = 3\n CancelTimer = <function> defined @Interface\\AddOns\\RecountFailBot\\Libs\\LibFail-1.0\\LibFail-1.0.lua:638\n CHAT_MSG_MONSTER_EMOTE = <function> defined @Interface\\AddOns\\RecountFailBot\\Libs\\LibFail-1.0\\LibFail-1.0.lua:767\n FAIL_TYPE_DISPELLING = \"dispelling\"\n ChargeCounter = <table> {\n }\n GetSupportedZoneEvents = <function> defined @Interface\\AddOns\\RecountFailBot\\Libs\\LibFail-1.0\\LibFail-1.0.lua:534\n YOGGSARON_GAZE_THRESHOLD = 15\n ONYXIA_DEEPBREATH_THRESHOLD = 7000\n GetMobId = <func", -- [1]
"tion> defined @Interface\\AddOns\\RecountFailBot\\Libs\\LibFail-1.0\\LibFail-1.0.lua:570\n IsTimerRunning = <function> defined @Interface\\AddOns\\RecountFailBot\\Libs\\LibFail-1.0\\LibFail-1.0.lua:648\n SPELL_DAMAGE = <function> defined @Interface\\AddOns\\RecountFailBot\\Libs\\LibFail-1.0\\LibFail-1.0.lua:775\n FAIL_TYPE_CASTING = \"casting\"\n SPELL_AURA_APPLIED = <function> defined @Interface\\AddOns\\RecountFailBot\\Libs\\LibFail-1.0\\LibFail-1.0.lua:1904\n ScheduleTimer = <function> defined @Interface\\AddOns\\RecountFailBot\\Libs\\LibFail-1.0\\LibFail-1.0.lua:627\n FAIL_TYPE_NOTMOVING = \"notmoving\"\n callbacks = <table> {\n }\n RaidTable = <table> {\n }\n FAIL_TYPE_NOTDISPELLING = \"notdispelling\"\n FAIL_TYPE_SPREADING = \"spreading\"\n GetSupportedEvents = <function> defined @Interface\\AddOns\\RecountFailBot\\Libs\\LibFail-1.0\\LibFail-1.0.lua:529\n VEZAX_LEECH_THRESHOLD = 350000\n SWING_DAMAGE = <function> defined @Interface\\AddOns\\RecountFailBot\\Libs\\LibFail-1.0\\LibFail-1.0.lua:1662\n SPELL_HEAL = <functi", -- [2]
"on> defined @Interface\\AddOns\\RecountFailBot\\Libs\\LibFail-1.0\\LibFail-1.0.lua:1876\n FAIL_TYPE_NOTCASTING = \"notcasting\"\n frame = <unnamed> {\n }\n ThreePeopleHugging = false\n FROGGER_DEATH_WINDOW = 4\n GetEventSpellId = <function> defined @Interface\\AddOns\\RecountFailBot\\Libs\\LibFail-1.0\\LibFail-1.0.lua:540\n MalygosAlive = true\n GetFailsWhereTanksDoNotFail = <function> defined @Interface\\AddOns\\RecountFailBot\\Libs\\LibFail-1.0\\LibFail-1.0.lua:545\n RAID_ROSTER_UPDATE = <function> defined @Interface\\AddOns\\RecountFailBot\\Libs\\LibFail-1.0\\LibFail-1.0.lua:754\n UNIT_DIED = <function> defined @Interface\\AddOns\\RecountFailBot\\Libs\\LibFail-1.0\\LibFail-1.0.lua:1734\n BigbangCasting = false\n SPELL_PERIODIC_DAMAGE = <function> defined @Interface\\AddOns\\RecountFailBot\\Libs\\LibFail-1.0\\LibFail-1.0.lua:1769\n RegisterCallback = <function> defined @Interface\\AddOns\\AddonLoader\\CallbackHandler-1.0\\CallbackHandler-1.0.lua:118\n InitRaidTable = <function> defined @Interface\\AddOns\\RecountFa", -- [3]
"ilBot\\Libs\\LibFail-1.0\\LibFail-1.0.lua:599\n UnregisterCallback = <function> defined @Interface\\AddOns\\AddonLoader\\CallbackHandler-1.0\\CallbackHandler-1.0.lua:181\n HODIR_COLD_THRESHOLD = 2\n ENVIRONMENTAL_DAMAGE = <function> defined @Interface\\AddOns\\RecountFailBot\\Libs\\LibFail-1.0\\LibFail-1.0.lua:1688\n IsSnared = <function> defined @Interface\\AddOns\\RecountFailBot\\Libs\\LibFail-1.0\\LibFail-1.0.lua:562\n THADDIUS_JUMP_WINDOW = 120\n SPELL_CAST_START = <function> defined @Interface\\AddOns\\RecountFailBot\\Libs\\LibFail-1.0\\LibFail-1.0.lua:1702\n InitVariables = <function> defined @Interface\\AddOns\\RecountFailBot\\Libs\\LibFail-1.0\\LibFail-1.0.lua:576\n active = true\n SINDRAGOSA_FROSTBOMB_THRESHOLD = 5000\n FAIL_TYPE_MOVING = \"moving\"\n TestEventIds = <function> defined @Interface\\AddOns\\RecountFailBot\\Libs\\LibFail-1.0\\LibFail-1.0.lua:518\n FAIL_TYPE_SWITCHING = \"switching\"\n DeathTime = 0\n SPELL_AURA_REMOVED = <function> defined @Interface\\AddOns\\RecountFailBot\\Libs\\LibFail-1.0\\LibF", -- [4]
"ail-1.0.lua:2071\n UnregisterAllCallbacks = <function> defined @Interface\\AddOns\\AddonLoader\\CallbackHandler-1.0\\CallbackHandler-1.0.lua:202\n GoInactive = <function> defined @Interface\\AddOns\\RecountFailBot\\Libs\\LibFail-1.0\\LibFail-1.0.lua:728\n FAIL_TYPE_NOTSPREADING = \"notspreading\"\n SPELL_DISPEL = <function> defined @Interface\\AddOns\\RecountFailBot\\Libs\\LibFail-1.0\\LibFa\n ---", -- [5]
},
["type"] = "error",
["session"] = 519,
["counter"] = 1,
}, -- [212]:
I got bored the last days, so i thought about the idea of a generic filter in LibFail again. I came up with a mix of BigWigs and CastYeller2.
The idea behind this is a generic function with a filter to handle most of the fails saved in LibFail in only one line of code.
Its also easier to add more fails in the future and even custom fails.
The code wasn't tested!
--[[
Usage:
self:Log(failName, failEvent, failZone, failType[, spellIds, ...])
failName: self explaining
failEvent: can be any combat event like SPELL_DAMAGE, SWING_DAMAGE and also CHAT_MSG_MONSTER_EMOTE
failZone: self explaining
failType: FAIL_TYPE_NOTMOVING, ...
spellIds: optional because of SWING_x
self:AddFilter(filterTable)
possible options:
- count: only trigger the fail after <count> times
- time: only trigger the fail, if the last same fail is <time> seconds later
- playerevent: check if the dest is a player (true or nil)
- overkill: check if the damage done was overkill
- damage: only trigger the fail, if the damage done is over <damage>
- tankfail: trigger the fail not for tanks (false or nil)
- snared: only trigger the fail, if the person was not snared (true or nil)
self:AddEventSpellId(spellId)
spellId: used for the spellName showed in the chat
self:AddFailFunction(function)
function: if the fail needs special treatment, function goes here!
If function is nil, the standard FailEvent function gets called. The filter only works with the FailEvent function,
so call it in your custom function if you need it.
Example:
self:Log("Fail_Gunship_Explosion", "SPELL_DAMAGE", "Icecrown Citadel", self.FAIL_TYPE_NOTMOVING, 12346):AddFilter({
count = 5, time = 5, playerevent = false, overkill = true, damage = 20000, tankfail = false, snared = true
}):AddEventSpellId(12345):AddFailFunction(self.SpecialFailFunction)
]]--
function lib:CHAT_MSG_MONSTER_EMOTE(message, sourceName, language, channelName, destName, ...)
if self.fail_info["CHAT_MSG_MONSTER_EMOTE"] ~= nil then
for k,v in pairs(self.fail_info["CHAT_MSG_MONSTER_EMOTE"]) do
if sourceName:find(GetSpellInfo(k)) then
function lib:FailEvent(v.name, v.type, self.event_spellids[v.name], self.fail_filter[v.name], nil, nil, nil, nil, nil, nil, destName)
end
end
end
end
function lib:COMBAT_LOG_EVENT_UNFILTERED(timestamp, event, ...)
if self.fail_info[event] ~= nil then
if event:find("SWING") then
for i = 1, #self.fail_info[event] do
local name = self.fail_info[event].name
self.fail_info[event][i].func(name, self.fail_info[event].type, self.event_spellids[name], self.fail_filter[name], timestamp, event, ...)
end
elseif event:find("SPELL") or event:find("RANGE") then
local _, _, _, _, _, _, spellId = ...
if self.fail_info[event][spellId] ~= nil then
local name = self.fail_info[event].name
self.fail_info[event][i].func(name, self.fail_info[event].type, self.event_spellids[name], self.fail_filter[name], timestamp, event, ...)
end
end
end
end
function lib:Filter(name, filter, ...)
local timestamp, type, sourceGUID, sourceName, sourceFlags, destGUID, destName, destFlags, spellId, spellName, spellSchool, damage, overkill = ...
if bit.band(sourceFlags or 0, COMBATLOG_OBJECT_TYPE_GUARDIAN) > 0 or bit.band(destFlags or 0, COMBATLOG_OBJECT_TYPE_GUARDIAN) > 0 or not spellId then return end
local is_playerevent = bit.band(destFlags or 0, COMBATLOG_OBJECT_TYPE_PLAYER) > 0
damage = damage ~= "ABSORB" and damage or 0
overkill = overkill or 0
if filter.overkill and overkill <= 0 then
return false
elseif filter.damage ~= nil and damage < filter.damage then
return false
elseif filter.playerevent ~= nil and filter.playerevent == false and is_playerevent == true then
return false
elseif (filter.playerevent == nil or (filter.playerevent ~= nil and filter.playerevent == true)) and is_playerevent == false then
elseif filter.snared and self.IsSnared(destName) then
return false
elseif filter.time then
if self.fail_players[name][destName] == nil then self.fail_players[name][destName] = {} end
local oldtimestamp = self.fail_players[name][destName].time
self.fail_players[name][destName].time = timestamp
if (timestamp - oldtimestamp) < filter.time then
return false
end
elseif filter.count then
if self.fail_players[name][destName] == nil then self.fail_players[name][destName] = { count = 0 } end
self.fail_players[name][destName].count = self.fail_players[name][destName].count + 1
if self.fail_players[name][destName].count < filter.count then
return false
else
self.fail_players[name][destName].count = 0
end
end
return true
end
function lib:FailEvent(name, type, textSpellId, filter, ...)
local playername = select(7, ...)
if self.Filter(name, filter, ...) then
self.CallFailEvent(name, playername, type)
end
end
function lib:CallFailEvent(failname, playername, failtype, ...)
callbacks:Fire(failname, playername, failtype, ...)
callbacks:Fire("AnyFail", failname, playername, failtype, ...)
end
do
self.filter = {}
function lib:AddFilter(filter)
self.filter = filter
return self
end
end
function lib:AddEventSpellId(spellId)
self.eventSpellId = spellId
return self
end
function lib:AddFailFunction(failFunction)
self.failFunction = failFunction
return self
end
function lib:Log(failName, event, zone, failType, filter, textSpellId, ...)
-- include the fail in the event list
tinsert(self.fail_events, failName)
-- not a tank fail
if filter.tankfail then
tinsert(self.fails_where_tanks_dont_fail, failName)
end
-- event spellId
if self.eventSpellId == nil and select(#, ...) > 0 then
self.event_spellids[failName] = select(1, ...)
else
self.event_spellids[failName] = self.eventSpellId
end
self.eventSpellId = nil
-- fail function
if self.failFunction ~= nil then
f = self.failFunction
else
f = self.FailEvent
end
self.failFunction = nil
-- zone fail
tinsert(self.zones_with_fails[zone], failName)
-- save the filter
self.fail_filter[failName] = self.filter
-- create fail player list
self.fail_players[failName] = {}
-- save the fail
-- if there is no spellid, we have a swing event!
if select("#", ...) > 0
for i = 1, select("#", ...) do
self.fail_info[event][(select(i, ...))] = {
name = failName,
type = failType,
func = f,
}
end
else
tinsert(self.fail_info[event], {
name = failName,
type = failType,
func = f,
})
end
end
The way defaults are defined in CY2 is just a syntax sugar because I was too lazy to type an unending list of tables. You should focus on a smart fail detector first and only then you could add the fun definitions.
Rollback Post to RevisionRollBack
To post a comment, please login or register a new account.
Not many meters show this sort of thing. Secondly, not suggesting it needs to post a 'fail' alert just flagging that this sort of feedback is pretty central to these fights. Bossmods don't seem to want to focus on these aspects, damage meter can't (the data they need to present is simply too specific). Seems like a pretty reasonable area for a 'fail' type lib to consider contributing.
(ie. the entire intent of libfail is when something screws up you know who caused it - eg. facetanking tramples. Similarly, when your portal doesn't die and it dumps a 'damage done' list and that $##$#$ hunter doesn't appear on it you can nail them then and there)
Not suggesting it has to be done within libfail, but if not, where else? Asking users to install a large laundry list of mods for each of these types of encounters doesn't seem a great solution either.
Edit: on second though it also requires the code to know the role of every member in term of tank/healer/damager dealer, which could be hard to guess.
first tanks were excluded with a simpel if health > 45000 = tank , doesnt fail, then it used LibGroupTalents to know the players role, but still, i think we can get fails like that without LibFail
r182 (the released-in-last-seven-hours r184 has no changes related to Saurfang), RecountFailBot r25, USEnglish client/server. Sometime during the fight (may or may not have been when I died to Mark of the Fallen Champion, almost certain did not die to a Blood Beast):
The idea behind this is a generic function with a filter to handle most of the fails saved in LibFail in only one line of code.
Its also easier to add more fails in the future and even custom fails.
The code wasn't tested!
Feel free to comment!