dobrograd-13-06-2022/garrysmod/addons/admin-tickets/lua/cats/client.lua
Jonny_Bro (Nikita) e4d5311906 first commit
2023-11-16 15:01:19 +05:00

674 lines
18 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

--#################################################
-- 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)