• 0

    posted a message on LibFail-1.0
    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


    Feel free to comment!
    Posted in: Libraries
  • 0

    posted a message on LibFail-1.0
    I thought about your idea with a filter library and came to a syntax like this:

    Filter:NewFilter("time", function(oldeventdata, eventdata, value)
                                if (oldeventdata ~= nil) then
                                   if (eventdata.timestamp - oldeventdata.timestamp) >= value then 
                                      return true 
                                   else 
                                      return false 
                                   end
                                end
                             end)
                             
    Filter:NewFilter("stack", function(oldeventdata, eventdata, value) 
                                if (oldeventdata.stack == nil) then
                                  eventdata.stack = 1
                                else
                                  eventdata.stack = oldeventdata.stack + 1
                                  
                                  if (eventdata.stack == value) then
                                     return true
                                  else
                                     return false
                                  end
                                end
                             end)
    
    Filter:NewEvent(callbackEvent, "SPELL_DAMAGE", 12345, {filter = "time", value = 5}, {filter = "stack", value = 5})


    Its very general so each addon can set their own filter functions, for example the filter above is for Libfail.
    Posted in: Libraries
  • 0

    posted a message on LibFail-1.0
    I have some ideas to improve LibFail, maybe someone want to comment on them.

    Before I committed the additional fails to LibFail I was working on my own Failbot to add some stuff you couldnt do with the original Failbot.
    I hated that you couldnt add your own buffs easily, like over an option menu, change the failcount like on Thaddius or turn some of the fails off because they were annoying like hell.
    My first version looks like this. Its working if someone wants to take a look at the GUI ;)

    The idea behind this is you can easily add own fails, because the routine is always the same like increasing the count until it reaches a special value or save the timestamp and wait x seconds until the fail can be triggered again.
    You can also enable or disable the fail, save the player name on one event and use it in another like life leech on Vezax, include boss scanning to only check for the fail if you actually engaged the boss or set the message string to all languages.
    For special things like the biting cold you could include a function for check and return true/false.

    Feel free to comment :)
    Posted in: Libraries
  • To post a comment, please or register a new account.