--[[ | This file was obtained through the combined efforts | of Madbluntz & Plymouth Antiquarian Society. | | Credits: lifestorm, Gregory Wayne Rossel JR., | Maloy, DrPepper10 @ RIP, Atle! | | Visit for more: https://plymouth.thetwilightzone.ru/ --]] if SAM_LOADED then return end local sam = sam local netstream = sam.netstream local SUI = sam.SUI local config = sam.config local Trim = string.Trim local muted_var = CreateClientConVar("sam_mute_reports", "0", false, false, "", 0, 1) local position = config.get_updated("Reports.Position", "Left") local max_reports = config.get_updated("Reports.MaxReports", 4) local always_show = config.get_updated("Reports.AlwaysShow", true) local pad_x = config.get_updated("Reports.XPadding", 5) local pad_y = config.get_updated("Reports.YPadding", 5) local duty_jobs = {} config.hook({"Reports.DutyJobs"}, function() local jobs = config.get("Reports.DutyJobs", ""):Split(",") for i = #jobs, 1, -1 do local v = Trim(jobs[i]) if v ~= "" then jobs[v] = true end jobs[i] = nil end duty_jobs = jobs end) local commands = {} config.hook({"Reports.Commands"}, function() local cmds = config.get("Reports.Commands", ""):Split(",") for i = 1, #cmds do local v = Trim(cmds[i]) if v ~= "" then cmds[i] = { name = v, func = function(_, ply) if IsValid(ply) then RunConsoleCommand("sam", v, "#" .. ply:EntIndex()) end end } end end commands = cmds end) local reports = {} local queued_reports = {} local new_report, remove_report, check_queued, get_report, append_report get_report = function(ply, index) for i = 1, #reports do local v = reports[i] local _ply = index and v.index or v.ply if _ply == ply then return v end end for i = 1, #queued_reports do local v = queued_reports[i] local _ply = index and v.index or v.ply if _ply == ply then return v, i end end end remove_report = function(ply) local report, delayed_i = get_report(ply) if delayed_i then return table.remove(queued_reports, delayed_i) end local panel = report.panel panel:MoveToNewX(position.value == "Right" and ScrW() or -panel:GetWide(), function() for i = report.pos + 1, #reports do local v = reports[i] v.pos = v.pos - 1 v.panel:MoveToNewY(v.panel:GetY()) end panel:Remove() table.remove(reports, report.pos) check_queued() end) end check_queued = function() while (max_reports.value - #reports > 0 and #queued_reports > 0) do new_report(table.remove(queued_reports, 1)) end end append_report = function(ply, text) local report, delayed = get_report(ply) if delayed then table.insert(report.comments, text) else report.panel:AddComment(text) end end new_report = function(report) if #reports >= max_reports.value then return table.insert(queued_reports, report) end report.pos = table.insert(reports, report) local panel = vgui.Create("SAM.Report") panel:SetReport(report) for k, v in ipairs(commands) do panel:AddButton(v.name:gsub("^%l", string.upper), v.func) end local claim = panel:AddButton("Claim", function(self, ply) if panel:HasReport() then return LocalPlayer():sam_send_message("You have an active case, close it first.") end self.DoClick = function() end local claim_query = netstream.async.Start("ClaimReport", nil, ply) claim_query:done(function(claimed) if not IsValid(panel) then return end if claimed then panel:SetHasReport(ply) self:SetText("Close") self.background = Color(231, 76, 60, 200) self.hover = Color(255, 255, 255, 25) panel:FixWide() for k, v in ipairs(panel:GetChildren()[3]:GetChildren()) do v:SetDisabled(false) v:SetCursor("hand") end self.DoClick = function() panel:Close() end else panel:SetClaimed() end end) end) local dismiss = panel:AddButton("Dismiss", function(self, ply) self.DoClick = function() end panel:Close() netstream.Start("DismissReport", ply) end) panel.claim = claim claim:SetCursor("hand") claim:SetDisabled(false) claim.background = Color(39, 174, 96, 200) claim.hover = Color(255, 255, 255, 25) panel.dismiss = dismiss dismiss:SetCursor("hand") dismiss:SetDisabled(false) dismiss.background = Color(231, 76, 60, 200) dismiss.hover = Color(255, 255, 255, 25) panel:FixWide() local x = pad_x.value if position.value == "Right" then x = (ScrW() - panel:GetWide()) - x end panel:MoveToNewX(x) panel:MoveToNewY(panel:GetY()) panel.new = true for k, v in ipairs(report.comments) do panel:AddComment(v) end panel.new = nil end netstream.Hook("Report", function(ply, comment) if not IsValid(ply) then return end if muted_var:GetBool() then return end local report = get_report(ply) if not report then report = { ply = ply, index = ply:EntIndex(), comments = {comment} } system.FlashWindow() if not always_show.value and not duty_jobs[team.GetName(LocalPlayer():Team())] then LocalPlayer():sam_send_message("({S Blue}) {S_2 Red}: {S_3}", { S = "Report", S_2 = ply:Name(), S_3 = comment }) else new_report(report) end else append_report(ply, comment) end end) netstream.Hook("ReportClaimed", function(ply) local report, delayed = get_report(ply) if not report then return end if delayed then table.remove(queued_reports, delayed) else report.panel:SetClaimed() end end) netstream.Hook("ReportClosed", function(index) local report, delayed = get_report(index, true) if not report then return end if delayed then table.remove(queued_reports, delayed) else report.panel:SetClosed() end end) do local REPORTS_HEADER = SUI.CreateFont("ReportHeader", "Roboto", 14, 540) local REPORT_COMMENT = SUI.CreateFont("ReportComment", "Roboto", 13, 540) local REPORT_BUTTONS = SUI.CreateFont("ReportButtons", "Roboto", 13, 550) local Panel = {} function Panel:Init() sui.TDLib.Start() self:Blur() :Background(Color(30, 30, 30, 240)) local p_w, p_h = SUI.Scale(350), SUI.Scale(145) self:SetSize(p_w, p_h) local x = p_w * 2 if position.value == "Right" then x = ScrW() + x else x = -x end self:SetPos(x, -p_h) local top_panel = self:Add("Panel") top_panel:Dock(TOP) top_panel:SetTall(SUI.Scale(24)) top_panel:Background(Color(60, 60, 60, 200)) local ply_name = top_panel:Add("DLabel") ply_name:Dock(LEFT) ply_name:DockMargin(5, 0, 0, 0) ply_name:SetTextColor(Color(200, 200, 200)) ply_name:SetFont(REPORTS_HEADER) self.ply_name = ply_name local scroll = self:Add("SAM.ScrollPanel") scroll:Dock(FILL) scroll:DockMargin(5, 5, 5, 5) scroll.Paint = nil self.scroll = scroll local comment = scroll:Add("DLabel") comment:Dock(TOP) comment:SetText("") comment:SetTextColor(Color(200, 200, 200)) comment:SetFont(REPORT_COMMENT) comment:SetMultiline(true) comment:SetWrap(true) comment:SetAutoStretchVertical(true) self.comment = comment local bottom = self:Add("Panel") bottom:Dock(BOTTOM) bottom:SetTall(SUI.Scale(24)) self.bottom = bottom sui.TDLib.End() end function Panel:GetY() return (self:GetTall() + 5) * (self.report.pos - 1) + pad_y.value end function Panel:Close() remove_report(self.report.ply) end local change_state = function(self, text) self.claim:SetText(text) self.claim.DoClick = function() end self.claim:SUI_TDLib() :Background(Color(41, 128, 185, 200)) timer.Simple(5, function() if IsValid(self) then self:Close() end end) if self:HasReport() == self.report.ply then self:SetHasReport() end self:FixWide() end function Panel:SetClaimed() change_state(self, "Case clamied!") end function Panel:SetClosed() change_state(self, "Case closed!") end function Panel:SetReport(report) surface.PlaySound("garrysmod/balloon_pop_cute.wav") report.panel = self self.report = report self.ply_name:SetText(report.ply:Name() .. " (" .. report.ply:SteamName() .. ")") self.ply_name:SetWide(self:GetWide()) end local disabled = Color(60, 60, 60, 200) local click = Color(255, 255, 255, 30) local button_paint = function(self, w, h) draw.RoundedBox(0, 0, 0, w, h, self.background) if self:GetDisabled() then draw.RoundedBox(0, 0, 0, w, h, disabled) else if self:IsHovered() then draw.RoundedBox(0, 0, 0, w, h, self.hover) end if self.Depressed then draw.RoundedBox(0, 0, 0, w, h, click) end end end local button_click = function(self) self.cb(self, self.report.ply) end local background = Color(60, 60, 60, 200) local hover = Color(14, 134, 204, 100) function Panel:AddButton(text, cb) local button = self.bottom:Add("DButton") button:Dock(LEFT) button:SetText(text) button:SetTextColor(Color(200, 200, 200)) button:SetFont(REPORT_BUTTONS) button:SetDisabled(true) button:SetCursor("arrow") button.Paint = button_paint button.DoClick = button_click button.background = background button.hover = hover button.cb = cb button.report = self.report return button end function Panel:FixWide() local wide = 0 for _, v in ipairs(self.bottom:GetChildren()) do v:SizeToContents() v:SetWide(v:GetWide() + 6) wide = wide + v:GetWide() end self:SetWide(wide) return wide end function Panel:OnRemove() local reporter = self:HasReport() if reporter then netstream.Start("CloseReport", reporter) self:SetHasReport() end end function Panel:AddComment(text) local comment = self.comment local old_text = comment:GetText() if old_text ~= "" then old_text = old_text .. "\n" end if not self.new then surface.PlaySound("ambient/water/drip4.wav") end comment:SetText(old_text .. "- " .. text) comment:SizeToContents() self.scroll:ScrollToBottom() end function Panel:HasReport() return LocalPlayer().sam_has_report end function Panel:SetHasReport(v) LocalPlayer().sam_has_report = v end local new_animation = function(panel, name) local new_name = "anim_" .. name panel["MoveToNew" .. name:upper()] = function(self, new, cb) if self[new_name] then table.RemoveByValue(self.m_AnimList, self[new_name]) end self[new_name] = self:NewAnimation(0.2, 0, -1, function() self[new_name] = nil if cb then cb() end end) self[new_name].Think = function(_, _, frac) self[name] = Lerp(frac, self[name], new) end end end new_animation(Panel, "x") new_animation(Panel, "y") vgui.Register("SAM.Report", Panel, "EditablePanel") end