9ab34fe802
Implement comprehensive client-side menu for the warning system including: - Responsive player list with search functionality - Warning history display with details - Modal dialog for applying warnings with reason selection - Custom styling with color palette and rounded corners - Font Awesome icon integration - Dynamic font scaling based on screen resolution - Network communication for warning actions
530 lines
20 KiB
Lua
530 lines
20 KiB
Lua
local rndx = include("lib/rndx.lua")
|
|
local logo = Material("nocode/announcement/logo.png")
|
|
|
|
BladW = BladW or {}
|
|
BladW.Warn = BladW.Warn or {}
|
|
BladW.Warn.List = BladW.Warn.List or {}
|
|
|
|
-- palette
|
|
local COL_BG = Color(24, 24, 28)
|
|
local COL_PANEL = Color(38, 38, 44)
|
|
local COL_INPUT = Color(30, 30, 35)
|
|
local COL_LINE = Color(255, 255, 255, 22)
|
|
local COL_TEXT = Color(236, 236, 240)
|
|
local COL_MUTED = Color(150, 150, 160)
|
|
local COL_BLUE = Color(74, 108, 224)
|
|
local COL_RED = Color(226, 76, 76)
|
|
local COL_GREEN = Color(45, 170, 96)
|
|
local COL_ORANGE = Color(232, 168, 32)
|
|
local COL_SEL = Color(50, 50, 60)
|
|
local COL_BAR = Color(92, 92, 104) -- petit bout à gauche des joueurs (non sélectionné)
|
|
local COL_BADGE = Color(22, 22, 26) -- fond du badge "warns actifs" (gris foncé)
|
|
|
|
local function Lighten(c, a)
|
|
a = a or 22
|
|
return Color(math.min(c.r + a, 255), math.min(c.g + a, 255), math.min(c.b + a, 255), c.a)
|
|
end
|
|
|
|
-- ============================================================
|
|
-- FontAwesome (vrais glyphes)
|
|
-- Le .ttf doit être chargé côté client : dépose un fa-solid-900.ttf
|
|
-- dans resource/fonts/. Change FA_FONT si ta police a un autre nom
|
|
-- de famille, et les codepoints si ta version de FA diffère.
|
|
-- ============================================================
|
|
local FA_FONT = "Font Awesome 6 Free Solid" -- nom de famille Windows (name ID 1), pas le typo "Font Awesome 6 Free"
|
|
local FA = {
|
|
search = 0xf002, -- magnifying-glass
|
|
shield = 0xf3ed, -- shield-halved (shield-alt en FA5)
|
|
card = 0xf2c2, -- id-card
|
|
copy = 0xf0c5, -- copy
|
|
trash = 0xf1f8, -- trash
|
|
}
|
|
|
|
local function FAIcon(cp, font, x, y, col, ax, ay)
|
|
draw.SimpleText(utf8.char(cp), font or "bw_fa", x, y, col, ax or TEXT_ALIGN_CENTER, ay or TEXT_ALIGN_CENTER)
|
|
end
|
|
|
|
-- fonts responsives
|
|
local FS = 1
|
|
local function BuildFonts()
|
|
FS = math.Clamp(ScrH() / 1080, 0.7, 1.5)
|
|
surface.CreateFont("bw_title", { font = "Poppins-Bold", size = math.Round(24 * FS), weight = 700, antialias = true })
|
|
surface.CreateFont("bw_big", { font = "Poppins-SemiBold", size = math.Round(19 * FS), weight = 600, antialias = true })
|
|
surface.CreateFont("bw_med", { font = "Poppins-Medium", size = math.Round(15 * FS), weight = 500, antialias = true })
|
|
surface.CreateFont("bw_bold", { font = "Poppins-Bold", size = math.Round(15 * FS), weight = 700, antialias = true })
|
|
surface.CreateFont("bw_small", { font = "Poppins-Regular", size = math.Round(13 * FS), weight = 400, antialias = true })
|
|
surface.CreateFont("bw_fa", { font = FA_FONT, size = math.Round(15 * FS), weight = 900, extended = true })
|
|
surface.CreateFont("bw_fa_lg", { font = FA_FONT, size = math.Round(19 * FS), weight = 900, extended = true })
|
|
end
|
|
BuildFonts()
|
|
hook.Add("OnScreenSizeChanged", "bw_warn_fonts", BuildFonts)
|
|
|
|
-- temps relatif ("2 mois", "3 j"...)
|
|
local function RelTime(t)
|
|
local d = os.time() - (t or 0)
|
|
if d < 60 then return "à l'instant"
|
|
elseif d < 3600 then return math.floor(d / 60) .. " min"
|
|
elseif d < 86400 then return math.floor(d / 3600) .. " h"
|
|
elseif d < 2592000 then return math.floor(d / 86400) .. " j"
|
|
elseif d < 31536000 then return math.floor(d / 2592000) .. " mois"
|
|
else return math.floor(d / 31536000) .. " an(s)" end
|
|
end
|
|
|
|
local function Btn(parent, label, col, font)
|
|
local b = vgui.Create("DButton", parent)
|
|
b:SetText("")
|
|
b.Paint = function(self, w, h)
|
|
rndx.Draw(8, 0, 0, w, h, self:IsHovered() and Lighten(col) or col, rndx.SHAPE_FIGMA)
|
|
draw.SimpleText(label, font or "bw_med", w * 0.5, h * 0.5, COL_TEXT, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
|
end
|
|
return b
|
|
end
|
|
|
|
local function StyleEntry(e, ph, multiline)
|
|
e:SetFont("bw_med")
|
|
e:SetTextColor(COL_TEXT)
|
|
e:SetPlaceholderText(ph or "")
|
|
e:SetPaintBackground(false)
|
|
if multiline then e:SetMultiline(true) end
|
|
e.Paint = function(self, w, h)
|
|
rndx.Draw(8, 0, 0, w, h, COL_INPUT, rndx.SHAPE_FIGMA)
|
|
self:DrawTextEntryText(COL_TEXT, COL_BLUE, COL_TEXT)
|
|
end
|
|
end
|
|
|
|
local function StyleScroll(sp)
|
|
local bar = sp:GetVBar()
|
|
bar:SetWide(8)
|
|
bar.Paint = function() end
|
|
bar.btnUp.Paint = function() end
|
|
bar.btnDown.Paint = function() end
|
|
bar.btnGrip.Paint = function(self, w, h) rndx.Draw(4, 0, 0, w, h, COL_LINE, rndx.SHAPE_FIGMA) end
|
|
end
|
|
|
|
local function GetEntry(sid)
|
|
for _, e in ipairs(BladW.Warn.List) do
|
|
if e.sid == sid then return e end
|
|
end
|
|
end
|
|
|
|
-- ============================================================
|
|
-- Modal « Appliquer un warn »
|
|
-- ============================================================
|
|
local function OpenWarnModal(sid)
|
|
local reasons = (BladW.Warn.Conf or {}).reasons or {}
|
|
local chosen, chosenLabel = nil, "Choisissez une raison..."
|
|
|
|
local m = vgui.Create("DFrame")
|
|
m:SetSize(math.min(ScrW() * 0.42, 580), math.min(ScrH() * 0.5, 400))
|
|
m:Center()
|
|
m:SetTitle("")
|
|
m:ShowCloseButton(false)
|
|
m:MakePopup()
|
|
m:DockPadding(30, 66, 30, 26)
|
|
m.Paint = function(self, w, h)
|
|
rndx.Draw(16, 0, 0, w, h, COL_PANEL, rndx.SHAPE_FIGMA)
|
|
rndx.DrawOutlined(16, 0, 0, w, h, COL_LINE, 2, rndx.SHAPE_FIGMA)
|
|
draw.SimpleText("Appliquer un warn", "bw_title", w * 0.5, 36, COL_TEXT, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
|
end
|
|
|
|
local mclose = vgui.Create("DButton", m)
|
|
mclose:SetSize(32, 32)
|
|
mclose:SetText("")
|
|
mclose.Paint = function(self, w, h)
|
|
rndx.Draw(8, 0, 0, w, h, self:IsHovered() and COL_RED or COL_BG, rndx.SHAPE_FIGMA)
|
|
draw.SimpleText("X", "bw_med", w * 0.5, h * 0.5, COL_RED, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
|
end
|
|
mclose.DoClick = function() m:Remove() end
|
|
m.PerformLayout = function(self, w, h) mclose:SetPos(w - 42, 14) end
|
|
|
|
-- dropdown des raisons
|
|
local rdd = vgui.Create("DButton", m)
|
|
rdd:Dock(TOP)
|
|
rdd:SetTall(46)
|
|
rdd:SetText("")
|
|
rdd.Paint = function(self, w, h)
|
|
rndx.Draw(8, 0, 0, w, h, COL_INPUT, rndx.SHAPE_FIGMA)
|
|
rndx.DrawOutlined(8, 0, 0, w, h, COL_LINE, 2, rndx.SHAPE_FIGMA)
|
|
draw.SimpleText(chosenLabel, "bw_med", 14, h * 0.5, chosen and COL_TEXT or COL_MUTED, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
|
|
draw.NoTexture()
|
|
surface.SetDrawColor(COL_MUTED)
|
|
local ax = w - 26
|
|
surface.DrawPoly({ { x = ax, y = h * 0.5 - 2 }, { x = ax + 10, y = h * 0.5 - 2 }, { x = ax + 5, y = h * 0.5 + 4 } })
|
|
end
|
|
rdd.DoClick = function(self)
|
|
local menu = DermaMenu()
|
|
menu:SetMinimumWidth(self:GetWide())
|
|
menu.Paint = function(s, w, h)
|
|
rndx.Draw(8, 0, 0, w, h, Color(20, 20, 24, 252), rndx.SHAPE_FIGMA)
|
|
rndx.DrawOutlined(8, 0, 0, w, h, COL_LINE, 1, rndx.SHAPE_FIGMA)
|
|
end
|
|
for _, r in ipairs(reasons) do
|
|
local o = menu:AddOption(r, function() chosen, chosenLabel = r, r end)
|
|
o:SetTall(30)
|
|
o:SetText("")
|
|
o.Paint = function(s, w, h)
|
|
if s:IsHovered() then rndx.Draw(6, 3, 1, w - 6, h - 2, COL_BLUE, rndx.SHAPE_FIGMA) end
|
|
draw.SimpleText(r, "bw_med", 12, h * 0.5, COL_TEXT, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
|
|
end
|
|
end
|
|
local mx, my = self:LocalToScreen(0, self:GetTall() + 2)
|
|
menu:Open(mx, my)
|
|
end
|
|
|
|
-- commentaire du staff
|
|
local clabel = vgui.Create("DLabel", m)
|
|
clabel:Dock(TOP)
|
|
clabel:DockMargin(0, 18, 0, 6)
|
|
clabel:SetTall(20)
|
|
clabel:SetFont("bw_med")
|
|
clabel:SetTextColor(COL_TEXT)
|
|
clabel:SetText("Commentaires du staff :")
|
|
|
|
local comment = vgui.Create("DTextEntry", m)
|
|
comment:Dock(TOP)
|
|
comment:SetTall(90)
|
|
StyleEntry(comment, "Joueur a surveiller...", true)
|
|
|
|
-- bouton Avertir
|
|
local warnBtn = Btn(m, "Avertir", COL_GREEN, "bw_big")
|
|
warnBtn:Dock(BOTTOM)
|
|
warnBtn:SetTall(46)
|
|
warnBtn:DockMargin(90, 16, 90, 0)
|
|
warnBtn.DoClick = function()
|
|
if not chosen then
|
|
surface.PlaySound("buttons/button10.wav")
|
|
return
|
|
end
|
|
net.Start("bladw_warn_add")
|
|
net.WriteString(sid)
|
|
net.WriteString(chosen)
|
|
net.WriteString(comment:GetValue() or "")
|
|
net.SendToServer()
|
|
m:Remove()
|
|
end
|
|
end
|
|
|
|
-- ============================================================
|
|
-- Menu principal
|
|
-- ============================================================
|
|
function BladW.Warn.Open()
|
|
if IsValid(BladW.Warn.Frame) then BladW.Warn.Frame:Remove() end
|
|
|
|
local W = math.min(ScrW() * 0.72, 1180)
|
|
local H = math.min(ScrH() * 0.80, 750)
|
|
|
|
local frame = vgui.Create("DFrame")
|
|
BladW.Warn.Frame = frame
|
|
frame:SetSize(W, H)
|
|
frame:Center()
|
|
frame:SetTitle("")
|
|
frame:ShowCloseButton(false)
|
|
frame:MakePopup()
|
|
frame:DockPadding(16, 16, 16, 16)
|
|
frame.Paint = function(self, w, h)
|
|
local lw = w * 0.3 + 24 -- fin de la zone gauche (alignée sur la colonne joueurs)
|
|
|
|
-- côté droit : blur + voile (assez sombre mais on voit le fond)
|
|
rndx.Draw(18, 0, 0, w, h, nil, rndx.SHAPE_FIGMA + rndx.BLUR)
|
|
rndx.Draw(18, 0, 0, w, h, Color(24, 24, 28, 200), rndx.SHAPE_FIGMA)
|
|
|
|
-- côté gauche : couleur pleine (coins arrondis à gauche, carré à droite)
|
|
rndx.Draw(18, 0, 0, lw, h, COL_BG, rndx.SHAPE_FIGMA + rndx.NO_TR + rndx.NO_BR)
|
|
|
|
-- bordure blanche (comme les annonces)
|
|
rndx.DrawOutlined(18, 0, 0, w, h, Color(255, 255, 255), 3, rndx.SHAPE_FIGMA)
|
|
end
|
|
|
|
local selectedSID
|
|
|
|
-- ===== colonne gauche =====
|
|
local left = vgui.Create("DPanel", frame)
|
|
left:Dock(LEFT)
|
|
left:SetWide(W * 0.3)
|
|
left:DockMargin(0, 0, 16, 0)
|
|
left:SetPaintBackground(false)
|
|
|
|
local lhead = vgui.Create("DPanel", left)
|
|
lhead:Dock(TOP)
|
|
lhead:SetTall(46)
|
|
lhead:SetPaintBackground(false)
|
|
lhead.Paint = function(self, w, h)
|
|
rndx.DrawMaterial(0, 0, (h - 42) * 0.5, 42, 42, color_white, logo)
|
|
draw.SimpleText("Joueurs", "bw_title", 54, h * 0.5, COL_TEXT, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
|
|
end
|
|
|
|
local searchBox = vgui.Create("DPanel", left)
|
|
searchBox:Dock(TOP)
|
|
searchBox:DockMargin(0, 8, 0, 10)
|
|
searchBox:SetTall(40)
|
|
searchBox:SetPaintBackground(false)
|
|
searchBox.Paint = function(self, w, h)
|
|
rndx.Draw(8, 0, 0, w, h, COL_INPUT, rndx.SHAPE_FIGMA)
|
|
FAIcon(FA.search, "bw_fa", 22, h * 0.5, COL_MUTED)
|
|
end
|
|
|
|
local search = vgui.Create("DTextEntry", searchBox)
|
|
search:Dock(FILL)
|
|
search:DockMargin(38, 0, 10, 0)
|
|
search:SetFont("bw_med")
|
|
search:SetTextColor(COL_TEXT)
|
|
search:SetPlaceholderText("Rechercher un joueur")
|
|
search:SetPaintBackground(false)
|
|
search.Paint = function(self, w, h)
|
|
self:DrawTextEntryText(COL_TEXT, COL_BLUE, COL_TEXT)
|
|
end
|
|
|
|
local plist = vgui.Create("DScrollPanel", left)
|
|
plist:Dock(FILL)
|
|
StyleScroll(plist)
|
|
|
|
-- ===== colonne droite =====
|
|
local right = vgui.Create("DPanel", frame)
|
|
right:Dock(FILL)
|
|
right:SetPaintBackground(false)
|
|
|
|
local rhead = vgui.Create("DPanel", right)
|
|
rhead:Dock(TOP)
|
|
rhead:SetTall(58)
|
|
rhead:SetPaintBackground(false)
|
|
rhead.Paint = function(self, w, h)
|
|
draw.SimpleText("Historique", "bw_title", w * 0.5, 16, COL_TEXT, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
|
FAIcon(FA.card, "bw_fa_lg", w * 0.5, 44, COL_TEXT)
|
|
end
|
|
|
|
local close = vgui.Create("DButton", rhead)
|
|
close:Dock(RIGHT)
|
|
close:SetWide(40)
|
|
close:SetText("")
|
|
close.Paint = function(self, w, h)
|
|
local y = (h - 34) * 0.5
|
|
rndx.Draw(8, w - 34, y, 34, 34, self:IsHovered() and COL_RED or COL_PANEL, rndx.SHAPE_FIGMA)
|
|
draw.SimpleText("X", "bw_big", w - 17, h * 0.5, COL_RED, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
|
end
|
|
close.DoClick = function() frame:Remove() end
|
|
|
|
local content = vgui.Create("DPanel", right)
|
|
content:Dock(FILL)
|
|
content:DockMargin(0, 8, 0, 0)
|
|
content:SetPaintBackground(false)
|
|
|
|
-- ===== refresh =====
|
|
local RefreshRight
|
|
|
|
local function RefreshPlayers()
|
|
plist:Clear()
|
|
local filter = string.lower(search:GetValue() or "")
|
|
|
|
for _, e in ipairs(BladW.Warn.List) do
|
|
if filter == "" or string.find(string.lower(e.name), filter, 1, true) then
|
|
local entry = e
|
|
local row = plist:Add("DButton")
|
|
row:Dock(TOP)
|
|
row:DockMargin(0, 0, 8, 8)
|
|
row:SetTall(52)
|
|
row:SetText("")
|
|
row.Paint = function(self, w, h)
|
|
local sel = selectedSID == entry.sid
|
|
rndx.Draw(8, 0, 0, w, h, sel and COL_SEL or COL_PANEL, rndx.SHAPE_FIGMA)
|
|
-- petit bout à gauche : bleu si sélectionné, gris clair sinon (coins gauche arrondis)
|
|
rndx.Draw(8, 0, 0, 5, h, sel and COL_BLUE or COL_BAR, rndx.SHAPE_FIGMA + rndx.NO_TR + rndx.NO_BR)
|
|
draw.SimpleText(entry.name, "bw_bold", 60, h * 0.5, COL_TEXT, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
|
|
|
|
if entry.staff then
|
|
FAIcon(FA.shield, "bw_fa", w - 28, h * 0.5, COL_ORANGE)
|
|
end
|
|
end
|
|
row.DoClick = function() selectedSID = entry.sid RefreshRight() end
|
|
|
|
local av = vgui.Create("AvatarImage", row)
|
|
av:SetSize(34, 34)
|
|
av:SetPos(14, 9)
|
|
av:SetSteamID(entry.sid, 64)
|
|
av:SetMouseInputEnabled(false)
|
|
end
|
|
end
|
|
end
|
|
|
|
function RefreshRight()
|
|
content:Clear()
|
|
|
|
local e = selectedSID and GetEntry(selectedSID)
|
|
if not e then
|
|
local ph = vgui.Create("DPanel", content)
|
|
ph:Dock(FILL)
|
|
ph:SetPaintBackground(false)
|
|
ph.Paint = function(self, w, h)
|
|
draw.SimpleText("Sélectionne un joueur à gauche", "bw_med", w * 0.5, h * 0.4, COL_MUTED, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
|
end
|
|
return
|
|
end
|
|
|
|
-- carte joueur
|
|
local card = vgui.Create("DPanel", content)
|
|
card:Dock(TOP)
|
|
card:SetTall(90)
|
|
card:SetPaintBackground(false)
|
|
card.Paint = function(self, w, h)
|
|
rndx.Draw(12, 0, 0, w, h, COL_PANEL, rndx.SHAPE_FIGMA)
|
|
draw.SimpleText(e.name, "bw_big", 96, 30, COL_TEXT, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
|
|
draw.SimpleText("Steam ID :", "bw_small", 96, 56, COL_MUTED, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
|
|
|
|
local n = #e.warns
|
|
local txt = n .. (n > 1 and " Warns actifs" or " Warn actif")
|
|
surface.SetFont("bw_bold")
|
|
local bw = surface.GetTextSize(txt) + 30
|
|
rndx.Draw(6, w * 0.5 - bw * 0.5, h * 0.5 - 16, bw, 32, COL_BADGE, rndx.SHAPE_FIGMA)
|
|
draw.SimpleText(txt, "bw_bold", w * 0.5, h * 0.5, COL_RED, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
|
end
|
|
local av = vgui.Create("AvatarImage", card)
|
|
av:SetSize(56, 56)
|
|
av:SetPos(22, 17)
|
|
av:SetSteamID(e.sid, 64)
|
|
av:SetMouseInputEnabled(false)
|
|
|
|
-- bouton copie du SteamID
|
|
local copyBtn = vgui.Create("DButton", card)
|
|
copyBtn:SetText("")
|
|
copyBtn:SetSize(16, 16)
|
|
copyBtn:SetTooltip("Copier le SteamID")
|
|
copyBtn.Paint = function(self, w, h)
|
|
FAIcon(FA.copy, "bw_fa", w * 0.5, h * 0.5, self:IsHovered() and COL_TEXT or COL_MUTED)
|
|
end
|
|
copyBtn.DoClick = function() SetClipboardText(e.sid) end
|
|
card.PerformLayout = function(self, w, h)
|
|
surface.SetFont("bw_small")
|
|
local tw = surface.GetTextSize("Steam ID :")
|
|
copyBtn:SetPos(96 + tw + 8, 49)
|
|
end
|
|
|
|
-- ligne « Ajouter un warn »
|
|
local addRow = vgui.Create("DPanel", content)
|
|
addRow:Dock(TOP)
|
|
addRow:SetTall(64)
|
|
addRow:DockMargin(0, 10, 0, 4)
|
|
addRow:SetPaintBackground(false)
|
|
addRow.Paint = function(self, w, h)
|
|
surface.SetDrawColor(COL_LINE)
|
|
surface.DrawRect(0, h * 0.5, w * 0.5 - 100, 1)
|
|
surface.DrawRect(w * 0.5 + 100, h * 0.5, w * 0.5 - 100, 1)
|
|
end
|
|
local addBtn = Btn(addRow, "Ajouter un warn", COL_BLUE, "bw_big")
|
|
addBtn:SetSize(190, 42)
|
|
addRow.PerformLayout = function(self, w, h) addBtn:SetPos(w * 0.5 - 95, h * 0.5 - 21) end
|
|
addBtn.DoClick = function() OpenWarnModal(e.sid) end
|
|
|
|
-- ligne « Vider le casier » (en bas)
|
|
local clearRow = vgui.Create("DPanel", content)
|
|
clearRow:Dock(BOTTOM)
|
|
clearRow:SetTall(54)
|
|
clearRow:SetPaintBackground(false)
|
|
local clearBtn = Btn(clearRow, "Vider le casier", COL_ORANGE, "bw_med")
|
|
clearBtn:SetSize(190, 40)
|
|
clearBtn.Paint = function(self, w, h)
|
|
rndx.Draw(8, 0, 0, w, h, self:IsHovered() and Lighten(COL_ORANGE) or COL_ORANGE, rndx.SHAPE_FIGMA)
|
|
FAIcon(FA.trash, "bw_fa", 32, h * 0.5, COL_TEXT)
|
|
draw.SimpleText("Vider le casier", "bw_med", w * 0.5 + 14, h * 0.5, COL_TEXT, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
|
end
|
|
clearRow.PerformLayout = function(self, w, h) clearBtn:SetPos(w - 190, h - 46) end
|
|
clearBtn.DoClick = function()
|
|
if #e.warns == 0 then return end
|
|
net.Start("bladw_warn_clear")
|
|
net.WriteString(e.sid)
|
|
net.SendToServer()
|
|
end
|
|
|
|
-- liste des warns
|
|
local warns = vgui.Create("DScrollPanel", content)
|
|
warns:Dock(FILL)
|
|
warns:DockMargin(0, 6, 0, 6)
|
|
StyleScroll(warns)
|
|
|
|
for i, w in ipairs(e.warns) do
|
|
local idx, warn = i, w
|
|
local wc = warns:Add("DPanel")
|
|
wc:Dock(TOP)
|
|
wc:DockMargin(0, 0, 8, 10)
|
|
wc:SetTall(116)
|
|
wc:SetPaintBackground(false)
|
|
wc.Paint = function(self, cw, ch)
|
|
rndx.Draw(10, 0, 0, cw, ch, COL_PANEL, rndx.SHAPE_FIGMA)
|
|
draw.SimpleText("Warn émis par :", "bw_med", 16, 16, COL_TEXT, TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP)
|
|
draw.SimpleText(warn.issuer ~= "" and warn.issuer or "?", "bw_med", 168, 16, COL_MUTED, TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP)
|
|
draw.SimpleText("Date :", "bw_med", 16, 40, COL_TEXT, TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP)
|
|
if warn.time and warn.time > 0 then
|
|
local d = ("(%s) %s"):format(RelTime(warn.time), os.date("%d/%m/%Y, %I:%M %p", warn.time))
|
|
draw.SimpleText(d, "bw_med", 168, 40, COL_MUTED, TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP)
|
|
end
|
|
draw.SimpleText("Raison :", "bw_med", 16, 64, COL_TEXT, TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP)
|
|
rndx.Draw(6, 14, ch - 40, cw - 28, 30, COL_INPUT, rndx.SHAPE_FIGMA)
|
|
draw.SimpleText(warn.reason or "", "bw_small", 26, ch - 25, COL_TEXT, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
|
|
end
|
|
|
|
local del = Btn(wc, "Supprimer", COL_RED, "bw_med")
|
|
del:SetSize(150, 36)
|
|
wc.PerformLayout = function(self, cw, ch) del:SetPos(cw - 164, 14) end
|
|
del.DoClick = function()
|
|
net.Start("bladw_warn_remove")
|
|
net.WriteString(e.sid)
|
|
net.WriteUInt(idx, 8)
|
|
net.SendToServer()
|
|
end
|
|
end
|
|
end
|
|
|
|
frame.RefreshAll = function()
|
|
RefreshPlayers()
|
|
RefreshRight()
|
|
end
|
|
|
|
search.OnChange = function() RefreshPlayers() end
|
|
|
|
RefreshPlayers()
|
|
RefreshRight()
|
|
end
|
|
|
|
-- ============================================================
|
|
-- Réseau
|
|
-- ============================================================
|
|
net.Receive("bladw_warn_open", function()
|
|
BladW.Warn.Open()
|
|
end)
|
|
|
|
-- annonce d'un warn dans le chat de base
|
|
net.Receive("bladw_warn_chat", function()
|
|
local target = net.ReadString()
|
|
local admin = net.ReadString()
|
|
local reason = net.ReadString()
|
|
chat.AddText(
|
|
COL_ORANGE, "[Warn] ",
|
|
COL_TEXT, target,
|
|
COL_MUTED, " a été averti par ",
|
|
COL_TEXT, admin,
|
|
COL_MUTED, " — ",
|
|
COL_RED, reason
|
|
)
|
|
end)
|
|
|
|
net.Receive("bladw_warn_sync", function()
|
|
local list = {}
|
|
local n = net.ReadUInt(8)
|
|
for i = 1, n do
|
|
local e = { name = net.ReadString(), sid = net.ReadString(), staff = net.ReadBool(), warns = {} }
|
|
local wn = net.ReadUInt(8)
|
|
for j = 1, wn do
|
|
e.warns[j] = {
|
|
issuer = net.ReadString(),
|
|
time = net.ReadUInt(32),
|
|
reason = net.ReadString(),
|
|
comment = net.ReadString(),
|
|
}
|
|
end
|
|
list[i] = e
|
|
end
|
|
BladW.Warn.List = list
|
|
|
|
if IsValid(BladW.Warn.Frame) and BladW.Warn.Frame.RefreshAll then
|
|
BladW.Warn.Frame.RefreshAll()
|
|
end
|
|
end)
|