--[=[
Please refer to the docs.txt within this file folder for a guide on how to use this library .
If you get lost on implementing the lib , be free to contact Tercio on Details ! discord : https : // discord.gg / AGSzAZX or email to terciob @ gmail.com
UnitID :
UnitID use : " player " , " target " , " raid18 " , " party3 " , etc ...
If passing the unit name , use GetUnitName ( unitId , true ) or Ambiguate ( playerName , ' none ' )
Code Rules :
- When a function or variable name refers to ' Player ' , it indicates the local player .
- When ' Unit ' is use instead , it indicates any entity .
- Internal callbacks are the internal communication of the library , e.g . when an event triggers it send to all modules that registered that event .
- Public callbacks are callbacks registered by an external addon .
Change Log ( most recent on 2022 Nov 18 ) :
- added racials with cooldown type 9
- added buff duration in the index 6 of the cooldownInfo table returned on any cooldown event
- added ' durationSpellId ' for cooldowns where the duration effect is another spell other than the casted cooldown spellId , add this member on cooldown table at LIB_OPEN_RAID_COOLDOWNS_INFO
------- Nov 07 and older
- added :
* added openRaidLib.GetSpellFilters ( spellId , defaultFilterOnly , customFiltersOnly ) ( see docs )
- passing a spellId of a non registered cooldown on LIB_OPEN_RAID_COOLDOWNS_INFO will trigger a diagnostic error if diagnostic errors are enabled .
- player cast doesn ' t check anymore for cooldowns in the player spec, now it check towards the cache LIB_OPEN_RAID_PLAYERCOOLDOWNS.
LIB_OPEN_RAID_PLAYERCOOLDOWNS is a cache built with cooldowns present in the player spellbook .
- things to maintain now has 1 file per expansion
- player conduits , covenant internally renamed to playerInfo1 and playerInfo2 to make the lib more future proof
- player conduits tree is now Borrowed Talents Tree , for future proof
- removed the talent size limitation on 7 indexes
- added :
* openRaidLib.GetFlaskInfoBySpellId ( spellId )
* openRaidLib.GetFlaskTierFromAura ( auraInfo )
* openRaidLib.GetFoodInfoBySpellId ( spellId )
* openRaidLib.GetFoodTierFromAura ( auraInfo )
* added dragonflight talents support
* added openRaidLib.RequestCooldownInfo ( spellId )
* added openRaidLib.AddCooldownFilter ( filterName , spells )
- ensure to register events after ' PLAYER_ENTERING_WORLD ' has triggered
TODO :
- add into gear info how many tier set parts the player has
- raid lockouts normal - heroic - mythic
BUGS :
- after a / reload , it is not starting new tickers for spells under cooldown
--]=]
local versionString , revision , launchDate , gameVersion = GetBuildInfo ( )
local isExpansion_Dragonflight = function ( )
if ( gameVersion >= 100000 ) then
return true
end
end
--don't load if it's not retail, emergencial patch due to classic and bcc stuff not transposed yet
if ( WOW_PROJECT_ID ~= WOW_PROJECT_MAINLINE and not isExpansion_Dragonflight ( ) ) then
return
end
local major = " LibOpenRaid-1.0 "
local CONST_LIB_VERSION = 97
if ( not LIB_OPEN_RAID_MAX_VERSION ) then
LIB_OPEN_RAID_MAX_VERSION = CONST_LIB_VERSION
else
LIB_OPEN_RAID_MAX_VERSION = math.max ( LIB_OPEN_RAID_MAX_VERSION , CONST_LIB_VERSION )
end
LIB_OPEN_RAID_CAN_LOAD = false
local unpack = table.unpack or _G.unpack
--declae the library within the LibStub
local libStub = _G.LibStub
local openRaidLib = libStub : NewLibrary ( major , CONST_LIB_VERSION )
if ( not openRaidLib ) then
return
end
openRaidLib.__version = CONST_LIB_VERSION
LIB_OPEN_RAID_CAN_LOAD = true
openRaidLib.__errors = { } --/dump LibStub:GetLibrary("LibOpenRaid-1.0").__errors
--default values
openRaidLib.inGroup = false
openRaidLib.UnitIDCache = { }
local CONST_CVAR_TEMPCACHE = " LibOpenRaidTempCache "
local CONST_CVAR_TEMPCACHE_DEBUG = " LibOpenRaidTempCacheDebug "
--delay to request all data from other players
local CONST_REQUEST_ALL_DATA_COOLDOWN = 30
--delay to send all data to other players
local CONST_SEND_ALL_DATA_COOLDOWN = 30
--show failures (when the function return an error) results to chat
local CONST_DIAGNOSTIC_ERRORS = false
--show the data to be sent and data received from comm
local CONST_DIAGNOSTIC_COMM = false
--show data received from other players
local CONST_DIAGNOSTIC_COMM_RECEIVED = false
local CONST_COMM_PREFIX = " LRS "
local CONST_COMM_FULLINFO_PREFIX = " F "
local CONST_COMM_COOLDOWNUPDATE_PREFIX = " U "
local CONST_COMM_COOLDOWNFULLLIST_PREFIX = " C "
local CONST_COMM_COOLDOWNCHANGES_PREFIX = " S "
local CONST_COMM_COOLDOWNREQUEST_PREFIX = " Z "
local CONST_COMM_GEARINFO_FULL_PREFIX = " G "
local CONST_COMM_GEARINFO_DURABILITY_PREFIX = " R "
local CONST_COMM_PLAYER_DEAD_PREFIX = " D "
local CONST_COMM_PLAYER_ALIVE_PREFIX = " A "
local CONST_COMM_PLAYERINFO_PREFIX = " P "
local CONST_COMM_PLAYERINFO_TALENTS_PREFIX = " T "
local CONST_COMM_PLAYERINFO_PVPTALENTS_PREFIX = " V "
local CONST_COMM_KEYSTONE_DATA_PREFIX = " K "
local CONST_COMM_KEYSTONE_DATAREQUEST_PREFIX = " J "
local CONST_COMM_SENDTO_PARTY = " 0x1 "
local CONST_COMM_SENDTO_RAID = " 0x2 "
local CONST_COMM_SENDTO_GUILD = " 0x4 "
local CONST_ONE_SECOND = 1.0
local CONST_TWO_SECONDS = 2.0
local CONST_THREE_SECONDS = 3.0
local CONST_SPECIALIZATION_VERSION_CLASSIC = 0
local CONST_SPECIALIZATION_VERSION_MODERN = 1
local CONST_COOLDOWN_CHECK_INTERVAL = CONST_ONE_SECOND
local CONST_COOLDOWN_TIMELEFT_HAS_CHANGED = CONST_ONE_SECOND
local CONST_COOLDOWN_INDEX_TIMELEFT = 1
local CONST_COOLDOWN_INDEX_CHARGES = 2
local CONST_COOLDOWN_INDEX_TIMEOFFSET = 3
local CONST_COOLDOWN_INDEX_DURATION = 4
local CONST_COOLDOWN_INDEX_UPDATETIME = 5
local CONST_COOLDOWN_INDEX_AURA_DURATION = 6
local CONST_COOLDOWN_INFO_SIZE = 6
local GetContainerNumSlots = GetContainerNumSlots or C_Container.GetContainerNumSlots
local GetContainerItemID = GetContainerItemID or C_Container.GetContainerItemID
local GetContainerItemLink = GetContainerItemLink or C_Container.GetContainerItemLink
--from vanilla to cataclysm, the specID did not existed, hence its considered version 0
--for mists of pandaria and beyond it's version 1
local getSpecializationVersion = function ( )
if ( gameVersion >= 50000 ) then
return CONST_SPECIALIZATION_VERSION_MODERN
else
return CONST_SPECIALIZATION_VERSION_CLASSIC
end
end
function openRaidLib . ShowDiagnosticErrors ( value )
CONST_DIAGNOSTIC_ERRORS = value
end
--make the 'pri-nt' word be only used once, this makes easier to find lost debug pri-nts in the code
local sendChatMessage = function ( ... )
print ( ... )
end
openRaidLib.DiagnosticError = function ( msg , ... )
if ( CONST_DIAGNOSTIC_ERRORS ) then
sendChatMessage ( " |cFFFF9922OpenRaidLib|r: " , msg , ... )
end
end
local diagnosticFilter = nil
local diagnosticComm = function ( msg , ... )
if ( CONST_DIAGNOSTIC_COMM ) then
if ( diagnosticFilter ) then
local lowerMessage = msg : lower ( )
if ( lowerMessage : find ( diagnosticFilter ) ) then
sendChatMessage ( " |cFFFF9922OpenRaidLib|r: " , msg , ... )
--dumpt(msg)
end
else
sendChatMessage ( " |cFFFF9922OpenRaidLib|r: " , msg , ... )
end
end
end
local diagnosticCommReceivedFilter = nil
openRaidLib.diagnosticCommReceived = function ( msg , ... )
if ( diagnosticCommReceivedFilter ) then
local lowerMessage = msg : lower ( )
if ( lowerMessage : find ( diagnosticCommReceivedFilter ) ) then
sendChatMessage ( " |cFFFF9922OpenRaidLib|r: " , msg , ... )
end
else
sendChatMessage ( " |cFFFF9922OpenRaidLib|r: " , msg , ... )
end
end
openRaidLib.DeprecatedMessage = function ( msg )
sendChatMessage ( " |cFFFF9922OpenRaidLib|r: " , " |cFFFF5555 " .. msg .. " |r " )
end
local isTimewalkWoW = function ( )
local _ , _ , _ , buildInfo = GetBuildInfo ( )
if ( buildInfo < 40000 ) then
return true
end
end
local checkClientVersion = function ( ... )
for i = 1 , select ( " # " , ... ) do
local clientVersion = select ( i , ... )
if ( clientVersion == " retail " and ( WOW_PROJECT_ID == WOW_PROJECT_MAINLINE or isExpansion_Dragonflight ( ) ) ) then --retail
return true
elseif ( clientVersion == " classic_era " and WOW_PROJECT_ID == WOW_PROJECT_CLASSIC ) then --classic era (vanila)
return true
elseif ( clientVersion == " bcc " and WOW_PROJECT_ID == WOW_PROJECT_BURNING_CRUSADE_CLASSIC ) then --the burning crusade classic
return true
end
end
end
--------------------------------------------------------------------------------------------------------------------------------
--~internal cache
--use a console variable to create a flash cache to keep data while the game reload
--this is not a long term database as saved variables are and it get clean up often
C_CVar.RegisterCVar ( CONST_CVAR_TEMPCACHE )
C_CVar.RegisterCVar ( CONST_CVAR_TEMPCACHE_DEBUG )
--internal namespace
local tempCache = {
debugString = " " ,
}
tempCache.copyCache = function ( t1 , t2 )
for key , value in pairs ( t2 ) do
if ( type ( value ) == " table " ) then
t1 [ key ] = t1 [ key ] or { }
tempCache.copyCache ( t1 [ key ] , t2 [ key ] )
else
t1 [ key ] = value
end
end
return t1
end
--use debug cvar to find issues that occurred during the logoff process
function openRaidLib . PrintTempCacheDebug ( )
local debugMessage = C_CVar.GetCVar ( CONST_CVAR_TEMPCACHE_DEBUG )
sendChatMessage ( " |cFFFF9922OpenRaidLib|r Temp CVar Result: \n " , debugMessage )
end
function tempCache . SaveDebugText ( )
C_CVar.SetCVar ( CONST_CVAR_TEMPCACHE_DEBUG , tempCache.debugString )
end
function tempCache . AddDebugText ( text )
tempCache.debugString = tempCache.debugString .. date ( " %H:%M:%S " ) .. " | " .. text .. " \n "
end
function tempCache . SaveCacheOnCVar ( data )
C_CVar.SetCVar ( CONST_CVAR_TEMPCACHE , data )
tempCache.AddDebugText ( " CVars Saved on saveCahceOnCVar(), Size: " .. # data )
end
function tempCache . RestoreData ( )
local data = C_CVar.GetCVar ( CONST_CVAR_TEMPCACHE )
if ( data and type ( data ) == " string " and string.len ( data ) > 1 ) then
local LibAceSerializer = LibStub : GetLibrary ( " AceSerializer-3.0 " , true )
if ( LibAceSerializer ) then
local okay , cacheInfo = LibAceSerializer : Deserialize ( data )
if ( okay ) then
local age = cacheInfo.createdAt
--if the data is older than 5 minutes, much has been changed from the group and the data is out dated
if ( age + ( 60 * 5 ) < time ( ) ) then
return
end
local unitsInfo = cacheInfo.unitsInfo
local cooldownsInfo = cacheInfo.cooldownsInfo
local gearInfo = cacheInfo.gearInfo
local okayUnitsInfo , unitsInfo = LibAceSerializer : Deserialize ( unitsInfo )
local okayCooldownsInfo , cooldownsInfo = LibAceSerializer : Deserialize ( cooldownsInfo )
local okayGearInfo , gearInfo = LibAceSerializer : Deserialize ( gearInfo )
if ( okayUnitsInfo and unitsInfo ) then
openRaidLib.UnitInfoManager . UnitData = tempCache.copyCache ( openRaidLib.UnitInfoManager . UnitData , unitsInfo )
else
tempCache.AddDebugText ( " invalid UnitInfo " )
end
if ( okayCooldownsInfo and cooldownsInfo ) then
openRaidLib.CooldownManager . UnitData = tempCache.copyCache ( openRaidLib.CooldownManager . UnitData , cooldownsInfo )
else
tempCache.AddDebugText ( " invalid CooldownsInfo " )
end
if ( okayGearInfo and gearInfo ) then
openRaidLib.GearManager . UnitData = tempCache.copyCache ( openRaidLib.GearManager . UnitData , gearInfo )
else
tempCache.AddDebugText ( " invalid GearInfo " )
end
else
tempCache.AddDebugText ( " Deserialization not okay, reason: " .. cacheInfo )
end
else
tempCache.AddDebugText ( " LibAceSerializer not found " )
end
else
if ( not data ) then
tempCache.AddDebugText ( " invalid temporary cache: getCVar returned nil " )
elseif ( type ( data ) ~= " string " ) then
tempCache.AddDebugText ( " invalid temporary cache: getCVar did not returned a string " )
elseif ( string.len ( data ) < 2 ) then
tempCache.AddDebugText ( " invalid temporary cache: data length lower than 2 bytes (first login?) " )
else
tempCache.AddDebugText ( " invalid temporary cache: no reason found " )
end
end
end
function tempCache . SaveData ( )
tempCache.AddDebugText ( " SaveData() called. " )
local LibAceSerializer = LibStub : GetLibrary ( " AceSerializer-3.0 " , true )
if ( LibAceSerializer ) then
local allUnitsInfo = openRaidLib.UnitInfoManager . UnitData
local allUnitsCooldowns = openRaidLib.CooldownManager . UnitData
local allPlayersGear = openRaidLib.GearManager . UnitData
local cacheInfo = {
createdAt = time ( ) ,
}
local unitsInfoSerialized = LibAceSerializer : Serialize ( allUnitsInfo )
local unitsCooldownsSerialized = LibAceSerializer : Serialize ( allUnitsCooldowns )
local playersGearSerialized = LibAceSerializer : Serialize ( allPlayersGear )
if ( unitsInfoSerialized ) then
cacheInfo.unitsInfo = unitsInfoSerialized
tempCache.AddDebugText ( " SaveData() units info serialized okay. " )
else
tempCache.AddDebugText ( " SaveData() units info serialized failed. " )
end
if ( unitsCooldownsSerialized ) then
cacheInfo.cooldownsInfo = unitsCooldownsSerialized
tempCache.AddDebugText ( " SaveData() cooldowns info serialized okay. " )
else
tempCache.AddDebugText ( " SaveData() cooldowns info serialized failed. " )
end
if ( playersGearSerialized ) then
cacheInfo.gearInfo = playersGearSerialized
tempCache.AddDebugText ( " SaveData() gear info serialized okay. " )
else
tempCache.AddDebugText ( " SaveData() gear info serialized failed. " )
end
local cacheInfoSerialized = LibAceSerializer : Serialize ( cacheInfo )
tempCache.SaveCacheOnCVar ( cacheInfoSerialized )
else
tempCache.AddDebugText ( " SaveData() AceSerializer not found. " )
end
tempCache.SaveDebugText ( )
end
--------------------------------------------------------------------------------------------------------------------------------
--~comms
openRaidLib.commHandler = { }
function openRaidLib . commHandler . OnReceiveComm ( self , event , prefix , text , channel , sender , target , zoneChannelID , localID , name , instanceID )
--check if the data belong to us
if ( prefix == CONST_COMM_PREFIX ) then
sender = Ambiguate ( sender , " none " )
--don't receive comms from the player it self
local playerName = UnitName ( " player " )
if ( playerName == sender ) then
return
end
local data = text
local LibDeflate = LibStub : GetLibrary ( " LibDeflate " )
local dataCompressed = LibDeflate : DecodeForWoWAddonChannel ( data )
data = LibDeflate : DecompressDeflate ( dataCompressed )
--some users are reporting errors where 'data is nil'. Making some sanitization
if ( not data ) then
openRaidLib.DiagnosticError ( " Invalid data from player: " , sender , " data: " , text )
return
elseif ( type ( data ) ~= " string " ) then
openRaidLib.DiagnosticError ( " Invalid data from player: " , sender , " data: " , text , " data type is: " , type ( data ) )
return
end
--get the first byte of the data, it indicates what type of data was transmited
local dataTypePrefix = data : match ( " ^. " )
if ( not dataTypePrefix ) then
openRaidLib.DiagnosticError ( " Invalid dataTypePrefix from player: " , sender , " data: " , data , " dataTypePrefix: " , dataTypePrefix )
return
elseif ( openRaidLib.commPrefixDeprecated [ dataTypePrefix ] ) then
openRaidLib.DiagnosticError ( " Invalid dataTypePrefix from player: " , sender , " data: " , data , " dataTypePrefix: " , dataTypePrefix )
return
end
--if this is isn't a keystone data comm, check if the lib can receive comms
if ( dataTypePrefix ~= CONST_COMM_KEYSTONE_DATA_PREFIX and dataTypePrefix ~= CONST_COMM_KEYSTONE_DATAREQUEST_PREFIX ) then
if ( not openRaidLib.IsCommAllowed ( ) ) then
openRaidLib.DiagnosticError ( " comm not allowed. " )
return
end
end
if ( CONST_DIAGNOSTIC_COMM_RECEIVED ) then
openRaidLib.diagnosticCommReceived ( data )
end
--get the table with functions regitered for this type of data
local callbackTable = openRaidLib.commHandler . commCallback [ dataTypePrefix ]
if ( not callbackTable ) then
openRaidLib.DiagnosticError ( " Not callbackTable for dataTypePrefix: " , dataTypePrefix , " from player: " , sender , " data: " , data )
return
end
--convert to table
local dataAsTable = { strsplit ( " , " , data ) }
--remove the first index (prefix)
tremove ( dataAsTable , 1 )
--trigger callbacks
for i = 1 , # callbackTable do
callbackTable [ i ] ( dataAsTable , sender )
end
end
end
C_ChatInfo.RegisterAddonMessagePrefix ( CONST_COMM_PREFIX )
openRaidLib.commHandler . eventFrame = CreateFrame ( " frame " )
openRaidLib.commHandler . eventFrame : RegisterEvent ( " CHAT_MSG_ADDON " )
openRaidLib.commHandler . eventFrame : SetScript ( " OnEvent " , openRaidLib.commHandler . OnReceiveComm )
openRaidLib.commHandler . commCallback = {
--when transmiting
[ CONST_COMM_FULLINFO_PREFIX ] = { } , --update all
[ CONST_COMM_COOLDOWNFULLLIST_PREFIX ] = { } , --all cooldowns of a player
[ CONST_COMM_COOLDOWNUPDATE_PREFIX ] = { } , --an update of a single cooldown
[ CONST_COMM_COOLDOWNCHANGES_PREFIX ] = { } , --cooldowns got added or removed
[ CONST_COMM_COOLDOWNREQUEST_PREFIX ] = { } , --a unit requested an update on a spell
[ CONST_COMM_GEARINFO_FULL_PREFIX ] = { } , --an update of gear information
[ CONST_COMM_GEARINFO_DURABILITY_PREFIX ] = { } , --an update of the player gear durability
[ CONST_COMM_PLAYER_DEAD_PREFIX ] = { } , --player is dead
[ CONST_COMM_PLAYER_ALIVE_PREFIX ] = { } , --player is alive
[ CONST_COMM_PLAYERINFO_PREFIX ] = { } , --info about the player
[ CONST_COMM_PLAYERINFO_TALENTS_PREFIX ] = { } , --talents info
[ CONST_COMM_PLAYERINFO_PVPTALENTS_PREFIX ] = { } , --pvp talents info
[ CONST_COMM_KEYSTONE_DATA_PREFIX ] = { } , --received keystone data
[ CONST_COMM_KEYSTONE_DATAREQUEST_PREFIX ] = { } , --received a request to send keystone data
}
function openRaidLib . commHandler . RegisterComm ( prefix , func )
--the table for the prefix need to be declared at the 'openRaidLib.commHandler.commCallback' table
tinsert ( openRaidLib.commHandler . commCallback [ prefix ] , func )
end
--@flags
--0x1: to party
--0x2: to raid
--0x4: to guild
local sendData = function ( dataEncoded , channel )
local aceComm = LibStub : GetLibrary ( " AceComm-3.0 " , true )
if ( aceComm ) then
aceComm : SendCommMessage ( CONST_COMM_PREFIX , dataEncoded , channel , nil , " ALERT " )
else
C_ChatInfo.SendAddonMessage ( CONST_COMM_PREFIX , dataEncoded , channel )
end
end
function openRaidLib . commHandler . SendCommData ( data , flags )
local LibDeflate = LibStub : GetLibrary ( " LibDeflate " )
local dataCompressed = LibDeflate : CompressDeflate ( data , { level = 9 } )
local dataEncoded = LibDeflate : EncodeForWoWAddonChannel ( dataCompressed )
if ( flags ) then
if ( bit.band ( flags , CONST_COMM_SENDTO_PARTY ) ) then --send to party
if ( IsInGroup ( ) and not IsInRaid ( ) ) then
sendData ( dataEncoded , IsInGroup ( LE_PARTY_CATEGORY_INSTANCE ) and " INSTANCE_CHAT " or " PARTY " )
end
end
if ( bit.band ( flags , CONST_COMM_SENDTO_RAID ) ) then --send to raid
if ( IsInRaid ( ) ) then
sendData ( dataEncoded , IsInRaid ( LE_PARTY_CATEGORY_INSTANCE ) and " INSTANCE_CHAT " or " RAID " )
end
end
if ( bit.band ( flags , CONST_COMM_SENDTO_GUILD ) ) then --send to guild
if ( IsInGuild ( ) ) then
sendData ( dataEncoded , " GUILD " )
end
end
else
if ( IsInGroup ( ) and not IsInRaid ( ) ) then --in party only
sendData ( dataEncoded , IsInGroup ( LE_PARTY_CATEGORY_INSTANCE ) and " INSTANCE_CHAT " or " PARTY " )
elseif ( IsInRaid ( ) ) then
sendData ( dataEncoded , IsInRaid ( LE_PARTY_CATEGORY_INSTANCE ) and " INSTANCE_CHAT " or " RAID " )
end
end
end
--------------------------------------------------------------------------------------------------------------------------------
--~schedule ~timers
openRaidLib.Schedules = {
registeredUniqueTimers = { }
}
--run a scheduled function with its payload
local triggerScheduledTick = function ( tickerObject )
local payload = tickerObject.payload
local callback = tickerObject.callback
if ( tickerObject.isUnique ) then
local namespace = tickerObject.namespace
local scheduleName = tickerObject.scheduleName
openRaidLib.Schedules . CancelUniqueTimer ( namespace , scheduleName )
end
local result , errortext = xpcall ( callback , geterrorhandler ( ) , unpack ( payload ) )
if ( not result ) then
sendChatMessage ( " openRaidLib: error on scheduler: " , tickerObject.scheduleName , tickerObject.stack )
end
return result
end
--create a new schedule
function openRaidLib . Schedules . NewTimer ( time , callback , ... )
local payload = { ... }
local newTimer = C_Timer.NewTimer ( time , triggerScheduledTick )
newTimer.payload = payload
newTimer.callback = callback
newTimer.stack = debugstack ( )
return newTimer
end
--create an unique schedule
--if a schedule already exists, cancels it and make a new
function openRaidLib . Schedules . NewUniqueTimer ( time , callback , namespace , scheduleName , ... )
openRaidLib.Schedules . CancelUniqueTimer ( namespace , scheduleName )
local newTimer = openRaidLib.Schedules . NewTimer ( time , callback , ... )
newTimer.namespace = namespace
newTimer.scheduleName = scheduleName
newTimer.stack = debugstack ( )
newTimer.isUnique = true
local registeredUniqueTimers = openRaidLib.Schedules . registeredUniqueTimers
registeredUniqueTimers [ namespace ] = registeredUniqueTimers [ namespace ] or { }
registeredUniqueTimers [ namespace ] [ scheduleName ] = newTimer
end
--cancel an unique schedule
function openRaidLib . Schedules . CancelUniqueTimer ( namespace , scheduleName )
local registeredUniqueTimers = openRaidLib.Schedules . registeredUniqueTimers
local currentSchedule = registeredUniqueTimers [ namespace ] and registeredUniqueTimers [ namespace ] [ scheduleName ]
if ( currentSchedule ) then
if ( not currentSchedule : IsCancelled ( ) ) then
currentSchedule : Cancel ( )
end
registeredUniqueTimers [ namespace ] [ scheduleName ] = nil
end
end
--cancel all unique timers
function openRaidLib . Schedules . CancelAllUniqueTimers ( )
local registeredUniqueTimers = openRaidLib.Schedules . registeredUniqueTimers
for namespace , schedulesTable in pairs ( registeredUniqueTimers ) do
for scheduleName , timerObject in pairs ( schedulesTable ) do
if ( timerObject and not timerObject : IsCancelled ( ) ) then
timerObject : Cancel ( )
end
end
end
table.wipe ( registeredUniqueTimers )
end
--------------------------------------------------------------------------------------------------------------------------------
--~public ~callbacks
--these are the events where other addons can register and receive calls
local allPublicCallbacks = {
" CooldownListUpdate " ,
" CooldownListWipe " ,
" CooldownUpdate " ,
" CooldownAdded " ,
" CooldownRemoved " ,
" UnitDeath " ,
" UnitAlive " ,
" GearListWipe " ,
" GearUpdate " ,
" GearDurabilityUpdate " ,
" UnitInfoUpdate " ,
" UnitInfoWipe " ,
" TalentUpdate " ,
" PvPTalentUpdate " ,
" KeystoneUpdate " ,
" KeystoneWipe " ,
}
--save build the table to avoid lose registered events on older versions
openRaidLib.publicCallback = openRaidLib.publicCallback or { }
openRaidLib.publicCallback . events = openRaidLib.publicCallback . events or { }
for _ , callbackName in ipairs ( allPublicCallbacks ) do
openRaidLib.publicCallback . events [ callbackName ] = openRaidLib.publicCallback . events [ callbackName ] or { }
end
local checkRegisterDataIntegrity = function ( addonObject , event , callbackMemberName )
--check of integrity
if ( type ( addonObject ) == " string " ) then
addonObject = _G [ addonObject ]
end
if ( type ( addonObject ) ~= " table " ) then
return 1
end
if ( not openRaidLib.publicCallback . events [ event ] ) then
return 2
elseif ( not addonObject [ callbackMemberName ] ) then
return 3
end
return true
end
--call the registered function within the addon namespace
--payload is sent together within the call
function openRaidLib . publicCallback . TriggerCallback ( event , ... )
local eventCallbacks = openRaidLib.publicCallback . events [ event ]
for i = 1 , # eventCallbacks do
local thisCallback = eventCallbacks [ i ] --got a case where this was nil, which is kinda impossible? | event: CooldownUpdate
local addonObject = thisCallback [ 1 ] --670: attempt to index local 'thisCallback' (a nil value)
local functionName = thisCallback [ 2 ]
--[=[
eventCallbacks = {
1 = { }
}
( for index ) = 2
( for limit ) = 2
( for step ) = 1
i = 2
thisCallback = nil
--]=]
--get the function from within the addon object
local functionToCallback = addonObject [ functionName ]
if ( functionToCallback ) then
--if this isn't a function, xpcall trigger an error
local okay , errorMessage = xpcall ( functionToCallback , geterrorhandler ( ) , ... )
if ( not okay ) then
sendChatMessage ( " error on callback for event: " , event )
end
else
--the registered function wasn't found
end
end
end
function openRaidLib . RegisterCallback ( addonObject , event , callbackMemberName )
--check of integrity
local passIntegrityTest = checkRegisterDataIntegrity ( addonObject , event , callbackMemberName )
if ( passIntegrityTest and type ( passIntegrityTest ) ~= " boolean " ) then
return passIntegrityTest
end
--register
tinsert ( openRaidLib.publicCallback . events [ event ] , { addonObject , callbackMemberName } )
return true
end
function openRaidLib . UnregisterCallback ( addonObject , event , callbackMemberName )
--check of integrity
local passIntegrityTest = checkRegisterDataIntegrity ( addonObject , event , callbackMemberName )
if ( passIntegrityTest and type ( passIntegrityTest ) ~= " boolean " ) then
return passIntegrityTest
end
for i = 1 , # openRaidLib.publicCallback . events [ event ] do
local registeredCallback = openRaidLib.publicCallback . events [ event ] [ i ]
if ( registeredCallback [ 1 ] == addonObject and registeredCallback [ 2 ] == callbackMemberName ) then
tremove ( openRaidLib.publicCallback . events [ event ] , i )
break
end
end
end
--------------------------------------------------------------------------------------------------------------------------------
--~internal ~callbacks
--internally, each module can register events through the internal callback to be notified when something happens in the game
openRaidLib.internalCallback = { }
openRaidLib.internalCallback . events = {
[ " onEnterGroup " ] = { } ,
[ " onLeaveGroup " ] = { } ,
[ " onLeaveCombat " ] = { } ,
[ " playerCast " ] = { } ,
[ " onEnterWorld " ] = { } ,
[ " talentUpdate " ] = { } ,
[ " pvpTalentUpdate " ] = { } ,
[ " onPlayerDeath " ] = { } ,
[ " onPlayerRess " ] = { } ,
[ " raidEncounterEnd " ] = { } ,
[ " mythicDungeonStart " ] = { } ,
[ " playerPetChange " ] = { } ,
[ " mythicDungeonEnd " ] = { } ,
[ " unitAuraRemoved " ] = { } ,
}
openRaidLib.internalCallback . RegisterCallback = function ( event , func )
tinsert ( openRaidLib.internalCallback . events [ event ] , func )
end
openRaidLib.internalCallback . UnRegisterCallback = function ( event , func )
local eventCallbacks = openRaidLib.internalCallback . events [ event ]
for i = 1 , # eventCallbacks do
if ( eventCallbacks [ i ] == func ) then
tremove ( eventCallbacks , i )
break
end
end
end
function openRaidLib . internalCallback . TriggerEvent ( event , ... )
local eventCallbacks = openRaidLib.internalCallback . events [ event ]
for i = 1 , # eventCallbacks do
local functionToCallback = eventCallbacks [ i ]
functionToCallback ( event , ... )
end
end
--create the frame for receiving game events
local eventFrame = _G.OpenRaidLibFrame
if ( not eventFrame ) then
eventFrame = CreateFrame ( " frame " , " OpenRaidLibFrame " , UIParent )
end
local talentChangedCallback = function ( )
openRaidLib.internalCallback . TriggerEvent ( " talentUpdate " )
end
local delayedTalentChange = function ( )
openRaidLib.Schedules . NewUniqueTimer ( math.random ( 3 , 6 ) , talentChangedCallback , " TalentChangeEventGroup " , " talentChangedCallback_Schedule " )
end
local eventFunctions = {
--check if the player joined a group
[ " GROUP_ROSTER_UPDATE " ] = function ( )
local bEventTriggered = false
if ( openRaidLib.IsInGroup ( ) ) then
if ( not openRaidLib.inGroup ) then
openRaidLib.inGroup = true
openRaidLib.internalCallback . TriggerEvent ( " onEnterGroup " )
bEventTriggered = true
end
else
if ( openRaidLib.inGroup ) then
openRaidLib.inGroup = false
openRaidLib.internalCallback . TriggerEvent ( " onLeaveGroup " )
bEventTriggered = true
end
end
if ( not bEventTriggered and openRaidLib.IsInGroup ( ) ) then --the player didn't left or enter a group
--the group has changed, trigger a long timer to send full data
--as the timer is unique, a new change to the group will replace and refresh the time
--using random time, players won't trigger all at the same time
local randomTime = 5 + math.random ( ) + math.random ( 1 , 5 )
openRaidLib.Schedules . NewUniqueTimer ( randomTime , openRaidLib.mainControl . SendFullData , " mainControl " , " sendFullData_Schedule " )
end
openRaidLib.UpdateUnitIDCache ( )
end ,
[ " UNIT_SPELLCAST_SUCCEEDED " ] = function ( ... )
local unitId , castGUID , spellId = ...
C_Timer.After ( 0.1 , function ( )
--some spells has many different spellIds, get the default
spellId = LIB_OPEN_RAID_SPELL_DEFAULT_IDS [ spellId ] or spellId
--trigger internal callbacks
openRaidLib.internalCallback . TriggerEvent ( " playerCast " , spellId , UnitIsUnit ( unitId , " pet " ) )
end )
end ,
[ " PLAYER_ENTERING_WORLD " ] = function ( ... )
--has the selected character just loaded?
if ( not openRaidLib.bHasEnteredWorld ) then
--register events
openRaidLib.OnEnterWorldRegisterEvents ( )
--openRaidLib.AuraTracker.StartScanUnitAuras("player")
if ( IsInGroup ( ) ) then
openRaidLib.RequestAllData ( )
openRaidLib.UpdateUnitIDCache ( )
end
--this part is under development
if ( Details ) then
local detailsEventListener = Details : CreateEventListener ( )
function detailsEventListener : UnitSpecFound ( event , unitId , specId , unitGuid )
local unitName = GetUnitName ( unitId , true ) or unitId
if ( not UnitInParty ( unitName ) and not UnitInRaid ( unitName ) ) then
return
end
--check if there's unit information about this unit
--is still did not received a list of cooldowns from this player
if ( not openRaidLib.CooldownManager . HasFullCooldownList [ unitName ] ) then
--build a generic list from the spec
end
end
function detailsEventListener : UnitTalentsFound ( event , unitId , talentTable , unitGuid )
local unitName = GetUnitName ( unitId , true ) or unitId
if ( not UnitInParty ( unitName ) and not UnitInRaid ( unitName ) ) then
return
end
end
detailsEventListener : RegisterEvent ( " UNIT_SPEC " , " UnitSpecFound " )
detailsEventListener : RegisterEvent ( " UNIT_TALENTS " , " UnitTalentsFound " )
end
openRaidLib.bHasEnteredWorld = true
end
openRaidLib.internalCallback . TriggerEvent ( " onEnterWorld " )
end ,
[ " PLAYER_SPECIALIZATION_CHANGED " ] = function ( ... )
delayedTalentChange ( )
end ,
[ " PLAYER_TALENT_UPDATE " ] = function ( ... )
delayedTalentChange ( )
end ,
[ " TRAIT_CONFIG_UPDATED " ] = function ( ... )
delayedTalentChange ( )
end ,
[ " TRAIT_TREE_CURRENCY_INFO_UPDATED " ] = function ( ... )
delayedTalentChange ( )
end ,
--SPELLS_CHANGED
[ " PLAYER_PVP_TALENT_UPDATE " ] = function ( ... )
openRaidLib.internalCallback . TriggerEvent ( " pvpTalentUpdate " )
end ,
[ " PLAYER_DEAD " ] = function ( ... )
openRaidLib.mainControl . UpdatePlayerAliveStatus ( )
end ,
[ " PLAYER_ALIVE " ] = function ( ... )
openRaidLib.mainControl . UpdatePlayerAliveStatus ( )
end ,
[ " PLAYER_UNGHOST " ] = function ( ... )
openRaidLib.mainControl . UpdatePlayerAliveStatus ( )
end ,
[ " PLAYER_REGEN_DISABLED " ] = function ( ... )
--entered in combat
end ,
[ " PLAYER_REGEN_ENABLED " ] = function ( ... )
openRaidLib.internalCallback . TriggerEvent ( " onLeaveCombat " )
end ,
[ " UPDATE_INVENTORY_DURABILITY " ] = function ( ... )
--an item has changed its durability
--do not trigger this event while in combat
if ( not InCombatLockdown ( ) ) then
openRaidLib.Schedules . NewUniqueTimer ( 5 + math.random ( 0 , 4 ) , openRaidLib.GearManager . SendDurability , " GearManager " , " sendDurability_Schedule " )
end
end ,
[ " PLAYER_EQUIPMENT_CHANGED " ] = function ( ... )
--player changed an equipment
openRaidLib.Schedules . NewUniqueTimer ( 4 + math.random ( 0 , 5 ) , openRaidLib.GearManager . SendAllGearInfo , " GearManager " , " sendAllGearInfo_Schedule " )
end ,
[ " ENCOUNTER_END " ] = function ( )
if ( IsInRaid ( ) ) then
openRaidLib.internalCallback . TriggerEvent ( " raidEncounterEnd " )
end
end ,
[ " CHALLENGE_MODE_START " ] = function ( )
openRaidLib.internalCallback . TriggerEvent ( " mythicDungeonStart " )
end ,
[ " UNIT_PET " ] = function ( unitId )
if ( UnitIsUnit ( unitId , " player " ) ) then
openRaidLib.Schedules . NewUniqueTimer ( 1.1 , function ( ) openRaidLib.internalCallback . TriggerEvent ( " playerPetChange " ) end , " mainControl " , " petStatus_Schedule " )
--if the pet is alive, register to know when it dies
if ( UnitExists ( " pet " ) and UnitHealth ( " pet " ) >= 1 ) then
eventFrame : RegisterUnitEvent ( " UNIT_FLAGS " , " pet " )
end
end
end ,
[ " UNIT_FLAGS " ] = function ( unitId )
local petHealth = UnitHealth ( unitId )
if ( petHealth < 1 ) then
eventFrame : UnregisterEvent ( " UNIT_FLAGS " )
openRaidLib.eventFunctions [ " UNIT_PET " ] ( " player " )
end
end ,
[ " CHALLENGE_MODE_COMPLETED " ] = function ( )
openRaidLib.internalCallback . TriggerEvent ( " mythicDungeonEnd " )
end ,
[ " PLAYER_LOGOUT " ] = function ( )
tempCache.SaveData ( )
end ,
}
openRaidLib.eventFunctions = eventFunctions
eventFrame : RegisterEvent ( " PLAYER_ENTERING_WORLD " )
eventFrame : SetScript ( " OnEvent " , function ( self , event , ... )
local eventCallbackFunc = eventFunctions [ event ]
eventCallbackFunc ( ... )
end )
--run when PLAYER_ENTERING_WORLD triggers, this avoid any attempt of getting information without the game has completed the load process
function openRaidLib . OnEnterWorldRegisterEvents ( )
eventFrame : RegisterEvent ( " GROUP_ROSTER_UPDATE " )
eventFrame : RegisterUnitEvent ( " UNIT_SPELLCAST_SUCCEEDED " , " player " , " pet " )
eventFrame : RegisterEvent ( " PLAYER_REGEN_DISABLED " )
eventFrame : RegisterEvent ( " PLAYER_REGEN_ENABLED " )
eventFrame : RegisterEvent ( " UPDATE_INVENTORY_DURABILITY " )
eventFrame : RegisterEvent ( " PLAYER_EQUIPMENT_CHANGED " )
eventFrame : RegisterEvent ( " UNIT_PET " )
eventFrame : RegisterEvent ( " PLAYER_DEAD " )
eventFrame : RegisterEvent ( " PLAYER_ALIVE " )
eventFrame : RegisterEvent ( " PLAYER_UNGHOST " )
eventFrame : RegisterEvent ( " PLAYER_LOGOUT " )
if ( checkClientVersion ( " retail " ) ) then
eventFrame : RegisterEvent ( " PLAYER_TALENT_UPDATE " )
eventFrame : RegisterEvent ( " PLAYER_PVP_TALENT_UPDATE " )
eventFrame : RegisterEvent ( " ENCOUNTER_END " )
eventFrame : RegisterEvent ( " CHALLENGE_MODE_START " )
eventFrame : RegisterEvent ( " CHALLENGE_MODE_COMPLETED " )
eventFrame : RegisterEvent ( " PLAYER_SPECIALIZATION_CHANGED " )
eventFrame : RegisterEvent ( " TRAIT_TREE_CURRENCY_INFO_UPDATED " )
eventFrame : RegisterEvent ( " TRAIT_CONFIG_UPDATED " )
end
end
--------------------------------------------------------------------------------------------------------------------------------
--~main ~control
openRaidLib.mainControl = {
playerAliveStatus = { } ,
}
--send full data (all data available)
function openRaidLib . mainControl . SendFullData ( )
--send player data
openRaidLib.UnitInfoManager . SendAllPlayerInfo ( )
--send gear data
openRaidLib.GearManager . SendAllGearInfo ( )
--send cooldown data
openRaidLib.CooldownManager . SendAllPlayerCooldowns ( )
end
openRaidLib.mainControl . onEnterWorld = function ( )
--update the alive status of the player
openRaidLib.mainControl . UpdatePlayerAliveStatus ( true )
--the game client is fully loadded and all information is available
if ( openRaidLib.IsInGroup ( ) ) then
openRaidLib.Schedules . NewUniqueTimer ( 1.0 , openRaidLib.mainControl . SendFullData , " mainControl " , " sendFullData_Schedule " )
end
end
--update player data, even if not in group
--called on every player_entering_world event
openRaidLib.mainControl . UpdatePlayerData = function ( )
local unitName = UnitName ( " player " )
--player data
local playerFullInfo = openRaidLib.UnitInfoManager . GetPlayerFullInfo ( )
openRaidLib.UnitInfoManager . AddUnitInfo ( unitName , unpack ( playerFullInfo ) )
--gear info
--C_Timer.After(2, function()
local playerGearInfo = openRaidLib.GearManager . GetPlayerFullGearInfo ( )
openRaidLib.GearManager . AddUnitGearList ( unitName , unpack ( playerGearInfo ) )
--end)
--cooldowns
openRaidLib.CooldownManager . UpdatePlayerCooldownsLocally ( )
end
--this function runs on all Player Entering World, it is delayed due to covenant data many times aren't available after a cold login
function openRaidLib . mainControl . scheduleUpdatePlayerData ( )
openRaidLib.Schedules . NewUniqueTimer ( 1.0 , openRaidLib.mainControl . UpdatePlayerData , " mainControl " , " updatePlayerData_Schedule " )
end
function openRaidLib . UpdatePlayer ( )
return openRaidLib.mainControl . UpdatePlayerData ( )
end
openRaidLib.mainControl . OnEnterGroup = function ( )
--the player entered in a group
--schedule to send data
openRaidLib.Schedules . NewUniqueTimer ( 1.0 , openRaidLib.mainControl . SendFullData , " mainControl " , " sendFullData_Schedule " )
end
openRaidLib.mainControl . OnLeftGroup = function ( )
--the player left a group
--wipe group data (each module registers the OnLeftGroup)
--cancel all schedules
openRaidLib.Schedules . CancelAllUniqueTimers ( )
--wipe alive status
table.wipe ( openRaidLib.mainControl . playerAliveStatus )
--toggle off comms
end
openRaidLib.mainControl . OnPlayerDeath = function ( )
local playerName = UnitName ( " player " )
openRaidLib.mainControl . playerAliveStatus [ playerName ] = false
local dataToSend = " " .. CONST_COMM_PLAYER_DEAD_PREFIX
openRaidLib.commHandler . SendCommData ( dataToSend )
diagnosticComm ( " OnPlayerDeath| " .. dataToSend ) --debug
openRaidLib.publicCallback . TriggerCallback ( " UnitDeath " , " player " )
end
openRaidLib.mainControl . OnPlayerRess = function ( )
local playerName = UnitName ( " player " )
openRaidLib.mainControl . playerAliveStatus [ playerName ] = true
local dataToSend = " " .. CONST_COMM_PLAYER_ALIVE_PREFIX
openRaidLib.commHandler . SendCommData ( dataToSend )
diagnosticComm ( " OnPlayerRess| " .. dataToSend ) --debug
openRaidLib.publicCallback . TriggerCallback ( " UnitAlive " , " player " )
end
openRaidLib.internalCallback . RegisterCallback ( " onEnterWorld " , openRaidLib.mainControl . onEnterWorld )
openRaidLib.internalCallback . RegisterCallback ( " onEnterWorld " , openRaidLib.mainControl . scheduleUpdatePlayerData )
openRaidLib.internalCallback . RegisterCallback ( " onEnterGroup " , openRaidLib.mainControl . OnEnterGroup )
openRaidLib.internalCallback . RegisterCallback ( " onLeaveGroup " , openRaidLib.mainControl . OnLeftGroup )
openRaidLib.internalCallback . RegisterCallback ( " onPlayerDeath " , openRaidLib.mainControl . OnPlayerDeath )
openRaidLib.internalCallback . RegisterCallback ( " onPlayerRess " , openRaidLib.mainControl . OnPlayerRess )
--a player in the group died
openRaidLib.commHandler . RegisterComm ( CONST_COMM_PLAYER_DEAD_PREFIX , function ( data , unitName )
openRaidLib.mainControl . playerAliveStatus [ unitName ] = false
openRaidLib.publicCallback . TriggerCallback ( " UnitDeath " , openRaidLib.GetUnitID ( unitName ) )
end )
--a player in the group is now alive
openRaidLib.commHandler . RegisterComm ( CONST_COMM_PLAYER_ALIVE_PREFIX , function ( data , unitName )
openRaidLib.mainControl . playerAliveStatus [ unitName ] = true
openRaidLib.publicCallback . TriggerCallback ( " UnitAlive " , openRaidLib.GetUnitID ( unitName ) )
end )
function openRaidLib . mainControl . UpdatePlayerAliveStatus ( onLogin )
if ( UnitIsDeadOrGhost ( " player " ) ) then
if ( openRaidLib.playerAlive ) then
openRaidLib.playerAlive = false
--trigger event if this isn't from login
if ( not onLogin ) then
openRaidLib.internalCallback . TriggerEvent ( " onPlayerDeath " )
end
end
else
if ( not openRaidLib.playerAlive ) then
openRaidLib.playerAlive = true
--trigger event if this isn't from login
if ( not onLogin ) then
openRaidLib.internalCallback . TriggerEvent ( " onPlayerRess " )
end
end
end
end
--------------------------------------------------------------------------------------------------------------------------------
--~all, request data from all players
--send a request to all players in the group to send their data
function openRaidLib . RequestAllData ( )
--the the player isn't in group, don't send the request
if ( not IsInGroup ( ) ) then
return
end
openRaidLib.requestAllInfoCooldown = openRaidLib.requestAllInfoCooldown or 0
--check if the player can sent another request
if ( openRaidLib.requestAllInfoCooldown > GetTime ( ) ) then
return
end
openRaidLib.commHandler . SendCommData ( CONST_COMM_FULLINFO_PREFIX )
diagnosticComm ( " RequestAllInfo| " .. CONST_COMM_FULLINFO_PREFIX ) --debug
openRaidLib.requestAllInfoCooldown = GetTime ( ) + CONST_REQUEST_ALL_DATA_COOLDOWN
return true
end
--this function handles the request from another player to send all data
function openRaidLib . commHandler . SendFullData ( )
openRaidLib.mainControl . SendFullData ( )
end
openRaidLib.commHandler . RegisterComm ( CONST_COMM_FULLINFO_PREFIX , function ( data , sourceName )
openRaidLib.sendRequestedAllInfoCooldown = openRaidLib.sendRequestedAllInfoCooldown or 0
--check if there's some delay before sending the data
if ( openRaidLib.sendRequestedAllInfoCooldown > GetTime ( ) ) then
--reschedule the function call
openRaidLib.Schedules . NewUniqueTimer ( openRaidLib.sendRequestedAllInfoCooldown - GetTime ( ) , openRaidLib.commHandler . SendFullData , " CommHandler " , " sendFullData_Schedule " )
return
end
openRaidLib.Schedules . NewUniqueTimer ( math.random ( 1 , 6 ) , openRaidLib.commHandler . SendFullData , " CommHandler " , " sendFullData_Schedule " )
--set the delay for the next request
openRaidLib.sendRequestedAllInfoCooldown = GetTime ( ) + CONST_SEND_ALL_DATA_COOLDOWN
end )
--------------------------------------------------------------------------------------------------------------------------------
--~player general ~info ~unit
--API calls
--return a table containing all information of units
--format: [playerName-realm] = {information}
function openRaidLib . GetAllUnitsInfo ( )
return openRaidLib.UnitInfoManager . GetAllUnitsInfo ( )
end
--return a table containing information of a single unit
function openRaidLib . GetUnitInfo ( unitId )
local unitName = GetUnitName ( unitId , true ) or unitId
return openRaidLib.UnitInfoManager . GetUnitInfo ( unitName )
end
--manager constructor
openRaidLib.UnitInfoManager = {
--structure:
--[playerName] = {ilevel = 100, durability = 100, weaponEnchant = 0, noGems = {}, noEnchants = {}}
UnitData = { } ,
}
local unitTablePrototype = {
specId = 0 ,
specName = " " ,
role = " " ,
renown = 1 ,
covenantId = 0 ,
talents = { } ,
conduits = { } ,
pvpTalents = { } ,
class = " " ,
classId = 0 ,
className = " " ,
name = " " ,
nameFull = " " ,
}
function openRaidLib . UnitInfoManager . GetAllUnitsInfo ( )
return openRaidLib.UnitInfoManager . UnitData
end
--get the unit table or create a new one if 'createNew' is true
function openRaidLib . UnitInfoManager . GetUnitInfo ( unitName , createNew )
local unitInfo = openRaidLib.UnitInfoManager . UnitData [ unitName ]
if ( not unitInfo and createNew ) then
unitInfo = { }
openRaidLib.TCopy ( unitInfo , unitTablePrototype )
openRaidLib.UnitInfoManager . UnitData [ unitName ] = unitInfo
end
return unitInfo
end
function openRaidLib . UnitInfoManager . EraseData ( )
table.wipe ( openRaidLib.UnitInfoManager . UnitData )
end
function openRaidLib . UnitInfoManager . SetUnitInfo ( unitName , unitInfo , specId , renown , covenantId , talentsTableUnpacked , conduitsTableUnpacked , pvpTalentsTableUnpacked )
if ( not GetSpecializationInfoByID ) then --tbc hot fix
return
end
local specId , specName , specDescription , specIcon , role = GetSpecializationInfoByID ( specId or 0 )
local className , classString , classId = UnitClass ( unitName )
--cold login bug where the player class info cannot be retrived by the player name, after a /reload it's all good
if ( not className ) then
local playerName = UnitName ( " player " )
if ( playerName == unitName ) then
className , classString , classId = UnitClass ( " player " )
end
end
unitInfo.specId = specId or unitInfo.specId
unitInfo.specName = specName or unitInfo.specName
unitInfo.role = role or " DAMAGER "
unitInfo.renown = renown or unitInfo.renown
unitInfo.covenantId = covenantId or unitInfo.covenantId
unitInfo.talents = talentsTableUnpacked or unitInfo.talents
unitInfo.conduits = conduitsTableUnpacked or unitInfo.conduits
unitInfo.pvpTalents = pvpTalentsTableUnpacked or unitInfo.pvpTalents
unitInfo.class = classString
unitInfo.classId = classId
unitInfo.className = className
unitInfo.name = unitName : gsub ( ( " %-.* " ) , " " )
unitInfo.nameFull = unitName
end
function openRaidLib . UnitInfoManager . AddUnitInfo ( unitName , specId , renown , covenantId , talentsTableUnpacked , conduitsTableUnpacked , pvpTalentsTableUnpacked )
local unitInfo = openRaidLib.UnitInfoManager . GetUnitInfo ( unitName , true )
openRaidLib.UnitInfoManager . SetUnitInfo ( unitName , unitInfo , specId , renown , covenantId , talentsTableUnpacked , conduitsTableUnpacked , pvpTalentsTableUnpacked )
openRaidLib.publicCallback . TriggerCallback ( " UnitInfoUpdate " , openRaidLib.GetUnitID ( unitName ) , openRaidLib.UnitInfoManager . UnitData [ unitName ] , openRaidLib.UnitInfoManager . GetAllUnitsInfo ( ) )
end
--triggered when the lib receives a unit information from another player in the raid
--@data: table received from comm
--@unitName: player name
function openRaidLib . UnitInfoManager . OnReceiveUnitFullInfo ( data , unitName )
local specId = tonumber ( data [ 1 ] )
local playerInfo1 = tonumber ( data [ 2 ] )
local playerInfo2 = tonumber ( data [ 3 ] )
if ( not playerInfo2 or playerInfo2 > 4 ) then --cleanup on 10.0
--invalid covanentId - different lib versions, it'll fix itself on dragonflight
return
end
local talentsSize = tonumber ( data [ 4 ] )
if ( not talentsSize ) then
return
end
local borrowedTalentsTableIndex = tonumber ( ( talentsSize + 1 ) + 3 ) + 1 -- +3 for spec, playerInfo1 and playerInfo2 data | talentSizeIndex + talentSize | +1 for talents size
local borrowedTalentsSize = data [ borrowedTalentsTableIndex ]
local pvpTalentsTableIndex = 3 + 3 + talentsSize + borrowedTalentsSize -- +3 for spec, playerInfo1 and playerInfo2 data | +3 for talents, conduit and pvptalents index for size
local pvpTalentsSize = data [ pvpTalentsTableIndex ]
--unpack the talents data as a ipairs table
local talentsTableUnpacked = openRaidLib.UnpackTable ( data , 4 , false , false , talentsSize )
--unpack the borrowed talents data as a ipairs table
local borrowedTalentsTableUnpacked = openRaidLib.UnpackTable ( data , borrowedTalentsTableIndex , false , false , borrowedTalentsSize )
--back compatibility with versions without pvp talents
if ( type ( data [ pvpTalentsTableIndex ] ) == " string " or not data [ pvpTalentsTableIndex ] ) then
--add a dummy table as pvp talents
openRaidLib.UnitInfoManager . AddUnitInfo ( unitName , specId , playerInfo1 , playerInfo2 , talentsTableUnpacked , borrowedTalentsTableUnpacked , { 0 , 0 , 0 } )
return
end
--unpack the pvp talents data as a ipairs table
local pvpTalentsTableUnpacked = openRaidLib.UnpackTable ( data , pvpTalentsTableIndex , false , false , pvpTalentsSize )
--add to the list of players information and also trigger a public callback
openRaidLib.UnitInfoManager . AddUnitInfo ( unitName , specId , playerInfo1 , playerInfo2 , talentsTableUnpacked , borrowedTalentsTableUnpacked , pvpTalentsTableUnpacked )
end
openRaidLib.commHandler . RegisterComm ( CONST_COMM_PLAYERINFO_PREFIX , openRaidLib.UnitInfoManager . OnReceiveUnitFullInfo )
function openRaidLib . UnitInfoManager . SendAllPlayerInfo ( )
local playerInfo = openRaidLib.UnitInfoManager . GetPlayerFullInfo ( )
local dataToSend = CONST_COMM_PLAYERINFO_PREFIX .. " , "
dataToSend = dataToSend .. playerInfo [ 1 ] .. " , " --spec id
dataToSend = dataToSend .. playerInfo [ 2 ] .. " , " --player info 1
dataToSend = dataToSend .. playerInfo [ 3 ] .. " , " --player info 2
dataToSend = dataToSend .. openRaidLib.PackTable ( playerInfo [ 4 ] ) .. " , " --player talents class-spec
dataToSend = dataToSend .. openRaidLib.PackTable ( playerInfo [ 5 ] ) .. " , " --player talents borrowed
dataToSend = dataToSend .. openRaidLib.PackTable ( playerInfo [ 6 ] ) .. " , " --player talents pvp
--send the data
openRaidLib.commHandler . SendCommData ( dataToSend )
diagnosticComm ( " SendGetUnitInfoFullData| " .. dataToSend ) --debug
end
--player info format:
--index 1: number: specId
--index 2: number: tbd, depends on expansion
--index 3: number: tbd, depends on expansion
--index 4: talents 1: player talents: length vary depends on talent system
--index 5: talents 2: borrowed power talents: length vary from expansions
--index 6: talents 3: pvp talents
function openRaidLib . UnitInfoManager . GetPlayerFullInfo ( )
local playerInfo = { }
if ( isTimewalkWoW ( ) ) then
--indexes: specId, renown, covenant, talent, conduits, pvp talents
--return a placeholder table
return { 0 , 0 , 0 , { 0 , 0 , 0 , 0 , 0 , 0 , 0 } , { 0 , 0 } , 0 }
end
local specId = 0
if ( getSpecializationVersion ( ) == CONST_SPECIALIZATION_VERSION_MODERN ) then
local selectedSpecialization = GetSpecialization ( )
if ( selectedSpecialization ) then
specId = GetSpecializationInfo ( selectedSpecialization ) or 0
end
end
playerInfo [ 1 ] = specId
--player information 1 (this can be different for each expansion)
playerInfo [ 2 ] = openRaidLib.UnitInfoManager . GetPlayerInfo1 ( )
--player information 2 (this can be different for each expansion)
playerInfo [ 3 ] = openRaidLib.UnitInfoManager . GetPlayerInfo2 ( )
--player class-spec talents
local talents = openRaidLib.UnitInfoManager . GetPlayerTalents ( )
playerInfo [ 4 ] = talents
--borrowed talents (conduits talents on shadowlands)
local borrowedTalents = openRaidLib.UnitInfoManager . GetPlayerBorrowedTalents ( )
playerInfo [ 5 ] = borrowedTalents
--pvp talents
local pvpTalents = openRaidLib.UnitInfoManager . GetPlayerPvPTalents ( )
playerInfo [ 6 ] = pvpTalents
return playerInfo
end
--talent update (when the player changes a talent and the lib needs to notify other players in the group)
function openRaidLib . UnitInfoManager . SendTalentUpdate ( )
--talents
local unitInfo = openRaidLib.UnitInfoManager . GetUnitInfo ( " player " , true )
local talentsToSend = unitInfo.talents
local dataToSend = " " .. CONST_COMM_PLAYERINFO_TALENTS_PREFIX .. " , "
local talentsString = openRaidLib.PackTable ( talentsToSend )
dataToSend = dataToSend .. talentsString
--send the data
openRaidLib.commHandler . SendCommData ( dataToSend )
diagnosticComm ( " SendTalentUpdateData| " .. dataToSend ) --debug
end
function openRaidLib . UnitInfoManager . OnPlayerTalentChanged ( )
--update the local player
local unitInfo = openRaidLib.UnitInfoManager . GetUnitInfo ( " player " , true )
local unitName = UnitName ( " player " )
openRaidLib.UnitInfoManager . SetUnitInfo ( unitName , unitInfo , nil , nil , nil , openRaidLib.UnitInfoManager . GetPlayerTalents ( ) )
--schedule send to the group
openRaidLib.Schedules . NewUniqueTimer ( 1 + math.random ( 0 , 1 ) , openRaidLib.UnitInfoManager . SendTalentUpdate , " UnitInfoManager " , " sendTalent_Schedule " )
--trigger public callback event
openRaidLib.publicCallback . TriggerCallback ( " TalentUpdate " , " player " , unitInfo.talents , unitInfo , openRaidLib.UnitInfoManager . GetAllUnitsInfo ( ) )
end
openRaidLib.internalCallback . RegisterCallback ( " talentUpdate " , openRaidLib.UnitInfoManager . OnPlayerTalentChanged )
function openRaidLib . UnitInfoManager . OnReceiveTalentsUpdate ( data , unitName )
local talentsTableUnpacked = openRaidLib.UnpackTable ( data , 1 , false , false , 7 ) --this 7 should be a constant
local unitInfo = openRaidLib.UnitInfoManager . GetUnitInfo ( unitName , true )
if ( unitInfo ) then
openRaidLib.UnitInfoManager . SetUnitInfo ( unitName , unitInfo , nil , nil , nil , talentsTableUnpacked )
--trigger public callback event
openRaidLib.publicCallback . TriggerCallback ( " TalentUpdate " , openRaidLib.GetUnitID ( unitName ) , unitInfo.talents , unitInfo , openRaidLib.UnitInfoManager . GetAllUnitsInfo ( ) )
end
end
openRaidLib.commHandler . RegisterComm ( CONST_COMM_PLAYERINFO_TALENTS_PREFIX , openRaidLib.UnitInfoManager . OnReceiveTalentsUpdate )
--pvp talent update (when the player changes a pvp talent and the lib needs to notify other players in the group)
function openRaidLib . UnitInfoManager . SendPvPTalentUpdate ( )
--pvp talents
local unitInfo = openRaidLib.UnitInfoManager . GetUnitInfo ( " player " , true )
local pvpTalentsToSend = unitInfo.pvpTalents
local pvpTalentsString = openRaidLib.PackTable ( pvpTalentsToSend )
local dataToSend = " " .. CONST_COMM_PLAYERINFO_PVPTALENTS_PREFIX .. " , "
dataToSend = dataToSend .. pvpTalentsString
--send the data
openRaidLib.commHandler . SendCommData ( dataToSend )
diagnosticComm ( " SendPvPTalentUpdateData| " .. dataToSend ) --debug
end
function openRaidLib . UnitInfoManager . OnPlayerPvPTalentChanged ( )
--update the local player
local unitInfo = openRaidLib.UnitInfoManager . GetUnitInfo ( " player " , true )
local unitName = UnitName ( " player " )
openRaidLib.UnitInfoManager . SetUnitInfo ( unitName , unitInfo , nil , nil , nil , nil , nil , openRaidLib.UnitInfoManager . GetPlayerPvPTalents ( ) )
--schedule send to the group
openRaidLib.Schedules . NewUniqueTimer ( 1 + math.random ( 0 , 1 ) , openRaidLib.UnitInfoManager . SendPvPTalentUpdate , " UnitInfoManager " , " sendPvPTalent_Schedule " )
--trigger public callback event
openRaidLib.publicCallback . TriggerCallback ( " PvPTalentUpdate " , " player " , unitInfo.pvpTalents , unitInfo , openRaidLib.UnitInfoManager . GetAllUnitsInfo ( ) )
end
openRaidLib.internalCallback . RegisterCallback ( " pvpTalentUpdate " , openRaidLib.UnitInfoManager . OnPlayerPvPTalentChanged )
function openRaidLib . UnitInfoManager . OnReceivePvPTalentsUpdate ( data , unitName )
local pvpTalentsTableUnpacked = openRaidLib.UnpackTable ( data , 1 , false , false , 3 ) --this 3 should be a constant
local unitInfo = openRaidLib.UnitInfoManager . GetUnitInfo ( unitName , true )
if ( unitInfo ) then
unitInfo.pvpTalents = pvpTalentsTableUnpacked
--trigger public callback event
openRaidLib.publicCallback . TriggerCallback ( " PvPTalentUpdate " , openRaidLib.GetUnitID ( unitName ) , unitInfo.pvpTalents , unitInfo , openRaidLib.UnitInfoManager . GetAllUnitsInfo ( ) )
end
end
openRaidLib.commHandler . RegisterComm ( CONST_COMM_PLAYERINFO_PVPTALENTS_PREFIX , openRaidLib.UnitInfoManager . OnReceivePvPTalentsUpdate )
function openRaidLib . UnitInfoManager . OnPlayerLeaveGroup ( )
local unitName = UnitName ( " player " )
--clear the data
openRaidLib.UnitInfoManager . EraseData ( )
--trigger a public callback
openRaidLib.publicCallback . TriggerCallback ( " UnitInfoWipe " , openRaidLib.UnitInfoManager . UnitData )
--need to build the player info again
local playerFullInfo = openRaidLib.UnitInfoManager . GetPlayerFullInfo ( )
openRaidLib.UnitInfoManager . AddUnitInfo ( unitName , unpack ( playerFullInfo ) )
end
openRaidLib.internalCallback . RegisterCallback ( " onLeaveGroup " , openRaidLib.UnitInfoManager . OnPlayerLeaveGroup )
--send data when leaving combat
function openRaidLib . UnitInfoManager . SendPlayerInfoAfterCombat ( )
openRaidLib.UnitInfoManager . SendAllPlayerInfo ( )
end
function openRaidLib . UnitInfoManager . OnLeaveCombat ( )
openRaidLib.Schedules . NewUniqueTimer ( 1 + math.random ( 1 , 4 ) , openRaidLib.UnitInfoManager . SendPlayerInfoAfterCombat , " UnitInfoManager " , " leaveCombat_Schedule " )
end
openRaidLib.internalCallback . RegisterCallback ( " onLeaveCombat " , openRaidLib.UnitInfoManager . OnLeaveCombat )
--------------------------------------------------------------------------------------------------------------------------------
--~equipment
openRaidLib.GearManager = {
--structure: [playerName] = {ilevel = 100, durability = 100, weaponEnchant = 0, noGems = {}, noEnchants = {}}
UnitData = { } ,
}
local gearTablePrototype = {
ilevel = 0 ,
durability = 0 ,
weaponEnchant = 0 ,
noGems = { } ,
noEnchants = { } ,
}
function openRaidLib . GetAllUnitsGear ( )
return openRaidLib.GearManager . GetAllUnitsGear ( )
end
function openRaidLib . GetUnitGear ( unitId , createNew )
local unitName = GetUnitName ( unitId , true ) or unitId
return openRaidLib.GearManager . GetUnitGear ( unitName )
end
function openRaidLib . GearManager . GetAllUnitsGear ( )
return openRaidLib.GearManager . UnitData
end
function openRaidLib . GearManager . GetUnitGear ( unitName , createNew )
local unitGearInfo = openRaidLib.GearManager . UnitData [ unitName ]
if ( not unitGearInfo and createNew ) then
unitGearInfo = { }
openRaidLib.TCopy ( unitGearInfo , gearTablePrototype )
openRaidLib.GearManager . UnitData [ unitName ] = unitGearInfo
end
return unitGearInfo
end
--clear data stored
function openRaidLib . GearManager . EraseData ( )
table.wipe ( openRaidLib.GearManager . UnitData )
end
function openRaidLib . GearManager . OnPlayerLeaveGroup ( )
local unitName = GetUnitName ( " player " )
--clear the data
openRaidLib.GearManager . EraseData ( )
--trigger a public callback
openRaidLib.publicCallback . TriggerCallback ( " GearListWipe " , openRaidLib.GearManager . UnitData )
--need to build the player gear again
local playerGearInfo = openRaidLib.GearManager . GetPlayerFullGearInfo ( )
openRaidLib.GearManager . AddUnitGearList ( unitName , unpack ( playerGearInfo ) )
end
openRaidLib.internalCallback . RegisterCallback ( " onLeaveGroup " , openRaidLib.GearManager . OnPlayerLeaveGroup )
--when the player is ressed while in a group, send the cooldown list
function openRaidLib . GearManager . OnPlayerRess ( )
--check if is in group
if ( openRaidLib.IsInGroup ( ) ) then
openRaidLib.Schedules . NewUniqueTimer ( 1.0 + math.random ( 0.0 , 6.0 ) , openRaidLib.GearManager . SendDurability , " GearManager " , " sendDurability_Schedule " )
end
end
openRaidLib.internalCallback . RegisterCallback ( " onPlayerRess " , openRaidLib.GearManager . OnPlayerRess )
--send data when leaving combat
function openRaidLib . GearManager . SendGearInfoAfterCombat ( )
openRaidLib.GearManager . SendAllGearInfo ( )
end
function openRaidLib . GearManager . OnLeaveCombat ( )
openRaidLib.Schedules . NewUniqueTimer ( 1 + math.random ( 1 , 4 ) , openRaidLib.GearManager . SendGearInfoAfterCombat , " GearManager " , " leaveCombat_Schedule " )
end
openRaidLib.internalCallback . RegisterCallback ( " onLeaveCombat " , openRaidLib.GearManager . OnLeaveCombat )
--send only the gear durability
function openRaidLib . GearManager . SendDurability ( )
local dataToSend = " " .. CONST_COMM_GEARINFO_DURABILITY_PREFIX .. " , "
local averageGearDurability , lowestDurability = openRaidLib.GearManager . GetPlayerGearDurability ( )
dataToSend = dataToSend .. averageGearDurability
--send the data
openRaidLib.commHandler . SendCommData ( dataToSend )
diagnosticComm ( " SendGearDurabilityData| " .. dataToSend ) --debug
end
function openRaidLib . GearManager . OnReceiveGearDurability ( data , unitName )
local durability = tonumber ( data [ 1 ] )
openRaidLib.GearManager . UpdateUnitGearDurability ( unitName , durability )
end
openRaidLib.commHandler . RegisterComm ( CONST_COMM_GEARINFO_DURABILITY_PREFIX , openRaidLib.GearManager . OnReceiveGearDurability )
--on receive the durability (sent when the player get a ress)
function openRaidLib . GearManager . UpdateUnitGearDurability ( unitName , durability )
local unitGearInfo = openRaidLib.GearManager . GetUnitGear ( unitName )
if ( unitGearInfo ) then
unitGearInfo.durability = durability
openRaidLib.publicCallback . TriggerCallback ( " GearDurabilityUpdate " , openRaidLib.GetUnitID ( unitName ) , durability , unitGearInfo , openRaidLib.GearManager . GetAllUnitsGear ( ) )
end
end
--get gear information from what the player has equipped at the moment
function openRaidLib . GearManager . GetPlayerFullGearInfo ( )
--get the player class and specId
local _ , playerClass = UnitClass ( " player " )
local specId = openRaidLib.GetPlayerSpecId ( )
--get which attribute the spec uses
local specMainAttribute = openRaidLib.specAttribute [ playerClass ] [ specId ] --1 int, 2 dex, 3 str
if ( not specId or not specMainAttribute ) then
return { 0 , 0 , 0 , { } , { } , { } , 0 , 0 }
end
--item level
local itemLevel = openRaidLib.GearManager . GetPlayerItemLevel ( )
--repair status
local gearDurability = openRaidLib.GearManager . GetPlayerGearDurability ( )
--get weapon enchant
local weaponEnchant , mainHandEnchantId , offHandEnchantId = openRaidLib.GearManager . GetPlayerWeaponEnchant ( )
--enchants and gems
local slotsWithoutGems , slotsWithoutEnchant = openRaidLib.GearManager . GetPlayerGemsAndEnchantInfo ( )
--full gear list {{slotId, gemAmount, itemLevel, itemLink}, {slotId, gemAmount, itemLevel, itemLink}, }
local equippedGearList = openRaidLib.GearManager . BuildPlayerEquipmentList ( )
--build the table with the gear information
local playerGearInfo = { }
playerGearInfo [ # playerGearInfo + 1 ] = itemLevel --[1] - one index
playerGearInfo [ # playerGearInfo + 1 ] = gearDurability --[2] - one index
playerGearInfo [ # playerGearInfo + 1 ] = weaponEnchant --[3] - one index
playerGearInfo [ # playerGearInfo + 1 ] = slotsWithoutEnchant --[4] - undefined
playerGearInfo [ # playerGearInfo + 1 ] = slotsWithoutGems --[5] - undefined
playerGearInfo [ # playerGearInfo + 1 ] = equippedGearList --[6] - undefined
playerGearInfo [ # playerGearInfo + 1 ] = mainHandEnchantId --[7]
playerGearInfo [ # playerGearInfo + 1 ] = offHandEnchantId --[8]
return playerGearInfo
end
--when received the gear update from another player, store it and trigger a callback
function openRaidLib . GearManager . AddUnitGearList ( unitName , itemLevel , durability , weaponEnchant , noEnchantTable , noGemsTable , equippedGearList , mainHandEnchantId , offHandEnchantId )
local unitGearInfo = openRaidLib.GearManager . GetUnitGear ( unitName , true )
unitGearInfo.ilevel = itemLevel
unitGearInfo.durability = durability
unitGearInfo.weaponEnchant = weaponEnchant
unitGearInfo.noGems = noGemsTable
unitGearInfo.noEnchants = noEnchantTable
unitGearInfo.mainHandEnchantId = mainHandEnchantId
unitGearInfo.offHandEnchantId = offHandEnchantId
--parse and replace the 'equippedGearList'
openRaidLib.GearManager . BuildEquipmentItemLinks ( equippedGearList )
unitGearInfo.equippedGear = equippedGearList
openRaidLib.publicCallback . TriggerCallback ( " GearUpdate " , openRaidLib.GetUnitID ( unitName ) , unitGearInfo , openRaidLib.GearManager . GetAllUnitsGear ( ) )
end
--triggered when the lib receives a gear information from another player in the raid
--@data: table received from comm
--@unitName: player name
function openRaidLib . GearManager . OnReceiveGearFullInfo ( data , unitName )
local itemLevel = tonumber ( data [ 1 ] ) --1 index
local durability = tonumber ( data [ 2 ] ) --1 index
local weaponEnchant = tonumber ( data [ 3 ] ) --1 index
local noEnchantTableSize = tonumber ( data [ 4 ] )
local noGemsTableIndex = tonumber ( noEnchantTableSize + 5 ) --5 is the three first indexes, the enchant table size and +1 to jump to next index
local noGemsTableSize = data [ noGemsTableIndex ]
local equippedGearListIndex = tonumber ( noEnchantTableSize + noGemsTableSize + 6 ) --6 is the same has the 5 but +1 index for the gems table size
local equippedGearListSize = data [ equippedGearListIndex ]
local mainHandEnchantId , offHandEnchantId = 0 , 0
if equippedGearListSize then
local mainHandEnchantIdIndex = tonumber ( noEnchantTableSize + noGemsTableSize + equippedGearListSize + 7 )
mainHandEnchantId = tonumber ( data [ mainHandEnchantIdIndex ] ) or 0
local offHandEnchantIdIndex = tonumber ( mainHandEnchantIdIndex + 1 )
offHandEnchantId = tonumber ( data [ offHandEnchantIdIndex ] ) or 0
end
--unpack the enchant data as a ipairs table
local noEnchantTableUnpacked = openRaidLib.UnpackTable ( data , 4 , false , false , noEnchantTableSize )
--unpack the enchant data as a ipairs table
local noGemsTableUnpacked = openRaidLib.UnpackTable ( data , noGemsTableIndex , false , false , noGemsTableSize )
--unpack the full gear
local equippedGearListUnpacked = equippedGearListIndex and openRaidLib.UnpackTable ( data , equippedGearListIndex , false , true , 4 ) or { }
--add to the list of gear information
openRaidLib.GearManager . AddUnitGearList ( unitName , itemLevel , durability , weaponEnchant , noEnchantTableUnpacked , noGemsTableUnpacked , equippedGearListUnpacked , mainHandEnchantId , offHandEnchantId )
end
openRaidLib.commHandler . RegisterComm ( CONST_COMM_GEARINFO_FULL_PREFIX , openRaidLib.GearManager . OnReceiveGearFullInfo )
--todo: on changing an item in the inventory, send an update only for the slot that got changed
function openRaidLib . GearManager . SendAllGearInfo ( )
--get gear information, gear info has 6 indexes:
--[1] int item level
--[2] int durability
--[3] int weapon enchant
--[4] table with integers of equipSlot without enchant
--[5] table with integers of equipSlot which has a gem slot but the slot is empty
--[6] table with all gear from the player
--[7] int mainHandEnchantId
--[8] int offHandEnchantId
local dataToSend = " " .. CONST_COMM_GEARINFO_FULL_PREFIX .. " , "
local playerGearInfo = openRaidLib.GearManager . GetPlayerFullGearInfo ( )
--update the player table
openRaidLib.GearManager . AddUnitGearList ( UnitName ( " player " ) , unpack ( playerGearInfo ) )
dataToSend = dataToSend .. playerGearInfo [ 1 ] .. " , " --item level
dataToSend = dataToSend .. playerGearInfo [ 2 ] .. " , " --durability
dataToSend = dataToSend .. playerGearInfo [ 3 ] .. " , " --weapon enchant
dataToSend = dataToSend .. openRaidLib.PackTable ( playerGearInfo [ 4 ] ) .. " , " --slots without enchant
dataToSend = dataToSend .. openRaidLib.PackTable ( playerGearInfo [ 5 ] ) .. " , " -- slots with empty gem sockets
dataToSend = dataToSend .. openRaidLib.PackTableAndSubTables ( playerGearInfo [ 6 ] ) --full equipped equipment
--send the data
openRaidLib.commHandler . SendCommData ( dataToSend )
diagnosticComm ( " SendGearFullData| " .. dataToSend ) --debug
end
--------------------------------------------------------------------------------------------------------------------------------
--~cooldowns
openRaidLib.CooldownManager = {
UnitData = { } , --stores the list of cooldowns each player has sent
UnitDataFilterCache = { } , --same as the table above but cooldowns are separated has offensive, defensive, etc. FilterCooldowns in functions.lua
NeedRebuildFilters = { } , --mark people that has invalid filter cache and need to rebuild it
CooldownTickers = { } , --store C_Timer.NewTicker
HasFullCooldownList = { } , --store player names with the library
}
--check if a cooldown time has changed or finished
--this function run within a ticker, the internal is CONST_COOLDOWN_CHECK_INTERVAL
local cooldownTimeLeftCheck_Ticker = function ( tickerObject )
local spellId = tickerObject.spellId
--if the spell does not exists anymore in the player table, cancel the ticker
local playerName = UnitName ( " player " )
if ( not openRaidLib.CooldownManager . UnitData [ playerName ] [ spellId ] ) then
tickerObject : Cancel ( )
return
end
tickerObject.cooldownTimeLeft = tickerObject.cooldownTimeLeft - CONST_COOLDOWN_CHECK_INTERVAL
local timeLeft , charges , startTimeOffset , duration , auraDuration = openRaidLib.CooldownManager . GetPlayerCooldownStatus ( spellId )
local bUpdateLocally = false
--is the spell ready to use?
if ( timeLeft == 0 ) then
--it's ready
openRaidLib.CooldownManager . SendPlayerCooldownUpdate ( spellId , 0 , charges , 0 , 0 , 0 )
openRaidLib.CooldownManager . CooldownTickers [ spellId ] = nil
tickerObject : Cancel ( )
bUpdateLocally = true
else
--check if the time left has changed, this check if the cooldown got its time reduced and if the cooldown time has been slow down by modRate
if ( not openRaidLib.isNearlyEqual ( tickerObject.cooldownTimeLeft , timeLeft , CONST_COOLDOWN_TIMELEFT_HAS_CHANGED ) ) then
--there's a deviation, send a comm to communicate the change in the time left
openRaidLib.CooldownManager . SendPlayerCooldownUpdate ( spellId , timeLeft , charges , startTimeOffset , duration , auraDuration )
tickerObject.cooldownTimeLeft = timeLeft
bUpdateLocally = true
end
end
if ( bUpdateLocally ) then
--get the cooldown time for this spell
local timeLeft , charges , startTimeOffset , duration , auraDuration = openRaidLib.CooldownManager . GetPlayerCooldownStatus ( spellId ) --return 5 values
--update the cooldown
openRaidLib.CooldownManager . CooldownSpellUpdate ( playerName , spellId , timeLeft , charges , startTimeOffset , duration , auraDuration ) --need 7 values
local playerCooldownTable = openRaidLib.GetUnitCooldowns ( playerName )
local cooldownInfo = openRaidLib.GetUnitCooldownInfo ( playerName , spellId )
openRaidLib.publicCallback . TriggerCallback ( " CooldownUpdate " , " player " , spellId , cooldownInfo , playerCooldownTable , openRaidLib.CooldownManager . UnitData )
end
end
--after a spell is casted by the player, start a ticker to check its cooldown
local cooldownStartTicker = function ( spellId , cooldownTimeLeft )
local existingTicker = openRaidLib.CooldownManager . CooldownTickers [ spellId ]
if ( existingTicker ) then
--if a ticker already exists, might be the cooldown of a charge
--if the ticker isn't about to expire, just keep the timer
--when the ticker finishes it'll check again for charges
if ( existingTicker.startTime + existingTicker.cooldownTimeLeft - GetTime ( ) > 2 ) then
return
end
--cancel the existing ticker
if ( not existingTicker : IsCancelled ( ) ) then
existingTicker : Cancel ( )
end
end
--create a new ticker
local maxTicks = ceil ( cooldownTimeLeft / CONST_COOLDOWN_CHECK_INTERVAL )
local newTicker = C_Timer.NewTicker ( CONST_COOLDOWN_CHECK_INTERVAL , cooldownTimeLeftCheck_Ticker , maxTicks )
--store the ticker
openRaidLib.CooldownManager . CooldownTickers [ spellId ] = newTicker
newTicker.spellId = spellId
newTicker.cooldownTimeLeft = cooldownTimeLeft
newTicker.startTime = GetTime ( )
newTicker.endTime = GetTime ( ) + cooldownTimeLeft
end
function openRaidLib . CooldownManager . CleanupCooldownTickers ( )
for spellId , tickerObject in pairs ( openRaidLib.CooldownManager . CooldownTickers ) do
local timeLeft , charges , startTimeOffset , duration , auraDuration = openRaidLib.CooldownManager . GetPlayerCooldownStatus ( spellId )
if ( timeLeft == 0 ) then
tickerObject : Cancel ( )
openRaidLib.CooldownManager . CooldownTickers [ spellId ] = nil
end
end
end
local cooldownGetUnitTable = function ( unitName , shouldWipe )
local unitCooldownTable = openRaidLib.CooldownManager . UnitData [ unitName ]
--check if the unit has a cooldownTable
if ( not unitCooldownTable ) then
unitCooldownTable = { }
openRaidLib.CooldownManager . UnitData [ unitName ] = unitCooldownTable
else
--as the unit could have changed a talent or spec, wipe the table before using it
if ( shouldWipe ) then
table.wipe ( unitCooldownTable )
end
end
return unitCooldownTable
end
local cooldownGetSpellInfo = function ( unitName , spellId )
local unitCooldownTable = cooldownGetUnitTable ( unitName )
local cooldownInfo = unitCooldownTable [ spellId ]
return cooldownInfo
end
--update a single cooldown timer
--called when the player casted a cooldown and when received a cooldown update from another player
--only update the db, no other action is taken
--cooldownInfo: [1] timeLeft [2] charges [3] startOffset [4] duration [5] updateTime [6] auraDuration
function openRaidLib . CooldownManager . CooldownSpellUpdate ( unitName , spellId , newTimeLeft , newCharges , startTimeOffset , duration , auraDuration )
--get the cooldown table where all cooldowns are stored for this unit
local unitCooldownTable = cooldownGetUnitTable ( unitName )
--is this a cooldown info?
local cooldownInfo = unitCooldownTable [ spellId ] or { }
cooldownInfo [ CONST_COOLDOWN_INDEX_TIMELEFT ] = newTimeLeft
cooldownInfo [ CONST_COOLDOWN_INDEX_CHARGES ] = newCharges
cooldownInfo [ CONST_COOLDOWN_INDEX_TIMEOFFSET ] = startTimeOffset
cooldownInfo [ CONST_COOLDOWN_INDEX_DURATION ] = duration
cooldownInfo [ CONST_COOLDOWN_INDEX_UPDATETIME ] = GetTime ( )
cooldownInfo [ CONST_COOLDOWN_INDEX_AURA_DURATION ] = auraDuration
unitCooldownTable [ spellId ] = cooldownInfo
end
--API Calls
--return a table with unit names as key and a table with unit cooldowns as the value
--table format: [playerName] = {[spellId] = cooldownInfo}
function openRaidLib . GetAllUnitsCooldown ( )
return openRaidLib.CooldownManager . UnitData
end
--return a table with all the unit cooldowns
--table format: [spellId] = cooldownInfo
function openRaidLib . GetUnitCooldowns ( unitId , filter )
local unitName = GetUnitName ( unitId , true ) or unitId
local allCooldowns = openRaidLib.CooldownManager . UnitData [ unitName ]
--check if there's a filter and if there's at least one cooldown existing
if ( allCooldowns and next ( allCooldowns ) ) then
if ( filter and filter ~= " " ) then
if ( type ( filter ) == " string " ) then
local filterCooldowns = openRaidLib.FilterCooldowns ( unitName , allCooldowns , filter )
return filterCooldowns
else
openRaidLib.DiagnosticError ( " CooldownManager|GetUnitCooldowns|filter isn't a string " )
end
else
return allCooldowns
end
else
return { }
end
end
function openRaidLib . DoesSpellPassFilters ( spellId , filter )
return openRaidLib.CooldownManager . DoesSpellPassFilters ( spellId , filter )
end
function openRaidLib . GetSpellFilters ( spellId , defaultFilterOnly , customFiltersOnly )
return openRaidLib.CooldownManager . GetSpellFilters ( spellId , defaultFilterOnly , customFiltersOnly )
end
--return values about the cooldown time
--values returned: timeLeft, charges, timeOffset, duration, updateTime
function openRaidLib . GetCooldownTimeFromUnitSpellID ( unitId , spellId )
local unitCooldownsTable = openRaidLib.GetUnitCooldowns ( unitId )
if ( unitCooldownsTable ) then
local cooldownInfo = unitCooldownsTable [ spellId ]
if ( cooldownInfo ) then
return openRaidLib.CooldownManager . GetCooldownInfoValues ( cooldownInfo )
end
end
end
--return values about the cooldown time from a cooldown info
--values returned: timeLeft, charges, timeOffset, duration, updateTime
function openRaidLib . GetCooldownTimeFromCooldownInfo ( cooldownInfo )
if ( cooldownInfo ) then
return openRaidLib.CooldownManager . GetCooldownInfoValues ( cooldownInfo )
end
end
--return a table containing values about the cooldown time
--values returned: {timeLeft, charges, timeOffset, duration, updateTime}
function openRaidLib . GetUnitCooldownInfo ( unitId , spellId )
local unitCooldownsTable = openRaidLib.GetUnitCooldowns ( unitId )
if ( unitCooldownsTable ) then
local cooldownInfo = unitCooldownsTable [ spellId ]
return cooldownInfo
end
end
local calculatePercent = function ( timeOffset , duration , updateTime , charges )
timeOffset = abs ( timeOffset )
local minValue = updateTime - timeOffset
local maxValue = minValue + duration
local currentValue = GetTime ( )
local percent = openRaidLib.GetRangePercent ( minValue , maxValue , currentValue )
percent = min ( percent , 1 )
local timeLeft = max ( maxValue - currentValue , 0 )
--lag compensation
if ( timeLeft <= 2 ) then
timeLeft = 0
if ( charges == 0 ) then
charges = 1
end
minValue = currentValue
maxValue = 1
currentValue = 1
end
local bIsReady = timeLeft <= 2
return bIsReady , percent , timeLeft , charges , minValue , maxValue , min ( currentValue , maxValue ) , duration
end
--return the values to be use on a progress bar or cooldown frame
--require a unitId and a spellId to query the values
--values returned: isReady, timeLeft, charges, normalized percent, minValue, maxValue, currentValue
--values are in the GetTime() format
function openRaidLib . GetCooldownStatusFromUnitSpellID ( unitId , spellId )
local timeLeft , charges , timeOffset , duration , updateTime , auraDuration
local unitCooldownsTable = openRaidLib.GetUnitCooldowns ( unitId )
if ( unitCooldownsTable ) then
local cooldownInfo = unitCooldownsTable [ spellId ]
if ( cooldownInfo ) then
timeLeft , charges , timeOffset , duration , updateTime , auraDuration = openRaidLib.CooldownManager . GetCooldownInfoValues ( cooldownInfo )
end
end
return calculatePercent ( timeOffset , duration , updateTime , charges )
end
---return the values to be use on a progress bar or cooldown frame
---values returned: bIsReady, percent, timeLeft, charges, minValue, maxValue, currentValue, duration
---@param cooldownInfo table
---@return boolean bIsReady
---@return number percent
---@return number timeLeft
---@return number charges
---@return number minValue
---@return number maxValue
---@return number currentValue
---@return number duration
function openRaidLib . GetCooldownStatusFromCooldownInfo ( cooldownInfo )
local timeLeft , charges , timeOffset , duration , updateTime , auraDuration = openRaidLib.CooldownManager . GetCooldownInfoValues ( cooldownInfo )
if ( not timeOffset ) then
return false , 0 , 0 , 0 , 0 , 0 , 0 , 0
end
return calculatePercent ( timeOffset , duration , updateTime , charges )
end
--internals
function openRaidLib . CooldownManager . GetCooldownInfoValues ( cooldownInfo )
local timeLeft , charges , timeOffset , duration , updateTime , auraDuration = unpack ( cooldownInfo )
return timeLeft , charges , timeOffset , duration , updateTime , auraDuration
end
function openRaidLib . CooldownManager . OnPlayerCast ( event , spellId , isPlayerPet ) --~cast
--player casted a spell, check if the spell is registered as cooldown
--issue: pet spells isn't in this table yet, might mess with pet interrupts
if ( LIB_OPEN_RAID_PLAYERCOOLDOWNS [ spellId ] ) then --check if the casted spell is a cooldown the player has available
local playerName = UnitName ( " player " )
--get the cooldown time for this spell
local timeLeft , charges , startTimeOffset , duration , auraDuration = openRaidLib.CooldownManager . GetPlayerCooldownStatus ( spellId ) --return 5 values
--check for shared cooldown time - warning: this block of code is duplicated at "openRaidLib.commHandler.RegisterComm(CONST_COMM_COOLDOWNUPDATE_PREFIX"
local spellData = LIB_OPEN_RAID_COOLDOWNS_INFO [ spellId ]
local sharedCooldownId = spellData and spellData.shareid
if ( sharedCooldownId ) then
local spellsWithSharedCooldown = LIB_OPEN_RAID_COOLDOWNS_SHARED_ID [ sharedCooldownId ]
for thisSpellId in pairs ( spellsWithSharedCooldown ) do
--don't run for the spell that triggered the shared cooldown
if ( thisSpellId ~= spellId ) then
openRaidLib.CooldownManager . CooldownSpellUpdate ( playerName , thisSpellId , timeLeft , charges , startTimeOffset , duration , auraDuration )
local cooldownInfo = cooldownGetSpellInfo ( playerName , thisSpellId )
local unitCooldownTable = openRaidLib.GetUnitCooldowns ( playerName )
--trigger a public callback
openRaidLib.publicCallback . TriggerCallback ( " CooldownUpdate " , openRaidLib.GetUnitID ( playerName ) , thisSpellId , cooldownInfo , unitCooldownTable , openRaidLib.CooldownManager . UnitData )
end
end
end
--update the cooldown
openRaidLib.CooldownManager . CooldownSpellUpdate ( playerName , spellId , timeLeft , charges , startTimeOffset , duration , auraDuration ) --receive 7 values
local cooldownInfo = cooldownGetSpellInfo ( playerName , spellId )
--trigger a public callback
local playerCooldownTable = openRaidLib.GetUnitCooldowns ( playerName )
openRaidLib.publicCallback . TriggerCallback ( " CooldownUpdate " , " player " , spellId , cooldownInfo , playerCooldownTable , openRaidLib.CooldownManager . UnitData )
--send to comm
openRaidLib.CooldownManager . SendPlayerCooldownUpdate ( spellId , timeLeft , charges , startTimeOffset , duration , auraDuration )
--create a timer to monitor the time of this cooldown
--as there's just a few of them to monitor, there's no issue on creating one timer per spell
cooldownStartTicker ( spellId , timeLeft )
end
end
--when the player is ressed while in a group, send the cooldown list
function openRaidLib . CooldownManager . OnPlayerRess ( )
--check if is in group
if ( openRaidLib.IsInGroup ( ) ) then
openRaidLib.Schedules . NewUniqueTimer ( 1.0 + math.random ( 0.0 , 6.0 ) , openRaidLib.CooldownManager . SendAllPlayerCooldowns , " CooldownManager " , " sendAllPlayerCooldowns_Schedule " )
end
end
function openRaidLib . CooldownManager . OnPlayerLeaveGroup ( )
--clear the data
openRaidLib.CooldownManager . EraseData ( )
--trigger a public callback
openRaidLib.publicCallback . TriggerCallback ( " CooldownListWipe " , openRaidLib.CooldownManager . UnitData )
--recreate the player cooldowns
openRaidLib.CooldownManager . UpdatePlayerCooldownsLocally ( )
end
--when a talent has changed, it might remove or add a cooldown
function openRaidLib . CooldownManager . OnPlayerTalentChanged ( )
--immediatelly update the player cooldowns locally
openRaidLib.CooldownManager . UpdatePlayerCooldownsLocally ( )
--schedule send to the group, using a large delay to send due to the player might change more talents at once
openRaidLib.Schedules . NewUniqueTimer ( 4 + math.random ( 0 , 1 ) , openRaidLib.CooldownManager . SendAllPlayerCooldowns , " CooldownManager " , " sendAllPlayerCooldowns_Schedule " )
end
--check cooldown reset after a raid encounter ends finishing ongoing timeLeft tickers
function openRaidLib . CooldownManager . CheckCooldownsAfterEncounterEnd ( )
openRaidLib.CooldownManager . CleanupCooldownTickers ( )
openRaidLib.Schedules . NewUniqueTimer ( 1 + math.random ( 1 , 4 ) , openRaidLib.CooldownManager . SendAllPlayerCooldowns , " CooldownManager " , " sendAllPlayerCooldowns_Schedule " )
end
function openRaidLib . CooldownManager . OnEncounterEnd ( )
--run on next frame
openRaidLib.Schedules . NewUniqueTimer ( 0.1 , openRaidLib.CooldownManager . CheckCooldownsAfterEncounterEnd , " CooldownManager " , " encounterEndCooldownsCheck_Schedule " )
end
function openRaidLib . CooldownManager . OnMythicPlusStart ( )
openRaidLib.Schedules . NewUniqueTimer ( 0.5 , openRaidLib.CooldownManager . SendAllPlayerCooldowns , " CooldownManager " , " sendAllPlayerCooldowns_Schedule " )
end
function openRaidLib . CooldownManager . OnPlayerPetChanged ( )
openRaidLib.CooldownManager . CheckCooldownChanges ( )
end
function openRaidLib . CooldownManager . OnAuraRemoved ( event , unitId , spellId )
--under development ~aura
local timeLeft , charges , startTimeOffset , duration , auraDuration = openRaidLib.CooldownManager . GetPlayerCooldownStatus ( spellId )
--do need to update?
if ( not timeLeft or timeLeft < 1 or not auraDuration or auraDuration < 1 ) then
return
end
local latencyCompensation = 1
if ( spellId ) then
if ( auraDuration > latencyCompensation ) then
--cooldown aura got removed before expiration
local newAuraDuration = 0
local unitName = GetUnitName ( unitId , true ) or unitId
openRaidLib.CooldownManager . CooldownSpellUpdate ( unitName , spellId , timeLeft , charges , startTimeOffset , duration , newAuraDuration )
--trigger a public callback
local playerCooldownTable = openRaidLib.GetUnitCooldowns ( unitName )
local cooldownInfo = cooldownGetSpellInfo ( unitName , spellId )
openRaidLib.publicCallback . TriggerCallback ( " CooldownUpdate " , " player " , spellId , cooldownInfo , playerCooldownTable , openRaidLib.CooldownManager . UnitData )
--send to comm
openRaidLib.CooldownManager . SendPlayerCooldownUpdate ( spellId , timeLeft , charges , startTimeOffset , duration , newAuraDuration )
end
end
end
openRaidLib.internalCallback . RegisterCallback ( " onLeaveGroup " , openRaidLib.CooldownManager . OnPlayerLeaveGroup )
openRaidLib.internalCallback . RegisterCallback ( " playerCast " , openRaidLib.CooldownManager . OnPlayerCast )
openRaidLib.internalCallback . RegisterCallback ( " onPlayerRess " , openRaidLib.CooldownManager . OnPlayerRess )
openRaidLib.internalCallback . RegisterCallback ( " talentUpdate " , openRaidLib.CooldownManager . OnPlayerTalentChanged )
openRaidLib.internalCallback . RegisterCallback ( " raidEncounterEnd " , openRaidLib.CooldownManager . OnEncounterEnd )
openRaidLib.internalCallback . RegisterCallback ( " onLeaveCombat " , openRaidLib.CooldownManager . OnEncounterEnd )
openRaidLib.internalCallback . RegisterCallback ( " mythicDungeonStart " , openRaidLib.CooldownManager . OnMythicPlusStart )
openRaidLib.internalCallback . RegisterCallback ( " playerPetChange " , openRaidLib.CooldownManager . OnPlayerPetChanged )
openRaidLib.internalCallback . RegisterCallback ( " unitAuraRemoved " , openRaidLib.CooldownManager . OnAuraRemoved )
--send a list through comm with cooldowns added or removed
function openRaidLib . CooldownManager . CheckCooldownChanges ( )
--important: CheckForSpellsAdeedOrRemoved() already change the cooldowns on the player locally
local spellsAdded , spellsRemoved = openRaidLib.CooldownManager . CheckForSpellsAdeedOrRemoved ( )
--add a prefix to make things easier during unpack
if ( # spellsAdded > 0 ) then
tinsert ( spellsAdded , 1 , " A " )
end
--insert the spells that has been removed at the end of the spells added table and pack the table
if ( # spellsRemoved > 0 ) then
spellsAdded [ # spellsAdded + 1 ] = " R "
for _ , spellId in ipairs ( spellsRemoved ) do
spellsAdded [ # spellsAdded + 1 ] = spellId
end
end
--send a comm if has any changes
if ( # spellsAdded > 0 ) then
--pack
local playerCooldownChangesString = openRaidLib.PackTable ( spellsAdded )
local dataToSend = CONST_COMM_COOLDOWNCHANGES_PREFIX .. " , "
dataToSend = dataToSend .. playerCooldownChangesString
openRaidLib.commHandler . SendCommData ( dataToSend )
diagnosticComm ( " CheckCooldownChanges| " .. dataToSend ) --debug
end
end
function openRaidLib . CooldownManager . OnReceiveUnitCooldownChanges ( data , unitName )
local currentCooldowns = openRaidLib.CooldownManager . UnitData [ unitName ]
--if does not have the full list of cooldowns of this unit, ignore cooldown add/remove comms
if ( not currentCooldowns or not openRaidLib.CooldownManager . HasFullCooldownList [ unitName ] ) then
return
end
--create a table to be ready to unpack
local addedCooldowns = { }
local removedCooldowns = { }
local bIsCooldownAdded = false
local bIsCooldownRemoved = false
--the letters A and R separate cooldowns added and cooldowns removed
for i = 1 , # data do
local thisData = data [ i ]
if ( thisData == " A " ) then
bIsCooldownAdded = true
elseif ( thisData == " R " ) then
bIsCooldownAdded = false
bIsCooldownRemoved = true
end
if ( bIsCooldownAdded ) then
thisData = tonumber ( thisData )
if ( thisData ) then
addedCooldowns [ # addedCooldowns + 1 ] = thisData
end
elseif ( bIsCooldownRemoved ) then
local spellId = tonumber ( thisData )
if ( spellId ) then
removedCooldowns [ # removedCooldowns + 1 ] = spellId
end
end
end
if ( # addedCooldowns > 0 ) then
tinsert ( addedCooldowns , 1 , # addedCooldowns ) --amount of indexes for UnpackTable()
local cooldownsAddedUnpacked = openRaidLib.UnpackTable ( addedCooldowns , 1 , true , true , CONST_COOLDOWN_INFO_SIZE )
for spellId , cooldownInfo in pairs ( cooldownsAddedUnpacked ) do
--add the spell into the list of cooldowns of this unit
local timeLeft , charges , timeOffset , duration , updateTime , auraDuration = openRaidLib.CooldownManager . GetCooldownInfoValues ( cooldownInfo )
openRaidLib.CooldownManager . CooldownSpellUpdate ( unitName , spellId , timeLeft , charges , timeOffset , duration , auraDuration )
--mark the filter cache of this unit as dirt
openRaidLib.CooldownManager . NeedRebuildFilters [ unitName ] = true
--trigger public callback
openRaidLib.publicCallback . TriggerCallback ( " CooldownAdded " , openRaidLib.GetUnitID ( unitName ) , spellId , cooldownInfo , openRaidLib.GetUnitCooldowns ( unitName ) , openRaidLib.CooldownManager . UnitData )
end
end
if ( # removedCooldowns > 0 ) then
for _ , spellId in ipairs ( removedCooldowns ) do
--remove the spell from this unit cooldown list
currentCooldowns [ spellId ] = nil
--mark the filter cache of this unit as dirt
openRaidLib.CooldownManager . NeedRebuildFilters [ unitName ] = true
--trigger public callback
openRaidLib.publicCallback . TriggerCallback ( " CooldownRemoved " , openRaidLib.GetUnitID ( unitName ) , spellId , openRaidLib.GetUnitCooldowns ( unitName ) , openRaidLib.CooldownManager . UnitData )
end
end
end
openRaidLib.commHandler . RegisterComm ( CONST_COMM_COOLDOWNCHANGES_PREFIX , openRaidLib.CooldownManager . OnReceiveUnitCooldownChanges )
--compare the current list of spells of the player with a new spell list generated
--add or remove spells from the player cooldown list
--return two tables, the first has added spells and is a index table ready to pack and send to comm
--the second table is a index table with a list of spells that has been removed, also ready to pack
function openRaidLib . CooldownManager . CheckForSpellsAdeedOrRemoved ( )
local playerName = UnitName ( " player " )
local currentCooldowns = openRaidLib.CooldownManager . UnitData [ playerName ]
local _ , newCooldownList = openRaidLib.CooldownManager . GetPlayerCooldownList ( )
local spellsAdded , spellsRemoved = { } , { }
for spellId , cooldownInfo in pairs ( newCooldownList ) do
if ( not currentCooldowns [ spellId ] ) then
--a spell has been added
local timeLeft , charges , timeOffset , duration , updateTime , auraDuration = openRaidLib.CooldownManager . GetCooldownInfoValues ( cooldownInfo )
openRaidLib.CooldownManager . CooldownSpellUpdate ( playerName , spellId , timeLeft , charges , timeOffset , duration , auraDuration )
local timeLeft , charges , startTimeOffset , duration , auraDuration = openRaidLib.CooldownManager . GetPlayerCooldownStatus ( spellId ) --return 5 values
spellsAdded [ # spellsAdded + 1 ] = spellId
spellsAdded [ # spellsAdded + 1 ] = timeLeft
spellsAdded [ # spellsAdded + 1 ] = charges
spellsAdded [ # spellsAdded + 1 ] = startTimeOffset
spellsAdded [ # spellsAdded + 1 ] = duration
spellsAdded [ # spellsAdded + 1 ] = auraDuration
--mark the filter cache of this unit as dirt
openRaidLib.CooldownManager . NeedRebuildFilters [ playerName ] = true
openRaidLib.publicCallback . TriggerCallback ( " CooldownAdded " , " player " , spellId , cooldownInfo , openRaidLib.GetUnitCooldowns ( " player " ) , openRaidLib.CooldownManager . UnitData )
end
end
for spellId in pairs ( currentCooldowns ) do
if ( not newCooldownList [ spellId ] ) then
--a spell has been removed
currentCooldowns [ spellId ] = nil
spellsRemoved [ # spellsRemoved + 1 ] = spellId
--mark the filter cache of this unit as dirt
openRaidLib.CooldownManager . NeedRebuildFilters [ playerName ] = true
openRaidLib.publicCallback . TriggerCallback ( " CooldownRemoved " , " player " , spellId , openRaidLib.GetUnitCooldowns ( " player " ) , openRaidLib.CooldownManager . UnitData )
end
end
return spellsAdded , spellsRemoved
end
--update the list of cooldowns of the player it self locally
--this is called right after changes in the player cooldowns
function openRaidLib . CooldownManager . UpdatePlayerCooldownsLocally ( playerCooldownHash )
if ( not playerCooldownHash ) then
playerCooldownHash = select ( 2 , openRaidLib.CooldownManager . GetPlayerCooldownList ( ) )
end
local playerName = UnitName ( " player " )
openRaidLib.CooldownManager . AddUnitCooldownsList ( playerName , playerCooldownHash )
end
--adds a list of cooldowns for another player in the group
--this is only called from the received cooldown list from comm
function openRaidLib . CooldownManager . AddUnitCooldownsList ( unitName , cooldownsTable , noCallback )
local unitCooldownTable = cooldownGetUnitTable ( unitName , true ) --sending true to wipe previous data
openRaidLib.TCopy ( unitCooldownTable , cooldownsTable )
--add the unitName to the list of units detected with the lib
openRaidLib.CooldownManager . HasFullCooldownList [ unitName ] = true
--mark the filter cache of this unit as dirt
openRaidLib.CooldownManager . NeedRebuildFilters [ unitName ] = true
--get the time where the cooldown data was received, this is used with the timeleft and startTimeOffset
local timeNow = GetTime ( )
for spellId , cooldownTable in pairs ( cooldownsTable ) do
cooldownTable [ CONST_COOLDOWN_INDEX_UPDATETIME ] = timeNow
end
--trigger a public callback
if ( not noCallback ) then
openRaidLib.publicCallback . TriggerCallback ( " CooldownListUpdate " , openRaidLib.GetUnitID ( unitName ) , unitCooldownTable , openRaidLib.CooldownManager . UnitData )
end
end
--received a cooldown update from another unit (sent by the function above)
openRaidLib.commHandler . RegisterComm ( CONST_COMM_COOLDOWNUPDATE_PREFIX , function ( data , unitName )
--get data
local dataAsArray = data
local spellId = tonumber ( dataAsArray [ 1 ] )
local cooldownTimer = tonumber ( dataAsArray [ 2 ] )
local charges = tonumber ( dataAsArray [ 3 ] )
local startTime = tonumber ( dataAsArray [ 4 ] )
local duration = tonumber ( dataAsArray [ 5 ] )
local auraDuration = tonumber ( dataAsArray [ 6 ] )
--check integrity
if ( not spellId or spellId == 0 ) then
return openRaidLib.DiagnosticError ( " CooldownManager|comm received|spellId is invalid " )
elseif ( not cooldownTimer ) then
return openRaidLib.DiagnosticError ( " CooldownManager|comm received|cooldownTimer is invalid " )
elseif ( not charges ) then
return openRaidLib.DiagnosticError ( " CooldownManager|comm received|charges is invalid " )
elseif ( not startTime ) then
return openRaidLib.DiagnosticError ( " CooldownManager|comm received|startTime is invalid " )
elseif ( not duration ) then
return openRaidLib.DiagnosticError ( " CooldownManager|comm received|duration is invalid " )
elseif ( not auraDuration ) then
return openRaidLib.DiagnosticError ( " CooldownManager|comm received|auraDuration is invalid " )
end
--check for shared cooldown time
local spellData = LIB_OPEN_RAID_COOLDOWNS_INFO [ spellId ] --warning this block of code is duplicated at warning: this block of code is duplicated at "openRaidLib.CooldownManager.OnPlayerCast"
local sharedCooldownId = spellData and spellData.shareid
if ( sharedCooldownId ) then
local spellsWithSharedCooldown = LIB_OPEN_RAID_COOLDOWNS_SHARED_ID [ sharedCooldownId ]
for thisSpellId in pairs ( spellsWithSharedCooldown ) do
--don't run for the spell that triggered the shared cooldown
if ( thisSpellId ~= spellId ) then
openRaidLib.CooldownManager . CooldownSpellUpdate ( unitName , thisSpellId , cooldownTimer , charges , startTime , duration , auraDuration )
local cooldownInfo = cooldownGetSpellInfo ( unitName , thisSpellId )
local unitCooldownTable = openRaidLib.GetUnitCooldowns ( unitName )
--trigger a public callback
openRaidLib.publicCallback . TriggerCallback ( " CooldownUpdate " , openRaidLib.GetUnitID ( unitName ) , thisSpellId , cooldownInfo , unitCooldownTable , openRaidLib.CooldownManager . UnitData )
end
end
end
--update
--unitName, spellId, cooldownTimer, charges, startTime, duration, auraDuration
openRaidLib.CooldownManager . CooldownSpellUpdate ( unitName , spellId , cooldownTimer , charges , startTime , duration , auraDuration )
local cooldownInfo = cooldownGetSpellInfo ( unitName , spellId )
local unitCooldownTable = openRaidLib.GetUnitCooldowns ( unitName )
--trigger a public callback
openRaidLib.publicCallback . TriggerCallback ( " CooldownUpdate " , openRaidLib.GetUnitID ( unitName ) , spellId , cooldownInfo , unitCooldownTable , openRaidLib.CooldownManager . UnitData )
end )
--clear data stored, this is called after the player quit from a group
function openRaidLib . CooldownManager . EraseData ( )
table.wipe ( openRaidLib.CooldownManager . UnitDataFilterCache )
table.wipe ( openRaidLib.CooldownManager . HasFullCooldownList )
table.wipe ( openRaidLib.CooldownManager . NeedRebuildFilters )
table.wipe ( openRaidLib.CooldownManager . UnitData )
end
--send to comm all cooldowns available for the player
function openRaidLib . CooldownManager . SendAllPlayerCooldowns ( )
--get the full cooldown list
local playerCooldownList , playerCooldownHash = openRaidLib.CooldownManager . GetPlayerCooldownList ( )
--update the player cooldowns locally
openRaidLib.CooldownManager . UpdatePlayerCooldownsLocally ( playerCooldownHash )
local dataToSend = " " .. CONST_COMM_COOLDOWNFULLLIST_PREFIX .. " , "
--pack
local playerCooldownString = openRaidLib.PackTable ( playerCooldownList )
dataToSend = dataToSend .. playerCooldownString
--send the data
openRaidLib.commHandler . SendCommData ( dataToSend )
diagnosticComm ( " SendAllPlayerCooldowns| " .. dataToSend ) --debug
end
--send to comm a specific cooldown that was just used, a charge got available or its cooldown is over (ready to use)
function openRaidLib . CooldownManager . SendPlayerCooldownUpdate ( spellId , cooldownTimeLeft , charges , startTimeOffset , duration , auraDuration )
local dataToSend = " " .. CONST_COMM_COOLDOWNUPDATE_PREFIX .. " , " .. spellId .. " , " .. cooldownTimeLeft .. " , " .. charges .. " , " .. startTimeOffset .. " , " .. duration .. " , " .. auraDuration
openRaidLib.commHandler . SendCommData ( dataToSend )
diagnosticComm ( " SendPlayerCooldownUpdate| " .. dataToSend ) --debug
end
--triggered when the lib receives a full list of cooldowns from another player in the raid
--@data: table received from comm
--@unitName: player name
function openRaidLib . CooldownManager . OnReceiveUnitCooldowns ( data , unitName )
--unpack the table as a pairs table
local unpackedTable = openRaidLib.UnpackTable ( data , 1 , true , true , CONST_COOLDOWN_INFO_SIZE )
--add the list of cooldowns
openRaidLib.CooldownManager . AddUnitCooldownsList ( unitName , unpackedTable )
end
openRaidLib.commHandler . RegisterComm ( CONST_COMM_COOLDOWNFULLLIST_PREFIX , openRaidLib.CooldownManager . OnReceiveUnitCooldowns )
--send a comm requesting other units in the raid to send an update on the requested spell
--any unit in the raid that has this cooldown should send a CONST_COMM_COOLDOWNUPDATE_PREFIX
--@spellId: spellId to query
function openRaidLib . CooldownManager . RequestCooldownInfo ( spellId )
local dataToSend = " " .. CONST_COMM_COOLDOWNREQUEST_PREFIX .. " , " .. spellId
openRaidLib.commHandler . SendCommData ( dataToSend )
diagnosticComm ( " RequestCooldownInfo| " .. dataToSend ) --debug
end
function openRaidLib . RequestCooldownInfo ( spellId ) --api alias
return openRaidLib.CooldownManager . RequestCooldownInfo ( spellId )
end
function openRaidLib . CooldownManager . OnReceiveRequestForCooldownInfoUpdate ( data , unitName )
local spellId = tonumber ( data [ 1 ] )
--check if this unit has this cooldown in its list of cooldowns
if ( not cooldownGetSpellInfo ( UnitName ( " player " ) , spellId ) ) then
return
end
--get the cooldown time for this spell
local timeLeft , charges , startTimeOffset , duration , auraDuration = openRaidLib.CooldownManager . GetPlayerCooldownStatus ( spellId )
openRaidLib.CooldownManager . SendPlayerCooldownUpdate ( spellId , timeLeft , charges , startTimeOffset , duration , auraDuration )
end
openRaidLib.commHandler . RegisterComm ( CONST_COMM_COOLDOWNREQUEST_PREFIX , openRaidLib.CooldownManager . OnReceiveRequestForCooldownInfoUpdate )
--------------------------------------------------------------------------------------------------------------------------------
--~keystones
--public callback does not check if the keystone has changed from the previous callback
--API calls
--return a table containing all information of units
--format: [playerName-realm] = {information}
function openRaidLib . GetAllKeystonesInfo ( )
return openRaidLib.KeystoneInfoManager . GetAllKeystonesInfo ( )
end
--return a table containing information of a single unit
function openRaidLib . GetKeystoneInfo ( unitId )
local unitName = GetUnitName ( unitId , true ) or unitId
return openRaidLib.KeystoneInfoManager . GetKeystoneInfo ( unitName )
end
function openRaidLib . RequestKeystoneDataFromGuild ( )
if ( IsInGuild ( ) ) then
local dataToSend = " " .. CONST_COMM_KEYSTONE_DATAREQUEST_PREFIX
openRaidLib.commHandler . SendCommData ( dataToSend , 0x4 )
diagnosticComm ( " RequestKeystoneDataFromGuild| " .. dataToSend ) --debug
return true
else
return false
end
end
function openRaidLib . RequestKeystoneDataFromParty ( )
if ( IsInGroup ( ) and not IsInRaid ( ) ) then
local dataToSend = " " .. CONST_COMM_KEYSTONE_DATAREQUEST_PREFIX
openRaidLib.commHandler . SendCommData ( dataToSend , 0x1 )
diagnosticComm ( " RequestKeystoneDataFromParty| " .. dataToSend ) --debug
return true
else
return false
end
end
function openRaidLib . RequestKeystoneDataFromRaid ( )
if ( IsInRaid ( ) ) then
local dataToSend = " " .. CONST_COMM_KEYSTONE_DATAREQUEST_PREFIX
openRaidLib.commHandler . SendCommData ( dataToSend , 0x2 )
diagnosticComm ( " RequestKeystoneDataFromRaid| " .. dataToSend ) --debug
return true
else
return false
end
end
function openRaidLib . WipeKeystoneData ( )
wipe ( openRaidLib.KeystoneInfoManager . KeystoneData )
--trigger public callback
openRaidLib.publicCallback . TriggerCallback ( " KeystoneWipe " , openRaidLib.KeystoneInfoManager . KeystoneData )
--keystones are only available on retail
if ( not checkClientVersion ( " retail " ) ) then
return
end
--generate keystone info for the player
local unitName = UnitName ( " player " )
local keystoneInfo = openRaidLib.KeystoneInfoManager . GetKeystoneInfo ( unitName , true )
openRaidLib.KeystoneInfoManager . UpdatePlayerKeystoneInfo ( keystoneInfo )
openRaidLib.publicCallback . TriggerCallback ( " KeystoneUpdate " , unitName , keystoneInfo , openRaidLib.KeystoneInfoManager . KeystoneData )
return true
end
--manager constructor
openRaidLib.KeystoneInfoManager = {
--structure:
--[playerName] = {level = 2, mapID = 222}
KeystoneData = { } ,
}
local keystoneTablePrototype = {
level = 0 ,
mapID = 0 ,
challengeMapID = 0 ,
classID = 0 ,
rating = 0 ,
mythicPlusMapID = 0 ,
}
--search the player backpack to find a mythic keystone
--with the keystone object, it'll attempt to get the mythicPlusMapID to be used with C_ChallengeMode.GetMapUIInfo(mythicPlusMapID)
--ATM we are obligated to do this due to C_MythicPlus.GetOwnedKeystoneMapID() return the same mapID for the two Tazavesh dungeons
local getMythicPlusMapID = function ( )
for backpackId = 0 , 4 do
for slotId = 1 , GetContainerNumSlots ( backpackId ) do
local itemId = GetContainerItemID ( backpackId , slotId )
if ( itemId == LIB_OPEN_RAID_MYTHICKEYSTONE_ITEMID ) then
local itemLink = GetContainerItemLink ( backpackId , slotId )
local destroyedItemLink = itemLink : gsub ( " | " , " " )
local color , itemID , mythicPlusMapID = strsplit ( " : " , destroyedItemLink )
return tonumber ( mythicPlusMapID )
end
end
end
end
function openRaidLib . KeystoneInfoManager . UpdatePlayerKeystoneInfo ( keystoneInfo )
keystoneInfo.level = C_MythicPlus.GetOwnedKeystoneLevel ( ) or 0
keystoneInfo.mapID = C_MythicPlus.GetOwnedKeystoneMapID ( ) or 0
keystoneInfo.mythicPlusMapID = getMythicPlusMapID ( ) or 0
keystoneInfo.challengeMapID = C_MythicPlus.GetOwnedKeystoneChallengeMapID ( ) or 0
local _ , _ , playerClassID = UnitClass ( " player " )
keystoneInfo.classID = playerClassID
local ratingSummary = C_PlayerInfo.GetPlayerMythicPlusRatingSummary ( " player " )
keystoneInfo.rating = ratingSummary and ratingSummary.currentSeasonScore or 0
end
function openRaidLib . KeystoneInfoManager . GetAllKeystonesInfo ( )
return openRaidLib.KeystoneInfoManager . KeystoneData
end
--get the keystone info table or create a new one if 'createNew' is true
function openRaidLib . KeystoneInfoManager . GetKeystoneInfo ( unitName , createNew )
local keystoneInfo = openRaidLib.KeystoneInfoManager . KeystoneData [ unitName ]
if ( not keystoneInfo and createNew ) then
keystoneInfo = { }
openRaidLib.TCopy ( keystoneInfo , keystoneTablePrototype )
openRaidLib.KeystoneInfoManager . KeystoneData [ unitName ] = keystoneInfo
end
return keystoneInfo
end
local getKeystoneInfoToComm = function ( )
local playerName = UnitName ( " player " )
local keystoneInfo = openRaidLib.KeystoneInfoManager . GetKeystoneInfo ( playerName , true )
openRaidLib.KeystoneInfoManager . UpdatePlayerKeystoneInfo ( keystoneInfo )
local dataToSend = CONST_COMM_KEYSTONE_DATA_PREFIX .. " , " .. keystoneInfo.level .. " , " .. keystoneInfo.mapID .. " , " .. keystoneInfo.challengeMapID .. " , " .. keystoneInfo.classID .. " , " .. keystoneInfo.rating .. " , " .. keystoneInfo.mythicPlusMapID
return dataToSend
end
function openRaidLib . KeystoneInfoManager . SendPlayerKeystoneInfoToParty ( )
local dataToSend = getKeystoneInfoToComm ( )
openRaidLib.commHandler . SendCommData ( dataToSend , CONST_COMM_SENDTO_PARTY )
diagnosticComm ( " SendPlayerKeystoneInfoToParty| " .. dataToSend ) --debug
end
function openRaidLib . KeystoneInfoManager . SendPlayerKeystoneInfoToGuild ( )
local dataToSend = getKeystoneInfoToComm ( )
openRaidLib.commHandler . SendCommData ( dataToSend , CONST_COMM_SENDTO_GUILD )
diagnosticComm ( " SendPlayerKeystoneInfoToGuild| " .. dataToSend ) --debug
end
--when a request data is received, only send the data to party and guild
--sending stuff to raid need to be called my the application with 'openRaidLib.RequestKeystoneDataFromRaid()'
function openRaidLib . KeystoneInfoManager . OnReceiveRequestData ( )
if ( not checkClientVersion ( " retail " ) ) then
return
end
--update the information about the key stone the player has
local keystoneInfo = openRaidLib.KeystoneInfoManager . GetKeystoneInfo ( UnitName ( " player " ) , true )
openRaidLib.KeystoneInfoManager . UpdatePlayerKeystoneInfo ( keystoneInfo )
if ( IsInGroup ( ) and not IsInRaid ( ) ) then
openRaidLib.Schedules . NewUniqueTimer ( 0.1 , openRaidLib.KeystoneInfoManager . SendPlayerKeystoneInfoToParty , " KeystoneInfoManager " , " sendKeystoneInfoToParty_Schedule " )
end
if ( IsInGuild ( ) ) then
openRaidLib.Schedules . NewUniqueTimer ( math.random ( 0 , 3 ) + math.random ( ) , openRaidLib.KeystoneInfoManager . SendPlayerKeystoneInfoToGuild , " KeystoneInfoManager " , " sendKeystoneInfoToGuild_Schedule " )
end
end
openRaidLib.commHandler . RegisterComm ( CONST_COMM_KEYSTONE_DATAREQUEST_PREFIX , openRaidLib.KeystoneInfoManager . OnReceiveRequestData )
function openRaidLib . KeystoneInfoManager . OnReceiveKeystoneData ( data , unitName )
if ( not checkClientVersion ( " retail " ) ) then
return
end
local level = tonumber ( data [ 1 ] )
local mapID = tonumber ( data [ 2 ] )
local challengeMapID = tonumber ( data [ 3 ] )
local classID = tonumber ( data [ 4 ] )
local rating = tonumber ( data [ 5 ] )
local mythicPlusMapID = tonumber ( data [ 6 ] )
if ( level and mapID and challengeMapID and classID and rating and mythicPlusMapID ) then
local keystoneInfo = openRaidLib.KeystoneInfoManager . GetKeystoneInfo ( unitName , true )
keystoneInfo.level = level
keystoneInfo.mapID = mapID
keystoneInfo.mythicPlusMapID = mythicPlusMapID
keystoneInfo.challengeMapID = challengeMapID
keystoneInfo.classID = classID
keystoneInfo.rating = rating
--trigger public callback
openRaidLib.publicCallback . TriggerCallback ( " KeystoneUpdate " , unitName , keystoneInfo , openRaidLib.KeystoneInfoManager . KeystoneData )
end
end
openRaidLib.commHandler . RegisterComm ( CONST_COMM_KEYSTONE_DATA_PREFIX , openRaidLib.KeystoneInfoManager . OnReceiveKeystoneData )
--on entering a group, send keystone information for the party
function openRaidLib . KeystoneInfoManager . OnPlayerEnterGroup ( )
--keystones are only available on retail
if ( not checkClientVersion ( " retail " ) ) then
return
end
if ( IsInGroup ( ) and not IsInRaid ( ) ) then
--update the information about the key stone the player has
local keystoneInfo = openRaidLib.KeystoneInfoManager . GetKeystoneInfo ( UnitName ( " player " ) , true )
openRaidLib.KeystoneInfoManager . UpdatePlayerKeystoneInfo ( keystoneInfo )
--send to the group which keystone the player has
openRaidLib.Schedules . NewUniqueTimer ( 1 + math.random ( 0 , 2 ) + math.random ( ) , openRaidLib.KeystoneInfoManager . SendPlayerKeystoneInfoToParty , " KeystoneInfoManager " , " sendKeystoneInfoToParty_Schedule " )
end
end
function openRaidLib . KeystoneInfoManager . OnPlayerEnterWorld ( )
--keystones are only available on retail
if ( not checkClientVersion ( " retail " ) ) then
return
end
--hack: on received data send data to party and guild
openRaidLib.KeystoneInfoManager . OnReceiveRequestData ( )
--trigger public callback
local unitName = UnitName ( " player " )
local keystoneInfo = openRaidLib.KeystoneInfoManager . GetKeystoneInfo ( unitName , true )
openRaidLib.KeystoneInfoManager . UpdatePlayerKeystoneInfo ( keystoneInfo )
openRaidLib.publicCallback . TriggerCallback ( " KeystoneUpdate " , unitName , keystoneInfo , openRaidLib.KeystoneInfoManager . KeystoneData )
end
function openRaidLib . KeystoneInfoManager . OnMythicDungeonFinished ( )
--keystones are only available on retail
if ( not checkClientVersion ( " retail " ) ) then
return
end
--hack: on received data send data to party and guild
openRaidLib.KeystoneInfoManager . OnReceiveRequestData ( )
--trigger public callback
local unitName = UnitName ( " player " )
local keystoneInfo = openRaidLib.KeystoneInfoManager . GetKeystoneInfo ( unitName , true )
openRaidLib.KeystoneInfoManager . UpdatePlayerKeystoneInfo ( keystoneInfo )
openRaidLib.publicCallback . TriggerCallback ( " KeystoneUpdate " , unitName , keystoneInfo , openRaidLib.KeystoneInfoManager . KeystoneData )
end
openRaidLib.internalCallback . RegisterCallback ( " onEnterWorld " , openRaidLib.KeystoneInfoManager . OnPlayerEnterWorld )
openRaidLib.internalCallback . RegisterCallback ( " onEnterGroup " , openRaidLib.KeystoneInfoManager . OnPlayerEnterGroup )
openRaidLib.internalCallback . RegisterCallback ( " mythicDungeonEnd " , openRaidLib.KeystoneInfoManager . OnMythicDungeonFinished )
--------------------------------------------------------------------------------------------------------------------------------
--data
--vintage cooldown tracker and interrupt tracker
C_Timer.After ( 0.1 , function ( )
local vintageCDTrackerFrame = CreateFrame ( " frame " )
vintageCDTrackerFrame : RegisterEvent ( " UNIT_SPELLCAST_SUCCEEDED " )
local allCooldownsFromLib = LIB_OPEN_RAID_COOLDOWNS_INFO
local recentCastedSpells = { }
vintageCDTrackerFrame : SetScript ( " OnEvent " , function ( self , event , ... )
if ( event == " UNIT_SPELLCAST_SUCCEEDED " ) then
local unit , castGUID , spellId = ...
local unitIsThePlayer = UnitIsUnit ( unit , " player " )
if ( not unitIsThePlayer ) then
local unitName = GetUnitName ( unit , true )
local hasLib = openRaidLib.CooldownManager . HasFullCooldownList [ unitName ]
if ( unitName and not hasLib ) then
local unitInGroup = UnitInParty ( unit ) or UnitInRaid ( unit )
if ( unitInGroup ) then
local spellData = allCooldownsFromLib [ spellId ]
if ( spellData ) then -- and not openRaidLib.GetUnitCooldown(unitName)
--check for cast_success spam from channel spells
local unitCastCooldown = recentCastedSpells [ unitName ]
if ( not unitCastCooldown ) then
unitCastCooldown = { }
recentCastedSpells [ unitName ] = unitCastCooldown
end
if ( not unitCastCooldown [ spellId ] or unitCastCooldown [ spellId ] + 5 < GetTime ( ) ) then
unitCastCooldown [ spellId ] = GetTime ( )
--trigger a cooldown usage
local duration = spellData.duration
--time left, charges, startTimeOffset, duration
openRaidLib.CooldownManager . CooldownSpellUpdate ( unitName , spellId , duration , 0 , 0 , duration , 0 )
local cooldownInfo = cooldownGetSpellInfo ( unitName , spellId )
local unitCooldownsTable = openRaidLib.GetUnitCooldowns ( unitName )
--trigger a public callback
openRaidLib.publicCallback . TriggerCallback ( " CooldownUpdate " , openRaidLib.GetUnitID ( unitName ) , spellId , cooldownInfo , unitCooldownsTable , openRaidLib.CooldownManager . UnitData )
end
end
end
end
end
end
end )
end )
tempCache.RestoreData ( )
--[=[
3 x ... ns / Details / Libs / LibOpenRaid / GetPlayerInformation.lua : 603 : attempt to index field ' ? ' ( a nil value )
[ string " @Interface/AddOns/Details/Libs/LibOpenRaid/GetPlayerInformation.lua " ] : 634 : in function ` GetPlayerCooldownStatus '
[ string " @Interface/AddOns/Details/Libs/LibOpenRaid/LibOpenRaid.lua " ] : 1696 : in function ` CleanupCooldownTickers '
[ string " @Interface/AddOns/Details/Libs/LibOpenRaid/LibOpenRaid.lua " ] : 1925 : in function < . . . face / AddOns / Details / Libs / LibOpenRaid / LibOpenRaid.lua : 1924 >
[ string " =[C] " ] : in function ` xpcall '
[ string " @Interface/AddOns/Details/Libs/LibOpenRaid/LibOpenRaid.lua " ] : 506 : in function < . . . face / AddOns / Details / Libs / LibOpenRaid / LibOpenRaid.lua : 496 >
] = ]