You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3587 lines
157 KiB
3587 lines
157 KiB
|
|
--[=[
|
|
search keys:
|
|
~internal
|
|
~comms
|
|
~timers
|
|
~callbacks
|
|
|
|
~unitinfo
|
|
~equipment
|
|
~opennotes
|
|
~cooldowns
|
|
~keystones
|
|
|
|
|
|
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
|
|
--PLAYER_AVG_ITEM_LEVEL_UPDATE
|
|
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.
|
|
|
|
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
|
|
|
|
--]=]
|
|
|
|
---@alias castername string
|
|
---@alias castspellid string
|
|
---@alias schedulename string
|
|
|
|
local GetSpecialization = C_SpecializationInfo and C_SpecializationInfo.GetSpecialization or GetSpecialization
|
|
local GetSpecializationInfo = C_SpecializationInfo and C_SpecializationInfo.GetSpecializationInfo or GetSpecializationInfo
|
|
|
|
LIB_OPEN_RAID_CAN_LOAD = false
|
|
|
|
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 = 163
|
|
|
|
if (LIB_OPEN_RAID_MAX_VERSION) then
|
|
if (CONST_LIB_VERSION <= LIB_OPEN_RAID_MAX_VERSION) then
|
|
return
|
|
end
|
|
end
|
|
|
|
--declare 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
|
|
LIB_OPEN_RAID_MAX_VERSION = CONST_LIB_VERSION
|
|
|
|
--locals
|
|
local unpack = table.unpack or _G.unpack
|
|
|
|
openRaidLib.__errors = {} --/dump LibStub:GetLibrary("LibOpenRaid-1.0").__errors
|
|
|
|
--default values
|
|
openRaidLib.inGroup = false
|
|
openRaidLib.UnitIDCache = {}
|
|
|
|
openRaidLib.Util = openRaidLib.Util or {}
|
|
|
|
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_PREFIX_LOGGED = "LRS_LOGGED"
|
|
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_KEYSTONE_DATA_PREFIX = "K"
|
|
local CONST_COMM_KEYSTONE_DATAREQUEST_PREFIX = "J"
|
|
|
|
local CONST_COMM_OPENNOTES_RECEIVED_PREFIX = "N" --when a note is received
|
|
local CONST_COMM_OPENNOTES_REQUESTED_PREFIX = "Q" --when received a request to send your note
|
|
|
|
local CONST_COMM_RATING_DATA_PREFIX = "M"
|
|
local CONST_COMM_RATING_DATAREQUEST_PREFIX = "O"
|
|
|
|
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_THREE_SECONDS --seconds between cooldown checks (ticker time)
|
|
local CONST_COOLDOWN_TIMELEFT_HAS_CHANGED = CONST_THREE_SECONDS --time tolerance when checking if the cooldown timeleft has changed
|
|
|
|
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 CONST_USE_DEFAULT_SCHEDULE_TIME = true
|
|
|
|
-- Real throttle is 10 messages per 1 second, but we want to be safe due to fact we dont know when it actually resets
|
|
local CONST_COMM_BURST_BUFFER_COUNT = 9
|
|
|
|
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 = false
|
|
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
|
|
|
|
--set the ticker interval to check if the cooldown has changed
|
|
function openRaidLib.SetCooldownCheckInterval(value)
|
|
CONST_COOLDOWN_CHECK_INTERVAL = value
|
|
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, "0")
|
|
--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, "0")
|
|
--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) > 2) 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 = {
|
|
aceComm = {},
|
|
eventFrame = CreateFrame("frame"),
|
|
}
|
|
|
|
function openRaidLib.commHandler.OnReceiveSafeComm(self, event, prefix, text, channel, sender, target, zoneChannelID, localID, name, instanceID)
|
|
if (prefix == CONST_COMM_PREFIX_LOGGED) 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 commId = 0
|
|
|
|
--verify if this is a safe comm
|
|
local data = ""
|
|
local bIsSafe = event == "CHAT_MSG_ADDON_LOGGED"
|
|
if (bIsSafe) then
|
|
data = text:gsub("%%", "\n")
|
|
--replace the the first ";" found in the data string with a ",", only the first occurence
|
|
data = data:gsub(";", ",", 1)
|
|
--get the commId
|
|
commId = data:match("#([^#]+)$")
|
|
--remove the commId from the data
|
|
data = data:gsub("#([^#]+)$", "")
|
|
--add the commId to the end of the data after a comma
|
|
data = data .. "," .. commId
|
|
|
|
openRaidLib.commHandler.OnReceiveComm(event, CONST_COMM_PREFIX, data, channel, sender, target, zoneChannelID, localID, name, instanceID, true)
|
|
end
|
|
end
|
|
end
|
|
|
|
openRaidLib.commHandler.eventFrame:RegisterEvent("CHAT_MSG_ADDON_LOGGED")
|
|
openRaidLib.commHandler.eventFrame:SetScript("OnEvent", openRaidLib.commHandler.OnReceiveSafeComm)
|
|
|
|
function openRaidLib.commHandler.aceComm.OnReceiveComm(event, prefix, text, channel, sender, target, zoneChannelID, localID, name, instanceID, bIsSafe)
|
|
--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
|
|
|
|
--if this received data is not a safe comm, then decode it
|
|
local data = text
|
|
if (not bIsSafe) then
|
|
local LibDeflate = LibStub:GetLibrary("LibDeflate")
|
|
local dataCompressed = LibDeflate:DecodeForWoWAddonChannel(data)
|
|
data = LibDeflate:DecompressDeflate(dataCompressed)
|
|
end
|
|
|
|
--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 transmitted
|
|
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 this is isn't a rating data comm, check if the lib can receive comms
|
|
if (dataTypePrefix ~= CONST_COMM_RATING_DATA_PREFIX and dataTypePrefix ~= CONST_COMM_RATING_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)
|
|
table.remove(dataAsTable, 1)
|
|
|
|
--trigger callbacks
|
|
for i = 1, #callbackTable do
|
|
callbackTable[i](dataAsTable, sender)
|
|
end
|
|
end
|
|
end
|
|
|
|
local aceComm = LibStub:GetLibrary("AceComm-3.0", true)
|
|
if (aceComm) then
|
|
aceComm:Embed(openRaidLib.commHandler.aceComm)
|
|
openRaidLib.commHandler.aceComm:RegisterComm(CONST_COMM_PREFIX, "OnReceiveComm")
|
|
end
|
|
|
|
|
|
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_KEYSTONE_DATA_PREFIX] = {}, --received keystone data
|
|
[CONST_COMM_KEYSTONE_DATAREQUEST_PREFIX] = {}, --received a request to send keystone data
|
|
[CONST_COMM_OPENNOTES_RECEIVED_PREFIX] = {}, --received notes
|
|
[CONST_COMM_OPENNOTES_REQUESTED_PREFIX] = {}, --requested notes
|
|
[CONST_COMM_RATING_DATA_PREFIX] = {}, --received rating data
|
|
[CONST_COMM_RATING_DATAREQUEST_PREFIX] = {}, --received a request to send rating data
|
|
}
|
|
|
|
function openRaidLib.commHandler.RegisterORComm(prefix, func)
|
|
--the table for the prefix need to be declared at the 'openRaidLib.commHandler.commCallback' table
|
|
table.insert(openRaidLib.commHandler.commCallback[prefix], func)
|
|
end
|
|
|
|
local charactesrPerMessage = 251
|
|
local receivingMsgInParts = {}
|
|
|
|
local debugCommReception = CreateFrame("frame")
|
|
debugCommReception:RegisterEvent("CHAT_MSG_ADDON_LOGGED")
|
|
debugCommReception:SetScript("OnEvent", function(self, event, prefix, text, channel, sender, target, zoneChannelID, localID, name, instanceID)
|
|
if (prefix == CONST_COMM_PREFIX_LOGGED) then
|
|
local chunkNumber, totalChunks, data = text:match("^%$(%d+)%$(%d+)(.*)")
|
|
local onlyData = text:match("^(.*)")
|
|
|
|
if (not chunkNumber and not totalChunks and onlyData) then
|
|
openRaidLib.commHandler.OnReceiveComm(self, "CHAT_MSG_ADDON_LOGGED", CONST_COMM_PREFIX, onlyData, channel, sender, target, zoneChannelID, localID, name, instanceID)
|
|
|
|
elseif (chunkNumber and totalChunks and data) then
|
|
chunkNumber = tonumber(chunkNumber)
|
|
totalChunks = tonumber(totalChunks)
|
|
|
|
if (chunkNumber and totalChunks) then
|
|
if (chunkNumber <= totalChunks and chunkNumber >= 1) then
|
|
if (not receivingMsgInParts[sender]) then
|
|
local parts = {}
|
|
for i = 1, totalChunks do
|
|
parts[i] = false
|
|
end
|
|
receivingMsgInParts[sender] = {
|
|
totalChunks = totalChunks,
|
|
chunks = parts
|
|
}
|
|
end
|
|
|
|
receivingMsgInParts[sender].chunks[chunkNumber] = data
|
|
|
|
--verify if all parts were received
|
|
local allPartsReceived = true
|
|
for i = 1, totalChunks do
|
|
if (not receivingMsgInParts[sender].chunks[i]) then
|
|
allPartsReceived = false
|
|
break
|
|
end
|
|
end
|
|
|
|
if (allPartsReceived) then
|
|
local fullData = ""
|
|
--sew the parts together
|
|
for i = 1, totalChunks do
|
|
fullData = fullData .. receivingMsgInParts[sender].chunks[i]
|
|
end
|
|
|
|
receivingMsgInParts[sender] = nil
|
|
openRaidLib.commHandler.OnReceiveComm(self, "CHAT_MSG_ADDON_LOGGED", CONST_COMM_PREFIX, fullData, channel, sender, target, zoneChannelID, localID, name, instanceID)
|
|
end
|
|
end
|
|
end
|
|
else
|
|
openRaidLib.DiagnosticError("Logged comm in parts missing information, sender:", sender, "chunkNumber:", chunkNumber, "totalChunks:", totalChunks, "data:", type(data))
|
|
end
|
|
end
|
|
end)
|
|
|
|
--@flags
|
|
--0x1: to party
|
|
--0x2: to raid
|
|
--0x4: to guild
|
|
local sendData = function(dataEncoded, channel, bIsSafe, plainText)
|
|
local aceComm = LibStub:GetLibrary("AceComm-3.0", true)
|
|
if (aceComm) then
|
|
if (bIsSafe) then
|
|
plainText = plainText:gsub("\n", "%%")
|
|
plainText = plainText:gsub(",", ";")
|
|
|
|
local commId = tostring(GetServerTime() + GetTime())
|
|
plainText = plainText .. "#" .. commId
|
|
|
|
if (plainText:len() > 255) then
|
|
local totalMessages = math.ceil(plainText:len() / charactesrPerMessage)
|
|
for i = 1, totalMessages do
|
|
local chunk = plainText:sub((i - 1) * charactesrPerMessage + 1, i * charactesrPerMessage)
|
|
local chunkNumberAndTotalChuncks = "$" .. i .. "$" .. totalMessages
|
|
local chunkMessage = chunkNumberAndTotalChuncks .. chunk
|
|
ChatThrottleLib:SendAddonMessageLogged("NORMAL", CONST_COMM_PREFIX_LOGGED, chunkMessage, channel)
|
|
end
|
|
else
|
|
ChatThrottleLib:SendAddonMessageLogged("NORMAL", CONST_COMM_PREFIX_LOGGED, plainText, channel)
|
|
end
|
|
else
|
|
aceComm:SendCommMessage(CONST_COMM_PREFIX, dataEncoded, channel, nil, "ALERT")
|
|
end
|
|
else
|
|
C_ChatInfo.SendAddonMessage(CONST_COMM_PREFIX, dataEncoded, channel)
|
|
end
|
|
end
|
|
|
|
if (C_ChatInfo) then
|
|
C_ChatInfo.RegisterAddonMessagePrefix(CONST_COMM_PREFIX_LOGGED)
|
|
else
|
|
RegisterAddonMessagePrefix(CONST_COMM_PREFIX_LOGGED)
|
|
end
|
|
|
|
---@class commdata : table
|
|
---@field data string
|
|
---@field channel string
|
|
---@field bIsSafe boolean
|
|
---@field plainText string
|
|
|
|
---@type {}[]
|
|
local commScheduler = {};
|
|
|
|
local commBurstBufferCount = CONST_COMM_BURST_BUFFER_COUNT;
|
|
local commServerTimeLastThrottleUpdate = GetServerTime();
|
|
|
|
do
|
|
--if there's an old version that already registered the comm ticker, cancel it
|
|
if (LIB_OPEN_RAID_COMM_SCHEDULER) then
|
|
LIB_OPEN_RAID_COMM_SCHEDULER:Cancel();
|
|
end
|
|
|
|
local newTickerHandle = C_Timer.NewTicker(0.05, function()
|
|
local serverTime = GetServerTime();
|
|
|
|
-- Replenish the counter if last server time is not the same as the last throttle update
|
|
-- Clamp it to CONST_COMM_BURST_BUFFER_COUNT
|
|
commBurstBufferCount = math.min((serverTime ~= commServerTimeLastThrottleUpdate) and commBurstBufferCount + 1 or commBurstBufferCount, CONST_COMM_BURST_BUFFER_COUNT);
|
|
commServerTimeLastThrottleUpdate = serverTime;
|
|
|
|
-- while (anything in queue) and (throttle allows it)
|
|
while(#commScheduler > 0 and commBurstBufferCount > 0) do
|
|
-- FIFO queue
|
|
---@type commdata
|
|
local commData = table.remove(commScheduler, 1);
|
|
sendData(commData.data, commData.channel, commData.bIsSafe, commData.plainText);
|
|
commBurstBufferCount = commBurstBufferCount - 1;
|
|
end
|
|
end);
|
|
|
|
LIB_OPEN_RAID_COMM_SCHEDULER = newTickerHandle
|
|
end
|
|
|
|
function openRaidLib.commHandler.SendCommData(data, flags, bIsSafe)
|
|
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
|
|
---@type commdata
|
|
local commData = {data = dataEncoded, channel = IsInGroup(LE_PARTY_CATEGORY_INSTANCE) and "INSTANCE_CHAT" or "PARTY", bIsSafe = bIsSafe, plainText = data}
|
|
table.insert(commScheduler, commData)
|
|
end
|
|
end
|
|
|
|
if (bit.band(flags, CONST_COMM_SENDTO_RAID)) then --send to raid
|
|
if (IsInRaid()) then
|
|
local commData = {data = dataEncoded, channel = IsInRaid(LE_PARTY_CATEGORY_INSTANCE) and "INSTANCE_CHAT" or "RAID", bIsSafe = bIsSafe, plainText = data}
|
|
table.insert(commScheduler, commData)
|
|
end
|
|
end
|
|
|
|
if (bit.band(flags, CONST_COMM_SENDTO_GUILD)) then --send to guild
|
|
if (IsInGuild()) then
|
|
--Guild has no 10 msg restriction so send it directly
|
|
sendData(dataEncoded, "GUILD");
|
|
end
|
|
end
|
|
else
|
|
if (IsInGroup() and not IsInRaid()) then --in party only
|
|
local commData = {data = dataEncoded, channel = IsInGroup(LE_PARTY_CATEGORY_INSTANCE) and "INSTANCE_CHAT" or "PARTY", bIsSafe = bIsSafe, plainText = data}
|
|
table.insert(commScheduler, commData)
|
|
|
|
elseif (IsInRaid()) then
|
|
local commData = {data = dataEncoded, channel = IsInRaid(LE_PARTY_CATEGORY_INSTANCE) and "INSTANCE_CHAT" or "RAID", bIsSafe = bIsSafe, plainText = data}
|
|
table.insert(commScheduler, commData)
|
|
end
|
|
end
|
|
end
|
|
|
|
--------------------------------------------------------------------------------------------------------------------------------
|
|
--~schedule ~timers
|
|
---@type table<schedulename, number>
|
|
local defaultScheduleCooldownTimeByScheduleName = {
|
|
["sendAllPlayerCooldownsFromTalentChange_Schedule"] = 2,
|
|
["talentChangedCallback_Schedule"] = 20,
|
|
["sendFullData_Schedule"] = 25,
|
|
["sendAllPlayerCooldowns_Schedule"] = 23,
|
|
["sendDurability_Schedule"] = 10,
|
|
["sendAllGearInfo_Schedule"] = 20,
|
|
["petStatus_Schedule"] = 8,
|
|
["updatePlayerData_Schedule"] = 22,
|
|
["sendTalent_Schedule"] = 20,
|
|
["sendPvPTalent_Schedule"] = 14,
|
|
["leaveCombat_Schedule"] = 18,
|
|
["encounterEndCooldownsCheck_Schedule"] = 24,
|
|
--["sendKeystoneInfoToParty_Schedule"] = 2,
|
|
--["sendKeystoneInfoToGuild_Schedule"] = 2,
|
|
}
|
|
|
|
openRaidLib.Schedules = {
|
|
registeredUniqueTimers = {}
|
|
}
|
|
|
|
local timersCanRunWithoutGroup = {
|
|
["mainControl"] = {
|
|
["updatePlayerData_Schedule"] = true
|
|
}
|
|
}
|
|
|
|
--run a scheduled function with its payload
|
|
local triggerScheduledTick = function(tickerObject)
|
|
local payload = tickerObject.payload
|
|
local callback = tickerObject.callback
|
|
local bCanRunWithoutGroup = tickerObject.bCanRunWithoutGroup
|
|
|
|
if (tickerObject.isUnique) then
|
|
local namespace = tickerObject.namespace
|
|
local scheduleName = tickerObject.scheduleName
|
|
openRaidLib.Schedules.CancelUniqueTimer(namespace, scheduleName)
|
|
end
|
|
|
|
--check if the player is still in group
|
|
if (not openRaidLib.IsInGroup()) then
|
|
if (not bCanRunWithoutGroup) then
|
|
return
|
|
end
|
|
end
|
|
|
|
local result, errortext = xpcall(callback, geterrorhandler(), unpack(payload))
|
|
--local result, errortext = pcall(callback, 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, bCanRunWithoutGroup, ...)
|
|
local payload = {...}
|
|
local newTimer = C_Timer.NewTimer(time, triggerScheduledTick)
|
|
newTimer.bCanRunWithoutGroup = bCanRunWithoutGroup
|
|
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 ~unique
|
|
function openRaidLib.Schedules.NewUniqueTimer(time, callback, namespace, scheduleName, ...)
|
|
--the the schedule uses a default time, get it from the table, if the timer already exists, quit
|
|
if (time == CONST_USE_DEFAULT_SCHEDULE_TIME) then
|
|
if (openRaidLib.Schedules.IsUniqueTimerOnCooldown(namespace, scheduleName)) then
|
|
return
|
|
end
|
|
time = defaultScheduleCooldownTimeByScheduleName[scheduleName] or time
|
|
else
|
|
openRaidLib.Schedules.CancelUniqueTimer(namespace, scheduleName)
|
|
end
|
|
|
|
local bCanRunWithoutGroup = timersCanRunWithoutGroup[namespace] and timersCanRunWithoutGroup[namespace][scheduleName]
|
|
|
|
local newTimer = openRaidLib.Schedules.NewTimer(time, callback, bCanRunWithoutGroup, ...)
|
|
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
|
|
|
|
--does timer by schedule name exists?
|
|
function openRaidLib.Schedules.IsUniqueTimerOnCooldown(namespace, scheduleName)
|
|
local registeredUniqueTimers = openRaidLib.Schedules.registeredUniqueTimers
|
|
local currentSchedule = registeredUniqueTimers[namespace] and registeredUniqueTimers[namespace][scheduleName]
|
|
|
|
if (currentSchedule) then
|
|
return true
|
|
end
|
|
return false
|
|
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", --deprecated
|
|
"PvPTalentUpdate", --deprecated
|
|
"KeystoneUpdate",
|
|
"KeystoneWipe",
|
|
"NoteUpdated",
|
|
"RatingUpdate",
|
|
"RatingWipe"
|
|
}
|
|
|
|
--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
|
|
table.remove(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
|
|
table.remove(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(4, 8), 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)) --unpack: specId, talentsString, pvpTalentsTableUnpacked
|
|
|
|
--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.RegisterORComm(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.RegisterORComm(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.RegisterORComm(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 ~unitinfo
|
|
|
|
---@class unitinfomanager : table
|
|
---@field Version number
|
|
---@field UnitData table<string, unitinfodata>
|
|
---@field GetAllUnitsInfo fun():table<string, unitinfodata>
|
|
---@field GetUnitInfo fun(unitId:string, createNew:boolean?):unitinfodata
|
|
---@field EraseData fun()
|
|
---@field SetUnitInfo fun(unitName:string, unitInfo:unitinfodata, specId:number, talentsString:string, pvpTalentsTableUnpacked:table)
|
|
---@field UpdateUnitInfo fun(unitName:string, specId:number?, talentsString:string?, pvpTalentsTableUnpacked:table?)
|
|
---@field AddUnitInfo fun(unitName:string, specId:number, talentsString:string, pvpTalentsTableUnpacked:table)
|
|
---@field OnReceiveUnitFullInfo fun(data:table, unitName:string)
|
|
---@field SendAllPlayerInfo fun()
|
|
---@field GetPlayerFullInfo fun():unitinfocomm
|
|
---@field GetPlayerPvPTalents fun():table<number, number, number>
|
|
---@field OnPlayerTalentChanged fun()
|
|
---@field OnReceivePvPTalentsUpdate fun(data:table, unitName:string)
|
|
---@field OnPlayerLeaveGroup fun()
|
|
---@field SendPlayerInfoAfterCombat fun()
|
|
---@field OnLeaveCombat fun()
|
|
|
|
---@class unitinfodata : table
|
|
---@field specId number
|
|
---@field specName string
|
|
---@field heroTalentId number
|
|
---@field role string
|
|
---@field talents string
|
|
---@field pvpTalents table<number, number, number>
|
|
---@field class string
|
|
---@field classId number
|
|
---@field className string
|
|
---@field name string
|
|
---@field nameFull string
|
|
|
|
---@class unitinfocomm : table
|
|
---@field [1] number specId
|
|
---@field [2] string talents
|
|
---@field [3] table<number, number, number> pvpTalents
|
|
|
|
--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
|
|
---@type unitinfomanager
|
|
---@diagnostic disable-next-line: missing-fields
|
|
local UnitInfoManager = {}
|
|
UnitInfoManager.UnitData = {}
|
|
UnitInfoManager.Version = 1
|
|
|
|
openRaidLib.UnitInfoManager = UnitInfoManager
|
|
|
|
---@type unitinfodata
|
|
local unitTablePrototype = {
|
|
specId = 0,
|
|
specName = "",
|
|
heroTalentId = 0,
|
|
role = "",
|
|
talents = "", --export string
|
|
pvpTalents = {}, --should be 3 spellIds
|
|
class = "",
|
|
classId = 0,
|
|
className = "",
|
|
name = "",
|
|
nameFull = "",
|
|
}
|
|
|
|
---@return unitinfodata
|
|
local createNewUnitInfo = function()
|
|
---@type unitinfodata
|
|
---@diagnostic disable-next-line: missing-fields
|
|
local newUnitInfo = {}
|
|
openRaidLib.TCopy(newUnitInfo, unitTablePrototype)
|
|
return newUnitInfo
|
|
end
|
|
|
|
function UnitInfoManager.GetAllUnitsInfo()
|
|
return UnitInfoManager.UnitData
|
|
end
|
|
|
|
--get the unit table or create a new one if 'createNew' is true
|
|
function UnitInfoManager.GetUnitInfo(unitName, createNew)
|
|
---@type unitinfodata
|
|
local unitInfo = UnitInfoManager.UnitData[unitName]
|
|
if (not unitInfo and createNew) then
|
|
unitInfo = createNewUnitInfo()
|
|
UnitInfoManager.UnitData[unitName] = unitInfo
|
|
end
|
|
return unitInfo
|
|
end
|
|
|
|
function UnitInfoManager.EraseData()
|
|
table.wipe(UnitInfoManager.UnitData)
|
|
end
|
|
|
|
---@param unitName string
|
|
---@param unitInfo unitinfodata
|
|
---@param specId number
|
|
---@param talentsString string
|
|
---@param pvpTalentsTableUnpacked table<number, number, number>
|
|
function UnitInfoManager.SetUnitInfo(unitName, unitInfo, specId, talentsString, pvpTalentsTableUnpacked)
|
|
if (not GetSpecializationInfoByID) then --tbc hot fix
|
|
return
|
|
end
|
|
|
|
local specId, specName, specDescription, specIcon, role, classFile, classLocName = 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
|
|
|
|
local talentString, heroTalent = openRaidLib.ParseTalentString(talentsString)
|
|
|
|
unitInfo.specId = specId or unitInfo.specId
|
|
unitInfo.specName = specName or unitInfo.specName
|
|
unitInfo.heroTalentId = heroTalent or unitInfo.heroTalentId
|
|
unitInfo.role = role or "DAMAGER"
|
|
unitInfo.talents = talentString or ""
|
|
unitInfo.pvpTalents = pvpTalentsTableUnpacked or unitInfo.pvpTalents
|
|
unitInfo.class = classString
|
|
unitInfo.classId = classId
|
|
unitInfo.className = className
|
|
unitInfo.name = unitName:gsub(("%-.*"), "")
|
|
unitInfo.nameFull = unitName
|
|
end
|
|
|
|
function UnitInfoManager.AddUnitInfo(unitName, specId, talentsString, pvpTalentsTableUnpacked)
|
|
local unitInfo = UnitInfoManager.GetUnitInfo(unitName, true) --returning nil
|
|
UnitInfoManager.SetUnitInfo(unitName, unitInfo, specId, talentsString, pvpTalentsTableUnpacked)
|
|
openRaidLib.publicCallback.TriggerCallback("UnitInfoUpdate", openRaidLib.GetUnitID(unitName), UnitInfoManager.UnitData[unitName], UnitInfoManager.GetAllUnitsInfo())
|
|
end
|
|
|
|
function UnitInfoManager.UpdateUnitInfo(playerName, specId, talentsString, pvpTalentsTableUnpacked)
|
|
local unitInfo = UnitInfoManager.GetUnitInfo(playerName, true)
|
|
UnitInfoManager.SetUnitInfo(playerName, unitInfo, specId or unitInfo.specId, talentsString or unitInfo.talents, pvpTalentsTableUnpacked or unitInfo.pvpTalents)
|
|
openRaidLib.publicCallback.TriggerCallback("UnitInfoUpdate", openRaidLib.GetUnitID(playerName), UnitInfoManager.UnitData[playerName], UnitInfoManager.GetAllUnitsInfo())
|
|
end
|
|
|
|
function UnitInfoManager.OnReceiveUnitFullInfo(data, unitName)
|
|
--triggered when the lib receives a unit information from another player in the raid
|
|
local specId = tonumber(data[1])
|
|
local talentsString = data[2]
|
|
|
|
local unitInfoVersion = data[7]
|
|
if (type(unitInfoVersion) ~= "string" or not unitInfoVersion:find("!")) then
|
|
openRaidLib.DiagnosticError("UnitInfoManager.OnReceiveUnitFullInfo: invalid version data", unitInfoVersion)
|
|
return
|
|
end
|
|
|
|
local versionNumber = tonumber(unitInfoVersion:match("!(%d+)"))
|
|
if (not versionNumber or versionNumber < UnitInfoManager.Version) then
|
|
openRaidLib.DiagnosticError("UnitInfoManager.OnReceiveUnitFullInfo: invalid version number", versionNumber, UnitInfoManager.Version)
|
|
return
|
|
end
|
|
|
|
--unpack the pvp talents data as a ipairs table
|
|
local pvpTalentsIndex = 3
|
|
local pvpTalentsSize = 3
|
|
local pvpTalentsTableUnpacked = openRaidLib.UnpackTable(data, pvpTalentsIndex, false, false, pvpTalentsSize)
|
|
|
|
--add to the list of players information and also trigger a public callback
|
|
UnitInfoManager.AddUnitInfo(unitName, specId, talentsString, pvpTalentsTableUnpacked)
|
|
end
|
|
openRaidLib.commHandler.RegisterORComm(CONST_COMM_PLAYERINFO_PREFIX, UnitInfoManager.OnReceiveUnitFullInfo)
|
|
|
|
|
|
function UnitInfoManager.SendAllPlayerInfo()
|
|
local playerInfo = UnitInfoManager.GetPlayerFullInfo()
|
|
local dataToSend = CONST_COMM_PLAYERINFO_PREFIX .. ","
|
|
dataToSend = dataToSend .. playerInfo[1] .. "," --spec id
|
|
dataToSend = dataToSend .. playerInfo[2] .. "," --talents string
|
|
dataToSend = dataToSend .. openRaidLib.PackTable(playerInfo[3]) .. ",!1" --player talents pvp
|
|
|
|
--send the data
|
|
openRaidLib.commHandler.SendCommData(dataToSend)
|
|
diagnosticComm("SendGetUnitInfoFullData| " .. dataToSend) --debug
|
|
end
|
|
|
|
--player info format:
|
|
--index 1: number: specId
|
|
--index 2: talents as string
|
|
--index 3: pvp talents as table
|
|
---@return unitinfocomm
|
|
function UnitInfoManager.GetPlayerFullInfo()
|
|
local playerInfo = {}
|
|
|
|
if (isTimewalkWoW()) then
|
|
--indexes: specId, renown, covenant, talent, conduits, pvp talents
|
|
--return a placeholder table
|
|
return {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
|
|
table.insert(playerInfo, specId)
|
|
|
|
--player class-spec talents
|
|
local talentsAsString = openRaidLib.GetDragonFlightTalentsAsString()
|
|
table.insert(playerInfo, talentsAsString)
|
|
|
|
--pvp talents
|
|
local pvpTalents = UnitInfoManager.GetPlayerPvPTalents()
|
|
table.insert(playerInfo, pvpTalents)
|
|
|
|
return playerInfo
|
|
end
|
|
|
|
--/dump LibStub:GetLibrary("LibOpenRaid-1.0", true).UnitInfoManager.GetUnitInfo(UnitName("player"))
|
|
|
|
function UnitInfoManager.OnPlayerTalentChanged()
|
|
--this talent update could be a specialization change, so we need to pass the specId as well
|
|
local playerName = UnitName("player")
|
|
local unitInfo = UnitInfoManager.GetUnitInfo(playerName, true)
|
|
local specId = 0
|
|
|
|
if (getSpecializationVersion() == CONST_SPECIALIZATION_VERSION_MODERN) then
|
|
local selectedSpecialization = GetSpecialization()
|
|
if (selectedSpecialization) then
|
|
specId = GetSpecializationInfo(selectedSpecialization) or 0
|
|
end
|
|
end
|
|
|
|
UnitInfoManager.SetUnitInfo(playerName, unitInfo, specId, openRaidLib.GetDragonFlightTalentsAsString(), UnitInfoManager.GetPlayerPvPTalents())
|
|
|
|
--trigger public callback event
|
|
openRaidLib.publicCallback.TriggerCallback("TalentUpdate", "player", unitInfo.talents, unitInfo, UnitInfoManager.GetAllUnitsInfo())
|
|
|
|
--schedule send to the group
|
|
openRaidLib.Schedules.NewUniqueTimer(2 + math.random(0, 1), UnitInfoManager.SendAllPlayerInfo, "UnitInfoManager", "sendAllPlayerInfo_Schedule")
|
|
end
|
|
openRaidLib.internalCallback.RegisterCallback("talentUpdate", UnitInfoManager.OnPlayerTalentChanged)
|
|
|
|
function UnitInfoManager.OnPlayerLeaveGroup()
|
|
local unitName = UnitName("player")
|
|
--clear the data
|
|
UnitInfoManager.EraseData()
|
|
|
|
--trigger a public callback
|
|
openRaidLib.publicCallback.TriggerCallback("UnitInfoWipe", UnitInfoManager.UnitData)
|
|
|
|
--need to build the player info again
|
|
local playerFullInfo = UnitInfoManager.GetPlayerFullInfo()
|
|
UnitInfoManager.AddUnitInfo(unitName, unpack(playerFullInfo))
|
|
end
|
|
openRaidLib.internalCallback.RegisterCallback("onLeaveGroup", UnitInfoManager.OnPlayerLeaveGroup)
|
|
|
|
--send data when leaving combat
|
|
function UnitInfoManager.SendPlayerInfoAfterCombat()
|
|
openRaidLib.Schedules.NewUniqueTimer(2 + math.random(0, 1), UnitInfoManager.SendAllPlayerInfo, "UnitInfoManager", "sendAllPlayerInfo_Schedule")
|
|
end
|
|
|
|
function UnitInfoManager.OnLeaveCombat()
|
|
openRaidLib.Schedules.NewUniqueTimer(1 + math.random(1, 4), UnitInfoManager.SendPlayerInfoAfterCombat, "UnitInfoManager", "leaveCombat_Schedule")
|
|
end
|
|
openRaidLib.internalCallback.RegisterCallback("onLeaveCombat", UnitInfoManager.OnLeaveCombat)
|
|
|
|
|
|
--------------------------------------------------------------------------------------------------------------------------------
|
|
--~equipment ~gear
|
|
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.RegisterORComm(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, lowestItemDurability = 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
|
|
|
|
local tierAmount = 0
|
|
|
|
for i = 1, #equippedGearList do
|
|
if (equippedGearList[i].isTier) then
|
|
tierAmount = tierAmount + 1
|
|
end
|
|
end
|
|
|
|
unitGearInfo.tierAmount = tierAmount
|
|
|
|
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 gems 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.RegisterORComm(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
|
|
dataToSend = dataToSend .. playerGearInfo[7] .. "," --main hand weapon enchant
|
|
dataToSend = dataToSend .. playerGearInfo[8] --off hand weapon enchant
|
|
|
|
--send the data
|
|
openRaidLib.commHandler.SendCommData(dataToSend)
|
|
diagnosticComm("SendGearFullData| " .. dataToSend) --debug
|
|
end
|
|
|
|
|
|
--------------------------------------------------------------------------------------------------------------------------------
|
|
--~open ~notes ~opennotes
|
|
|
|
---type and prototype for the note system, when adding or removeing fields, this is the only place to change
|
|
|
|
---@class noteinfo : table
|
|
---@field note string
|
|
---@field commId string
|
|
|
|
---@type noteinfo
|
|
local notePrototype = {
|
|
note = "",
|
|
commId = "",
|
|
}
|
|
|
|
openRaidLib.OpenNotesManager = {
|
|
--structure: [playerName] = {note = "note text", lastUpdate = 0}
|
|
---@type table<string, noteinfo>
|
|
UnitData = {},
|
|
}
|
|
|
|
--the note context saves the context of when the note was last sent, this is to avoid the player sending a note used on other dungeon or group when the a note is request
|
|
local noteContext = {
|
|
mapId = 0,
|
|
difficultyId = 0,
|
|
instanceType = "none",
|
|
---@type table<string, boolean>
|
|
groupMembers = {},
|
|
time = 0,
|
|
}
|
|
|
|
local checkContext = function()
|
|
if (noteContext.time == 0) then
|
|
return false
|
|
end
|
|
|
|
local name, instanceType, difficultyID, difficultyName, maxPlayers, dynamicDifficulty, isDynamic, instanceID, instanceGroupSize, LfgDungeonID = GetInstanceInfo()
|
|
|
|
if (noteContext.mapId ~= instanceID or noteContext.difficultyId ~= difficultyID or noteContext.instanceType ~= instanceType) then
|
|
return false
|
|
end
|
|
|
|
--if the note context time is more than 25 minutes ago, ignore
|
|
if (time() - noteContext.time > 1500) then
|
|
return false
|
|
end
|
|
|
|
--check if the group members are the same
|
|
local groupMembers = openRaidLib.GetPlayersInTheGroup()
|
|
for unitName in pairs(noteContext.groupMembers) do
|
|
if (not groupMembers[unitName]) then
|
|
return false
|
|
end
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
--API notes
|
|
---return the table where the notes are stored, format: [playerName] = {note = "note text", lastUpdate = time()}
|
|
---can return an empty table if no unit sent note yet
|
|
---@return table<string, noteinfo>
|
|
function openRaidLib.GetAllUnitsNotes()
|
|
return openRaidLib.OpenNotesManager.GetAllUnitsNotes()
|
|
end
|
|
|
|
---return information about a note for a unit, return value is a table of type noteinfo, see the type declaration to know the fields
|
|
---always return values, if the note does not exist it'll return an empty string and 0
|
|
---@param unitId string
|
|
---@return noteinfo
|
|
function openRaidLib.GetUnitNote(unitId)
|
|
---@type string
|
|
local unitName = GetUnitName(unitId, true) or unitId
|
|
---@type noteinfo
|
|
local noteInfo = openRaidLib.OpenNotesManager.GetUnitNote(unitName)
|
|
return noteInfo
|
|
end
|
|
|
|
---set a note for the player
|
|
---@param note string
|
|
function openRaidLib.SetPlayerNote(note)
|
|
assert(type(note) == "string", "OpenRaid: SetPlayerNote(#1) expect a string.")
|
|
assert(note:len() <= 1500, "OpenRaid: SetPlayerNote(#1) too long.")
|
|
openRaidLib.OpenNotesManager.SetUnitNote(UnitName("player"), note, "")
|
|
end
|
|
|
|
---send the player note to the group
|
|
function openRaidLib.SendPlayerNote()
|
|
openRaidLib.OpenNotesManager.SendNote()
|
|
end
|
|
|
|
|
|
|
|
--INTERNAL notes
|
|
function openRaidLib.OpenNotesManager.GetAllUnitsNotes()
|
|
return openRaidLib.OpenNotesManager.UnitData
|
|
end
|
|
|
|
---get a unit note, if it does not exist, create a new one
|
|
---@param unitName string
|
|
---@return noteinfo
|
|
function openRaidLib.OpenNotesManager.GetUnitNote(unitName)
|
|
local unitNote = openRaidLib.OpenNotesManager.UnitData[unitName]
|
|
|
|
if (not unitNote) then
|
|
local newNote = {}
|
|
openRaidLib.TCopy(newNote, notePrototype)
|
|
openRaidLib.OpenNotesManager.UnitData[unitName] = newNote
|
|
unitNote = newNote
|
|
end
|
|
|
|
return unitNote
|
|
end
|
|
|
|
---set a note of a unit, this do not send the note yet, just store it
|
|
---@param unitName string
|
|
---@param note string
|
|
---@param commId string
|
|
function openRaidLib.OpenNotesManager.SetUnitNote(unitName, note, commId)
|
|
local unitNote = openRaidLib.OpenNotesManager.GetUnitNote(unitName)
|
|
unitNote.note = note
|
|
unitNote.commId = commId
|
|
end
|
|
|
|
---clear all data stored
|
|
function openRaidLib.OpenNotesManager.EraseData()
|
|
table.wipe(openRaidLib.OpenNotesManager.UnitData)
|
|
|
|
--create a note for the local player
|
|
local playerName = UnitName("player")
|
|
openRaidLib.OpenNotesManager.GetUnitNote(playerName)
|
|
end
|
|
|
|
---clear all data except the local player
|
|
function openRaidLib.OpenNotesManager.EraseDataKeepPlayer()
|
|
local playerName = UnitName("player")
|
|
local localNote = openRaidLib.OpenNotesManager.UnitData[playerName]
|
|
table.wipe(openRaidLib.OpenNotesManager.UnitData)
|
|
openRaidLib.OpenNotesManager.UnitData[playerName] = localNote
|
|
end
|
|
|
|
function openRaidLib.OpenNotesManager.OnPlayerEnterWorld()
|
|
--call erase data hence create a note for the local player
|
|
openRaidLib.OpenNotesManager.EraseData()
|
|
end
|
|
openRaidLib.internalCallback.RegisterCallback("onEnterWorld", openRaidLib.OpenNotesManager.OnPlayerEnterWorld)
|
|
|
|
function openRaidLib.OpenNotesManager.OnReceiveNoteData(data, unitName)
|
|
---@type string
|
|
local note = data[1]
|
|
local commId = data[2]
|
|
|
|
if (note and type(note) == "string" and commId and type(commId) == "string") then
|
|
openRaidLib.OpenNotesManager.SetUnitNote(unitName, note, commId)
|
|
---@type noteinfo
|
|
local unitNote = openRaidLib.OpenNotesManager.GetUnitNote(unitName)
|
|
--trigger public callback
|
|
openRaidLib.publicCallback.TriggerCallback("NoteUpdated", openRaidLib.GetUnitID(unitName), unitNote, openRaidLib.OpenNotesManager.GetAllUnitsNotes())
|
|
end
|
|
end
|
|
openRaidLib.commHandler.RegisterORComm(CONST_COMM_OPENNOTES_RECEIVED_PREFIX, openRaidLib.OpenNotesManager.OnReceiveNoteData)
|
|
|
|
local timeOfLastNoteSent = 0
|
|
function openRaidLib.OpenNotesManager.SendNote()
|
|
local name, instanceType, difficultyID, difficultyName, maxPlayers, dynamicDifficulty, isDynamic, instanceID, instanceGroupSize, LfgDungeonID = GetInstanceInfo()
|
|
|
|
--deny if not in group or if the player is in open world
|
|
if (instanceType == "none") then
|
|
--return
|
|
elseif (not openRaidLib.IsInGroup()) then
|
|
return
|
|
end
|
|
--ACTIVE_DELVE_DATA_UPDATE
|
|
---@type noteinfo
|
|
local playerNote = openRaidLib.OpenNotesManager.GetUnitNote(UnitName("player"))
|
|
if (type(playerNote) == "table" and playerNote.note) then
|
|
assert(type(playerNote.note) == "string", "OpenRaid: SendNote() invalid note.")
|
|
assert(playerNote.note:len() <= 1500, "OpenRaid: SendNote() note too long.")
|
|
assert(playerNote.note:len() >= 50, "OpenRaid: SendNote() note too short.")
|
|
|
|
local dataToSend = "" .. CONST_COMM_OPENNOTES_RECEIVED_PREFIX .. "," .. playerNote.note
|
|
|
|
local sendFunc = function()
|
|
local flags = nil
|
|
local bIsSafe = true
|
|
openRaidLib.commHandler.SendCommData(dataToSend, flags, bIsSafe)
|
|
diagnosticComm("SendAllNotesData| " .. dataToSend) --debug
|
|
end
|
|
|
|
if (timeOfLastNoteSent+5 > time()) then
|
|
openRaidLib.Schedules.NewUniqueTimer(2 + math.random(0, 2) + math.random(), sendFunc, "OpenNotesManager", "sendNoteInfo_Schedule")
|
|
else
|
|
openRaidLib.Schedules.NewUniqueTimer(0.1, sendFunc, "OpenNotesManager", "sendNoteInfo_Schedule")
|
|
end
|
|
|
|
timeOfLastNoteSent = time()
|
|
|
|
noteContext.time = time()
|
|
noteContext.mapId = instanceID
|
|
noteContext.difficultyId = difficultyID
|
|
noteContext.instanceType = instanceType
|
|
noteContext.groupMembers = openRaidLib.GetPlayersInTheGroup()
|
|
end
|
|
end
|
|
|
|
function openRaidLib.OpenNotesManager.OnReceiveNoteRequest()
|
|
---@type noteinfo
|
|
local playerNote = openRaidLib.OpenNotesManager.GetUnitNote(UnitName("player"))
|
|
|
|
--check if there is text in the note
|
|
if (playerNote and playerNote.note and playerNote.note:len() >= 50) then
|
|
--check if the context is the same
|
|
if (not checkContext()) then
|
|
return
|
|
end
|
|
openRaidLib.Schedules.NewUniqueTimer(2 + math.random(0, 2) + math.random(), openRaidLib.OpenNotesManager.SendNote, "OpenNotesManager", "sendNoteInfo_Schedule")
|
|
end
|
|
end
|
|
openRaidLib.commHandler.RegisterORComm(CONST_COMM_OPENNOTES_REQUESTED_PREFIX, openRaidLib.OpenNotesManager.OnReceiveNoteRequest)
|
|
|
|
--------------------------------------------------------------------------------------------------------------------------------
|
|
--~cooldowns ~cooldown
|
|
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 - tickerObject.tickInterval
|
|
local timeLeft, charges, startTimeOffset, duration, auraDuration = openRaidLib.CooldownManager.GetPlayerCooldownStatus(spellId)
|
|
|
|
local bUpdateLocally = false
|
|
|
|
--is the spell ready to use?, 0.5 seconds for latency compensation
|
|
if (timeLeft <= tickerObject.latencyCompensation) 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
|
|
|
|
local tickInterval = CONST_COOLDOWN_CHECK_INTERVAL
|
|
local maxTicks = ceil(cooldownTimeLeft / tickInterval)
|
|
local latencyCompensation = 0
|
|
|
|
local cooldownOptions = LIB_OPEN_RAID_COOLDOWNS_CONFIG and LIB_OPEN_RAID_COOLDOWNS_CONFIG[spellId]
|
|
if (cooldownOptions) then
|
|
if (cooldownOptions.tickInterval) then
|
|
tickInterval = cooldownOptions.tickInterval
|
|
maxTicks = ceil(cooldownTimeLeft / tickInterval)
|
|
end
|
|
if (cooldownOptions.latencyCompensation) then
|
|
latencyCompensation = cooldownOptions.latencyCompensation
|
|
end
|
|
end
|
|
|
|
--create a new ticker
|
|
local newTicker = C_Timer.NewTicker(tickInterval, cooldownTimeLeftCheck_Ticker, maxTicks)
|
|
|
|
--store the ticker
|
|
openRaidLib.CooldownManager.CooldownTickers[spellId] = newTicker
|
|
newTicker.tickInterval = tickInterval
|
|
newTicker.latencyCompensation = latencyCompensation
|
|
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, auraDuration}
|
|
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.RegisterORComm(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
|
|
--before triggering the cooldown, check if the player has the spell
|
|
if (cooldownGetSpellInfo(playerName, thisSpellId)) then --won't have BOP because it's not talented
|
|
local spellInfo = C_Spell.GetSpellInfo(thisSpellId)
|
|
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
|
|
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", "sendAllPlayerCooldownsFromTalentChange_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.RegisterORComm(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]
|
|
|
|
if (not currentCooldowns) then
|
|
--generate the list of cooldowns for the player
|
|
openRaidLib.CooldownManager.UpdatePlayerCooldownsLocally()
|
|
currentCooldowns = openRaidLib.CooldownManager.UnitData[playerName]
|
|
end
|
|
|
|
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.RegisterORComm(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
|
|
--before triggering the cooldown, check if the player has the spell
|
|
if (cooldownGetSpellInfo(unitName, thisSpellId)) then
|
|
local spellInfo = C_Spell.GetSpellInfo(thisSpellId)
|
|
|
|
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
|
|
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)
|
|
|
|
--[=[ --debug for data received from Evokers
|
|
local _, class = UnitClass(unitName)
|
|
if (class == "EVOKER") then
|
|
print(unitName)
|
|
dumpt(unpackedTable)
|
|
end
|
|
--]=]
|
|
|
|
--add the list of cooldowns
|
|
openRaidLib.CooldownManager.AddUnitCooldownsList(unitName, unpackedTable)
|
|
end
|
|
openRaidLib.commHandler.RegisterORComm(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.RegisterORComm(CONST_COMM_COOLDOWNREQUEST_PREFIX, openRaidLib.CooldownManager.OnReceiveRequestForCooldownInfoUpdate)
|
|
|
|
--------------------------------------------------------------------------------------------------------------------------------
|
|
--~keystones
|
|
|
|
---@class keystoneinfo
|
|
---@field level number
|
|
---@field mapID number
|
|
---@field challengeMapID number
|
|
---@field classID number
|
|
---@field rating number
|
|
---@field mythicPlusMapID number
|
|
|
|
--manager constructor
|
|
openRaidLib.KeystoneInfoManager = {
|
|
--structure:
|
|
--[playerName] = keystoneinfo
|
|
---@type table<string, keystoneinfo>
|
|
KeystoneData = {},
|
|
}
|
|
|
|
--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
|
|
|
|
local checkForKeystoneChange = function()
|
|
--clear the timer reference
|
|
openRaidLib.KeystoneInfoManager.KeystoneChangedTimer = nil
|
|
|
|
--check if the player has a keystone in the backpack by quering the keystone level
|
|
local level = C_MythicPlus.GetOwnedKeystoneLevel()
|
|
if (not level) then
|
|
return
|
|
end
|
|
local mapID = C_MythicPlus.GetOwnedKeystoneMapID()
|
|
|
|
--get the current player keystone info and then compare with the keystone info from the bag, if there is differences update the player keystone info
|
|
local unitName = UnitName("player")
|
|
---@type keystoneinfo
|
|
local keystoneInfo = openRaidLib.KeystoneInfoManager.GetKeystoneInfo(unitName, true)
|
|
|
|
if (keystoneInfo.level ~= level or keystoneInfo.mapID ~= mapID) then
|
|
openRaidLib.KeystoneInfoManager.UpdatePlayerKeystoneInfo(keystoneInfo)
|
|
--hack: trigger a received data request to send data to party and guild when logging in
|
|
openRaidLib.KeystoneInfoManager.OnReceiveRequestData()
|
|
end
|
|
end
|
|
|
|
local bagUpdateEventFrame = _G["OpenRaidBagUpdateFrame"] or CreateFrame("frame", "OpenRaidBagUpdateFrame")
|
|
bagUpdateEventFrame:RegisterEvent("BAG_UPDATE")
|
|
bagUpdateEventFrame:RegisterEvent("ITEM_CHANGED")
|
|
bagUpdateEventFrame:SetScript("OnEvent", function(bagUpdateEventFrame, event, ...)
|
|
if (openRaidLib.KeystoneInfoManager.KeystoneChangedTimer) then
|
|
return
|
|
else
|
|
openRaidLib.KeystoneInfoManager.KeystoneChangedTimer = C_Timer.NewTimer(2, checkForKeystoneChange)
|
|
end
|
|
end)
|
|
|
|
--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
|
|
|
|
--privite stuff, these function can still be called, but not advised
|
|
|
|
---@type keystoneinfo
|
|
local keystoneTablePrototype = {
|
|
level = 0,
|
|
mapID = 0,
|
|
challengeMapID = 0,
|
|
classID = 0,
|
|
rating = 0,
|
|
mythicPlusMapID = 0,
|
|
}
|
|
|
|
function openRaidLib.KeystoneInfoManager.UpdatePlayerKeystoneInfo(keystoneInfo)
|
|
keystoneInfo.level = C_MythicPlus.GetOwnedKeystoneLevel() or 0
|
|
keystoneInfo.mapID = C_MythicPlus.GetOwnedKeystoneMapID() or 0 --returning nil?
|
|
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)
|
|
|
|
local _, instanceType = GetInstanceInfo()
|
|
if (instanceType == "party") then
|
|
openRaidLib.Schedules.NewUniqueTimer(math.random(1), openRaidLib.KeystoneInfoManager.SendPlayerKeystoneInfoToParty, "KeystoneInfoManager", "sendKeystoneInfoToParty_Schedule")
|
|
|
|
elseif (instanceType == "raid" or instanceType == "pvp") then
|
|
openRaidLib.Schedules.NewUniqueTimer(math.random(0, 30) + math.random(1), openRaidLib.KeystoneInfoManager.SendPlayerKeystoneInfoToParty, "KeystoneInfoManager", "sendKeystoneInfoToParty_Schedule")
|
|
|
|
else
|
|
openRaidLib.Schedules.NewUniqueTimer(math.random(4), openRaidLib.KeystoneInfoManager.SendPlayerKeystoneInfoToParty, "KeystoneInfoManager", "sendKeystoneInfoToParty_Schedule")
|
|
end
|
|
|
|
if (IsInGuild()) then
|
|
openRaidLib.Schedules.NewUniqueTimer(math.random(0, 6) + math.random(), openRaidLib.KeystoneInfoManager.SendPlayerKeystoneInfoToGuild, "KeystoneInfoManager", "sendKeystoneInfoToGuild_Schedule")
|
|
end
|
|
end
|
|
openRaidLib.commHandler.RegisterORComm(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.RegisterORComm(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
|
|
|
|
local keystoneManagerOnPlayerEnterWorld = function()
|
|
--hack: trigger a received data request to send data to party and guild when logging in
|
|
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.OnPlayerEnterWorld()
|
|
--keystones are only available on retail
|
|
if (not checkClientVersion("retail")) then
|
|
return
|
|
end
|
|
|
|
--attempt to load keystone item link as reports indicate it can be nil
|
|
getMythicPlusMapID()
|
|
|
|
C_Timer.After(2, keystoneManagerOnPlayerEnterWorld)
|
|
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)
|
|
|
|
--------------------------------------------------------------------------------------------------------------------------------
|
|
--~rating
|
|
|
|
---@class MythicPlusRatingMapSummary
|
|
---@field challengeModeID number
|
|
---@field mapScore number
|
|
---@field bestRunLevel number
|
|
---@field bestRunDurationMS number
|
|
---@field finishedSuccess boolean
|
|
|
|
---@class ratinginfo
|
|
---@field classID number
|
|
---@field currentSeasonScore number
|
|
---@field runs MythicPlusRatingMapSummary[]
|
|
|
|
--manager constructor
|
|
openRaidLib.RatingInfoManager = {
|
|
--structure:
|
|
--[playerName] = ratinginfo
|
|
---@type table<string, ratinginfo>
|
|
RatingData = {},
|
|
}
|
|
|
|
--API calls
|
|
--return a table containing all information of units
|
|
--format: [playerName-realm] = {information}
|
|
function openRaidLib.GetAllRatingInfo()
|
|
return openRaidLib.RatingInfoManager.GetAllRatingInfo()
|
|
end
|
|
|
|
--return a table containing information of a single unit
|
|
function openRaidLib.GetRatingInfo(unitId)
|
|
local unitName = GetUnitName(unitId, true) or unitId
|
|
return openRaidLib.RatingInfoManager.GetRatingInfo(unitName)
|
|
end
|
|
|
|
function openRaidLib.RequestRatingDataFromGuild()
|
|
if (IsInGuild()) then
|
|
local dataToSend = "" .. CONST_COMM_RATING_DATAREQUEST_PREFIX
|
|
openRaidLib.commHandler.SendCommData(dataToSend, 0x4)
|
|
diagnosticComm("RequestRatingDataFromGuild| " .. dataToSend) --debug
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
function openRaidLib.RequestRatingDataFromParty()
|
|
if (IsInGroup() and not IsInRaid()) then
|
|
local dataToSend = "" .. CONST_COMM_RATING_DATAREQUEST_PREFIX
|
|
openRaidLib.commHandler.SendCommData(dataToSend, 0x1)
|
|
diagnosticComm("RequestRatingDataFromParty| " .. dataToSend) --debug
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
function openRaidLib.RequestRatingDataFromRaid()
|
|
if (IsInRaid()) then
|
|
local dataToSend = "" .. CONST_COMM_RATING_DATAREQUEST_PREFIX
|
|
openRaidLib.commHandler.SendCommData(dataToSend, 0x2)
|
|
diagnosticComm("RequestRatingDataFromRaid| " .. dataToSend) --debug
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
function openRaidLib.WipeRatingData()
|
|
wipe(openRaidLib.RatingInfoManager.RatingData)
|
|
--trigger public callback
|
|
openRaidLib.publicCallback.TriggerCallback("RatingWipe", openRaidLib.RatingInfoManager.RatingData)
|
|
|
|
--rating are only available on retail
|
|
if (not checkClientVersion("retail")) then
|
|
return
|
|
end
|
|
|
|
--generate rating info for the player
|
|
local unitName = UnitName("player")
|
|
local ratingInfo = openRaidLib.RatingInfoManager.GetRatingInfo(unitName, true)
|
|
openRaidLib.RatingInfoManager.UpdatePlayerRatingInfo(ratingInfo)
|
|
|
|
openRaidLib.publicCallback.TriggerCallback("RatingUpdate", unitName, ratingInfo, openRaidLib.RatingInfoManager.RatingData)
|
|
return true
|
|
end
|
|
|
|
--privite stuff, these function can still be called, but not advised
|
|
---@type ratinginfo
|
|
local ratingTablePrototype = {
|
|
classID = 0,
|
|
currentSeasonScore = 0,
|
|
runs = {}
|
|
}
|
|
|
|
function openRaidLib.RatingInfoManager.UpdatePlayerRatingInfo(ratingInfo)
|
|
--- I really just want this whole thing
|
|
local summary = C_PlayerInfo.GetPlayerMythicPlusRatingSummary("player")
|
|
|
|
ratingInfo.currentSeasonScore = summary and summary.currentSeasonScore or 0
|
|
ratingInfo.runs = summary and summary.runs or {}
|
|
|
|
local _, _, playerClassID = UnitClass("player")
|
|
ratingInfo.classID = playerClassID
|
|
end
|
|
|
|
function openRaidLib.RatingInfoManager.GetAllRatingInfo()
|
|
return openRaidLib.RatingInfoManager.RatingData
|
|
end
|
|
|
|
--get the rating info table or create a new one if 'createNew' is true
|
|
function openRaidLib.RatingInfoManager.GetRatingInfo(unitName, createNew)
|
|
local ratingInfo = openRaidLib.RatingInfoManager.RatingData[unitName]
|
|
if (not ratingInfo and createNew) then
|
|
ratingInfo = {}
|
|
openRaidLib.TCopy(ratingInfo, ratingTablePrototype)
|
|
openRaidLib.RatingInfoManager.RatingData[unitName] = ratingInfo
|
|
end
|
|
return ratingInfo
|
|
end
|
|
|
|
local getRatingInfoToComm = function()
|
|
local playerName = UnitName("player")
|
|
local ratingInfo = openRaidLib.RatingInfoManager.GetRatingInfo(playerName, true)
|
|
openRaidLib.RatingInfoManager.UpdatePlayerRatingInfo(ratingInfo)
|
|
|
|
local dataToSend = "" .. CONST_COMM_RATING_DATA_PREFIX .. ","
|
|
|
|
local runs = {}
|
|
for _, runInfo in ipairs(ratingInfo.runs) do
|
|
runs[#runs+1] = {
|
|
runInfo.challengeModeID,
|
|
runInfo.bestRunDurationMS,
|
|
runInfo.finishedSuccess and 1 or 0,
|
|
runInfo.mapScore,
|
|
runInfo.bestRunLevel
|
|
}
|
|
end
|
|
|
|
dataToSend = dataToSend .. ratingInfo.classID .. ","
|
|
dataToSend = dataToSend .. ratingInfo.currentSeasonScore .. ","
|
|
dataToSend = dataToSend .. openRaidLib.PackTableAndSubTables(runs)
|
|
|
|
return dataToSend
|
|
end
|
|
|
|
function openRaidLib.RatingInfoManager.SendPlayerRatingInfoToParty()
|
|
local dataToSend = getRatingInfoToComm()
|
|
openRaidLib.commHandler.SendCommData(dataToSend, CONST_COMM_SENDTO_PARTY)
|
|
diagnosticComm("SendPlayerRatingInfoToParty| " .. dataToSend) --debug
|
|
end
|
|
|
|
function openRaidLib.RatingInfoManager.SendPlayerRatingInfoToGuild()
|
|
local dataToSend = getRatingInfoToComm()
|
|
openRaidLib.commHandler.SendCommData(dataToSend, CONST_COMM_SENDTO_GUILD)
|
|
diagnosticComm("SendPlayerRatingInfoToGuild| " .. 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.RequestRatingDataFromRaid()'
|
|
function openRaidLib.RatingInfoManager.OnReceiveRequestData()
|
|
if (not checkClientVersion("retail")) then
|
|
return
|
|
end
|
|
|
|
--update the information about the key stone the player has
|
|
local ratingInfo = openRaidLib.RatingInfoManager.GetRatingInfo(UnitName("player"), true)
|
|
openRaidLib.RatingInfoManager.UpdatePlayerRatingInfo(ratingInfo)
|
|
|
|
local _, instanceType = GetInstanceInfo()
|
|
if (instanceType == "party") then
|
|
openRaidLib.Schedules.NewUniqueTimer(math.random(1), openRaidLib.RatingInfoManager.SendPlayerRatingInfoToParty, "RatingInfoManager", "sendRatingInfoToParty_Schedule")
|
|
|
|
elseif (instanceType == "raid" or instanceType == "pvp") then
|
|
openRaidLib.Schedules.NewUniqueTimer(math.random(0, 30) + math.random(1), openRaidLib.RatingInfoManager.SendPlayerRatingInfoToParty, "RatingInfoManager", "sendRatingInfoToParty_Schedule")
|
|
|
|
else
|
|
openRaidLib.Schedules.NewUniqueTimer(math.random(4), openRaidLib.RatingInfoManager.SendPlayerRatingInfoToParty, "RatingInfoManager", "sendRatingInfoToParty_Schedule")
|
|
end
|
|
|
|
if (IsInGuild()) then
|
|
openRaidLib.Schedules.NewUniqueTimer(math.random(0, 6) + math.random(), openRaidLib.RatingInfoManager.SendPlayerRatingInfoToGuild, "RatingInfoManager", "sendRatingInfoToGuild_Schedule")
|
|
end
|
|
end
|
|
openRaidLib.commHandler.RegisterORComm(CONST_COMM_RATING_DATAREQUEST_PREFIX, openRaidLib.RatingInfoManager.OnReceiveRequestData)
|
|
|
|
function openRaidLib.RatingInfoManager.OnReceiveRatingData(data, unitName)
|
|
if (not checkClientVersion("retail")) then
|
|
return
|
|
end
|
|
|
|
local classID = tonumber(data[1])
|
|
local currentSeasonScore = tonumber(data[2])
|
|
|
|
local unpackedTable = openRaidLib.UnpackTable(data, 3, false, true, 5) -- 5 is the number of items in the run table
|
|
|
|
local runs = {}
|
|
for _, runInfo in ipairs(unpackedTable) do
|
|
local challengeModeID, bestRunDurationMS, finishedSuccess, mapScore, bestRunLevel = unpack(runInfo)
|
|
|
|
runs[#runs+1] = {
|
|
challengeModeID = challengeModeID,
|
|
bestRunDurationMS = bestRunDurationMS,
|
|
finishedSuccess = finishedSuccess == 1 and true or false,
|
|
mapScore = mapScore,
|
|
bestRunLevel = bestRunLevel
|
|
}
|
|
end
|
|
|
|
local ratingInfo = openRaidLib.RatingInfoManager.GetRatingInfo(unitName, true)
|
|
ratingInfo.classID = classID
|
|
ratingInfo.currentSeasonScore = currentSeasonScore
|
|
ratingInfo.runs = runs
|
|
|
|
--trigger public callback
|
|
openRaidLib.publicCallback.TriggerCallback("RatingUpdate", unitName, ratingInfo, openRaidLib.RatingInfoManager.RatingData)
|
|
end
|
|
openRaidLib.commHandler.RegisterORComm(CONST_COMM_RATING_DATA_PREFIX, openRaidLib.RatingInfoManager.OnReceiveRatingData)
|
|
|
|
--on entering a group, send rating information for the party
|
|
function openRaidLib.RatingInfoManager.OnPlayerEnterGroup()
|
|
--rating is only available on retail
|
|
if (not checkClientVersion("retail")) then
|
|
return
|
|
end
|
|
|
|
if (IsInGroup() and not IsInRaid()) then
|
|
--update the information about the rating the player has
|
|
local ratingInfo = openRaidLib.RatingInfoManager.GetRatingInfo(UnitName("player"), true)
|
|
openRaidLib.RatingInfoManager.UpdatePlayerRatingInfo(ratingInfo)
|
|
|
|
--send to the group what rating the player has
|
|
openRaidLib.Schedules.NewUniqueTimer(1 + math.random(0, 2) + math.random(), openRaidLib.RatingInfoManager.SendPlayerRatingInfoToParty, "RatingInfoManager", "sendRatingInfoToParty_Schedule")
|
|
end
|
|
end
|
|
|
|
local ratingManagerOnPlayerEnterWorld = function()
|
|
--hack: trigger a received data request to send data to party and guild when logging in
|
|
openRaidLib.RatingInfoManager.OnReceiveRequestData()
|
|
|
|
--trigger public callback
|
|
local unitName = UnitName("player")
|
|
local ratingInfo = openRaidLib.RatingInfoManager.GetRatingInfo(unitName, true)
|
|
openRaidLib.RatingInfoManager.UpdatePlayerRatingInfo(ratingInfo)
|
|
|
|
openRaidLib.publicCallback.TriggerCallback("RatingUpdate", unitName, ratingInfo, openRaidLib.RatingInfoManager.RatingData)
|
|
end
|
|
|
|
function openRaidLib.RatingInfoManager.OnPlayerEnterWorld()
|
|
--rating is only available on retail
|
|
if (not checkClientVersion("retail")) then
|
|
return
|
|
end
|
|
|
|
C_Timer.After(2, ratingManagerOnPlayerEnterWorld)
|
|
end
|
|
|
|
function openRaidLib.RatingInfoManager.OnMythicDungeonFinished()
|
|
--rating is only available on retail
|
|
if (not checkClientVersion("retail")) then
|
|
return
|
|
end
|
|
--hack: on received data send data to party and guild
|
|
openRaidLib.RatingInfoManager.OnReceiveRequestData()
|
|
|
|
--trigger public callback
|
|
local unitName = UnitName("player")
|
|
local ratingInfo = openRaidLib.RatingInfoManager.GetRatingInfo(unitName, true)
|
|
openRaidLib.RatingInfoManager.UpdatePlayerRatingInfo(ratingInfo)
|
|
|
|
openRaidLib.publicCallback.TriggerCallback("RatingUpdate", unitName, ratingInfo, openRaidLib.RatingInfoManager.RatingData)
|
|
end
|
|
|
|
openRaidLib.internalCallback.RegisterCallback("onEnterWorld", openRaidLib.RatingInfoManager.OnPlayerEnterWorld)
|
|
openRaidLib.internalCallback.RegisterCallback("onEnterGroup", openRaidLib.RatingInfoManager.OnPlayerEnterGroup)
|
|
openRaidLib.internalCallback.RegisterCallback("mythicDungeonEnd", openRaidLib.RatingInfoManager.OnMythicDungeonFinished)
|
|
|
|
--------------------------------------------------------------------------------------------------------------------------------
|
|
--data
|
|
|
|
local createLocalCooldownTracker = function()
|
|
local cdTrackerFrame = CreateFrame("frame")
|
|
cdTrackerFrame:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED")
|
|
local allCooldownsFromLib = LIB_OPEN_RAID_COOLDOWNS_INFO
|
|
|
|
---@type table<castername, table<castspellid, number>>
|
|
local recentCastedSpells = {}
|
|
|
|
cdTrackerFrame:SetScript("OnEvent", function(self, event, ...)
|
|
if (event == "UNIT_SPELLCAST_SUCCEEDED") then
|
|
local unitId, castGUID, spellId = ...
|
|
|
|
--don't track spells casted by the player
|
|
local bUnitIsThePlayer = UnitIsUnit(unitId, "player")
|
|
if (not bUnitIsThePlayer) then
|
|
--get the caster name and check if it's a unit in the group
|
|
local casterName = GetUnitName(unitId, true)
|
|
if (casterName) then
|
|
local unitInGroup = UnitInParty(unitId) or UnitInRaid(unitId)
|
|
if (unitInGroup) then
|
|
--check if the library has the spell in the list of cooldowns
|
|
local spellData = allCooldownsFromLib[spellId]
|
|
|
|
--check for overwrite spell ids
|
|
|
|
if (spellData) then
|
|
--check for cast_success spam from channel spells using a cooldown timer
|
|
local unitCastCooldown = recentCastedSpells[casterName]
|
|
if (not unitCastCooldown) then
|
|
unitCastCooldown = {}
|
|
recentCastedSpells[casterName] = unitCastCooldown
|
|
end
|
|
|
|
--don't register the cooldown if the spell was casted recently
|
|
if (not unitCastCooldown[spellId] or unitCastCooldown[spellId]+5 < GetTime()) then
|
|
unitCastCooldown[spellId] = GetTime()
|
|
|
|
--local auraName, texture, count, auraType, auraDuration, expirationTime = openRaidLib.AuraTracker.FindBuffDurationByUnitName(casterName, casterName, spellId)
|
|
local auraDuration = openRaidLib.CooldownManager.GetSpellBuffDuration(spellId, unitId)
|
|
|
|
--trigger a cooldown usage
|
|
local timeLeft = spellData.cooldown
|
|
local duration = spellData.duration or 0
|
|
local newCharges = 0
|
|
local startTimeOffset = 0
|
|
local buffDuration = auraDuration or spellData.duration or 0
|
|
|
|
openRaidLib.CooldownManager.CooldownSpellUpdate(casterName, spellId, timeLeft, newCharges, startTimeOffset, duration, buffDuration)
|
|
local cooldownInfo = cooldownGetSpellInfo(casterName, spellId)
|
|
local unitCooldownsTable = openRaidLib.GetUnitCooldowns(casterName)
|
|
|
|
--trigger a public callback
|
|
openRaidLib.publicCallback.TriggerCallback("CooldownUpdate", openRaidLib.GetUnitID(casterName), spellId, cooldownInfo, unitCooldownsTable, openRaidLib.CooldownManager.UnitData)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
end
|
|
|
|
--vintage cooldown tracker and interrupt tracker
|
|
C_Timer.After(0.1, function()
|
|
createLocalCooldownTracker()
|
|
end)
|
|
|
|
tempCache.RestoreData()
|
|
|