add sborka
This commit is contained in:
202
garrysmod/gamemodes/militaryrp/plugins/warn/cl_derma.lua
Normal file
202
garrysmod/gamemodes/militaryrp/plugins/warn/cl_derma.lua
Normal file
@@ -0,0 +1,202 @@
|
||||
local PANEL = {}
|
||||
|
||||
local COLOR_BG = Color(30, 30, 30, 240)
|
||||
local COLOR_HEADER = Color(20, 20, 20, 255)
|
||||
local COLOR_ACCENT = Color(200, 50, 50) -- Red for warnings
|
||||
local COLOR_TEXT = Color(240, 240, 240)
|
||||
local COLOR_TEXT_DIM = Color(180, 180, 180)
|
||||
|
||||
function PANEL:Init()
|
||||
self:SetSize(ScrW() * 0.6, ScrH() * 0.7)
|
||||
self:Center()
|
||||
self:MakePopup()
|
||||
self:SetTitle("")
|
||||
self:ShowCloseButton(false)
|
||||
|
||||
self.admins = {}
|
||||
self.warns = {}
|
||||
|
||||
-- Close Button
|
||||
local closeBtn = self:Add("DButton")
|
||||
closeBtn:SetSize(40, 30)
|
||||
closeBtn:SetPos(self:GetWide() - 40, 0)
|
||||
closeBtn:SetText("X")
|
||||
closeBtn:SetFont("ixMediumFont")
|
||||
closeBtn:SetTextColor(COLOR_TEXT)
|
||||
closeBtn.Paint = function(s, w, h)
|
||||
if (s:IsHovered()) then
|
||||
draw.RoundedBox(0, 0, 0, w, h, Color(200, 50, 50))
|
||||
end
|
||||
end
|
||||
closeBtn.DoClick = function()
|
||||
self:Close()
|
||||
end
|
||||
|
||||
-- Container
|
||||
self.container = self:Add("Panel")
|
||||
self.container:Dock(FILL)
|
||||
self.container:DockMargin(10, 35, 10, 10)
|
||||
|
||||
-- Left: Admin List
|
||||
self.leftPanel = self.container:Add("Panel")
|
||||
self.leftPanel:Dock(LEFT)
|
||||
self.leftPanel:SetWide(self:GetWide() * 0.4)
|
||||
self.leftPanel:DockMargin(0, 0, 10, 0)
|
||||
|
||||
self.scroll = self.leftPanel:Add("DScrollPanel")
|
||||
self.scroll:Dock(FILL)
|
||||
|
||||
-- Right: Details & History
|
||||
self.rightPanel = self.container:Add("Panel")
|
||||
self.rightPanel:Dock(FILL)
|
||||
self.rightPanel.Paint = function(s, w, h)
|
||||
draw.RoundedBox(4, 0, 0, w, h, Color(40, 40, 40, 255))
|
||||
end
|
||||
|
||||
self.details = self.rightPanel:Add("Panel")
|
||||
self.details:Dock(TOP)
|
||||
self.details:SetTall(150)
|
||||
self.details:DockPadding(15, 15, 15, 15)
|
||||
|
||||
self.historyScroll = self.rightPanel:Add("DScrollPanel")
|
||||
self.historyScroll:Dock(FILL)
|
||||
self.historyScroll:DockMargin(10, 10, 10, 10)
|
||||
|
||||
self.noSelected = self.rightPanel:Add("DLabel")
|
||||
self.noSelected:SetText("Выберите администратора из списка слева")
|
||||
self.noSelected:SetFont("ixMediumFont")
|
||||
self.noSelected:Dock(FILL)
|
||||
self.noSelected:SetContentAlignment(5)
|
||||
self.noSelected:SetTextColor(COLOR_TEXT_DIM)
|
||||
end
|
||||
|
||||
function PANEL:Populate(admins, warns)
|
||||
self.admins = admins
|
||||
self.warns = warns
|
||||
|
||||
local sortedAdmins = {}
|
||||
for k, v in pairs(admins) do
|
||||
table.insert(sortedAdmins, v)
|
||||
end
|
||||
table.sort(sortedAdmins, function(a, b)
|
||||
if (a.online != b.online) then return a.online end
|
||||
return a.name < b.name
|
||||
end)
|
||||
|
||||
for _, admin in ipairs(sortedAdmins) do
|
||||
local btn = self.scroll:Add("DButton")
|
||||
btn:Dock(TOP)
|
||||
btn:SetTall(50)
|
||||
btn:DockMargin(0, 0, 0, 5)
|
||||
btn:SetText("")
|
||||
|
||||
local warnCount = (warns[admin.steamID] and warns[admin.steamID].count) or 0
|
||||
|
||||
btn.Paint = function(s, w, h)
|
||||
local bg = s:IsHovered() and Color(60, 60, 60) or Color(45, 45, 45)
|
||||
draw.RoundedBox(4, 0, 0, w, h, bg)
|
||||
|
||||
-- Online/Offline Indicator
|
||||
surface.SetDrawColor(admin.online and Color(100, 255, 100) or Color(150, 150, 150))
|
||||
surface.DrawRect(3, 3, 4, h - 6)
|
||||
|
||||
draw.SimpleText(admin.name, "ixMediumFont", 15, h/2 - 8, COLOR_TEXT, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
|
||||
draw.SimpleText(admin.rank, "ixSmallFont", 15, h/2 + 10, COLOR_TEXT_DIM, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
|
||||
|
||||
local warnCol = warnCount > 0 and COLOR_ACCENT or COLOR_TEXT_DIM
|
||||
draw.SimpleText("Warns: " .. warnCount .. "/3", "ixMediumFont", w - 15, h/2, warnCol, TEXT_ALIGN_RIGHT, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
|
||||
btn.DoClick = function()
|
||||
self:ShowAdminDetails(admin)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:ShowAdminDetails(admin)
|
||||
self.noSelected:SetVisible(false)
|
||||
self.details:Clear()
|
||||
self.historyScroll:Clear()
|
||||
|
||||
local name = self.details:Add("DLabel")
|
||||
name:SetText(admin.name)
|
||||
name:SetFont("ixMediumFont") -- Fixed font
|
||||
name:Dock(TOP)
|
||||
name:SetTextColor(COLOR_TEXT)
|
||||
name:SizeToContents()
|
||||
|
||||
local steamid = self.details:Add("DLabel")
|
||||
steamid:SetText(admin.steamID)
|
||||
steamid:SetFont("ixSmallFont")
|
||||
steamid:Dock(TOP)
|
||||
steamid:SetTextColor(COLOR_TEXT_DIM)
|
||||
steamid:SetMouseInputEnabled(true)
|
||||
steamid:SetCursor("hand")
|
||||
steamid.DoClick = function()
|
||||
SetClipboardText(admin.steamID)
|
||||
surface.PlaySound("ui/buttonclick.wav")
|
||||
LocalPlayer():Notify("SteamID скопирован: " .. admin.steamID)
|
||||
end
|
||||
|
||||
local actionPanel = self.details:Add("Panel")
|
||||
actionPanel:Dock(BOTTOM)
|
||||
actionPanel:SetTall(40)
|
||||
|
||||
local addWarn = actionPanel:Add("ixMenuButton")
|
||||
addWarn:SetText("ВЫДАТЬ ВЫГОВОР")
|
||||
addWarn:Dock(LEFT)
|
||||
addWarn:SetWide(150)
|
||||
addWarn.DoClick = function()
|
||||
Derma_StringRequest("Выдача выговора", "Введите причину выговора для " .. admin.name, "", function(text)
|
||||
net.Start("ixWarnAdd")
|
||||
net.WriteString(admin.steamID)
|
||||
net.WriteString(text)
|
||||
net.SendToServer()
|
||||
self:Close()
|
||||
end)
|
||||
end
|
||||
|
||||
-- History
|
||||
local warnData = self.warns[admin.steamID]
|
||||
if (warnData and warnData.history) then
|
||||
for i = #warnData.history, 1, -1 do
|
||||
local h = warnData.history[i]
|
||||
local entry = self.historyScroll:Add("Panel")
|
||||
entry:Dock(TOP)
|
||||
entry:SetTall(60)
|
||||
entry:DockMargin(0, 0, 0, 5)
|
||||
|
||||
entry.Paint = function(s, w, h_tall)
|
||||
draw.RoundedBox(4, 0, 0, w, h_tall, Color(50, 50, 50))
|
||||
local typeText = h.type == "add" and "[ВЫДАНО]" or "[СНЯТО]"
|
||||
local typeCol = h.type == "add" and COLOR_ACCENT or Color(100, 200, 100)
|
||||
|
||||
draw.SimpleText(typeText .. " От: " .. h.admin, "ixSmallFont", 10, 15, typeCol)
|
||||
draw.SimpleText(os.date("%d/%m/%Y %H:%M", h.time), "ixSmallFont", w - 10, 15, COLOR_TEXT_DIM, TEXT_ALIGN_RIGHT)
|
||||
draw.SimpleText(h.reason, "ixMediumFont", 10, 40, COLOR_TEXT)
|
||||
end
|
||||
|
||||
if (h.type == "add" and admin.online) then
|
||||
local remove = entry:Add("DButton")
|
||||
remove:SetText("Снять")
|
||||
remove:SetSize(60, 20)
|
||||
remove:SetPos(self.historyScroll:GetWide() - 70, 35)
|
||||
remove.DoClick = function()
|
||||
net.Start("ixWarnRemove")
|
||||
net.WriteString(admin.steamID)
|
||||
net.WriteInt(i, 16)
|
||||
net.SendToServer()
|
||||
self:Close()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:Paint(w, h)
|
||||
draw.RoundedBox(4, 0, 0, w, h, COLOR_BG)
|
||||
draw.RoundedBoxEx(4, 0, 0, w, 30, COLOR_HEADER, true, true, false, false)
|
||||
draw.SimpleText("СИСТЕМА ВЫГОВОРОВ (WARN SYSTEM)", "ixMediumFont", 10, 15, COLOR_TEXT, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
|
||||
vgui.Register("ixWarnMenu", PANEL, "DFrame")
|
||||
13
garrysmod/gamemodes/militaryrp/plugins/warn/cl_plugin.lua
Normal file
13
garrysmod/gamemodes/militaryrp/plugins/warn/cl_plugin.lua
Normal file
@@ -0,0 +1,13 @@
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
net.Receive("ixWarnMenu", function()
|
||||
local admins = net.ReadTable()
|
||||
local warns = net.ReadTable()
|
||||
|
||||
if (IsValid(ix.gui.warnMenu)) then
|
||||
ix.gui.warnMenu:Remove()
|
||||
end
|
||||
|
||||
ix.gui.warnMenu = vgui.Create("ixWarnMenu")
|
||||
ix.gui.warnMenu:Populate(admins, warns)
|
||||
end)
|
||||
89
garrysmod/gamemodes/militaryrp/plugins/warn/sh_plugin.lua
Normal file
89
garrysmod/gamemodes/militaryrp/plugins/warn/sh_plugin.lua
Normal file
@@ -0,0 +1,89 @@
|
||||
local PLUGIN = PLUGIN
|
||||
PLUGIN.name = "Warn System"
|
||||
PLUGIN.author = "Scripty"
|
||||
PLUGIN.description = "Modern Warning System for administrators with automatic demotion."
|
||||
|
||||
print("[Warn System] Loading Plugin...")
|
||||
|
||||
ix.util.Include("sv_plugin.lua")
|
||||
ix.util.Include("cl_plugin.lua")
|
||||
ix.util.Include("cl_derma.lua")
|
||||
|
||||
if (SERVER) then
|
||||
util.AddNetworkString("ixWarnSync")
|
||||
util.AddNetworkString("ixWarnAdd")
|
||||
util.AddNetworkString("ixWarnRemove")
|
||||
util.AddNetworkString("ixWarnMenu")
|
||||
end
|
||||
|
||||
ix.command.Add("warn", {
|
||||
description = "Open the warning management menu.",
|
||||
adminOnly = true,
|
||||
superAdminOnly = true, -- Helix support varies, so we keep CanRun
|
||||
CanRun = function(self, client)
|
||||
return client:IsSuperAdmin()
|
||||
end,
|
||||
OnRun = function(self, client)
|
||||
if (!client:IsSuperAdmin()) then return end
|
||||
|
||||
local admins = {}
|
||||
local warns = ix.data.Get("warns", {})
|
||||
|
||||
-- Get all players who are online and admins
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
if (v:IsAdmin()) then
|
||||
admins[v:SteamID()] = {
|
||||
name = v:Name(),
|
||||
steamID = v:SteamID(),
|
||||
rank = v:GetUserGroup(),
|
||||
online = true
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
-- Add offline admins who have warnings (Helix data)
|
||||
for steamID, data in pairs(warns) do
|
||||
if (!admins[steamID]) then
|
||||
admins[steamID] = {
|
||||
name = data.name or "Unknown",
|
||||
steamID = steamID,
|
||||
rank = data.rank or "N/A",
|
||||
online = false
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
-- Try to fetch ALL admins from SAM database if available
|
||||
if (SERVER and sam and sam.SQL) then
|
||||
sam.SQL.FQuery("SELECT `steamid`, `name`, `rank` FROM `sam_players` WHERE `rank` != 'user'", {}, function(data)
|
||||
if (data) then
|
||||
for _, row in ipairs(data) do
|
||||
local steamID = row.steamid
|
||||
-- Use SteamID if it's already a string, or format it
|
||||
if (!admins[steamID]) then
|
||||
admins[steamID] = {
|
||||
name = (row.name != "" and row.name) or "Unknown",
|
||||
steamID = steamID,
|
||||
rank = row.rank or "N/A",
|
||||
online = false
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (IsValid(client)) then
|
||||
net.Start("ixWarnMenu")
|
||||
net.WriteTable(admins)
|
||||
net.WriteTable(warns)
|
||||
net.Send(client)
|
||||
end
|
||||
end)
|
||||
else
|
||||
-- Fallback/Helix only
|
||||
net.Start("ixWarnMenu")
|
||||
net.WriteTable(admins)
|
||||
net.WriteTable(warns)
|
||||
net.Send(client)
|
||||
end
|
||||
end
|
||||
})
|
||||
93
garrysmod/gamemodes/militaryrp/plugins/warn/sv_plugin.lua
Normal file
93
garrysmod/gamemodes/militaryrp/plugins/warn/sv_plugin.lua
Normal file
@@ -0,0 +1,93 @@
|
||||
local PLUGIN = PLUGIN
|
||||
print("[Warn System] Server Side Loaded")
|
||||
|
||||
-- Load warnings on startup
|
||||
function PLUGIN:Initialize()
|
||||
self.warns = ix.data.Get("warns", {})
|
||||
end
|
||||
|
||||
-- Save warnings to permanent storage
|
||||
function PLUGIN:SaveWarns()
|
||||
ix.data.Set("warns", self.warns)
|
||||
end
|
||||
|
||||
net.Receive("ixWarnAdd", function(length, client)
|
||||
if (!client:IsSuperAdmin()) then return end
|
||||
|
||||
local targetSteamID = net.ReadString()
|
||||
local reason = net.ReadString() or "No reason provided"
|
||||
|
||||
PLUGIN.warns = ix.data.Get("warns", {})
|
||||
PLUGIN.warns[targetSteamID] = PLUGIN.warns[targetSteamID] or {count = 0, history = {}}
|
||||
|
||||
local targetData = PLUGIN.warns[targetSteamID]
|
||||
targetData.count = targetData.count + 1
|
||||
|
||||
table.insert(targetData.history, {
|
||||
admin = client:Name(),
|
||||
adminSteamID = client:SteamID(),
|
||||
reason = reason,
|
||||
time = os.time(),
|
||||
type = "add"
|
||||
})
|
||||
|
||||
local targetPlayer = player.GetBySteamID(targetSteamID)
|
||||
if (IsValid(targetPlayer)) then
|
||||
targetData.name = targetPlayer:Name()
|
||||
targetData.rank = targetPlayer:GetUserGroup()
|
||||
|
||||
targetPlayer:Notify("Вы получили выговор (" .. targetData.count .. "/3). Причина: " .. reason)
|
||||
end
|
||||
|
||||
-- Automatic demotion (3/3 warnings) - Now works for offline players too
|
||||
if (targetData.count >= 3) then
|
||||
if (sam) then
|
||||
-- Use SAM's Lua API for reliable rank setting (offline or online)
|
||||
sam.player.set_rank_id(targetSteamID, "user", 0)
|
||||
print("[Warn System] Initiated demotion for " .. targetSteamID .. " via SAM.")
|
||||
else
|
||||
-- Fallback for other systems
|
||||
if (IsValid(targetPlayer)) then
|
||||
targetPlayer:SetUserGroup("user")
|
||||
else
|
||||
-- If offline and not SAM, we attempt console command as last resort
|
||||
RunConsoleCommand("ulx", "adduser", targetSteamID, "user")
|
||||
end
|
||||
end
|
||||
|
||||
PLUGIN.warns[targetSteamID] = nil -- Remove from system after demotion
|
||||
|
||||
if (IsValid(targetPlayer)) then
|
||||
targetPlayer:Notify("Вы были автоматически сняты с админ-прав за достижение 3 выговоров.")
|
||||
end
|
||||
|
||||
ix.util.Notify((targetData.name or targetSteamID) .. " был автоматически снят с админ-прав (3/3 выговоров).")
|
||||
end
|
||||
|
||||
PLUGIN:SaveWarns()
|
||||
ix.util.Notify(client:Name() .. " выдал выговор " .. (targetData.name or targetSteamID) .. ". Причина: " .. reason)
|
||||
end)
|
||||
|
||||
net.Receive("ixWarnRemove", function(length, client)
|
||||
if (!client:IsSuperAdmin()) then return end
|
||||
|
||||
local targetSteamID = net.ReadString()
|
||||
local index = net.ReadInt(16)
|
||||
|
||||
PLUGIN.warns = ix.data.Get("warns", {})
|
||||
local targetData = PLUGIN.warns[targetSteamID]
|
||||
|
||||
if (targetData and targetData.history[index]) then
|
||||
targetData.count = math.max(0, targetData.count - 1)
|
||||
table.insert(targetData.history, {
|
||||
admin = client:Name(),
|
||||
adminSteamID = client:SteamID(),
|
||||
reason = "Снятие выговора #" .. index,
|
||||
time = os.time(),
|
||||
type = "remove"
|
||||
})
|
||||
|
||||
PLUGIN:SaveWarns()
|
||||
ix.util.Notify(client:Name() .. " снял выговор у " .. (targetData.name or targetSteamID))
|
||||
end
|
||||
end)
|
||||
Reference in New Issue
Block a user