local rndx = include("lib/rndx.lua") local logo = Material("nocode/announcement/logo.png") BladW = BladW or {} BladW.Announcement = BladW.Announcement or {} -- mêmes couleurs que les annonces local COL_BG = Color(0, 0, 0, 200) local COL_PANEL = Color(0, 0, 0, 160) local COL_ACCENT = Color(76, 119, 200) local COL_ACCENT_DIM = Color(60, 90, 150) local COL_TEXT = Color(255, 255, 255) local COL_RED = Color(255, 80, 80) local COL_GRAY = Color(54, 54, 54, 255) local COL_MUTED = Color(160, 160, 160) local COL_WARN = Color(255, 200, 50) -- nb de caractères d'un champ (UTF-8, un accent = 1) local function EntryLen(entry) local v = entry:GetValue() return utf8.len(v) or #v end -- petit triangle warning dessiné à la main (comme ça pas besoin de font emoji) local function DrawWarning(x, y, size) draw.NoTexture() surface.SetDrawColor(COL_WARN) surface.DrawPoly({ { x = x + size * 0.5, y = y }, { x = x + size, y = y + size }, { x = x, y = y + size }, }) draw.SimpleText("!", "DermaDefaultBold", x + size * 0.5, y + size * 0.62, Color(30, 30, 30), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER) end -- mon style pour les champs de texte local function StyleEntry(entry, placeholder, default) entry:SetFont("bladw_text_Medium") entry:SetText(default or "") entry:SetPlaceholderText(placeholder or "") entry:SetTextColor(COL_TEXT) entry:SetPaintBackground(false) entry.Paint = function(self, w, h) rndx.Draw(10, 0, 0, w, h, COL_PANEL, rndx.SHAPE_FIGMA) local outline = self:IsEditing() and COL_ACCENT or Color(255, 255, 255, 60) rndx.DrawOutlined(10, 0, 0, w, h, outline, 2, rndx.SHAPE_FIGMA) self:DrawTextEntryText(COL_TEXT, COL_ACCENT, COL_TEXT) end end local function OpenAAdminMenu() -- si le menu est déjà ouvert je le vire if IsValid(BladW_AdminMenu) then BladW_AdminMenu:Remove() end local selectedPreset = "" local maxLen = (BladW.Announcement.Conf or {}).announcement_max_length or 120 local frame = vgui.Create("DFrame") BladW_AdminMenu = frame frame:SetSize(500, 460) frame:Center() frame:SetTitle("") frame:ShowCloseButton(false) frame:MakePopup() frame.Paint = function(self, w, h) -- fond flou + sombre + contour blanc (comme la box d'annonce) rndx.Draw(20, 0, 0, w, h, nil, rndx.SHAPE_FIGMA + rndx.BLUR) rndx.Draw(20, 0, 0, w, h, COL_BG, rndx.SHAPE_FIGMA) rndx.DrawOutlined(20, 0, 0, w, h, COL_TEXT, 3, rndx.SHAPE_FIGMA) -- logo + titre rndx.DrawMaterial(0, w * 0.5 - 30, 20, 60, 60, COL_TEXT, logo) draw.SimpleText("Envoyer une annonce", "bladw_text", w * 0.5, 94, COL_TEXT, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER) draw.SimpleText("Presets", "bladw_text_Medium", 40, 118, COL_TEXT, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER) end -- ESC ferme mon menu sans laisser le menu pause s'afficher. -- le moteur ouvre le menu pause sur ESC avant que je puisse l'intercepter, donc je le recache direct et je ferme. frame.Think = function(self) if gui.IsGameUIVisible() then gui.HideGameUI() self:Remove() end end -- bouton pour fermer local close = vgui.Create("DButton", frame) close:SetSize(30, 30) close:SetPos(frame:GetWide() - 42, 14) close:SetText("") close.Paint = function(self, w, h) rndx.Draw(8, 0, 0, w, h, self:IsHovered() and COL_RED or COL_GRAY, rndx.SHAPE_FIGMA) draw.SimpleText("X", "bladw_text_Medium", w * 0.5, h * 0.5, COL_TEXT, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER) end close.DoClick = function() frame:Remove() end -- les champs (créés avant les boutons preset pour être captés dans leurs closures) local titleEntry = vgui.Create("DTextEntry", frame) titleEntry:SetPos(40, 240) titleEntry:SetSize(420, 40) StyleEntry(titleEntry, "Titre de l'annonce", "Annonce") local msgEntry = vgui.Create("DTextEntry", frame) msgEntry:SetPos(40, 288) msgEntry:SetSize(420, 40) StyleEntry(msgEntry, "Message de l'annonce") -- bord rouge quand ça dépasse la limite msgEntry.Paint = function(self, w, h) local over = EntryLen(self) > maxLen rndx.Draw(10, 0, 0, w, h, COL_PANEL, rndx.SHAPE_FIGMA) local outline = over and COL_RED or (self:IsEditing() and COL_ACCENT or Color(255, 255, 255, 60)) rndx.DrawOutlined(10, 0, 0, w, h, outline, over and 3 or 2, rndx.SHAPE_FIGMA) self:DrawTextEntryText(COL_TEXT, COL_ACCENT, COL_TEXT) end -- ligne du bas : warning à gauche, compteur à droite local status = vgui.Create("DPanel", frame) status:SetPos(40, 332) status:SetSize(420, 26) status:SetPaintBackground(false) status.Paint = function(self, w, h) local len = EntryLen(msgEntry) local over = len > maxLen -- retour du serveur (ex : annonce déjà en cours) sinon le warning de dépassement if frame.sendFeedback and (frame.sendFeedbackUntil or 0) > SysTime() then DrawWarning(0, h * 0.5 - 8, 16) draw.SimpleText(frame.sendFeedback, "bladw_text_Medium", 24, h * 0.5, COL_WARN, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER) elseif over then DrawWarning(0, h * 0.5 - 8, 16) draw.SimpleText("Trop de caractères", "bladw_text_Medium", 24, h * 0.5, COL_RED, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER) end draw.SimpleText(len .. " / " .. maxLen, "bladw_text_Medium", w, h * 0.5, over and COL_RED or COL_MUTED, TEXT_ALIGN_RIGHT, TEXT_ALIGN_CENTER) end -- boutons preset : remplissent les champs et retiennent le preset choisi local presets = vgui.Create("DIconLayout", frame) presets:SetPos(40, 134) presets:SetSize(420, 90) presets:SetSpaceX(8) presets:SetSpaceY(8) for key, preset in SortedPairs(BladW.Announcement.Presets or {}) do local b = presets:Add("DButton") b:SetSize(134, 34) b:SetText("") b.Paint = function(self, w, h) local on = (selectedPreset == key) or self:IsHovered() rndx.Draw(8, 0, 0, w, h, on and COL_ACCENT or COL_GRAY, rndx.SHAPE_FIGMA) draw.SimpleText(preset.title or key, "bladw_text_Medium", w * 0.5, h * 0.5, COL_TEXT, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER) end b.DoClick = function() selectedPreset = key titleEntry:SetText(preset.title or "") msgEntry:SetText(preset.text or "") end end -- bouton envoyer (grisé + bloqué si ça dépasse) local send = vgui.Create("DButton", frame) send:SetPos(40, 372) send:SetSize(420, 50) send:SetText("") send.Paint = function(self, w, h) local over = EntryLen(msgEntry) > maxLen local col = over and COL_GRAY or (self:IsHovered() and COL_ACCENT or COL_ACCENT_DIM) rndx.Draw(12, 0, 0, w, h, col, rndx.SHAPE_FIGMA) draw.SimpleText("Envoyer", "bladw_text", w * 0.5, h * 0.5, over and COL_MUTED or COL_TEXT, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER) end send.DoClick = function() if EntryLen(msgEntry) > maxLen then surface.PlaySound("buttons/button10.wav") return end if msgEntry:GetValue() == "" and selectedPreset == "" then surface.PlaySound("buttons/button10.wav") return end -- on envoie mais on NE ferme PAS : c'est la réponse serveur qui fermera si c'est publié, -- comme ça si c'est refusé l'admin garde son texte net.Start("bladWSendAnnouncement") net.WriteString(selectedPreset) net.WriteString(titleEntry:GetValue()) net.WriteString(msgEntry:GetValue()) net.SendToServer() end end -- réponse du serveur après un envoi : publié -> on ferme, refusé -> on garde le menu + on dit pourquoi net.Receive("bladWAnnouncementResult", function() local ok = net.ReadBool() local left = net.ReadUInt(8) if not IsValid(BladW_AdminMenu) then return end if ok then BladW_AdminMenu:Remove() else BladW_AdminMenu.sendFeedback = left > 0 and ("Annonce déjà en cours (" .. left .. "s)") or "Envoi impossible" BladW_AdminMenu.sendFeedbackUntil = SysTime() + 5 end end) return OpenAAdminMenu