init
This commit is contained in:
@@ -0,0 +1,474 @@
|
||||
local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
|
||||
local M = E:GetModule("Misc")
|
||||
|
||||
--Lua functions
|
||||
local pairs, ipairs, unpack = pairs, ipairs, unpack
|
||||
local find, format = string.find, string.format
|
||||
local tinsert, twipe = table.insert, table.wipe
|
||||
--WoW API / Variables
|
||||
local ChatEdit_InsertLink = ChatEdit_InsertLink
|
||||
local CreateFrame = CreateFrame
|
||||
local CursorOnUpdate = CursorOnUpdate
|
||||
local CursorUpdate = CursorUpdate
|
||||
local DressUpItemLink = DressUpItemLink
|
||||
local GetLootRollItemInfo = GetLootRollItemInfo
|
||||
local GetLootRollItemLink = GetLootRollItemLink
|
||||
local GetLootRollTimeLeft = GetLootRollTimeLeft
|
||||
local IsModifiedClick = IsModifiedClick
|
||||
local ResetCursor = ResetCursor
|
||||
local RollOnLoot = RollOnLoot
|
||||
local SetDesaturation = SetDesaturation
|
||||
local UnitClass = UnitClass
|
||||
|
||||
local ITEM_QUALITY_COLORS = ITEM_QUALITY_COLORS
|
||||
local ROLL_DISENCHANT = ROLL_DISENCHANT
|
||||
|
||||
local POSITION = "TOP"
|
||||
local FRAME_WIDTH, FRAME_HEIGHT = 328, 28
|
||||
|
||||
M.RollBars = {}
|
||||
|
||||
local locale = GetLocale()
|
||||
local rollMessages = locale == "deDE" and {
|
||||
["(.*) passt automatisch bei (.+), weil [ersi]+ den Gegenstand nicht benutzen kann.$"] = 0,
|
||||
["(.*) würfelt nicht für: (.+|r)$"] = 0,
|
||||
["(.*) hat für (.+) 'Bedarf' ausgewählt"] = 1,
|
||||
["(.*) hat für (.+) 'Gier' ausgewählt"] = 2,
|
||||
["(.*) hat für '(.+)' Entzauberung gewählt."] = 3,
|
||||
} or locale == "frFR" and {
|
||||
["(.*) a passé pour : (.+) parce qu'((il)|(elle)) ne peut pas ramasser cette objet.$"] = 0,
|
||||
["(.*) a passé pour : (.+)"] = 0,
|
||||
["(.*) a choisi Besoin pour : (.+)"] = 1,
|
||||
["(.*) a choisi Cupidité pour : (.+)"] = 2,
|
||||
["(.*) a choisi Désenchantement pour : (.+)"] = 3,
|
||||
} or locale == "zhCN" and {
|
||||
["(.*)自动放弃了:(.+),因为他无法拾取该物品$"] = 0,
|
||||
["(.*)自动放弃了:(.+),因为她无法拾取该物品$"] = 0,
|
||||
["(.*)放弃了:(.+)"] = 0,
|
||||
["(.*)选择了需求取向:(.+)"] = 1,
|
||||
["(.*)选择了贪婪取向:(.+)"] = 2,
|
||||
["(.*)选择了分解取向:(.+)"] = 3,
|
||||
} or locale == "zhTW" and {
|
||||
["(.*)自動放棄:(.+),因為他無法拾取該物品$"] = 0,
|
||||
["(.*)自動放棄:(.+),因為她無法拾取該物品$"] = 0,
|
||||
["(.*)放棄了:(.+)"] = 0,
|
||||
["(.*)選擇了需求:(.+)"] = 1,
|
||||
["(.*)選擇了貪婪:(.+)"] = 2,
|
||||
["(.*)選擇了分解:(.+)"] = 3,
|
||||
} or locale == "ruRU" and {
|
||||
["(.*) автоматически передает предмет (.+), поскольку не может его забрать"] = 0,
|
||||
["(.*) пропускает розыгрыш предмета \"(.+)\", поскольку не может его забрать"] = 0,
|
||||
["(.*) отказывается от предмета (.+)%."] = 0,
|
||||
["Разыгрывается: (.+)%. (.*): \"Мне это нужно\""] = 1,
|
||||
["Разыгрывается: (.+)%. (.*): \"Не откажусь\""] = 2,
|
||||
["Разыгрывается: (.+)%. (.*): \"Распылить\""] = 3,
|
||||
} or locale == "koKR" and {
|
||||
["(.*)님이 획득할 수 없는 아이템이어서 자동으로 주사위 굴리기를 포기했습니다: (.+)"] = 0,
|
||||
["(.*)님이 주사위 굴리기를 포기했습니다: (.+)"] = 0,
|
||||
["(.*)님이 입찰을 선택했습니다: (.+)"] = 1,
|
||||
["(.*)님이 차비를 선택했습니다: (.+)"] = 2,
|
||||
["(.*)님이 마력 추출을 선택했습니다: (.+)"] = 3,
|
||||
} or locale == "esES" and {
|
||||
["^(.*) pasó automáticamente de: (.+) porque no puede despojar este objeto.$"] = 0,
|
||||
["^(.*) pasó de: (.+|r)$"] = 0,
|
||||
["(.*) eligió Necesidad para: (.+)"] = 1,
|
||||
["(.*) eligió Codicia para: (.+)"] = 2,
|
||||
["(.*) eligió Desencantar para: (.+)"] = 3,
|
||||
} or locale == "esMX" and {
|
||||
["^(.*) pasó automáticamente de: (.+) porque no puede despojar este objeto.$"] = 0,
|
||||
["^(.*) pasó de: (.+|r)$"] = 0,
|
||||
["(.*) eligió Necesidad para: (.+)"] = 1,
|
||||
["(.*) eligió Codicia para: (.+)"] = 2,
|
||||
["(.*) eligió Desencantar para: (.+)"] = 3,
|
||||
} or {
|
||||
["^(.*) automatically passed on: (.+) because s?he cannot loot that item.$"] = 0,
|
||||
["^(.*) passed on: (.+|r)$"] = 0,
|
||||
["(.*) has selected Need for: (.+)"] = 1,
|
||||
["(.*) has selected Greed for: (.+)"] = 2,
|
||||
["(.*) has selected Disenchant for: (.+)"] = 3
|
||||
}
|
||||
|
||||
local rollTypes = {
|
||||
[0] = {
|
||||
tooltipText = PASS,
|
||||
normalTexture = "Interface\\Buttons\\UI-GroupLoot-Pass-Up",
|
||||
pushedTexture = "Interface\\Buttons\\UI-GroupLoot-Pass-Down",
|
||||
highlightTexture = nil,
|
||||
},
|
||||
[1] = {
|
||||
tooltipText = NEED,
|
||||
newbieText = NEED_NEWBIE,
|
||||
normalTexture = "Interface\\Buttons\\UI-GroupLoot-Dice-Up",
|
||||
pushedTexture = "Interface\\Buttons\\UI-GroupLoot-Dice-Down",
|
||||
highlightTexture = "Interface\\Buttons\\UI-GroupLoot-Dice-Highlight",
|
||||
},
|
||||
[2] = {
|
||||
tooltipText = GREED,
|
||||
newbieText = GREED_NEWBIE,
|
||||
normalTexture = "Interface\\Buttons\\UI-GroupLoot-Coin-Up",
|
||||
pushedTexture = "Interface\\Buttons\\UI-GroupLoot-Coin-Down",
|
||||
highlightTexture = "Interface\\Buttons\\UI-GroupLoot-Coin-Highlight",
|
||||
},
|
||||
[3] = {
|
||||
tooltipText = ROLL_DISENCHANT,
|
||||
newbieText = ROLL_DISENCHANT_NEWBIE,
|
||||
normalTexture = "Interface\\Buttons\\UI-GroupLoot-DE-Up",
|
||||
pushedTexture = "Interface\\Buttons\\UI-GroupLoot-DE-Down",
|
||||
highlightTexture = "Interface\\Buttons\\UI-GroupLoot-DE-Highlight",
|
||||
},
|
||||
}
|
||||
|
||||
local reasons = {
|
||||
LOOT_ROLL_INELIGIBLE_REASON1,
|
||||
LOOT_ROLL_INELIGIBLE_REASON2,
|
||||
LOOT_ROLL_INELIGIBLE_REASON3,
|
||||
LOOT_ROLL_INELIGIBLE_REASON4,
|
||||
LOOT_ROLL_INELIGIBLE_REASON5,
|
||||
}
|
||||
|
||||
local function buttonOnEnter(self)
|
||||
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
|
||||
|
||||
GameTooltip:SetText(self.tooltipText)
|
||||
|
||||
if self.newbieText and SHOW_NEWBIE_TIPS == "1" then
|
||||
GameTooltip:AddLine(self.newbieText, 1, 0.82, 0, true)
|
||||
end
|
||||
|
||||
if self:IsEnabled() == 0 then
|
||||
GameTooltip:AddLine(self.reason, 1, 0.1, 0.1, true)
|
||||
end
|
||||
|
||||
for playerName, rollData in pairs(self.parent.rollResults) do
|
||||
if self.rollType == rollData[1] and rollData[2] then
|
||||
local classColor = E.media.herocolor
|
||||
GameTooltip:AddLine(playerName, classColor.r, classColor.g, classColor.b)
|
||||
end
|
||||
end
|
||||
|
||||
GameTooltip:Show()
|
||||
end
|
||||
|
||||
local function buttonOnLeave()
|
||||
GameTooltip:Hide()
|
||||
end
|
||||
|
||||
local function buttonOnClick(self)
|
||||
RollOnLoot(self.parent.rollID, self.rollType)
|
||||
end
|
||||
|
||||
local function toggleLootButton(self, state, reason, reasonValue)
|
||||
if state then
|
||||
self:Enable()
|
||||
self:SetAlpha(1)
|
||||
self.reason = nil
|
||||
SetDesaturation(self:GetNormalTexture(), false)
|
||||
else
|
||||
self:Disable()
|
||||
self:SetAlpha(0.2)
|
||||
self.reason = reasonValue and format(reasons[reason], reasonValue) or reasons[reason]
|
||||
SetDesaturation(self:GetNormalTexture(), true)
|
||||
end
|
||||
end
|
||||
|
||||
local function increaseRollCount(self, count)
|
||||
local text = self.text:GetText()
|
||||
if not text or text == "" then
|
||||
self.text:SetText(count or 1)
|
||||
else
|
||||
self.text:SetText(self.text:GetText() + (count or 1))
|
||||
end
|
||||
end
|
||||
|
||||
function M:CreateRollButton(parent, rollType)
|
||||
local data = rollTypes[rollType]
|
||||
|
||||
local button = CreateFrame("Button", nil, parent)
|
||||
button:Size(FRAME_HEIGHT - 4)
|
||||
button:SetNormalTexture(data.normalTexture)
|
||||
button:SetPushedTexture(data.highlightTexture)
|
||||
button:SetHighlightTexture(data.pushedTexture)
|
||||
|
||||
button:SetMotionScriptsWhileDisabled(true)
|
||||
button:SetScript("OnEnter", buttonOnEnter)
|
||||
button:SetScript("OnLeave", buttonOnLeave)
|
||||
button:SetScript("OnClick", buttonOnClick)
|
||||
|
||||
button.ToggleLootButton = toggleLootButton
|
||||
button.IncreaseRollCount = increaseRollCount
|
||||
button.parent = parent
|
||||
button.rollType = rollType
|
||||
button.tooltipText = data.tooltipText
|
||||
button.newbieText = data.newbieText
|
||||
|
||||
button.text = button:CreateFontString(nil, nil)
|
||||
button.text:FontTemplate(E.Media.Fonts.Homespun, nil, "MONOCHROMEOUTLINE")
|
||||
|
||||
return button
|
||||
end
|
||||
|
||||
local function itemOnEnter(self)
|
||||
GameTooltip:SetOwner(self, POSITION == "TOP" and "ANCHOR_BOTTOMLEFT" or "ANCHOR_TOPLEFT")
|
||||
GameTooltip:SetLootRollItem(self.rollID)
|
||||
|
||||
CursorUpdate(self)
|
||||
end
|
||||
|
||||
local function itemOnLeave()
|
||||
GameTooltip:Hide()
|
||||
ResetCursor()
|
||||
end
|
||||
|
||||
local function itemOnUpdate(self)
|
||||
if GameTooltip:IsOwned(self) then
|
||||
GameTooltip:SetOwner(self, POSITION == "TOP" and "ANCHOR_BOTTOMLEFT" or "ANCHOR_TOPLEFT")
|
||||
GameTooltip:SetLootRollItem(self.rollID)
|
||||
end
|
||||
|
||||
CursorOnUpdate(self)
|
||||
end
|
||||
|
||||
local function itemOnClick(self)
|
||||
if IsModifiedClick("CHATLINK") then
|
||||
ChatEdit_InsertLink(self.link)
|
||||
elseif IsModifiedClick("DRESSUP") then
|
||||
DressUpItemLink(self.link)
|
||||
end
|
||||
end
|
||||
|
||||
local function statusbarOnUpdate(self)
|
||||
local timeLeft = GetLootRollTimeLeft(self.parent.rollID)
|
||||
if timeLeft < 0 or timeLeft > self.parent.rollTime then
|
||||
timeLeft = 0
|
||||
else
|
||||
self.spark:Point("CENTER", self, "LEFT", (timeLeft / self.parent.rollTime) * self:GetWidth(), 0)
|
||||
end
|
||||
|
||||
self:SetValue(timeLeft)
|
||||
end
|
||||
|
||||
function M:CreateRollFrame()
|
||||
self.numFrames = self.numFrames + 1
|
||||
|
||||
local frame = CreateFrame("Frame", format("ElvUI_GroupLootFrame%d", self.numFrames), E.UIParent)
|
||||
frame:Size(FRAME_WIDTH, FRAME_HEIGHT)
|
||||
frame:SetTemplate()
|
||||
frame:SetFrameStrata("DIALOG")
|
||||
frame:Hide()
|
||||
|
||||
if POSITION == "TOP" then
|
||||
frame:Point("TOP", self.numFrames > 1 and self.RollBars[self.numFrames - 1] or AlertFrameHolder, "BOTTOM", 0, -4)
|
||||
else
|
||||
frame:Point("BOTTOM", self.numFrames > 1 and self.RollBars[self.numFrames - 1] or AlertFrameHolder, "TOP", 0, 4)
|
||||
end
|
||||
|
||||
local itemButton = CreateFrame("Button", "$parentIconFrame", frame)
|
||||
itemButton:Size(FRAME_HEIGHT - (E.Border * 2))
|
||||
itemButton:Point("RIGHT", frame, "LEFT", -(E.Spacing * 3), 0)
|
||||
itemButton:CreateBackdrop()
|
||||
itemButton:SetScript("OnEnter", itemOnEnter)
|
||||
itemButton:SetScript("OnLeave", itemOnLeave)
|
||||
itemButton:SetScript("OnUpdate", itemOnUpdate)
|
||||
itemButton:SetScript("OnClick", itemOnClick)
|
||||
itemButton.hasItem = 1
|
||||
frame.itemButton = itemButton
|
||||
|
||||
itemButton.icon = itemButton:CreateTexture(nil, "OVERLAY")
|
||||
itemButton.icon:SetAllPoints()
|
||||
itemButton.icon:SetTexCoord(unpack(E.TexCoords))
|
||||
|
||||
local fade = frame:CreateTexture(nil, "BORDER")
|
||||
fade:Point("TOPLEFT", frame, "TOPLEFT", 4, 0)
|
||||
fade:Point("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -4, 0)
|
||||
fade:SetTexture("Interface\\ChatFrame\\ChatFrameBackground")
|
||||
fade:SetBlendMode("ADD")
|
||||
fade:SetGradientAlpha("VERTICAL", 0.1, 0.1, 0.1, 0, 0.1, 0.1, 0.1, 0)
|
||||
frame.fade = fade
|
||||
|
||||
local status = CreateFrame("StatusBar", "$parentStatusBar", frame)
|
||||
status:SetInside()
|
||||
status:SetFrameLevel(status:GetFrameLevel() - 1)
|
||||
status:SetStatusBarTexture(E.media.normTex)
|
||||
status:SetStatusBarColor(0.8, 0.8, 0.8, 0.9)
|
||||
status.parent = frame
|
||||
E:RegisterStatusBar(status)
|
||||
status:SetScript("OnUpdate", statusbarOnUpdate)
|
||||
frame.status = status
|
||||
|
||||
status.bg = status:CreateTexture(nil, "BACKGROUND")
|
||||
status.bg:SetAlpha(0.1)
|
||||
status.bg:SetAllPoints()
|
||||
|
||||
local spark = frame:CreateTexture(nil, "OVERLAY")
|
||||
spark:Size(14, FRAME_HEIGHT)
|
||||
spark:SetTexture("Interface\\CastingBar\\UI-CastingBar-Spark")
|
||||
spark:SetBlendMode("ADD")
|
||||
status.spark = spark
|
||||
|
||||
frame.passButton = self:CreateRollButton(frame, 0)
|
||||
frame.needButton = self:CreateRollButton(frame, 1)
|
||||
frame.greedButton = self:CreateRollButton(frame, 2)
|
||||
frame.disenchantButton = self:CreateRollButton(frame, 3)
|
||||
|
||||
frame.needButton:SetHitRectInsets(0, 0, -2, 0)
|
||||
frame.greedButton:SetHitRectInsets(0, 0, -3, 1)
|
||||
frame.disenchantButton:SetHitRectInsets(0, 0, -2, 0)
|
||||
frame.passButton:SetHitRectInsets(0, 0, 0, -2)
|
||||
|
||||
frame.needButton:Point("LEFT", frame.itemButton, "RIGHT", 4, -1)
|
||||
frame.greedButton:Point("LEFT", frame.needButton, "RIGHT", 1, -1)
|
||||
frame.disenchantButton:Point("LEFT", frame.greedButton, "RIGHT", 0, 1)
|
||||
frame.passButton:Point("LEFT", frame.disenchantButton, "RIGHT", 0, 2)
|
||||
|
||||
frame.needButton.text:Point("CENTER", -1, 4)
|
||||
frame.greedButton.text:Point("CENTER", 1, 5)
|
||||
frame.disenchantButton.text:Point("CENTER", 1, 4)
|
||||
frame.passButton.text:Point("CENTER", 1, 2)
|
||||
|
||||
frame.bindText = frame:CreateFontString()
|
||||
frame.bindText:Point("LEFT", frame.passButton, "RIGHT", 2, 0)
|
||||
frame.bindText:FontTemplate(nil, nil, "OUTLINE")
|
||||
|
||||
local itemName = frame:CreateFontString(nil, "ARTWORK")
|
||||
itemName:FontTemplate(nil, nil, "OUTLINE")
|
||||
itemName:Point("LEFT", frame.bindText, "RIGHT", 1, 0)
|
||||
itemName:Point("RIGHT", frame, "RIGHT", -5, 0)
|
||||
itemName:Size(200, 10)
|
||||
itemName:SetJustifyH("LEFT")
|
||||
frame.itemName = itemName
|
||||
|
||||
frame.rollResults = {}
|
||||
frame.rollButtons = {
|
||||
[0] = frame.passButton,
|
||||
[1] = frame.needButton,
|
||||
[2] = frame.greedButton,
|
||||
[3] = frame.disenchantButton,
|
||||
}
|
||||
|
||||
tinsert(self.RollBars, frame)
|
||||
|
||||
return frame
|
||||
end
|
||||
|
||||
function M:ReleaseFrame(frame)
|
||||
frame:Hide()
|
||||
frame.rollID = nil
|
||||
frame.rollTime = nil
|
||||
|
||||
for i = 0, 3 do
|
||||
frame.rollButtons[i].text:SetText("")
|
||||
end
|
||||
|
||||
twipe(frame.rollResults)
|
||||
end
|
||||
|
||||
function M:GetFrame()
|
||||
for _, frame in ipairs(M.RollBars) do
|
||||
if not frame.rollID then
|
||||
return frame
|
||||
end
|
||||
end
|
||||
|
||||
return self:CreateRollFrame()
|
||||
end
|
||||
|
||||
function M:START_LOOT_ROLL(_, rollID, rollTime)
|
||||
local f = self:GetFrame()
|
||||
f.rollID = rollID
|
||||
f.rollTime = rollTime
|
||||
|
||||
local texture, name, count, quality, bindOnPickUp, canNeed, canGreed, canDisenchant, reasonNeed, reasonGreed, reasonDisenchant, deSkillRequired = GetLootRollItemInfo(rollID)
|
||||
|
||||
f.itemButton.icon:SetTexture(texture)
|
||||
f.itemButton.rollID = rollID
|
||||
f.itemButton.link = GetLootRollItemLink(rollID)
|
||||
|
||||
if count > 1 then
|
||||
f.itemName:SetFormattedText("%dx %s", count, name)
|
||||
else
|
||||
f.itemName:SetText(name)
|
||||
end
|
||||
|
||||
f.status:SetMinMaxValues(0, rollTime)
|
||||
f.status:SetValue(rollTime)
|
||||
|
||||
local color = ITEM_QUALITY_COLORS[quality]
|
||||
f.status:SetStatusBarColor(color.r, color.g, color.b, 0.7)
|
||||
f.status.bg:SetTexture(color.r, color.g, color.b)
|
||||
|
||||
f.bindText:SetText(bindOnPickUp and "BoP" or "BoE")
|
||||
f.bindText:SetVertexColor(bindOnPickUp and 1 or 0.3, bindOnPickUp and 0.3 or 1, bindOnPickUp and 0.1 or 0.3)
|
||||
|
||||
f.needButton:ToggleLootButton(canNeed, reasonNeed)
|
||||
f.greedButton:ToggleLootButton(canGreed, reasonGreed)
|
||||
f.disenchantButton:ToggleLootButton(canDisenchant, reasonDisenchant, deSkillRequired)
|
||||
|
||||
f:Show()
|
||||
|
||||
AlertFrame_FixAnchors()
|
||||
|
||||
if E.db.general.autoRoll and E.mylevel == MAX_PLAYER_LEVEL and quality == 2 and not bindOnPickUp then
|
||||
if canDisenchant then
|
||||
RollOnLoot(rollID, 3)
|
||||
else
|
||||
RollOnLoot(rollID, 2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function M:CANCEL_LOOT_ROLL(_, rollID)
|
||||
for _, frame in ipairs(self.RollBars) do
|
||||
if frame.rollID == rollID then
|
||||
self:ReleaseFrame(frame)
|
||||
E:StaticPopup_Hide("CONFIRM_LOOT_ROLL", self.rollID)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function M:ParseRollChoice(msg)
|
||||
for regex, rollType in pairs(rollMessages) do
|
||||
local _, _, playerName, itemName = find(msg, regex)
|
||||
|
||||
if playerName and itemName and playerName ~= "Everyone" then
|
||||
if locale == "ruRU" and rollType ~= 0 then
|
||||
playerName, itemName = itemName, playerName
|
||||
end
|
||||
|
||||
return playerName, itemName, rollType
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function M:CHAT_MSG_LOOT(_, msg)
|
||||
local playerName, itemName, rollType = self:ParseRollChoice(msg)
|
||||
|
||||
if playerName and itemName then
|
||||
local _, class = UnitClass(playerName)
|
||||
|
||||
for _, frame in ipairs(self.RollBars) do
|
||||
if frame.rollID and frame.itemButton.link == itemName and not frame.rollResults[playerName] then
|
||||
frame.rollResults[playerName] = {rollType, class}
|
||||
frame.rollButtons[rollType]:IncreaseRollCount()
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function M:LoadLootRoll()
|
||||
if not E.private.general.lootRoll then return end
|
||||
|
||||
self.numFrames = 0
|
||||
|
||||
self:RegisterEvent("CHAT_MSG_LOOT")
|
||||
self:RegisterEvent("START_LOOT_ROLL")
|
||||
self:RegisterEvent("CANCEL_LOOT_ROLL")
|
||||
|
||||
UIParent:UnregisterEvent("START_LOOT_ROLL")
|
||||
UIParent:UnregisterEvent("CANCEL_LOOT_ROLL")
|
||||
|
||||
for i = 1, NUM_GROUP_LOOT_FRAMES do
|
||||
_G["GroupLootFrame"..i]:UnregisterEvent("CANCEL_LOOT_ROLL")
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user