Open Raid Lib: added cooldown added and removed events; added Keystone information
This commit is contained in:
@@ -15,6 +15,7 @@ Code Rules:
|
||||
- Public callbacks are callbacks registered by an external addon.
|
||||
|
||||
Change Log:
|
||||
- finished keystone info, see docs
|
||||
- added interrupts to cooldown tracker, new filter: "interrupt"
|
||||
- after encounter_end cooldowns now check for cooldowns reset.
|
||||
- each module now controls what to do with regen_enabled.
|
||||
@@ -23,13 +24,12 @@ Change Log:
|
||||
- major function and variables rename.
|
||||
- implemented pvp talents.
|
||||
- player information is always available even when not in a group.
|
||||
- added cooldown check to se which cooldown has removed or added into the list.
|
||||
- added two new callbacks: "CooldownAdded" and "CooldownRemoved", see documents.
|
||||
|
||||
TODO:
|
||||
- (finished but not active atm) need to finish the CheckForSpellsAdeedOrRemoved(), need to send the comm, need to create the local callbacks
|
||||
- (finished but not active atm) create comm to add or remove a cooldown from an unit
|
||||
- make talents changes also send only cooldowns added or changed
|
||||
- add into gear info how many tier set parts the player has
|
||||
- keystone info (portion of the logic is implemented, need to share the information)
|
||||
- raid lockouts normal-heroic-mythic
|
||||
- soulbind character (covenant choise) - probably not used in 10.0
|
||||
|
||||
@@ -45,7 +45,7 @@ if (WOW_PROJECT_ID ~= WOW_PROJECT_MAINLINE) then
|
||||
end
|
||||
|
||||
local major = "LibOpenRaid-1.0"
|
||||
local CONST_LIB_VERSION = 34
|
||||
local CONST_LIB_VERSION = 35
|
||||
LIB_OPEN_RAID_CAN_LOAD = false
|
||||
|
||||
--declae the library within the LibStub
|
||||
@@ -82,6 +82,13 @@ LIB_OPEN_RAID_CAN_LOAD = false
|
||||
local CONST_COMM_PLAYERINFO_TALENTS_PREFIX = "T"
|
||||
local CONST_COMM_PLAYERINFO_PVPTALENTS_PREFIX = "V"
|
||||
|
||||
local CONST_COMM_KEYSTONE_DATA_PREFIX = "K"
|
||||
local CONST_COMM_KEYSTONE_DATAREQUEST_PREFIX = "J"
|
||||
|
||||
local CONST_COMM_SENDTO_PARTY = "0x1"
|
||||
local CONST_COMM_SENDTO_RAID = "0x2"
|
||||
local CONST_COMM_SENDTO_GUILD = "0x4"
|
||||
|
||||
local CONST_ONE_SECOND = 1.0
|
||||
local CONST_TWO_SECONDS = 2.0
|
||||
local CONST_THREE_SECONDS = 3.0
|
||||
@@ -123,6 +130,22 @@ LIB_OPEN_RAID_CAN_LOAD = false
|
||||
end
|
||||
end
|
||||
|
||||
local checkClientVersion = function(...)
|
||||
for i = 1, select("#", ...) do
|
||||
local clientVersion = select(i, ...)
|
||||
|
||||
if (clientVersion == "retail" and WOW_PROJECT_ID == WOW_PROJECT_MAINLINE) 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
|
||||
|
||||
--------------------------------------------------------------------------------------------------------------------------------
|
||||
--> ~comms
|
||||
openRaidLib.commHandler = {}
|
||||
@@ -140,7 +163,7 @@ LIB_OPEN_RAID_CAN_LOAD = false
|
||||
--don't receive comms from the player it self
|
||||
local playerName = UnitName("player")
|
||||
if (playerName == sender) then
|
||||
return
|
||||
--return
|
||||
end
|
||||
|
||||
local data = text
|
||||
@@ -193,6 +216,8 @@ LIB_OPEN_RAID_CAN_LOAD = false
|
||||
[CONST_COMM_PLAYERINFO_PREFIX] = {}, --info about the player
|
||||
[CONST_COMM_PLAYERINFO_TALENTS_PREFIX] = {}, --talents info
|
||||
[CONST_COMM_PLAYERINFO_PVPTALENTS_PREFIX] = {}, --pvp talents info
|
||||
[CONST_COMM_KEYSTONE_DATA_PREFIX] = {}, --received keystone data
|
||||
[CONST_COMM_KEYSTONE_DATAREQUEST_PREFIX] = {}, --received a request to send keystone data
|
||||
}
|
||||
|
||||
function openRaidLib.commHandler.RegisterComm(prefix, func)
|
||||
@@ -200,17 +225,41 @@ LIB_OPEN_RAID_CAN_LOAD = false
|
||||
tinsert(openRaidLib.commHandler.commCallback[prefix], func)
|
||||
end
|
||||
|
||||
function openRaidLib.commHandler.SendCommData(data)
|
||||
--@flags
|
||||
--0x1: to party
|
||||
--0x2: to raid
|
||||
--0x4: to guild
|
||||
function openRaidLib.commHandler.SendCommData(data, flags)
|
||||
local LibDeflate = LibStub:GetLibrary("LibDeflate")
|
||||
local dataCompressed = LibDeflate:CompressDeflate(data, {level = 9})
|
||||
local dataEncoded = LibDeflate:EncodeForWoWAddonChannel(dataCompressed)
|
||||
|
||||
if (IsInGroup() and not IsInRaid()) then --in party only
|
||||
C_ChatInfo.SendAddonMessage(CONST_COMM_PREFIX, dataEncoded, IsInGroup(LE_PARTY_CATEGORY_INSTANCE) and "INSTANCE_CHAT" or "PARTY")
|
||||
if (flags) then
|
||||
if (bit.band(flags, CONST_COMM_SENDTO_PARTY)) then --send to party
|
||||
if (IsInGroup() and not IsInRaid()) then
|
||||
C_ChatInfo.SendAddonMessage(CONST_COMM_PREFIX, dataEncoded, IsInGroup(LE_PARTY_CATEGORY_INSTANCE) and "INSTANCE_CHAT" or "PARTY")
|
||||
end
|
||||
end
|
||||
|
||||
elseif (IsInRaid()) then
|
||||
C_ChatInfo.SendAddonMessage(CONST_COMM_PREFIX, dataEncoded, IsInRaid(LE_PARTY_CATEGORY_INSTANCE) and "INSTANCE_CHAT" or "RAID")
|
||||
end
|
||||
if (bit.band(flags, CONST_COMM_SENDTO_RAID)) then --send to raid
|
||||
if (IsInRaid()) then
|
||||
C_ChatInfo.SendAddonMessage(CONST_COMM_PREFIX, dataEncoded, IsInRaid(LE_PARTY_CATEGORY_INSTANCE) and "INSTANCE_CHAT" or "RAID")
|
||||
end
|
||||
end
|
||||
|
||||
if (bit.band(flags, CONST_COMM_SENDTO_GUILD)) then --send to guild
|
||||
if (IsInGuild()) then
|
||||
C_ChatInfo.SendAddonMessage(CONST_COMM_PREFIX, dataEncoded, "GUILD")
|
||||
end
|
||||
end
|
||||
else
|
||||
if (IsInGroup() and not IsInRaid()) then --in party only
|
||||
C_ChatInfo.SendAddonMessage(CONST_COMM_PREFIX, dataEncoded, IsInGroup(LE_PARTY_CATEGORY_INSTANCE) and "INSTANCE_CHAT" or "PARTY")
|
||||
|
||||
elseif (IsInRaid()) then
|
||||
C_ChatInfo.SendAddonMessage(CONST_COMM_PREFIX, dataEncoded, IsInRaid(LE_PARTY_CATEGORY_INSTANCE) and "INSTANCE_CHAT" or "RAID")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -311,6 +360,7 @@ LIB_OPEN_RAID_CAN_LOAD = false
|
||||
"UnitInfoWipe",
|
||||
"TalentUpdate",
|
||||
"PvPTalentUpdate",
|
||||
"KeystoneUpdate",
|
||||
}
|
||||
|
||||
--save build the table to avoid lose registered events on older versions
|
||||
@@ -407,6 +457,7 @@ LIB_OPEN_RAID_CAN_LOAD = false
|
||||
["raidEncounterEnd"] = {},
|
||||
["mythicDungeonStart"] = {},
|
||||
["playerPetChange"] = {},
|
||||
["mythicDungeonEnd"] = {},
|
||||
}
|
||||
|
||||
openRaidLib.internalCallback.RegisterCallback = function(event, func)
|
||||
@@ -586,7 +637,11 @@ LIB_OPEN_RAID_CAN_LOAD = false
|
||||
eventFrame:UnregisterEvent("UNIT_FLAGS")
|
||||
openRaidLib.eventFunctions["UNIT_PET"]("player")
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
["CHALLENGE_MODE_COMPLETED"] = function()
|
||||
openRaidLib.internalCallback.TriggerEvent("mythicDungeonEnd")
|
||||
end,
|
||||
}
|
||||
openRaidLib.eventFunctions = eventFunctions
|
||||
|
||||
@@ -598,18 +653,20 @@ LIB_OPEN_RAID_CAN_LOAD = false
|
||||
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_SPECIALIZATION_CHANGED")
|
||||
if (not isTimewalkWoW()) then
|
||||
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")
|
||||
end
|
||||
|
||||
eventFrame:RegisterEvent("PLAYER_DEAD")
|
||||
eventFrame:RegisterEvent("PLAYER_ALIVE")
|
||||
eventFrame:RegisterEvent("PLAYER_UNGHOST")
|
||||
|
||||
|
||||
eventFrame:SetScript("OnEvent", function(self, event, ...)
|
||||
eventFunctions[event](...)
|
||||
@@ -1596,8 +1653,7 @@ end
|
||||
end
|
||||
|
||||
function openRaidLib.CooldownManager.OnPlayerPetChanged()
|
||||
--disabled atm
|
||||
--openRaidLib.CooldownManager.CheckCooldownChanges()
|
||||
openRaidLib.CooldownManager.CheckCooldownChanges()
|
||||
end
|
||||
|
||||
openRaidLib.internalCallback.RegisterCallback("onLeaveGroup", openRaidLib.CooldownManager.OnPlayerLeaveGroup)
|
||||
@@ -1863,6 +1919,7 @@ openRaidLib.commHandler.RegisterComm(CONST_COMM_COOLDOWNFULLLIST_PREFIX, openRai
|
||||
|
||||
--------------------------------------------------------------------------------------------------------------------------------
|
||||
--> ~keystones
|
||||
--public callback does not check if the keystone has changed from the previous callback
|
||||
|
||||
--> API calls
|
||||
--return a table containing all information of units
|
||||
@@ -1877,6 +1934,39 @@ openRaidLib.commHandler.RegisterComm(CONST_COMM_COOLDOWNFULLLIST_PREFIX, openRai
|
||||
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
|
||||
|
||||
--> manager constructor
|
||||
openRaidLib.KeystoneInfoManager = {
|
||||
--structure:
|
||||
@@ -1890,10 +1980,10 @@ openRaidLib.commHandler.RegisterComm(CONST_COMM_COOLDOWNFULLLIST_PREFIX, openRai
|
||||
challengeMapID = 0,
|
||||
}
|
||||
|
||||
local updateKeystoneInfo = function(keystoneInfo)
|
||||
keystoneInfo.level = C_MythicPlus.GetOwnedKeystoneLevel() or 0
|
||||
keystoneInfo.mapID = C_MythicPlus.GetOwnedKeystoneMapID() or 0
|
||||
keystoneInfo.challengeMapID = C_MythicPlus.GetOwnedKeystoneChallengeMapID() or 0
|
||||
local updateKeystoneInfo = function(keystoneInfo, level, mapID, challengeMapID)
|
||||
keystoneInfo.level = level or C_MythicPlus.GetOwnedKeystoneLevel() or 0
|
||||
keystoneInfo.mapID = mapID or C_MythicPlus.GetOwnedKeystoneMapID() or 0
|
||||
keystoneInfo.challengeMapID = challengeMapID or C_MythicPlus.GetOwnedKeystoneChallengeMapID() or 0
|
||||
end
|
||||
|
||||
function openRaidLib.KeystoneInfoManager.GetAllKeystonesInfo()
|
||||
@@ -1913,6 +2003,127 @@ openRaidLib.commHandler.RegisterComm(CONST_COMM_COOLDOWNFULLLIST_PREFIX, openRai
|
||||
return keystoneInfo
|
||||
end
|
||||
|
||||
local getKeystoneInfoToComm = function()
|
||||
local playerName = UnitName("player")
|
||||
local keystoneInfo = openRaidLib.KeystoneInfoManager.GetKeystoneInfo(playerName, true)
|
||||
local dataToSend = CONST_COMM_KEYSTONE_DATA_PREFIX .. "," .. keystoneInfo.level .. "," .. keystoneInfo.mapID .. "," .. keystoneInfo.challengeMapID
|
||||
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
|
||||
|
||||
function openRaidLib.KeystoneInfoManager.OnReceiveRequestData()
|
||||
if (not checkClientVersion("retail")) then
|
||||
return
|
||||
end
|
||||
|
||||
--update the information about the key stone the player has
|
||||
openRaidLib.KeystoneInfoManager.GetKeystoneInfo(UnitName("player"), true)
|
||||
|
||||
if (IsInGroup() and not IsInRaid()) then
|
||||
openRaidLib.Schedules.NewUniqueTimer(1 + math.random(1, 3), openRaidLib.KeystoneInfoManager.SendPlayerKeystoneInfoToParty, "KeystoneInfoManager", "sendKeystoneInfoToParty_Schedule")
|
||||
end
|
||||
|
||||
if (IsInGuild()) then
|
||||
openRaidLib.Schedules.NewUniqueTimer(1 + math.random(1, 3), openRaidLib.KeystoneInfoManager.SendPlayerKeystoneInfoToGuild, "KeystoneInfoManager", "sendKeystoneInfoToGuild_Schedule")
|
||||
end
|
||||
end
|
||||
openRaidLib.commHandler.RegisterComm(CONST_COMM_KEYSTONE_DATAREQUEST_PREFIX, openRaidLib.KeystoneInfoManager.OnReceiveRequestData)
|
||||
|
||||
function openRaidLib.KeystoneInfoManager.OnReceiveKeystoneData(data, unitName)
|
||||
if (not checkClientVersion("retail")) then
|
||||
return
|
||||
end
|
||||
|
||||
local level = tonumber(data[1])
|
||||
local mapID = tonumber(data[2])
|
||||
local challengeMapID = tonumber(data[3])
|
||||
|
||||
if (level and mapID and challengeMapID) then
|
||||
local keystoneInfo = openRaidLib.KeystoneInfoManager.GetKeystoneInfo(unitName, true)
|
||||
updateKeystoneInfo(keystoneInfo, level, mapID, challengeMapID)
|
||||
|
||||
--trigger public callback
|
||||
openRaidLib.publicCallback.TriggerCallback("KeystoneUpdate", openRaidLib.GetUnitID(unitName), keystoneInfo, openRaidLib.KeystoneInfoManager.KeystoneData)
|
||||
end
|
||||
end
|
||||
openRaidLib.commHandler.RegisterComm(CONST_COMM_KEYSTONE_DATA_PREFIX, openRaidLib.KeystoneInfoManager.OnReceiveKeystoneData)
|
||||
|
||||
--on entering a group, send keystone information for the party
|
||||
function openRaidLib.KeystoneInfoManager.OnPlayerEnterGroup()
|
||||
--keystones are only available on retail
|
||||
if (not checkClientVersion("retail")) then
|
||||
return
|
||||
end
|
||||
|
||||
if (IsInGroup() and not IsInRaid()) then
|
||||
--update the information about the key stone the player has
|
||||
openRaidLib.KeystoneInfoManager.GetKeystoneInfo(UnitName("player"), true)
|
||||
--send to the group which kstone the player has
|
||||
openRaidLib.Schedules.NewUniqueTimer(1 + math.random(1, 3), openRaidLib.KeystoneInfoManager.SendPlayerKeystoneInfoToParty, "KeystoneInfoManager", "sendKeystoneInfoToParty_Schedule")
|
||||
end
|
||||
end
|
||||
|
||||
function openRaidLib.KeystoneInfoManager.OnPlayerEnterWorld()
|
||||
--keystones are only available on retail
|
||||
if (not checkClientVersion("retail")) then
|
||||
return
|
||||
end
|
||||
--hack: on received data send data to party and guild
|
||||
openRaidLib.KeystoneInfoManager.OnReceiveRequestData()
|
||||
|
||||
--trigger public callback
|
||||
local keystoneInfo = openRaidLib.KeystoneInfoManager.GetKeystoneInfo(UnitName("player"), true)
|
||||
openRaidLib.publicCallback.TriggerCallback("KeystoneUpdate", "player", keystoneInfo, openRaidLib.KeystoneInfoManager.KeystoneData)
|
||||
end
|
||||
|
||||
function openRaidLib.KeystoneInfoManager.OnMythicDungeonFinished()
|
||||
--keystones are only available on retail
|
||||
if (not checkClientVersion("retail")) then
|
||||
return
|
||||
end
|
||||
--hack: on received data send data to party and guild
|
||||
openRaidLib.KeystoneInfoManager.OnReceiveRequestData()
|
||||
|
||||
--trigger public callback
|
||||
local keystoneInfo = openRaidLib.KeystoneInfoManager.GetKeystoneInfo(UnitName("player"), true)
|
||||
openRaidLib.publicCallback.TriggerCallback("KeystoneUpdate", "player", 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)
|
||||
|
||||
|
||||
--DEBUG TEST
|
||||
--[=[
|
||||
local ff = {}
|
||||
function ff.OnKSUpdate(unitId, keystoneInfo, allKeystonesInfo)
|
||||
print(unitId, keystoneInfo, allKeystonesInfo)
|
||||
print(keystoneInfo.level, keystoneInfo.mapID, keystoneInfo.challengeMapID)
|
||||
end
|
||||
openRaidLib.RegisterCallback(ff, "KeystoneUpdate", "OnKSUpdate")
|
||||
|
||||
C_Timer.After(7, function()
|
||||
openRaidLib.GetAllKeystonesInfo()
|
||||
print("> ", openRaidLib.GetKeystoneInfo("player"))
|
||||
|
||||
openRaidLib.RequestKeystoneDataFromGuild()
|
||||
openRaidLib.RequestKeystoneDataFromParty()
|
||||
openRaidLib.RequestKeystoneDataFromRaid()
|
||||
end)
|
||||
--]=]
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------------------------------------------------------
|
||||
--> data
|
||||
|
||||
@@ -93,6 +93,37 @@ unitInfo = {
|
||||
}
|
||||
|
||||
|
||||
KEYSTONE
|
||||
--send and receive mythic+ keystone information and works only in retail
|
||||
--the event "KeystoneUpdate" is triggered on receive keystone info from another player
|
||||
|
||||
local allKeystoneInfo = openRaidLib.GetAllKeystonesInfo()
|
||||
allKeystoneInfo = {
|
||||
["unitName1"] = keystoneInfo,
|
||||
["unitName2"] = keystoneInfo,
|
||||
["unitName3"] = keystoneInfo,
|
||||
}
|
||||
|
||||
local keystoneInfo = openRaidLib.GetKeystoneInfo(unitId)
|
||||
keystoneInfo = {
|
||||
.level = number,
|
||||
.mapID = number,
|
||||
.challengeMapID = number,
|
||||
}
|
||||
|
||||
--request all online players in the guild to send their keystone information
|
||||
--this call fails if the player isn't in a guild
|
||||
local requestSent = openRaidLib.RequestKeystoneDataFromGuild()
|
||||
|
||||
--request to players in your party to send their keystone information
|
||||
--this call fails if the player is in raid
|
||||
local requestSent = openRaidLib.RequestKeystoneDataFromParty()
|
||||
|
||||
--request to players in your raid to send their keystone information
|
||||
--this call fails if not in a raid group
|
||||
local requestSent = openRaidLib.RequestKeystoneDataFromRaid()
|
||||
|
||||
|
||||
Callbacks:
|
||||
|
||||
===================================================================================================================================
|
||||
|
||||
Reference in New Issue
Block a user