dobrograd-13-06-2022/garrysmod/addons/admin-tickets/lua/cats/client.lua

675 lines
18 KiB
Lua
Raw Normal View History

2023-11-16 15:01:19 +05:00
--#################################################
-- Main frame
--#################################################
---------------------------------------------------
-- FONTS
---------------------------------------------------
surface.CreateFont("cats.small", {
font = "Roboto Bold",
extended = true,
size = 16,
weight = 500,
})
surface.CreateFont("cats.small-underline", {
font = "Roboto Bold",
extended = true,
underline = true,
size = 16,
weight = 500,
})
---------------------------------------------------
-- HELPER FUNCTIONS
---------------------------------------------------
-- apply table to action button
local buttons -- declare here, fill later
local function applyButton(pnl, name, ply, steamID)
local data = buttons[name]
if not data then data = {
tooltip = 'error',
icon = Material(octolib.icons.silk16('error')),
click = function() end
} end
pnl:SetToolTip(data.tooltip)
pnl.icon = data.icon
pnl.DoClick = function(self) data.click(self, ply, steamID) end
end
-- nice time
local function niceTime(time)
local h, m, s
h = math.floor(time / 60 / 60)
m = math.floor(time / 60) % 60
s = math.floor(time) % 60
return string.format("%02i:%02i:%02i", h, m, s)
end
---------------------------------------------------
-- CACHE
---------------------------------------------------
-- icons cache
local icons = {
action_claim = Material(octolib.icons.silk16('accept_button')),
action_unclaim = Material(octolib.icons.silk16('cancel')),
actions = Material(octolib.icons.silk16('text_list_bullets')),
action_callon = Material(octolib.icons.silk16('lightbulb_off')),
action_calloff = Material(octolib.icons.silk16('lightbulb')),
action_close = Material(octolib.icons.silk16('application_form_delete')),
noStar = Material(octolib.icons.silk16('bullet_white')),
star = Material(octolib.icons.silk16('star')),
}
-- button tables
buttons = {
action_claim = {
tooltip = cats.lang.action_claim,
icon = icons.action_claim,
click = function(self, ply, steamID)
netstream.Start('cats.claimTicket', steamID, true)
applyButton(self, 'action_unclaim', ply, steamID)
end
},
action_unclaim = {
tooltip = cats.lang.action_unclaim,
icon = icons.action_unclaim,
click = function(self, ply, steamID)
netstream.Start('cats.claimTicket', steamID, false)
applyButton(self, 'action_claim', ply, steamID)
end
},
actions = {
tooltip = cats.lang.actions,
icon = icons.actions,
click = function(self, ply)
local m = DermaMenu()
for i, act in ipairs( cats.config.commands ) do
m:AddOption( act.text, function()
act.click(ply)
end):SetIcon(octolib.icons.silk16(act.icon or 'wand'))
end
m:SetPos( input.GetCursorPos() )
m:Open()
end
},
action_callon = {
tooltip = cats.lang.action_callon,
icon = icons.action_callon,
click = function(self, ply)
applyButton(self, 'action_calloff', ply, steamID)
end
},
action_calloff = {
tooltip = cats.lang.action_calloff,
icon = icons.action_calloff,
click = function(self, ply)
applyButton(self, 'action_callon', ply, steamID)
end
},
action_close = {
tooltip = cats.lang.action_close,
icon = icons.action_close,
click = function(self, ply, steamID)
netstream.Start('cats.closeTicket', steamID)
end
},
}
-- default button list
local actionList = {
'action_claim',
'actions',
-- 'action_callon',
'action_close',
}
-- debug ticket data
local debugTicket = {
user = LocalPlayer(),
userID = 'STEAM_X:X:XXXXXXXX',
admin = LocalPlayer(),
adminID = 'STEAM_X:X:XXXXXXXX',
chatLog = {
{"Зюзя", "Админ тп, застрял", false},
{"СуперВася", "Ща, погоди", true},
{"Зюзя", "Ну где вы???", false},
{"УберПетя", "Бля, Вася, да вытащи ты его уже, наконец, он заебал вопить, как малое дите, сука, ебаный в рот", true},
{"СуперВася", "Ну ща-ща, я дорешаю жалобу", true},
{"УберПетя", "Да с хера ли ты берешь столько жалоб? Разберись сначала с одной, потом уж на другие иди", true},
{"СуперВася", "Да хорошо, блять, но дай сейчас-то разберусь", true},
{"Зюзя", "Идите оба нахуй, я выбрался уже", false},
}
}
-- my ticket
local myTicket
---------------------------------------------------
-- MAIN CODE
---------------------------------------------------
-- add a ticket frame to container
local function addTicketToFrame( data )
-- sound notification
surface.PlaySound(cats.config.newTicketSound)
-- ticket panel
local t = cats.ticketContainer:Add("DButton")
t:SetSize(cats.config.spawnSize[1], 180)
t:SetText('')
t.expanded = true
t.ticket = data -- apply ticket
t.Paint = function(self, w, h)
local user, admin = self.ticket.user, self.ticket.admin
surface.SetDrawColor(30,40,50, 220)
surface.DrawRect(0, 0, w, h)
if self.Hovered then
surface.SetDrawColor(255,255,255, 2)
surface.DrawRect(0, 0, w, h)
end
surface.SetDrawColor(0,0,0, 255)
surface.DrawLine(0, -1, 0, h)
surface.DrawLine(-1, h-1, w, h-1)
surface.DrawLine(w-1, h, w-1, -1)
local time = '(' .. os.date( "%M:%S", CurTime() - self.ticket.created ) .. ')'
local userName = IsValid(user) and user:Name() or cats.lang.userDisconnected
draw.SimpleText(time .. ' ' .. userName, 'cats.small', 8, 15, Color(220,220,220), TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
if IsValid(admin) then
draw.SimpleText(admin:Name(), 'cats.small', w-8, 15, Color(180,200,240), TEXT_ALIGN_RIGHT, TEXT_ALIGN_CENTER)
end
end
t.DoClick = function(self)
self.expanded = not self.expanded
for i,v in ipairs(cats.ticketContainer:GetChildren()) do
-- if self ~= v then v.expanded = false end
v:InvalidateLayout(true)
end
cats.ticketContainer:Layout()
timer.Simple(0, function()
self.chatLog:GotoTextEnd()
end)
end
t.PerformLayout = function(self)
self:SetSize(self:GetParent():GetWide(), self.expanded and 180 or 30)
self.controls:SetVisible(self.expanded)
end
-- controls for ticket
local c = vgui.Create("DPanel", t)
c:DockMargin(1,1,1,1)
c:Dock(BOTTOM)
c:SetTall(150)
c.Paint = function() end
t.controls = c
-- action buttons for controls
t.controls.buttons = {}
for i, v in pairs(actionList) do
local b = vgui.Create("DButton", c)
b:SetSize(30, 30)
b:SetPos(0, (i-1)*30)
b:SetText('')
b.Paint = function(self, w, h)
if self.Hovered then draw.RoundedBox(0, 0, 0, w, h, Color(255,255,255,2)) end
surface.SetMaterial(self.icon)
surface.SetDrawColor(255,255,255)
surface.DrawTexturedRect(7, 7, 16, 16)
end
applyButton(b, v, t.ticket.user, t.ticket.userID)
t.controls.buttons[v] = b
end
-- chat
local cp = vgui.Create("DPanel", t.controls)
cp:Dock(FILL)
cp:DockMargin(30,0,0,0)
cp.Paint = function(self, w, h)
surface.SetDrawColor(0,0,0, 100)
surface.DrawRect(0, 0, w, h-20)
surface.SetDrawColor(0,0,0, 255)
surface.DrawLine(0, h, 0, 0)
surface.DrawLine(-1, 0, w, 0)
surface.DrawLine(-1, h-21, w, h-21)
end
t.chat = cp
-- chat entry
local ce = vgui.Create("DButton", t.chat)
ce:Dock(BOTTOM)
ce:SetText('')
ce:SetTall(20)
ce:SetCursor('beam')
ce.Paint = function(self, w, h)
if self.Hovered then draw.RoundedBox(0, 0, 0, w, h, Color(255,255,255,1)) end
draw.SimpleText(cats.lang.sendMessage, 'cats.small', 8, 10, Color(220,220,220, 50), TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
end
ce.DoClick = function(self)
Derma_StringRequest(
cats.lang.sendMessage, cats.lang.typeYourMessage, '',
function(val)
netstream.Start('cats.dispatchMessage', data.userID, val)
end,
nil,
cats.lang.ok, cats.lang.cancel
)
end
-- chat log
local cl = vgui.Create("RichText", t.chat)
cl:Dock(FILL)
cl.Paint = function(self) -- WHAT. THE. FUCK.
self.m_FontName = "cats.small"
self:SetFontInternal("cats.small")
self:SetUnderlineFont("cats.small-underline")
self:SetBGColor(Color(0,0,0,0))
self.Paint = nil
end
cl.ActionSignal = function(_, name, val)
if name == "TextClicked" then
octoesc.OpenURL(val)
end
end
t.chatLog = cl
cats.ticketContainer[data.userID] = t
cats.ticketFrame:PerformLayout()
end
local function addTicketChatLog(steamID, sender, msg, isAdmin)
octolib.func.chain({
function(reply)
if not octolib.string.isSteamID(sender) then return reply(sender) end
steamworks.RequestPlayerInfo(util.SteamIDTo64(sender), reply)
end,
function(_, name)
local cl = cats.ticketContainer[steamID].chatLog
if not IsValid(cl) then return end
if isAdmin then
cl:InsertColorChange(50,120,180, 255)
else
cl:InsertColorChange(180,160,50, 255)
end
cl:AppendText("\n" .. name)
cl:InsertColorChange(220,220,220, 255)
cl:AppendText(": ")
local data = octolib.string.splitByUrl(msg)
for _,v in ipairs(data) do
if type(v) == 'string' then
cl:AppendText(v)
else
cl:InsertClickableTextStart(v[1])
cl:InsertColorChange(0, 130, 255, 255)
cl:AppendText(v[1])
cl:InsertColorChange(220,220,220, 255)
cl:InsertClickableTextEnd()
end
end
end,
})
end
-- generate main frame
hook.Add("PlayerFinishedLoading", "cats", function()
if IsValid(cats.ticketFrame) then cats.ticketFrame:Remove() end
local w, h = cats.config.spawnSize[1], cats.config.spawnSize[2]
local x, y = cats.config.spawnPosAdmin[1], cats.config.spawnPosAdmin[2]
-- main frame
local p0 = vgui.Create("DFrame")
p0:SetSize(w, h)
p0:SetPos(x, y)
p0:DockPadding(0, 24, 0, 0)
p0:SetTitle('')
p0:ShowCloseButton(false)
cats.ticketFrame = p0
-- scroll panel
local p1 = vgui.Create("DScrollPanel", p0)
p1:Dock(FILL)
local oldLayout = p1.PerformLayout
p1.PerformLayout = function(self)
oldLayout(self)
for i, v in ipairs(cats.ticketContainer:GetChildren()) do
v:InvalidateLayout()
end
end
local oldThink = p0.Think
function p0:Think()
if isfunction(oldThink) then oldThink(self) end
p1:SetVisible(hook.Run('HUDShouldDraw', 'cats') ~= false)
end
-- icon layout
local p2 = vgui.Create("DIconLayout", p1)
p2:Dock(FILL)
p2:SetSpaceX(0)
p2:SetSpaceY(0)
cats.ticketContainer = p2
-- finish up main frame with some spicy hooks
local oldLayout = p0.PerformLayout
p0.PerformLayout = function(self)
oldLayout(self)
self:SetTall( math.min(p2:GetTall(), ScrH() - 100, 600) + 27 )
self:SetVisible(#p2:GetChildren() > 0)
end
p0.Paint = function(self, w, h)
if hook.Run('HUDShouldDraw', 'cats') == false then return end
surface.SetDrawColor(30,40,50, 255)
surface.DrawRect(0, 0, w, 24)
surface.SetDrawColor(0,0,0, 255)
surface.DrawOutlinedRect(0, 0, w, 24)
draw.SimpleText(cats.lang.openTickets .. ' (' .. #p2:GetChildren() .. ')', 'cats.small', 8, 12, Color(220,220,220), TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
end
end)
-- my ticket frame
if IsValid(cats.myTicketFrame) then cats.myTicketFrame:Remove() end
local function createMyTicket( data )
-- sound notification
surface.PlaySound(cats.config.newTicketSound)
myTicket = data
local w, h = cats.config.spawnSize[1], cats.config.spawnSize[2]
local x, y = cats.config.spawnPosUser[1], cats.config.spawnPosUser[2]
-- ticket frame
local t = vgui.Create("DFrame")
t:ShowCloseButton(false)
t:SetSize(w, 220)
t:SetPos(x, y)
t:DockPadding(0,30,0,0)
t:SetTitle('')
t.ticket = myTicket -- apply ticket
t.Paint = function(self, w, h)
if not self.visible then return end
local user, admin = self.ticket.user, self.ticket.admin
surface.SetDrawColor(30,40,50, 220)
surface.DrawRect(0, 0, w, h)
surface.SetDrawColor(0,0,0, 255)
surface.DrawOutlinedRect(0,0,w,h)
local time = '(' .. os.date( "%M:%S", CurTime() - self.ticket.created ) .. ')'
draw.SimpleText(time .. ' ' .. cats.lang.myTicket, 'cats.small', 8, 15, Color(220,220,220), TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
if IsValid(admin) then
draw.SimpleText(admin:Name(), 'cats.small', w-8, 15, Color(180,200,240), TEXT_ALIGN_RIGHT, TEXT_ALIGN_CENTER)
end
end
-- close ticket button
surface.SetFont('cats.small')
local tw, th = surface.GetTextSize(cats.lang.action_close)
local b = vgui.Create("DButton", t)
b:SetText('')
b:SetSize(tw + 16,30)
b:AlignRight(1)
b.Paint = function(self, w, h)
if self.Hovered then draw.RoundedBox(0, 0, 0, w, h, Color(255,255,255,1)) end
draw.SimpleText(cats.lang.action_close, 'cats.small', w/2, h/2, Color(220,220,220), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
b.DoClick = function(self)
netstream.Start('cats.closeTicket', LocalPlayer():SteamID())
end
t.closeBut = b
-- chat
local cp = vgui.Create("DPanel", t)
cp:Dock(FILL)
cp.Paint = function(self, w, h)
surface.SetDrawColor(0,0,0, 100)
surface.DrawRect(0, 0, w, h-20)
surface.SetDrawColor(0,0,0, 255)
surface.DrawLine(0, h, 0, 0)
surface.DrawLine(-1, 0, w, 0)
surface.DrawLine(-1, h-21, w, h-21)
end
t.chat = cp
-- chat entry
local ce = vgui.Create("DButton", t.chat)
ce:Dock(BOTTOM)
ce:SetText('')
ce:SetTall(20)
ce:SetCursor('beam')
ce.Paint = function(self, w, h)
if self.Hovered then draw.RoundedBox(0, 0, 0, w, h, Color(255,255,255,1)) end
draw.SimpleText(cats.lang.sendMessage, 'cats.small', 8, 10, Color(220,220,220, 50), TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
end
ce.DoClick = function(self)
Derma_StringRequest(
cats.lang.sendMessage, cats.lang.typeYourMessage, '',
function(val)
netstream.Start('cats.dispatchMessage', LocalPlayer():SteamID(), val)
end,
nil,
cats.lang.ok, cats.lang.cancel
)
end
-- chat log
local cl = vgui.Create("RichText", t.chat)
cl:Dock(FILL)
cl.Paint = function(self) -- WHAT. THE. FUCK.
self.m_FontName = "cats.small"
self:SetFontInternal("cats.small")
self:SetUnderlineFont("cats.small-underline")
self:SetBGColor(Color(0,0,0,0))
self.Paint = nil
end
cl.ActionSignal = function(_, name, val)
if name == "TextClicked" then
octoesc.OpenURL(val)
end
end
t.chatLog = cl
-- You can't just set visibility for self because it stops thinking
local oldThink = t.Think
function t:Think()
if isfunction(oldThink) then oldThink(self) end
self.visible = hook.Run('HUDShouldDraw', 'cats') ~= false
if not (myTicket and myTicket.adminID) then
b:SetVisible(self.visible)
end
cp:SetVisible(self.visible)
ce:SetVisible(self.visible)
cl:SetVisible(self.visible)
end
cats.myTicketFrame = t
end
local function addMyTicketChatLog(sender, msg, isAdmin)
octolib.func.chain({
function(reply)
if not octolib.string.isSteamID(sender) then return reply(sender) end
steamworks.RequestPlayerInfo(util.SteamIDTo64(sender), reply)
end,
function(_, name)
local cl = cats.myTicketFrame.chatLog
if not IsValid(cl) then return end
if isAdmin then
cl:InsertColorChange(50,120,180, 255)
else
cl:InsertColorChange(180,160,50, 255)
end
cl:AppendText("\n" .. name)
cl:InsertColorChange(220,220,220, 255)
cl:AppendText(": ")
local data = octolib.string.splitByUrl(msg)
for _,v in ipairs(data) do
if type(v) == 'string' then
cl:AppendText(v)
else
cl:InsertClickableTextStart(v[1])
cl:InsertColorChange(0, 130, 255, 255)
cl:AppendText(v[1])
cl:InsertColorChange(220,220,220, 255)
cl:InsertClickableTextEnd()
end
end
end,
})
end
netstream.Hook('cats.dispatchMessage', function(steamID, sender, msg)
if steamID == LocalPlayer():SteamID() then
if sender ~= LocalPlayer():SteamID() then surface.PlaySound(cats.config.newTicketSound) end
if myTicket then
addMyTicketChatLog(sender, msg, sender ~= LocalPlayer():SteamID())
else
createMyTicket({created = CurTime()})
addMyTicketChatLog(sender, msg, sender ~= LocalPlayer():SteamID())
end
elseif IsValid(cats.ticketContainer[steamID]) then
addTicketChatLog(steamID, sender, msg, sender ~= steamID)
else
local user = player.GetBySteamID(steamID)
-- if not IsValid(user) then return end
addTicketToFrame({
user = user,
userID = steamID,
created = CurTime(),
})
addTicketChatLog(steamID, sender, msg, sender ~= steamID)
end
end)
netstream.Hook('cats.claimTicket', function(steamID, admin, doClaim)
if not IsValid(admin) then return end
if steamID == LocalPlayer():SteamID() and myTicket then
myTicket.admin = doClaim and admin or nil
myTicket.adminID = doClaim and admin:SteamID() or nil
cats.myTicketFrame.closeBut:SetVisible(not doClaim)
elseif IsValid(cats.ticketContainer[steamID]) then
local ticket = cats.ticketContainer[steamID].ticket
ticket.admin = doClaim and admin or nil
ticket.adminID = doClaim and admin:SteamID() or nil
if ticket.adminID ~= LocalPlayer():SteamID() then
local b = cats.ticketContainer[steamID].controls.buttons['action_claim']
local user = player.GetBySteamID(ticket.userID)
if doClaim then
applyButton(b, 'action_unclaim', user, ticket.userID)
b:SetEnabled(false)
else
applyButton(b, 'action_claim', user, ticket.userID)
b:SetEnabled(true)
end
end
end
end)
netstream.Hook('cats.closeTicket', function(steamID)
if steamID == LocalPlayer():SteamID() and myTicket then
cats.myTicketFrame:Remove()
myTicket = nil
elseif IsValid(cats.ticketContainer[steamID]) then
cats.ticketContainer[steamID].ticket = nil
cats.ticketContainer[steamID]:Remove()
cats.ticketFrame:PerformLayout()
end
end)
netstream.Hook('cats.syncTickets', function(tickets)
for steamID, t in pairs(tickets) do
local user = player.GetBySteamID(steamID)
if not IsValid(user) then continue end
addTicketToFrame({
user = user,
userID = steamID,
created = t.createdGameTime,
admin = t.admin,
adminID = t.adminID
})
if IsValid(t.admin) then
local b = cats.ticketContainer[steamID].controls.buttons['action_claim']
applyButton(b, 'action_unclaim', t.user, steamID)
b:SetEnabled(false)
end
for k, v in pairs(t.chatLog) do
addTicketChatLog(steamID, v[1], v[2], v[3])
end
end
end)
concommand.Add("cats_test_admin", function()
local steamID = LocalPlayer():SteamID()
addTicketToFrame({
user = LocalPlayer(),
userID = steamID,
created = CurTime(),
admin = LocalPlayer(),
adminID = steamID,
})
addTicketChatLog(steamID, "chelog", "Admin let me test my ticket!", false)
addTicketChatLog(steamID, "Admin", "Alright.", true)
end)
concommand.Add("cats_test_admin_clear", function()
cats.ticketContainer:Clear()
end)
concommand.Add("cats_test_myticket", function()
createMyTicket({created = CurTime()})
addMyTicketChatLog("chelog", "Admin let me test my ticket!", false)
addMyTicketChatLog("Admin", "Alright.", true)
end)