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.
535 lines
15 KiB
535 lines
15 KiB
local E = select(2, ...):unpack()
|
|
local P, CM = E.Party, E.Comm
|
|
|
|
local pairs, next, concat, tonumber, strmatch, strsplit, format, gsub, floor, abs = pairs, next, table.concat, tonumber, string.match, string.split, string.format, string.gsub, math.floor, math.abs
|
|
local GetTime = GetTime
|
|
local GetSpellCooldown = GetSpellCooldown or function(spellID)
|
|
local spellCooldownInfo = C_Spell.GetSpellCooldown(spellID);
|
|
if spellCooldownInfo then
|
|
return spellCooldownInfo.startTime, spellCooldownInfo.duration, spellCooldownInfo.isEnabled and 1 or 0, spellCooldownInfo.modRate;
|
|
end
|
|
end
|
|
local GetSpellCharges = GetSpellCharges or function(spellID)
|
|
local spellChargeInfo = C_Spell.GetSpellCharges(spellID);
|
|
if spellChargeInfo then
|
|
return spellChargeInfo.currentCharges, spellChargeInfo.maxCharges, spellChargeInfo.cooldownStartTime, spellChargeInfo.cooldownDuration, spellChargeInfo.chargeModRate;
|
|
end
|
|
end
|
|
local GetItemCount = C_Item and C_Item.GetItemCount or GetItemCount
|
|
|
|
local LibDeflate = LibStub("LibDeflate")
|
|
local CooldownSyncFrame = CreateFrame("Frame")
|
|
local COOLDOWN_SYNC_INTERVAL = 2
|
|
local MSG_DESYNC = "DESYNC"
|
|
local MSG_INFO_REQUEST = "REQ"
|
|
local MSG_INFO_UPDATE = "UPD"
|
|
local MSG_STRIVE_PVP = "STRIVE"
|
|
local MSG_COOLDOWN_SYNC = "CD"
|
|
local NULL = ""
|
|
|
|
CM.syncedGroupMembers = {}
|
|
CM.cooldownSyncIDs = {}
|
|
CM.serializedSyncData = NULL
|
|
|
|
function CM:SendComm(...)
|
|
local message = strjoin(",", ...)
|
|
|
|
|
|
|
|
|
|
if IsInRaid() then
|
|
|
|
self:SendCommMessage(self.AddonPrefix, message, (not IsInRaid(LE_PARTY_CATEGORY_HOME) and IsInRaid(LE_PARTY_CATEGORY_INSTANCE)) and "INSTANCE_CHAT" or "RAID")
|
|
elseif IsInGroup() then
|
|
|
|
self:SendCommMessage(self.AddonPrefix, message, (not IsInGroup(LE_PARTY_CATEGORY_HOME) and IsInGroup(LE_PARTY_CATEGORY_INSTANCE)) and "INSTANCE_CHAT" or "PARTY")
|
|
end
|
|
end
|
|
|
|
function CM:RequestSync()
|
|
self:SendComm(MSG_INFO_REQUEST, E.userGUID, self.serializedSyncData)
|
|
end
|
|
|
|
function CM:SendUserSyncData(sender)
|
|
if self.serializedSyncData == NULL then
|
|
self:InspectUser()
|
|
end
|
|
self:SendComm(sender or MSG_INFO_UPDATE, E.userGUID, self.serializedSyncData)
|
|
end
|
|
|
|
function CM:DesyncFromGroup()
|
|
wipe(self.syncedGroupMembers)
|
|
CooldownSyncFrame:Hide()
|
|
self:SendComm(MSG_DESYNC, E.userGUID, 1)
|
|
end
|
|
|
|
function CM:IsVersionIncompatible(serializationVersion)
|
|
return serializationVersion ~= self.SERIALIZATION_VERSION
|
|
end
|
|
|
|
local aceUserNameFix = CM.ACECOMM and E.userName or gsub(E.userNameWithRealm, " ", "")
|
|
|
|
function CM:CHAT_MSG_ADDON(prefix, message, _, sender)
|
|
if prefix ~= self.AddonPrefix or sender == aceUserNameFix then
|
|
return
|
|
end
|
|
|
|
local header, guid, body = strmatch(message, "(.-),(.-),(.+)")
|
|
local info = P.groupInfo[guid]
|
|
if not info then
|
|
return
|
|
end
|
|
|
|
local isSyncedUnit = self.syncedGroupMembers[guid]
|
|
if header == MSG_COOLDOWN_SYNC then
|
|
if isSyncedUnit then
|
|
self.SyncCooldowns(guid, body)
|
|
end
|
|
return
|
|
elseif header == MSG_INFO_REQUEST then
|
|
self:SendUserSyncData(guid)
|
|
elseif header == MSG_INFO_UPDATE then
|
|
if not isSyncedUnit then
|
|
return
|
|
end
|
|
elseif header == MSG_DESYNC then
|
|
if isSyncedUnit then
|
|
self.syncedGroupMembers[guid] = nil
|
|
end
|
|
self:ToggleCooldownSync()
|
|
return
|
|
elseif header == MSG_STRIVE_PVP then
|
|
if isSyncedUnit and (not P.loginsessionData[guid] or not P.loginsessionData[guid]["strivedPvpCD"]) then
|
|
local elapsed, cd = strsplit(":", body, 3)
|
|
self:SyncStrivePvpTalentCD(guid, tonumber(elapsed), tonumber(cd))
|
|
end
|
|
return
|
|
elseif header ~= E.userGUID then
|
|
return
|
|
end
|
|
|
|
local decodedData = LibDeflate:DecodeForWoWAddonChannel(body)
|
|
if not decodedData then
|
|
error("Error decoding sync message from " .. info.name)
|
|
end
|
|
local decompressedData = LibDeflate:DecompressDeflate(decodedData)
|
|
if not decompressedData then
|
|
error("Error decompressing sync message from " .. info.name)
|
|
end
|
|
|
|
while ( decompressedData ) do
|
|
local t, rest = strsplit("^", decompressedData, 2)
|
|
decompressedData = rest
|
|
|
|
local k, v = strsplit(",", t, 2)
|
|
if ( k == "T" ) then
|
|
while ( v ) do
|
|
local id, idlist = strsplit(",", v, 2)
|
|
v = idlist
|
|
local spellID, rank = strsplit(":", id)
|
|
spellID = tonumber(spellID)
|
|
if ( spellID ) then
|
|
if ( spellID > 0 ) then
|
|
info.talentData[spellID] = tonumber(rank) or 1
|
|
else
|
|
info.talentData[-spellID] = "PVP"
|
|
end
|
|
end
|
|
end
|
|
elseif ( k == "M" ) then
|
|
while ( v ) do
|
|
local id, idlist = strsplit(",", v, 2)
|
|
v = idlist
|
|
local key, src = strsplit(":", id)
|
|
local spellID = tonumber(key)
|
|
local value = tonumber(src) or src or true
|
|
if ( not spellID ) then
|
|
info.talentData[key] = value
|
|
elseif ( spellID > 0 ) then
|
|
if ( src == "AE" ) then
|
|
local rank1 = CM.essencePowerIDs[spellID]
|
|
if ( rank1 ) then
|
|
info.talentData[rank1] = src
|
|
info.talentData["essMajorRank1"] = rank1
|
|
info.talentData["essMajorID"] = spellID
|
|
end
|
|
elseif ( src == "ae" ) then
|
|
info.talentData["essStriveMult"] = spellID
|
|
else
|
|
info.talentData[spellID] = value
|
|
end
|
|
else
|
|
info.talentData[-spellID] = value
|
|
end
|
|
end
|
|
elseif ( k == "E" ) then
|
|
while ( v ) do
|
|
local id, idlist = strsplit(",", v, 2)
|
|
v = idlist
|
|
id = tonumber(id)
|
|
if ( id ) then
|
|
if ( id > 0 ) then
|
|
info.itemData[id] = true
|
|
else
|
|
info.rangedWeaponSpeed = -id
|
|
end
|
|
end
|
|
end
|
|
elseif ( k == "C" ) then
|
|
wipe(info.shadowlandsData)
|
|
local covenantID, soulbindID, conduits = strsplit(",", v, 3)
|
|
covenantID = tonumber(covenantID)
|
|
soulbindID = tonumber(soulbindID)
|
|
local covenantSpellID = E.covenant_to_spellid[covenantID]
|
|
info.shadowlandsData.covenantID = covenantSpellID
|
|
info.shadowlandsData.soulbindID = soulbindID
|
|
info.talentData[covenantSpellID] = "C"
|
|
while ( conduits ) do
|
|
local id, idlist = strsplit(",", conduits, 2)
|
|
conduits = idlist
|
|
local conduitSpellID, rankValue = strsplit(":", id)
|
|
conduitSpellID = tonumber(conduitSpellID)
|
|
rankValue = tonumber(rankValue)
|
|
if ( rankValue ) then
|
|
info.talentData[conduitSpellID] = rankValue
|
|
elseif ( conduitSpellID ) then
|
|
info.talentData[conduitSpellID] = 0
|
|
end
|
|
end
|
|
else
|
|
k = tonumber(k)
|
|
if ( not k or self:IsVersionIncompatible(k) ) then
|
|
return
|
|
end
|
|
info.spec = tonumber(v)
|
|
wipe(info.talentData)
|
|
wipe(info.itemData)
|
|
end
|
|
end
|
|
|
|
local unit = info.unit
|
|
if info.name == "" or info.name == "Unknown" then
|
|
info.name = GetUnitName(unit, true)
|
|
end
|
|
if info.level == 200 then
|
|
local lvl = UnitLevel(unit)
|
|
if lvl > 0 then
|
|
info.level = lvl
|
|
end
|
|
end
|
|
|
|
self.syncedGroupMembers[guid] = true
|
|
self:DequeueInspect(guid)
|
|
P:UpdateUnitBar(guid)
|
|
|
|
self:ToggleCooldownSync()
|
|
end
|
|
|
|
local function SendUpdatedUserSyncData()
|
|
CM:InspectUser()
|
|
CM:SendUserSyncData()
|
|
end
|
|
|
|
function CM:CHARACTER_POINTS_CHANGED(change)
|
|
if change == -1 then
|
|
SendUpdatedUserSyncData()
|
|
end
|
|
end
|
|
|
|
local equipmentTimer
|
|
|
|
local SendUserSyncData_OnTimerEnd = function()
|
|
SendUpdatedUserSyncData()
|
|
equipmentTimer = nil
|
|
end
|
|
|
|
function CM:PLAYER_EQUIPMENT_CHANGED(slotID)
|
|
if equipmentTimer or slotID > 16 then
|
|
return
|
|
end
|
|
equipmentTimer = C_Timer.NewTicker(0.1, SendUserSyncData_OnTimerEnd, 1)
|
|
end
|
|
|
|
CM.PLAYER_TALENT_UPDATE = SendUpdatedUserSyncData
|
|
CM.PLAYER_SPECIALIZATION_CHANGED = SendUpdatedUserSyncData
|
|
CM.COVENANT_CHOSEN = SendUpdatedUserSyncData
|
|
CM.SOULBIND_ACTIVATED = SendUpdatedUserSyncData
|
|
CM.SOULBIND_NODE_LEARNED = SendUpdatedUserSyncData
|
|
CM.SOULBIND_NODE_UNLEARNED = SendUpdatedUserSyncData
|
|
CM.SOULBIND_NODE_UPDATED = SendUpdatedUserSyncData
|
|
CM.SOULBIND_CONDUIT_INSTALLED = SendUpdatedUserSyncData
|
|
CM.SOULBIND_PATH_CHANGED = SendUpdatedUserSyncData
|
|
CM.COVENANT_SANCTUM_RENOWN_LEVEL_CHANGED = SendUpdatedUserSyncData
|
|
CM.TRAIT_CONFIG_UPDATED = SendUpdatedUserSyncData
|
|
|
|
|
|
|
|
CM.PLAYER_LEAVING_WORLD = CM.DesyncFromGroup
|
|
|
|
function CM:SyncStrivePvpTalentCD(guid, elapsed, cd)
|
|
local info = P.groupInfo[guid]
|
|
if not info then
|
|
return
|
|
end
|
|
|
|
local spellID = info.talentData["essStrivedPvpID"]
|
|
local icon = info.spellIcons[spellID]
|
|
if icon then
|
|
local active = info.active[spellID]
|
|
if active then
|
|
local modRate = active.iconModRate or 1
|
|
local newTime = GetTime() - elapsed * modRate
|
|
local newCd = cd * modRate
|
|
icon.cooldown:SetCooldown(newTime, newCd, modRate)
|
|
active.startTime = newTime
|
|
active.duration = newCd
|
|
end
|
|
icon.duration = cd
|
|
end
|
|
P.loginsessionData[guid] = P.loginsessionData[guid] or {}
|
|
P.loginsessionData[guid]["strivedPvpCD"] = cd
|
|
end
|
|
|
|
function CM.SendStrivePvpTalentCD(spellID)
|
|
local st, cd, _, modRate = GetSpellCooldown(spellID)
|
|
if cd < 2 then
|
|
return
|
|
end
|
|
|
|
local elapsed = GetTime() - st/modRate
|
|
cd = cd/modRate
|
|
if not P.isUserDisabled then
|
|
CM:SyncStrivePvpTalentCD(E.userGUID, elapsed, cd)
|
|
end
|
|
CM:SendComm(MSG_STRIVE_PVP, E.userGUID, elapsed, cd)
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function CM.SyncCooldowns(guid, encodedData)
|
|
local info = P.groupInfo[guid]
|
|
if not info then return end
|
|
|
|
local compressedData = LibDeflate:DecodeForWoWAddonChannel(encodedData)
|
|
if not compressedData then
|
|
return
|
|
end
|
|
local serializedCooldownData = LibDeflate:DecompressDeflate(compressedData)
|
|
if not serializedCooldownData then
|
|
return
|
|
end
|
|
|
|
local now = GetTime()
|
|
while ( serializedCooldownData ) do
|
|
local spellID, duration, remainingTime, modRate, charges, rest = strsplit(",", serializedCooldownData, 6)
|
|
serializedCooldownData = rest
|
|
spellID = tonumber(spellID)
|
|
if ( spellID ) then
|
|
local icon = info.spellIcons[spellID]
|
|
if ( not icon ) then
|
|
spellID = E.spell_merged[spellID]
|
|
icon = info.spellIcons[spellID]
|
|
end
|
|
if ( icon ) then
|
|
duration = tonumber(duration)
|
|
remainingTime = tonumber(remainingTime)
|
|
modRate = tonumber(modRate)
|
|
charges = tonumber(charges)
|
|
local rawCharges = charges
|
|
charges = icon.maxcharges and charges ~= -1 and charges or nil
|
|
local active = icon.active and info.active[spellID]
|
|
if ( active and duration == 0 ) then
|
|
P:ResetCooldown(icon)
|
|
|
|
if spellID == 6262 then
|
|
icon.cooldown:Clear()
|
|
info.preactiveIcons[spellID] = icon
|
|
icon.icon:SetVertexColor(0.4, 0.4, 0.4)
|
|
icon.count:SetText(rawCharges)
|
|
info.auras.healthStoneStacks = rawCharges
|
|
local statusBar = icon.statusBar
|
|
if statusBar then
|
|
statusBar.BG:SetVertexColor(0.7, 0.7, 0.7)
|
|
end
|
|
end
|
|
|
|
elseif ( active and (spellID ~= 642 or not active.forbearanceOvertime or active.forbearanceOvertime ~= 0) and (abs(active.duration - (now - active.startTime) - remainingTime) > 1 or active.charges ~= charges) )
|
|
or ( not active and duration > 0 and E.sync_periodic[spellID] ) then
|
|
local startTime = now - (duration - remainingTime)
|
|
icon.cooldown:SetCooldown(startTime, duration, modRate)
|
|
P:SetCooldownElements(nil, icon, charges)
|
|
|
|
if ( not active ) then
|
|
info.active[spellID] = {}
|
|
active = info.active[spellID]
|
|
end
|
|
active.startTime = startTime
|
|
active.duration = duration
|
|
active.iconModRate = modRate
|
|
if ( charges ) then
|
|
active.charges = charges
|
|
icon.active = charges
|
|
icon.count:SetText(charges)
|
|
elseif ( spellID == 6262 ) then
|
|
info.preactiveIcons[spellID] = nil
|
|
icon.icon:SetVertexColor(1, 1, 1)
|
|
icon.count:SetText(rawCharges)
|
|
info.auras.healthStoneStacks = rawCharges
|
|
end
|
|
|
|
local statusBar = icon.statusBar
|
|
if ( statusBar ) then
|
|
P.OmniCDCastingBarFrame_OnEvent(statusBar.CastingBar, E.db.extraBars[statusBar.key].reverseFill and 'UNIT_SPELLCAST_CHANNEL_UPDATE' or 'UNIT_SPELLCAST_CAST_UPDATE')
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
|
|
local function GetCooldownFix(spellID)
|
|
local start, duration, enabled, modRate = GetSpellCooldown(spellID)
|
|
local currentCharges, maxCharges, cooldownStart, cooldownDuration, chargeModRate = GetSpellCharges(spellID)
|
|
local charges = maxCharges and maxCharges > 1 and currentCharges or -1
|
|
if enabled == 1 then
|
|
if start and start > 0 then
|
|
if duration < 1.5 or (currentCharges and currentCharges > 0) then
|
|
return nil
|
|
end
|
|
return start, duration, modRate, charges
|
|
elseif maxCharges and maxCharges > currentCharges then
|
|
return cooldownStart, cooldownDuration, chargeModRate, charges
|
|
end
|
|
end
|
|
return 0, 0, 1, charges, enabled
|
|
end
|
|
|
|
local cooldownData = {}
|
|
local elapsedTime = 0
|
|
local OFF_CD, THIRD_DECIMAL, TRUNCATE_ZEROS = "0,0,1", "%.3f", "%.?0+$"
|
|
|
|
local function CooldownSyncFrame_OnUpdate(_, elapsed)
|
|
elapsedTime = elapsedTime + elapsed
|
|
if elapsedTime < COOLDOWN_SYNC_INTERVAL then
|
|
return
|
|
end
|
|
|
|
local now = GetTime()
|
|
local c = 0
|
|
for id, cooldownInfo in pairs(CM.cooldownSyncIDs) do
|
|
local start, duration, modRate, charges, enabled = GetCooldownFix(id)
|
|
if start then
|
|
if id == 6262 then
|
|
charges = GetItemCount(5512, false, true)
|
|
end
|
|
local prevStart, prevCharges = cooldownInfo[1], cooldownInfo[2]
|
|
local isPeriodic = E.sync_periodic[id]
|
|
if duration == 0 then
|
|
if isPeriodic and (prevStart ~= 0 or enabled == 0) then
|
|
cooldownInfo[1] = start
|
|
cooldownInfo[2] = charges
|
|
cooldownData[c + 1] = id
|
|
cooldownData[c + 2] = OFF_CD
|
|
cooldownData[c + 3] = charges
|
|
c = c + 3
|
|
end
|
|
else
|
|
|
|
if abs(start - prevStart) > .49 or charges > prevCharges then
|
|
cooldownInfo[1] = start
|
|
cooldownInfo[2] = charges
|
|
local remainingTime = start + duration - now
|
|
if modRate == 1 then
|
|
remainingTime = floor(remainingTime)
|
|
else
|
|
duration = format(THIRD_DECIMAL, duration):gsub(TRUNCATE_ZEROS, NULL)
|
|
modRate = format(THIRD_DECIMAL, modRate):gsub(TRUNCATE_ZEROS, NULL)
|
|
remainingTime = format(THIRD_DECIMAL, remainingTime):gsub(TRUNCATE_ZEROS, NULL)
|
|
end
|
|
cooldownData[c + 1] = id
|
|
cooldownData[c + 2] = duration
|
|
cooldownData[c + 3] = remainingTime
|
|
cooldownData[c + 4] = modRate
|
|
cooldownData[c + 5] = charges
|
|
c = c + 5
|
|
elseif start == prevStart and charges > -1 and charges < prevCharges then
|
|
cooldownInfo[2] = charges
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
|
|
|
|
end
|
|
|
|
elapsedTime = 0
|
|
|
|
if c == 0 then
|
|
return
|
|
end
|
|
|
|
for i = #cooldownData, c + 1, -1 do
|
|
cooldownData[i] = nil
|
|
end
|
|
|
|
local serializedCooldownData = concat(cooldownData, ",")
|
|
local compressedData = LibDeflate:CompressDeflate(serializedCooldownData)
|
|
local encodedData = LibDeflate:EncodeForWoWAddonChannel(compressedData)
|
|
if not P.isUserDisabled then
|
|
CM.SyncCooldowns(E.userGUID, encodedData)
|
|
end
|
|
if next(CM.syncedGroupMembers) then
|
|
CM:SendComm(MSG_COOLDOWN_SYNC, E.userGUID, encodedData)
|
|
end
|
|
end
|
|
|
|
function CM:ForceSyncCooldowns()
|
|
elapsedTime = 100
|
|
end
|
|
|
|
function CM:ToggleCooldownSync()
|
|
if E.preCata then
|
|
return
|
|
end
|
|
if next(self.cooldownSyncIDs) and P.disabled == false and (not P.isUserDisabled or next(self.syncedGroupMembers)) then
|
|
CooldownSyncFrame:Show()
|
|
else
|
|
CooldownSyncFrame:Hide()
|
|
end
|
|
end
|
|
|
|
local CooldownSyncFrame_OnShow = function(self)
|
|
self.isShown = true
|
|
end
|
|
|
|
local CooldownSyncFrame_OnHide = function(self)
|
|
self.isShown = false
|
|
end
|
|
|
|
function CM:InitCooldownSync()
|
|
if self.initCooldownSync or E.preCata then
|
|
return
|
|
end
|
|
CooldownSyncFrame:Hide()
|
|
|
|
CooldownSyncFrame:SetScript("OnShow", CooldownSyncFrame_OnShow)
|
|
CooldownSyncFrame:SetScript("OnHide", CooldownSyncFrame_OnHide)
|
|
CooldownSyncFrame:SetScript("OnUpdate", CooldownSyncFrame_OnUpdate)
|
|
|
|
self.initCooldownSync = true
|
|
end
|
|
|