add sborka
This commit is contained in:
460
garrysmod/gamemodes/militaryrp/plugins/scoreboard/cl_plugin.lua
Normal file
460
garrysmod/gamemodes/militaryrp/plugins/scoreboard/cl_plugin.lua
Normal file
@@ -0,0 +1,460 @@
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
-- Создание шрифтов
|
||||
surface.CreateFont("ScoreboardHeader", {
|
||||
font = "Exo 2",
|
||||
size = 24,
|
||||
weight = 600
|
||||
})
|
||||
|
||||
surface.CreateFont("ScoreboardPlayer", {
|
||||
font = "Exo 2",
|
||||
size = 22,
|
||||
weight = 500
|
||||
})
|
||||
|
||||
surface.CreateFont("ScoreboardButton", {
|
||||
font = "Exo 2",
|
||||
size = 20,
|
||||
weight = 600
|
||||
})
|
||||
|
||||
surface.CreateFont("ScoreboardButtonSmall", {
|
||||
font = "Exo 2",
|
||||
size = 16,
|
||||
weight = 600
|
||||
})
|
||||
|
||||
-- Фоновый материал
|
||||
local bgMaterial = Material("materials/ft_ui/military/vnu/charcreate/bg.png")
|
||||
local logoMaterial = Material("materials/ft_ui/military/vnu/charcreate/logo.png")
|
||||
|
||||
-- Переменные
|
||||
PLUGIN.scoreboardFrame = nil
|
||||
PLUGIN.selectedPlayer = nil
|
||||
|
||||
-- Получение иконки фракции
|
||||
local function GetFactionIcon(faction)
|
||||
if faction == FACTION_RUSSIAN then
|
||||
return Material("materials/ft_ui/military/vnu/scoreboard/rf.png")
|
||||
elseif faction == FACTION_UKRAINE then
|
||||
return Material("materials/ft_ui/military/vnu/scoreboard/ua.png")
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Создание заголовка секции
|
||||
local function CreateSectionHeader(parent, y, faction)
|
||||
local header = vgui.Create("DPanel", parent)
|
||||
header:SetPos(200, y)
|
||||
header:SetSize(1520, 40)
|
||||
header.Paint = function(s, w, h)
|
||||
surface.SetDrawColor(1, 54, 23)
|
||||
draw.RoundedBox(5, 0, 0, w, h, Color(1, 54, 23))
|
||||
surface.SetDrawColor(1, 104, 44)
|
||||
surface.DrawOutlinedRect(0, 0, w, h, 1)
|
||||
end
|
||||
|
||||
-- Иконка фракции
|
||||
local icon = vgui.Create("DImage", header)
|
||||
icon:SetPos(4, 5)
|
||||
icon:SetSize(45, 30)
|
||||
local mat = GetFactionIcon(faction)
|
||||
if mat then
|
||||
icon:SetMaterial(mat)
|
||||
end
|
||||
|
||||
-- Заголовки колонок
|
||||
local columns = {
|
||||
{text = "Имя", x = 55},
|
||||
{text = "Подразделение", x = 320},
|
||||
{text = "Специализация", x = 640},
|
||||
{text = "Звание", x = 960},
|
||||
{text = "Привилегия", x = 1200},
|
||||
{text = "Пинг", x = 1420}
|
||||
}
|
||||
|
||||
for _, col in ipairs(columns) do
|
||||
local label = vgui.Create("DLabel", header)
|
||||
label:SetPos(col.x, 5)
|
||||
label:SetFont("ScoreboardHeader")
|
||||
label:SetText(col.text)
|
||||
label:SetTextColor(Color(255, 255, 255))
|
||||
label:SizeToContents()
|
||||
end
|
||||
|
||||
return header
|
||||
end
|
||||
|
||||
-- Создание строки игрока
|
||||
local function CreatePlayerRow(parent, y, ply, isExpanded)
|
||||
local height = isExpanded and 100 or 40
|
||||
|
||||
local row = vgui.Create("DButton", parent)
|
||||
row:SetPos(200, y)
|
||||
row:SetSize(1520, height)
|
||||
row:SetText("")
|
||||
row.Player = ply
|
||||
|
||||
row.Paint = function(s, w, h)
|
||||
local bgColor = Color(13, 13, 13, 217)
|
||||
if PLUGIN.selectedPlayer == ply then
|
||||
bgColor = Color(1, 54, 23, 150)
|
||||
elseif s:IsHovered() then
|
||||
bgColor = Color(20, 20, 20, 217)
|
||||
end
|
||||
|
||||
draw.RoundedBox(3, 0, 0, w, h, bgColor)
|
||||
end
|
||||
|
||||
row.DoClick = function()
|
||||
if PLUGIN.selectedPlayer == ply then
|
||||
PLUGIN.selectedPlayer = nil
|
||||
else
|
||||
PLUGIN.selectedPlayer = ply
|
||||
end
|
||||
end
|
||||
|
||||
-- Проверка на противоположную фракцию
|
||||
local localChar = LocalPlayer():GetCharacter()
|
||||
local targetChar = ply:GetCharacter()
|
||||
local isEnemyFaction = false
|
||||
|
||||
if localChar and targetChar then
|
||||
local localFaction = localChar:GetFaction()
|
||||
local targetFaction = targetChar:GetFaction()
|
||||
|
||||
local iAmAdmin = LocalPlayer().IsAdminMode and LocalPlayer():IsAdminMode()
|
||||
local targetIsAdmin = (ply.IsAdminMode and ply:IsAdminMode()) or (FACTION_ADMIN and targetFaction == FACTION_ADMIN)
|
||||
|
||||
isEnemyFaction = (localFaction ~= targetFaction) and not iAmAdmin and not targetIsAdmin
|
||||
end
|
||||
|
||||
-- Аватар (скрыт для противоположной фракции)
|
||||
if not isEnemyFaction then
|
||||
local avatar = vgui.Create("AvatarImage", row)
|
||||
avatar:SetPos(4, 3)
|
||||
avatar:SetSize(33, 34)
|
||||
avatar:SetPlayer(ply, 64)
|
||||
end
|
||||
|
||||
-- Имя (скрыт для противоположной фракции)
|
||||
local name = vgui.Create("DLabel", row)
|
||||
name:SetPos(55, 7)
|
||||
name:SetFont("ScoreboardPlayer")
|
||||
name:SetText(isEnemyFaction and "???" or ply:Nick())
|
||||
name:SetTextColor(Color(255, 255, 255))
|
||||
name:SizeToContents()
|
||||
|
||||
-- Подразделение
|
||||
local char = ply:GetCharacter()
|
||||
if char then
|
||||
local podrID = char:GetPodr()
|
||||
local factionTable = ix.faction.Get(char:GetFaction())
|
||||
local podrName = "—"
|
||||
|
||||
if not isEnemyFaction and factionTable and factionTable.Podr and factionTable.Podr[podrID] then
|
||||
podrName = factionTable.Podr[podrID].name or "—"
|
||||
elseif isEnemyFaction then
|
||||
podrName = "???"
|
||||
end
|
||||
|
||||
local podr = vgui.Create("DLabel", row)
|
||||
podr:SetPos(320, 7)
|
||||
podr:SetFont("ScoreboardPlayer")
|
||||
podr:SetText(podrName)
|
||||
podr:SetTextColor(Color(255, 255, 255))
|
||||
podr:SizeToContents()
|
||||
|
||||
-- Специализация
|
||||
local specID = char:GetSpec()
|
||||
local specName = "—"
|
||||
|
||||
if not isEnemyFaction and factionTable and factionTable.Spec and factionTable.Spec[specID] then
|
||||
specName = factionTable.Spec[specID].name or "—"
|
||||
elseif isEnemyFaction then
|
||||
specName = "???"
|
||||
end
|
||||
|
||||
local spec = vgui.Create("DLabel", row)
|
||||
spec:SetPos(640, 7)
|
||||
spec:SetFont("ScoreboardPlayer")
|
||||
spec:SetText(specName)
|
||||
spec:SetTextColor(Color(255, 255, 255))
|
||||
spec:SizeToContents()
|
||||
|
||||
-- Звание
|
||||
local rankName = "—"
|
||||
|
||||
if not isEnemyFaction then
|
||||
rankName = ply.GetRankName and ply:GetRankName() or "—"
|
||||
else
|
||||
rankName = "???"
|
||||
end
|
||||
|
||||
local rank = vgui.Create("DLabel", row)
|
||||
rank:SetPos(960, 7)
|
||||
rank:SetFont("ScoreboardPlayer")
|
||||
rank:SetText(rankName)
|
||||
rank:SetTextColor(Color(255, 255, 255))
|
||||
rank:SizeToContents()
|
||||
end
|
||||
|
||||
-- Привилегия
|
||||
local usergroup = ply:GetUserGroup()
|
||||
local priv = vgui.Create("DLabel", row)
|
||||
priv:SetPos(1200, 7)
|
||||
priv:SetFont("ScoreboardPlayer")
|
||||
priv:SetText(PLUGIN.NiceUserGroupName[usergroup] or usergroup)
|
||||
priv:SetTextColor(Color(255, 255, 255))
|
||||
priv:SizeToContents()
|
||||
|
||||
-- Пинг
|
||||
local ping = vgui.Create("DLabel", row)
|
||||
ping:SetPos(1420, 7)
|
||||
ping:SetFont("ScoreboardPlayer")
|
||||
ping:SetText(tostring(ply:Ping()))
|
||||
ping:SetTextColor(Color(255, 255, 255))
|
||||
ping:SizeToContents()
|
||||
|
||||
-- Кнопки действий (появляются при клике на игрока)
|
||||
if isExpanded then
|
||||
local buttons = {
|
||||
{text = "Скопировать SteamID", x = 204, w = 200, action = "copy_steamid"},
|
||||
{text = "Скопировать SteamID64", x = 414, w = 200, action = "copy_steamid64"},
|
||||
{text = "Скопировать никнейм", x = 624, w = 200, action = "copy_nick"},
|
||||
{text = "Открыть профиль", x = 834, w = 200, action = "open_profile"}
|
||||
}
|
||||
|
||||
for _, btn in ipairs(buttons) do
|
||||
local button = vgui.Create("DButton", row)
|
||||
button:SetPos(btn.x, 47)
|
||||
button:SetSize(btn.w, 28)
|
||||
button:SetText("")
|
||||
button.Paint = function(s, w, h)
|
||||
surface.SetDrawColor(1, 54, 23)
|
||||
draw.RoundedBox(3, 0, 0, w, h, Color(1, 54, 23))
|
||||
surface.SetDrawColor(1, 104, 44)
|
||||
surface.DrawOutlinedRect(0, 0, w, h, 1)
|
||||
|
||||
if s:IsHovered() then
|
||||
surface.SetDrawColor(255, 255, 255, 20)
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
end
|
||||
|
||||
draw.SimpleText(btn.text, "ScoreboardButtonSmall", w/2, h/2, Color(255, 255, 255), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
button.DoClick = function()
|
||||
if btn.action == "copy_steamid" then
|
||||
SetClipboardText(ply:SteamID())
|
||||
chat.AddText(Color(0, 255, 0), "SteamID скопирован: " .. ply:SteamID())
|
||||
elseif btn.action == "copy_steamid64" then
|
||||
SetClipboardText(ply:SteamID64())
|
||||
chat.AddText(Color(0, 255, 0), "SteamID64 скопирован: " .. ply:SteamID64())
|
||||
elseif btn.action == "copy_nick" then
|
||||
SetClipboardText(ply:Nick())
|
||||
chat.AddText(Color(0, 255, 0), "Никнейм скопирован: " .. ply:Nick())
|
||||
elseif btn.action == "open_profile" then
|
||||
ply:ShowProfile()
|
||||
chat.AddText(Color(0, 255, 0), "Открыт профиль игрока: " .. ply:Nick())
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return row
|
||||
end
|
||||
|
||||
-- Создание скроллпанели
|
||||
local function CreatePlayerList(parent)
|
||||
local scroll = vgui.Create("DScrollPanel", parent)
|
||||
scroll:SetPos(0, 179)
|
||||
scroll:SetSize(1920, 754)
|
||||
|
||||
-- Кастомный скроллбар
|
||||
local vbar = scroll:GetVBar()
|
||||
vbar:SetWide(7)
|
||||
vbar:SetPos(1714, 0)
|
||||
|
||||
function vbar:Paint(w, h)
|
||||
draw.RoundedBox(10, 0, 0, 3, h, Color(0, 0, 0, 191))
|
||||
end
|
||||
|
||||
function vbar.btnUp:Paint(w, h) end
|
||||
function vbar.btnDown:Paint(w, h) end
|
||||
|
||||
function vbar.btnGrip:Paint(w, h)
|
||||
draw.RoundedBox(10, 0, 0, w, h, Color(1, 104, 44))
|
||||
end
|
||||
|
||||
return scroll
|
||||
end
|
||||
|
||||
-- Обновление списка игроков
|
||||
local function UpdatePlayerList(scroll)
|
||||
scroll:Clear()
|
||||
|
||||
local yPos = 0
|
||||
local players = player.GetAll()
|
||||
|
||||
-- Группируем игроков по фракциям
|
||||
local factions = {}
|
||||
local factionKeys = {}
|
||||
for _, ply in ipairs(players) do
|
||||
local char = ply:GetCharacter()
|
||||
if char then
|
||||
local faction = char:GetFaction()
|
||||
if not factions[faction] then
|
||||
factions[faction] = {}
|
||||
table.insert(factionKeys, faction)
|
||||
end
|
||||
table.insert(factions[faction], ply)
|
||||
end
|
||||
end
|
||||
|
||||
-- Сортируем фракции (Админы всегда первые)
|
||||
table.sort(factionKeys, function(a, b)
|
||||
if a == FACTION_ADMIN then return true end
|
||||
if b == FACTION_ADMIN then return false end
|
||||
return a < b
|
||||
end)
|
||||
|
||||
-- Отображаем каждую фракцию
|
||||
for _, faction in ipairs(factionKeys) do
|
||||
local plyList = factions[faction]
|
||||
|
||||
-- Сортировка игроков внутри фракции
|
||||
table.sort(plyList, function(a, b)
|
||||
local charA = a:GetCharacter()
|
||||
local charB = b:GetCharacter()
|
||||
|
||||
if charA and charB then
|
||||
local podrA = charA:GetPodr() or 0
|
||||
local podrB = charB:GetPodr() or 0
|
||||
|
||||
-- Веса для подразделений: 8 (Штаб) первый, 1 (Новоприбывшие) последний
|
||||
local function GetPodrWeight(id)
|
||||
if id == 8 then return 0 end
|
||||
if id == 1 then return 999 end
|
||||
return id
|
||||
end
|
||||
|
||||
local weightA = GetPodrWeight(podrA)
|
||||
local weightB = GetPodrWeight(podrB)
|
||||
|
||||
if weightA ~= weightB then
|
||||
return weightA < weightB
|
||||
end
|
||||
|
||||
-- Если подразделения одинаковые, сортируем по рангу
|
||||
local rankA = charA:GetRank() or 0
|
||||
local rankB = charB:GetRank() or 0
|
||||
|
||||
if rankA ~= rankB then
|
||||
return rankA > rankB -- Высший ранг выше
|
||||
end
|
||||
end
|
||||
|
||||
return a:Nick() < b:Nick()
|
||||
end)
|
||||
|
||||
-- Заголовок секции
|
||||
local header = CreateSectionHeader(scroll, yPos, faction)
|
||||
yPos = yPos + 49
|
||||
|
||||
-- Игроки
|
||||
for _, ply in ipairs(plyList) do
|
||||
local isExpanded = (PLUGIN.selectedPlayer == ply)
|
||||
local row = CreatePlayerRow(scroll, yPos, ply, isExpanded)
|
||||
yPos = yPos + (isExpanded and 103 or 43)
|
||||
end
|
||||
|
||||
yPos = yPos + 10 -- Отступ между секциями
|
||||
end
|
||||
end
|
||||
|
||||
-- Создание скорборда
|
||||
function PLUGIN:CreateScoreboard()
|
||||
if IsValid(self.scoreboardFrame) then
|
||||
return
|
||||
end
|
||||
|
||||
local scrW, scrH = ScrW(), ScrH()
|
||||
|
||||
self.scoreboardFrame = vgui.Create("DFrame")
|
||||
self.scoreboardFrame:SetSize(scrW, scrH)
|
||||
self.scoreboardFrame:SetPos(0, 0)
|
||||
self.scoreboardFrame:SetTitle("")
|
||||
self.scoreboardFrame:SetDraggable(false)
|
||||
self.scoreboardFrame:ShowCloseButton(false)
|
||||
self.scoreboardFrame:SetKeyboardInputEnabled(false)
|
||||
self.scoreboardFrame:SetMouseInputEnabled(true)
|
||||
self.scoreboardFrame:MakePopup()
|
||||
|
||||
gui.EnableScreenClicker(true)
|
||||
|
||||
self.scoreboardFrame.Paint = function(s, w, h)
|
||||
-- Затемнение
|
||||
surface.SetDrawColor(10, 10, 10, 140)
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
end
|
||||
|
||||
-- Верхняя панель
|
||||
local topBar = vgui.Create("DPanel", self.scoreboardFrame)
|
||||
topBar:SetPos(0, 0)
|
||||
topBar:SetSize(scrW, 100)
|
||||
topBar.Paint = function(s, w, h)
|
||||
surface.SetDrawColor(13, 13, 13)
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
end
|
||||
|
||||
-- Логотип
|
||||
local logo = vgui.Create("DImage", topBar)
|
||||
logo:SetPos((scrW - 150) / 2, 23)
|
||||
logo:SetSize(150, 53)
|
||||
if logoMaterial then
|
||||
logo:SetMaterial(logoMaterial)
|
||||
end
|
||||
|
||||
-- Список игроков
|
||||
local scroll = CreatePlayerList(self.scoreboardFrame)
|
||||
self.scoreboardScroll = scroll
|
||||
|
||||
UpdatePlayerList(scroll)
|
||||
|
||||
-- Таймер обновления
|
||||
timer.Create("ScoreboardUpdate", 1, 0, function()
|
||||
if IsValid(self.scoreboardFrame) then
|
||||
UpdatePlayerList(scroll)
|
||||
else
|
||||
timer.Remove("ScoreboardUpdate")
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- Удаление скорборда
|
||||
function PLUGIN:RemoveScoreboard()
|
||||
if IsValid(self.scoreboardFrame) then
|
||||
self.scoreboardFrame:Remove()
|
||||
self.scoreboardFrame = nil
|
||||
end
|
||||
|
||||
gui.EnableScreenClicker(false)
|
||||
|
||||
timer.Remove("ScoreboardUpdate")
|
||||
self.selectedPlayer = nil
|
||||
end
|
||||
|
||||
-- Хуки
|
||||
function PLUGIN:ScoreboardShow()
|
||||
self:CreateScoreboard()
|
||||
end
|
||||
|
||||
function PLUGIN:ScoreboardHide()
|
||||
self:RemoveScoreboard()
|
||||
end
|
||||
|
||||
-- Закрытие при смерти
|
||||
function PLUGIN:OnCharacterDeleted()
|
||||
self:RemoveScoreboard()
|
||||
end
|
||||
@@ -0,0 +1,24 @@
|
||||
PLUGIN.name = "Custom Scoreboard"
|
||||
PLUGIN.author = "Server"
|
||||
PLUGIN.description = "Custom TAB menu scoreboard"
|
||||
|
||||
ix.util.Include("sv_plugin.lua")
|
||||
ix.util.Include("cl_plugin.lua")
|
||||
|
||||
PLUGIN.NiceUserGroupName = {
|
||||
["superadmin"] = "Супер Администратор",
|
||||
["teh.admin"] = "Тех. Администратор",
|
||||
["projectteam"] = "Команда Проекта",
|
||||
["curator"] = "Куратор",
|
||||
["disp"] = "Дисциплинёр",
|
||||
["sudo-curator"] = "Судо-Куратор",
|
||||
["assistant"] = "Ассистент Куратора",
|
||||
["st.admin"] = "Старший Администратор",
|
||||
["admin"] = "Администратор",
|
||||
["st.event"] = "Старший Ивентолог",
|
||||
["event"] = "Ивентолог",
|
||||
["media"] = "Медиа-Партнёр",
|
||||
["sponsor"] = "Спонсор",
|
||||
["vip"] = "VIP",
|
||||
["user"] = "Игрок"
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
-- Обработка действий администратора
|
||||
net.Receive("ixScoreboardAction", function(len, client)
|
||||
if not IsValid(client) or not client:IsAdmin() then return end
|
||||
|
||||
local action = net.ReadString()
|
||||
local target = net.ReadEntity()
|
||||
|
||||
if not IsValid(target) or not target:IsPlayer() then return end
|
||||
|
||||
if action == "goto" then
|
||||
client:SetPos(target:GetPos())
|
||||
client:Notify("Телепортирован к " .. target:Nick())
|
||||
|
||||
elseif action == "bring" then
|
||||
target:SetPos(client:GetPos() + client:GetForward() * 100)
|
||||
client:Notify("Телепортирован " .. target:Nick())
|
||||
|
||||
elseif action == "respawn" then
|
||||
target:Spawn()
|
||||
client:Notify("Респавн " .. target:Nick())
|
||||
|
||||
elseif action == "return" then
|
||||
local char = target:GetCharacter()
|
||||
if char and char.lastPos then
|
||||
target:SetPos(char.lastPos)
|
||||
client:Notify("Возвращен " .. target:Nick())
|
||||
end
|
||||
|
||||
elseif action == "whitelist" then
|
||||
-- Открыть меню whitelist для целевого игрока
|
||||
-- Реализация зависит от вашей системы whitelist
|
||||
|
||||
elseif action == "mute" then
|
||||
target:SetNWBool("IsMuted", not target:GetNWBool("IsMuted", false))
|
||||
client:Notify((target:GetNWBool("IsMuted") and "Замучен " or "Размучен ") .. target:Nick())
|
||||
|
||||
elseif action == "ban" then
|
||||
-- Открыть меню бана
|
||||
-- Реализация через SAM или другую систему администрирования
|
||||
|
||||
elseif action == "kick" then
|
||||
target:Kick("Kicked by " .. client:Nick())
|
||||
client:Notify("Кикнут " .. target:Nick())
|
||||
end
|
||||
end)
|
||||
Reference in New Issue
Block a user