add sborka
This commit is contained in:
@@ -0,0 +1,384 @@
|
||||
local menuFrame = nil
|
||||
local factions = {}
|
||||
|
||||
local function GetPlayerBySteamID(steamID)
|
||||
for _, ply in ipairs(player.GetAll()) do
|
||||
if ply:SteamID() == steamID then
|
||||
return ply
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function GetDisplayName(input)
|
||||
if isstring(input) then
|
||||
return input
|
||||
elseif istable(input) then
|
||||
return input[1] or tostring(input)
|
||||
else
|
||||
return tostring(input)
|
||||
end
|
||||
end
|
||||
|
||||
local selectedPlayer = nil
|
||||
local selectedFaction = nil
|
||||
local selectedPodr = nil
|
||||
local selectedSpec = nil
|
||||
local selectedRank = nil
|
||||
|
||||
local playerButtons = {}
|
||||
local factionButtons = {}
|
||||
local podrButtons = {}
|
||||
local specButtons = {}
|
||||
local rankButtons = {}
|
||||
|
||||
local originalPlayers = {}
|
||||
|
||||
net.Receive("ixWhitelistMenuOpen", function()
|
||||
if menuFrame and menuFrame:IsValid() then menuFrame:Close() end
|
||||
net.Start("ixWhitelistMenuDataRequest")
|
||||
net.SendToServer()
|
||||
end)
|
||||
|
||||
net.Receive("ixWhitelistMenuData", function()
|
||||
if menuFrame and menuFrame:IsValid() then menuFrame:Close() end
|
||||
|
||||
originalPlayers = net.ReadTable()
|
||||
factions = net.ReadTable()
|
||||
|
||||
selectedPlayer = nil
|
||||
selectedFaction = nil
|
||||
selectedPodr = nil
|
||||
selectedSpec = nil
|
||||
selectedRank = nil
|
||||
|
||||
playerButtons = {}
|
||||
factionButtons = {}
|
||||
podrButtons = {}
|
||||
specButtons = {}
|
||||
rankButtons = {}
|
||||
|
||||
local scrW, scrH = ScrW(), ScrH()
|
||||
|
||||
menuFrame = vgui.Create("DFrame")
|
||||
menuFrame:SetSize(900, 650)
|
||||
menuFrame:SetPos(scrW/2 - 450, scrH/2 - 325)
|
||||
menuFrame:SetTitle("")
|
||||
menuFrame:SetDraggable(false)
|
||||
menuFrame:ShowCloseButton(false)
|
||||
menuFrame:MakePopup()
|
||||
menuFrame.Paint = function(s, w, h)
|
||||
-- Фон
|
||||
surface.SetDrawColor(Color(13, 13, 13, 240))
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
|
||||
-- Обводка
|
||||
surface.SetDrawColor(Color(1, 67, 29))
|
||||
surface.DrawOutlinedRect(0, 0, w, h, 2)
|
||||
|
||||
-- Верхняя панель
|
||||
surface.SetDrawColor(Color(1, 67, 29))
|
||||
surface.DrawRect(0, 0, w, 40)
|
||||
|
||||
-- Заголовок
|
||||
draw.SimpleText("МЕНЕДЖЕР ВАЙТЛИСТА", "ixMenuButtonFont", w/2, 20, color_white, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
|
||||
local closeBtn = vgui.Create("DButton", menuFrame)
|
||||
closeBtn:SetText("")
|
||||
closeBtn:SetSize(30, 30)
|
||||
closeBtn:SetPos(menuFrame:GetWide() - 40, 5)
|
||||
closeBtn.Paint = function(s, w, h)
|
||||
surface.SetDrawColor(Color(200, 50, 50))
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
surface.SetDrawColor(Color(29, 29, 29))
|
||||
surface.DrawOutlinedRect(0, 0, w, h, 2)
|
||||
draw.SimpleText("×", "ixSmallFont", w/2, h/2, color_white, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
|
||||
if s:IsHovered() then
|
||||
surface.SetDrawColor(Color(255, 255, 255, 30))
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
end
|
||||
end
|
||||
closeBtn.DoClick = function()
|
||||
menuFrame:Close()
|
||||
end
|
||||
|
||||
local function CreateRow(parent, text, onSelect, data, buttonGroup)
|
||||
local btn = parent:Add("DButton")
|
||||
btn:SetText("")
|
||||
btn:SetTall(30)
|
||||
btn:SetFont("ixSmallFont")
|
||||
|
||||
btn.Paint = function(s, w, h)
|
||||
if s.isSelected then
|
||||
surface.SetDrawColor(Color(1, 67, 29, 200))
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
else
|
||||
surface.SetDrawColor(Color(23, 23, 23))
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
end
|
||||
|
||||
surface.SetDrawColor(Color(1, 67, 29))
|
||||
surface.DrawOutlinedRect(0, 0, w, h, 1)
|
||||
|
||||
local textColor = s.isSelected and color_white or Color(200, 200, 200)
|
||||
draw.SimpleText(text, "ixSmallFont", 8, h/2, textColor, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
|
||||
|
||||
if s:IsHovered() and not s.isSelected then
|
||||
surface.SetDrawColor(Color(255, 255, 255, 20))
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
end
|
||||
end
|
||||
|
||||
btn.DoClick = function()
|
||||
for _, otherBtn in ipairs(buttonGroup) do
|
||||
if otherBtn ~= btn then
|
||||
otherBtn.isSelected = false
|
||||
end
|
||||
end
|
||||
btn.isSelected = true
|
||||
if onSelect then onSelect(data) end
|
||||
end
|
||||
|
||||
btn:DockMargin(2, 2, 2, 2)
|
||||
btn:Dock(TOP)
|
||||
|
||||
table.insert(buttonGroup, btn)
|
||||
return btn
|
||||
end
|
||||
|
||||
local colWidth = 160
|
||||
local colSpacing = 10
|
||||
local colX = 20
|
||||
local headerHeight = 30
|
||||
local scrollHeight = 500
|
||||
local totalHeight = headerHeight + scrollHeight
|
||||
local startY = 60
|
||||
local buttonHeight = 40
|
||||
|
||||
local function CreateColumnPanel(name, x)
|
||||
local panel = vgui.Create("DPanel", menuFrame)
|
||||
panel:SetSize(colWidth, totalHeight)
|
||||
panel:SetPos(x, startY)
|
||||
panel.Paint = function(s, w, h)
|
||||
surface.SetDrawColor(Color(23, 23, 23))
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
surface.SetDrawColor(Color(1, 67, 29))
|
||||
surface.DrawOutlinedRect(0, 0, w, h, 1)
|
||||
end
|
||||
|
||||
local header = vgui.Create("DLabel", panel)
|
||||
header:SetText(name)
|
||||
header:SetPos(0, 5)
|
||||
header:SetSize(colWidth, 20)
|
||||
header:SetFont("ixSmallFont")
|
||||
header:SetTextColor(color_white)
|
||||
header:SetContentAlignment(5)
|
||||
|
||||
local scroll = vgui.Create("DScrollPanel", panel)
|
||||
scroll:SetSize(colWidth - 4, scrollHeight)
|
||||
scroll:SetPos(2, headerHeight)
|
||||
|
||||
return scroll
|
||||
end
|
||||
|
||||
-- Панель игроков с поиском
|
||||
local playerPanel = vgui.Create("DPanel", menuFrame)
|
||||
playerPanel:SetSize(colWidth, totalHeight)
|
||||
playerPanel:SetPos(colX, startY)
|
||||
playerPanel.Paint = function(s, w, h)
|
||||
surface.SetDrawColor(Color(23, 23, 23))
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
surface.SetDrawColor(Color(1, 67, 29))
|
||||
surface.DrawOutlinedRect(0, 0, w, h, 1)
|
||||
end
|
||||
|
||||
local playerHeader = vgui.Create("DLabel", playerPanel)
|
||||
playerHeader:SetText("ИГРОКИ")
|
||||
playerHeader:SetPos(0, 5)
|
||||
playerHeader:SetSize(colWidth, 20)
|
||||
playerHeader:SetFont("ixSmallFont")
|
||||
playerHeader:SetTextColor(color_white)
|
||||
playerHeader:SetContentAlignment(5)
|
||||
|
||||
local searchBox = vgui.Create("DTextEntry", playerPanel)
|
||||
searchBox:SetPos(5, 30)
|
||||
searchBox:SetSize(colWidth - 10, 25)
|
||||
searchBox:SetPlaceholderText("Поиск...")
|
||||
searchBox:SetFont("ixSmallFont")
|
||||
searchBox:SetUpdateOnType(true)
|
||||
searchBox.Paint = function(s, w, h)
|
||||
surface.SetDrawColor(Color(23, 23, 23))
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
surface.SetDrawColor(Color(1, 67, 29))
|
||||
surface.DrawOutlinedRect(0, 0, w, h, 1)
|
||||
s:DrawTextEntryText(color_white, Color(30, 130, 255), color_white)
|
||||
end
|
||||
|
||||
local playerScroll = vgui.Create("DScrollPanel", playerPanel)
|
||||
playerScroll:SetSize(colWidth - 4, scrollHeight - 40)
|
||||
playerScroll:SetPos(2, 60)
|
||||
|
||||
-- Создаем колонки
|
||||
local factionScroll = CreateColumnPanel("ФРАКЦИИ", colX + (colWidth + colSpacing) * 1)
|
||||
local podrScroll = CreateColumnPanel("ПОДРАЗДЕЛЕНИЯ", colX + (colWidth + colSpacing) * 2)
|
||||
local specScroll = CreateColumnPanel("СПЕЦИАЛИЗАЦИИ", colX + (colWidth + colSpacing) * 3)
|
||||
local rankScroll = CreateColumnPanel("ЗВАНИЯ", colX + (colWidth + colSpacing) * 4)
|
||||
|
||||
local function UpdatePlayerList(filterText)
|
||||
playerScroll:Clear()
|
||||
playerButtons = {}
|
||||
|
||||
local lowerFilter = string.lower(filterText or "")
|
||||
|
||||
for _, p in ipairs(originalPlayers) do
|
||||
if filterText == "" or string.find(string.lower(p.name), lowerFilter, 1, true) then
|
||||
CreateRow(playerScroll, p.name, function(data)
|
||||
selectedPlayer = data
|
||||
factionScroll:Clear()
|
||||
podrScroll:Clear()
|
||||
specScroll:Clear()
|
||||
rankScroll:Clear()
|
||||
selectedFaction = nil
|
||||
selectedPodr = nil
|
||||
selectedSpec = nil
|
||||
selectedRank = nil
|
||||
|
||||
factionButtons = {}
|
||||
podrButtons = {}
|
||||
specButtons = {}
|
||||
rankButtons = {}
|
||||
|
||||
for id, f in pairs(factions) do
|
||||
CreateRow(factionScroll, GetDisplayName(f.name), function(factionData)
|
||||
selectedFaction = factionData
|
||||
local f = factions[factionData.id]
|
||||
|
||||
podrScroll:Clear()
|
||||
specScroll:Clear()
|
||||
rankScroll:Clear()
|
||||
selectedPodr = nil
|
||||
selectedSpec = nil
|
||||
selectedRank = nil
|
||||
|
||||
podrButtons = {}
|
||||
specButtons = {}
|
||||
rankButtons = {}
|
||||
|
||||
for podId, podr in pairs(f.Podr) do
|
||||
CreateRow(podrScroll, podr.name, function(podrData)
|
||||
selectedPodr = podrData
|
||||
local f2 = factions[selectedFaction.id]
|
||||
|
||||
-- Update specialties
|
||||
specScroll:Clear()
|
||||
selectedSpec = nil
|
||||
specButtons = {}
|
||||
for specId, spec in pairs(f2.Spec) do
|
||||
if spec.podr == podrData.id then
|
||||
CreateRow(specScroll, spec.name, function(specData)
|
||||
selectedSpec = specData
|
||||
end, { id = specId }, specButtons)
|
||||
end
|
||||
end
|
||||
|
||||
-- Update ranks based on subdivision
|
||||
rankScroll:Clear()
|
||||
selectedRank = nil
|
||||
rankButtons = {}
|
||||
local targetRanks = (f2.Podr[podrData.id] and f2.Podr[podrData.id].ranks) or f2.Ranks
|
||||
if targetRanks then
|
||||
local keys = {}
|
||||
for k in pairs(targetRanks) do table.insert(keys, k) end
|
||||
table.sort(keys)
|
||||
for _, rankId in ipairs(keys) do
|
||||
local rank = targetRanks[rankId]
|
||||
CreateRow(rankScroll, GetDisplayName(rank[1]), function(rankData)
|
||||
selectedRank = rankData
|
||||
end, { id = rankId }, rankButtons)
|
||||
end
|
||||
end
|
||||
end, { id = podId }, podrButtons)
|
||||
end
|
||||
|
||||
local factionRanks = f.Ranks
|
||||
if factionRanks then
|
||||
local keys = {}
|
||||
for k in pairs(factionRanks) do table.insert(keys, k) end
|
||||
table.sort(keys)
|
||||
for _, rankId in ipairs(keys) do
|
||||
local rank = factionRanks[rankId]
|
||||
CreateRow(rankScroll, GetDisplayName(rank[1]), function(rankData)
|
||||
selectedRank = rankData
|
||||
end, { id = rankId }, rankButtons)
|
||||
end
|
||||
end
|
||||
end, { id = id }, factionButtons)
|
||||
end
|
||||
end, { steamID = p.steamID }, playerButtons)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
searchBox.OnValueChange = function(_, value)
|
||||
UpdatePlayerList(value)
|
||||
end
|
||||
|
||||
UpdatePlayerList("")
|
||||
|
||||
-- Кнопка назначения
|
||||
local applyBtn = vgui.Create("DButton", menuFrame)
|
||||
applyBtn:SetText("")
|
||||
applyBtn:SetSize(200, 40)
|
||||
applyBtn:SetPos(menuFrame:GetWide()/2 - 100, startY + totalHeight + 10)
|
||||
applyBtn.Paint = function(s, w, h)
|
||||
surface.SetDrawColor(Color(1, 67, 29))
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
surface.SetDrawColor(Color(29, 29, 29))
|
||||
surface.DrawOutlinedRect(0, 0, w, h, 2)
|
||||
draw.SimpleText("НАЗНАЧИТЬ", "ixSmallFont", w/2, h/2, color_white, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
|
||||
if s:IsHovered() then
|
||||
surface.SetDrawColor(Color(255, 255, 255, 30))
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
end
|
||||
end
|
||||
applyBtn.DoClick = function()
|
||||
if not (selectedPlayer and selectedFaction and selectedPodr and selectedSpec and selectedRank) then
|
||||
LocalPlayer():Notify("Выберите все поля!")
|
||||
return
|
||||
end
|
||||
|
||||
net.Start("ixWhitelistMenuApply")
|
||||
net.WriteString(selectedPlayer.steamID)
|
||||
net.WriteUInt(selectedFaction.id, 16)
|
||||
net.WriteUInt(selectedPodr.id, 8)
|
||||
net.WriteUInt(selectedSpec.id, 8)
|
||||
net.WriteUInt(selectedRank.id, 8)
|
||||
net.SendToServer()
|
||||
|
||||
menuFrame:Close()
|
||||
LocalPlayer():Notify("Вайтлист успешно назначен!")
|
||||
end
|
||||
|
||||
-- Статус выбора
|
||||
local statusPanel = vgui.Create("DPanel", menuFrame)
|
||||
statusPanel:SetSize(400, 30)
|
||||
statusPanel:SetPos(menuFrame:GetWide()/2 - 200, startY + totalHeight - 553.5)
|
||||
statusPanel.Paint = function(s, w, h)
|
||||
local statusText = "Выберите игрока и все параметры"
|
||||
local statusColor = Color(200, 200, 200)
|
||||
|
||||
if selectedPlayer and selectedFaction and selectedPodr and selectedSpec and selectedRank then
|
||||
statusText = "Готово к назначению"
|
||||
statusColor = Color(84, 147, 90)
|
||||
end
|
||||
|
||||
draw.SimpleText(statusText, "ixSmallFont", w/2, h/2, statusColor, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
|
||||
menuFrame.OnCursorEntered = function(s)
|
||||
s:MoveToFront()
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,63 @@
|
||||
PLUGIN.name = "Whitelist Menu"
|
||||
PLUGIN.author = "Refosel"
|
||||
PLUGIN.description = "Меню для выдачи фракции, подразделения, специализации и звания."
|
||||
|
||||
-- Регистрация CAMI-привилегии при загрузке
|
||||
function PLUGIN:Load()
|
||||
CAMI.RegisterPrivilege({
|
||||
Name = "WhitelistMenu - Open",
|
||||
Description = "Может открывать меню назначения роли (фракция, подразделение, специализация, звание).",
|
||||
MinAccess = "user"
|
||||
})
|
||||
end
|
||||
|
||||
PLUGIN.AllowedGroups = {
|
||||
["superadmin"] = true,
|
||||
["super admin"] = true,
|
||||
["projectteam"] = true,
|
||||
["teh.admin"] = true,
|
||||
["curator"] = true,
|
||||
["sudo-curator"] = true,
|
||||
["asist-sudo"] = true,
|
||||
["admin"] = true,
|
||||
["st.admin"] = true,
|
||||
["ivent"] = true,
|
||||
["st.event"] = true,
|
||||
["event"] = true,
|
||||
["disp"] = true,
|
||||
["assistant"] = true,
|
||||
["specadmin"] = true,
|
||||
["sponsor"] = true,
|
||||
["prem"] = true,
|
||||
["cmd"] = true,
|
||||
["inst"] = true
|
||||
}
|
||||
|
||||
function PLUGIN:HasAccess(client)
|
||||
local character = client:GetCharacter()
|
||||
if (character and (character:HasFlags("W") or character:HasFlags("w"))) then
|
||||
return true
|
||||
end
|
||||
|
||||
local userGroup = string.lower(client:GetUserGroup() or "user")
|
||||
return self.AllowedGroups[userGroup] or client:IsAdmin() or client:IsSuperAdmin()
|
||||
end
|
||||
|
||||
ix.util.Include("cl_plugin.lua")
|
||||
ix.util.Include("sv_plugin.lua")
|
||||
|
||||
-- Команда без аргументов
|
||||
ix.command.Add("wl", {
|
||||
description = "Открыть меню назначения роли",
|
||||
arguments = {},
|
||||
CanRun = function(self, client)
|
||||
return PLUGIN:HasAccess(client)
|
||||
end,
|
||||
OnRun = function(self, client)
|
||||
if not client:Alive() then
|
||||
return "@notAlive"
|
||||
end
|
||||
net.Start("ixWhitelistMenuOpen")
|
||||
net.Send(client)
|
||||
end
|
||||
})
|
||||
@@ -0,0 +1,105 @@
|
||||
local PLUGIN = PLUGIN
|
||||
util.AddNetworkString("ixWhitelistMenuOpen")
|
||||
util.AddNetworkString("ixWhitelistMenuDataRequest")
|
||||
util.AddNetworkString("ixWhitelistMenuData")
|
||||
util.AddNetworkString("ixWhitelistMenuApply")
|
||||
|
||||
local function GetFactionData()
|
||||
local data = {}
|
||||
for k, v in pairs(ix.faction.indices) do
|
||||
if v.Podr and v.Spec and v.Ranks then
|
||||
data[k] = {
|
||||
name = v.name,
|
||||
Podr = v.Podr,
|
||||
Spec = v.Spec,
|
||||
Ranks = v.Ranks
|
||||
}
|
||||
end
|
||||
end
|
||||
return data
|
||||
end
|
||||
|
||||
net.Receive("ixWhitelistMenuDataRequest", function(_, client)
|
||||
if not PLUGIN:HasAccess(client) then
|
||||
return
|
||||
end
|
||||
|
||||
local players = {}
|
||||
for _, ply in ipairs(player.GetAll()) do
|
||||
if ply:Alive() and ply:GetCharacter() then
|
||||
table.insert(players, {
|
||||
steamID = ply:SteamID(),
|
||||
name = ply:Name()
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
net.Start("ixWhitelistMenuData")
|
||||
net.WriteTable(players)
|
||||
net.WriteTable(GetFactionData())
|
||||
net.Send(client)
|
||||
end)
|
||||
|
||||
net.Receive("ixWhitelistMenuApply", function(_, client)
|
||||
if not PLUGIN:HasAccess(client) then
|
||||
return
|
||||
end
|
||||
|
||||
local targetSteamID = net.ReadString()
|
||||
local factionID = net.ReadUInt(16)
|
||||
local podrID = net.ReadUInt(8)
|
||||
local specID = net.ReadUInt(8)
|
||||
local rankID = net.ReadUInt(8)
|
||||
|
||||
local target = nil
|
||||
for _, ply in ipairs(player.GetAll()) do
|
||||
if ply:SteamID() == targetSteamID then
|
||||
target = ply
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not target or not target:GetCharacter() then
|
||||
client:Notify("Игрок не найден.")
|
||||
return
|
||||
end
|
||||
|
||||
local faction = ix.faction.indices[factionID]
|
||||
if not faction then
|
||||
client:Notify("Фракция не найдена.")
|
||||
return
|
||||
end
|
||||
|
||||
if not faction.Podr[podrID] then
|
||||
client:Notify("Подразделение не найдено.")
|
||||
return
|
||||
end
|
||||
|
||||
if not faction.Spec[specID] then
|
||||
client:Notify("Специализация не найдена.")
|
||||
return
|
||||
end
|
||||
|
||||
local targetRanks = (faction.Podr[podrID] and faction.Podr[podrID].ranks) or faction.Ranks
|
||||
if not (targetRanks and targetRanks[rankID]) then
|
||||
client:Notify("Звание не найдено.")
|
||||
return
|
||||
end
|
||||
|
||||
if faction.Spec[specID].podr ~= podrID then
|
||||
client:Notify("Специализация не соответствует подразделению.")
|
||||
return
|
||||
end
|
||||
|
||||
local char = target:GetCharacter()
|
||||
char:SetFaction(factionID)
|
||||
char:SetPodr(podrID)
|
||||
char:SetSpec(specID)
|
||||
char:SetRank(rankID)
|
||||
|
||||
-- Респавн игрока для применения изменений
|
||||
target:Spawn()
|
||||
|
||||
client:Notify("Роль назначена: " .. target:Name())
|
||||
target:Notify("Вам назначена новая роль!")
|
||||
end)
|
||||
Reference in New Issue
Block a user