371 lines
14 KiB
Lua
371 lines
14 KiB
Lua
local PLUGIN = PLUGIN
|
||
|
||
surface.CreateFont("ixAdminShopTitle", { font = "Montserrat", size = 28, weight = 900, antialias = true })
|
||
surface.CreateFont("ixAdminShopTab", { font = "Montserrat", size = 18, weight = 700, antialias = true })
|
||
surface.CreateFont("ixAdminShopItem", { font = "Montserrat", size = 20, weight = 800, antialias = true })
|
||
surface.CreateFont("ixAdminShopDesc", { font = "Montserrat", size = 14, weight = 500, antialias = true })
|
||
surface.CreateFont("ixAdminShopPrice", { font = "Montserrat", size = 18, weight = 900, antialias = true })
|
||
surface.CreateFont("ixAdminShopSmall", { font = "Montserrat", size = 14, weight = 600, antialias = true })
|
||
surface.CreateFont("ixAdminShopBold", { font = "Montserrat", size = 16, weight = 800, antialias = true })
|
||
surface.CreateFont("ixAdminShopTableHeader", { font = "Montserrat", size = 15, weight = 800, antialias = true })
|
||
surface.CreateFont("ixAdminShopTableCell", { font = "Montserrat", size = 14, weight = 500, antialias = true })
|
||
|
||
-- Modern Color Palette #00431c
|
||
local COLOR_BASE = Color(0, 67, 28)
|
||
local COLOR_BG = Color(15, 18, 16, 250)
|
||
local COLOR_CARD = Color(22, 26, 24, 255)
|
||
local COLOR_CARD_LIGHT = Color(30, 36, 32, 255)
|
||
local COLOR_TEXT = Color(240, 245, 240)
|
||
local COLOR_TEXT_DIM = Color(140, 150, 145)
|
||
local COLOR_ACCENT = Color(0, 140, 60)
|
||
local COLOR_DANGER = Color(200, 50, 50)
|
||
local COLOR_WARN = Color(200, 150, 0)
|
||
|
||
function PLUGIN:WrapText(text, width, font)
|
||
surface.SetFont(font)
|
||
local words = string.Explode(" ", text)
|
||
local lines = {}
|
||
local currentLine = ""
|
||
|
||
for _, word in ipairs(words) do
|
||
local testLine = currentLine == "" and word or currentLine .. " " .. word
|
||
local w, _ = surface.GetTextSize(testLine)
|
||
|
||
if w > width then
|
||
table.insert(lines, currentLine)
|
||
currentLine = word
|
||
else
|
||
currentLine = testLine
|
||
end
|
||
end
|
||
|
||
if currentLine ~= "" then
|
||
table.insert(lines, currentLine)
|
||
end
|
||
|
||
return lines
|
||
end
|
||
|
||
local PANEL = {}
|
||
|
||
function PANEL:Init()
|
||
self:SetSize(ScrW() * 0.7, ScrH() * 0.75)
|
||
self:Center()
|
||
self:MakePopup()
|
||
self:SetTitle("")
|
||
self:ShowCloseButton(false)
|
||
self.currentView = "shop"
|
||
|
||
self:SetAlpha(0)
|
||
self:AlphaTo(255, 0.2, 0)
|
||
|
||
self.startTime = SysTime()
|
||
|
||
self.Paint = function(s, w, h)
|
||
Derma_DrawBackgroundBlur(s, s.startTime)
|
||
draw.RoundedBox(8, 0, 0, w, h, COLOR_BG)
|
||
surface.SetDrawColor(COLOR_BASE.r, COLOR_BASE.g, COLOR_BASE.b, 150)
|
||
surface.SetMaterial(Material("vgui/gradient-u"))
|
||
surface.DrawTexturedRect(0, 0, w, 80)
|
||
surface.SetDrawColor(COLOR_BASE)
|
||
surface.DrawRect(0, 80, w, 2)
|
||
|
||
local title = self.currentView == "shop" and "АДМИН МАГАЗИН" or (self.currentView == "inventory" and "МОЙ ИНВЕНТАРЬ" or "УПРАВЛЕНИЕ АДМИНИСТРАТОРАМИ")
|
||
draw.SimpleText(title, "ixAdminShopTitle", 40, 40, COLOR_TEXT, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
|
||
|
||
local points = LocalPlayer():GetAdminPoints()
|
||
draw.SimpleText("Текущий баланс:", "ixAdminShopSmall", w * 0.45, 40, COLOR_TEXT_DIM, TEXT_ALIGN_RIGHT, TEXT_ALIGN_CENTER)
|
||
draw.SimpleText(points .. " Очков", "ixAdminShopItem", w * 0.45 + 10, 40, COLOR_ACCENT, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
|
||
end
|
||
|
||
local closeBtn = self:Add("DButton")
|
||
closeBtn:SetSize(40, 40)
|
||
closeBtn:SetPos(self:GetWide() - 50, 20)
|
||
closeBtn:SetText("✕")
|
||
closeBtn:SetFont("ixAdminShopTitle")
|
||
closeBtn:SetTextColor(COLOR_TEXT_DIM)
|
||
closeBtn.Paint = nil
|
||
closeBtn.DoClick = function()
|
||
self:AlphaTo(0, 0.2, 0, function() self:Close() end)
|
||
end
|
||
closeBtn.OnCursorEntered = function(s) s:SetTextColor(COLOR_DANGER) end
|
||
closeBtn.OnCursorExited = function(s) s:SetTextColor(COLOR_TEXT_DIM) end
|
||
|
||
self.nav = self:Add("Panel")
|
||
self.nav:SetSize(600, 40)
|
||
self.nav:SetPos(self:GetWide() - 670, 20)
|
||
|
||
local function CreateNavBtn(text, view, x, activeColor)
|
||
local btn = self.nav:Add("DButton")
|
||
btn:SetSize(140, 40)
|
||
btn:SetPos(x, 0)
|
||
btn:SetText(text)
|
||
btn:SetFont("ixAdminShopBold")
|
||
btn:SetTextColor(COLOR_TEXT)
|
||
btn.Paint = function(s, w, h)
|
||
if self.currentView == view then
|
||
draw.RoundedBox(4, 0, 0, w, h, activeColor)
|
||
elseif s:IsHovered() then
|
||
draw.RoundedBox(4, 0, 0, w, h, Color(255, 255, 255, 10))
|
||
end
|
||
end
|
||
btn.DoClick = function()
|
||
surface.PlaySound("ui/buttonclick.wav")
|
||
self.currentView = view
|
||
self:Refresh()
|
||
end
|
||
return btn
|
||
end
|
||
|
||
CreateNavBtn("Магазин", "shop", 0, COLOR_BASE)
|
||
|
||
-- Кнопка "Панель" - видима всем, сервер проверяет права
|
||
CreateNavBtn("Панель", "panel", 160, Color(120, 0, 180))
|
||
|
||
self.content = self:Add("DScrollPanel")
|
||
self.content:Dock(FILL)
|
||
self.content:DockMargin(40, 100, 40, 40)
|
||
|
||
local sbar = self.content:GetVBar()
|
||
sbar:SetWide(8)
|
||
sbar:SetHideButtons(true)
|
||
sbar.Paint = function(s, w, h)
|
||
draw.RoundedBox(4, 0, 0, w, h, Color(0, 0, 0, 100))
|
||
end
|
||
sbar.btnGrip.Paint = function(s, w, h)
|
||
draw.RoundedBox(4, 2, 0, w-4, h, s:IsHovered() and COLOR_ACCENT or COLOR_BASE)
|
||
end
|
||
|
||
self:Refresh()
|
||
end
|
||
|
||
function PANEL:Refresh()
|
||
self.content:Clear()
|
||
|
||
if self.currentView == "shop" then
|
||
local layout = self.content:Add("DIconLayout")
|
||
layout:Dock(TOP)
|
||
layout:SetSpaceX(20)
|
||
layout:SetSpaceY(20)
|
||
|
||
for id, item in pairs(PLUGIN.Items) do
|
||
local card = layout:Add("DPanel")
|
||
card:SetSize(250, 340)
|
||
card.Paint = function(s, w, h)
|
||
draw.RoundedBox(8, 0, 0, w, h, COLOR_CARD)
|
||
surface.SetDrawColor(COLOR_CARD_LIGHT)
|
||
surface.SetMaterial(Material("vgui/gradient-d"))
|
||
surface.DrawTexturedRect(0, 0, w, 140)
|
||
|
||
draw.SimpleText(item.name, "ixAdminShopItem", w/2, 160, COLOR_TEXT, TEXT_ALIGN_CENTER)
|
||
|
||
local descLines = PLUGIN:WrapText(item.desc, w - 30, "ixAdminShopDesc")
|
||
for i, line in ipairs(descLines) do
|
||
if i > 4 then break end
|
||
draw.SimpleText(line, "ixAdminShopDesc", w/2, 185 + (i-1)*18, COLOR_TEXT_DIM, TEXT_ALIGN_CENTER)
|
||
end
|
||
end
|
||
|
||
local icon = card:Add("DPanel")
|
||
icon:SetSize(60, 60)
|
||
icon:SetPos(250/2 - 30, 40)
|
||
icon.Paint = function(s, w, h)
|
||
draw.RoundedBox(8, 0, 0, w, h, COLOR_BG)
|
||
surface.SetDrawColor(item.color or COLOR_ACCENT)
|
||
surface.DrawOutlinedRect(0, 0, w, h, 2)
|
||
end
|
||
|
||
local buy = card:Add("DButton")
|
||
buy:SetSize(210, 45)
|
||
buy:SetPos(20, 275)
|
||
buy:SetText("Купить за " .. item.price .. " Очков")
|
||
buy:SetFont("ixAdminShopBold")
|
||
buy:SetTextColor(COLOR_TEXT)
|
||
buy.Paint = function(s, w, h)
|
||
draw.RoundedBox(6, 0, 0, w, h, s:IsHovered() and COLOR_ACCENT or COLOR_BASE)
|
||
end
|
||
buy.DoClick = function()
|
||
surface.PlaySound("ui/buttonclick.wav")
|
||
net.Start("ixAdminShopBuy")
|
||
net.WriteString(id)
|
||
net.SendToServer()
|
||
end
|
||
end
|
||
elseif self.currentView == "panel" then
|
||
-- Проверка прав на клиенте
|
||
local ply = LocalPlayer()
|
||
if not IsValid(ply) or not (ply:IsSuperAdmin() or ply:GetUserGroup() == "superadmin") then
|
||
local errorPanel = self.content:Add("DPanel")
|
||
errorPanel:Dock(FILL)
|
||
errorPanel.Paint = function(s, w, h)
|
||
draw.SimpleText("Доступ запрещен", "ixAdminShopTitle", w/2, h/2 - 20, COLOR_DANGER, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||
draw.SimpleText("Эта панель доступна только суперадминистраторам", "ixAdminShopSmall", w/2, h/2 + 20, COLOR_TEXT_DIM, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||
end
|
||
return
|
||
end
|
||
|
||
-- Запрос данных с сервера
|
||
net.Start("ixAdminShopPanelOpen")
|
||
net.SendToServer()
|
||
|
||
-- Заголовок панели
|
||
local headerPanel = self.content:Add("DPanel")
|
||
headerPanel:Dock(TOP)
|
||
headerPanel:SetTall(50)
|
||
headerPanel.Paint = function(s, w, h)
|
||
draw.SimpleText("Управление очками администраторов", "ixAdminShopBold", 10, h/2, COLOR_TEXT, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
|
||
end
|
||
|
||
-- Таблица
|
||
self.panelTable = self.content:Add("DListView")
|
||
self.panelTable:Dock(TOP)
|
||
self.panelTable:SetTall(400)
|
||
self.panelTable:DockMargin(0, 10, 0, 10)
|
||
self.panelTable:SetMultiSelect(false)
|
||
self.panelTable.Paint = function(s, w, h)
|
||
draw.RoundedBox(6, 0, 0, w, h, COLOR_CARD)
|
||
end
|
||
|
||
local col1 = self.panelTable:AddColumn("Игрок")
|
||
local col2 = self.panelTable:AddColumn("SteamID")
|
||
local col3 = self.panelTable:AddColumn("Ранг")
|
||
local col4 = self.panelTable:AddColumn("Очки")
|
||
|
||
col1:SetWidth(160)
|
||
col2:SetWidth(140)
|
||
col3:SetWidth(100)
|
||
col4:SetWidth(80)
|
||
|
||
-- Настройка заголовков таблицы
|
||
for _, col in ipairs(self.panelTable.Columns) do
|
||
col.Header:SetFont("ixAdminShopTableHeader")
|
||
col.Header:SetTextColor(COLOR_TEXT)
|
||
col.Header.Paint = function(s, w, h)
|
||
draw.RoundedBox(0, 0, 0, w, h, COLOR_BASE)
|
||
end
|
||
end
|
||
|
||
-- Панель управления
|
||
local controlPanel = self.content:Add("DPanel")
|
||
controlPanel:Dock(TOP)
|
||
controlPanel:SetTall(120)
|
||
controlPanel.Paint = function(s, w, h)
|
||
draw.RoundedBox(6, 0, 0, w, h, COLOR_CARD)
|
||
end
|
||
|
||
local selectedSID = nil
|
||
|
||
-- Поле ввода количества очков
|
||
local amountEntry = controlPanel:Add("DTextEntry")
|
||
amountEntry:SetSize(200, 40)
|
||
amountEntry:SetPos(10, 10)
|
||
amountEntry:SetPlaceholderText("Количество очков...")
|
||
amountEntry:SetNumeric(true)
|
||
amountEntry:SetFont("ixAdminShopBold")
|
||
|
||
-- Кнопка выдачи очков
|
||
local giveBtn = controlPanel:Add("DButton")
|
||
giveBtn:SetSize(180, 40)
|
||
giveBtn:SetPos(220, 10)
|
||
giveBtn:SetText("Выдать очки")
|
||
giveBtn:SetFont("ixAdminShopBold")
|
||
giveBtn:SetTextColor(COLOR_TEXT)
|
||
giveBtn.Paint = function(s, w, h)
|
||
draw.RoundedBox(6, 0, 0, w, h, s:IsHovered() and Color(0, 180, 80) or COLOR_ACCENT)
|
||
end
|
||
giveBtn.DoClick = function()
|
||
if not selectedSID then
|
||
surface.PlaySound("buttons/button10.wav")
|
||
return
|
||
end
|
||
local amount = tonumber(amountEntry:GetValue()) or 0
|
||
if amount < 1 then
|
||
surface.PlaySound("buttons/button10.wav")
|
||
return
|
||
end
|
||
surface.PlaySound("ui/buttonclick.wav")
|
||
net.Start("ixAdminShopPanelGivePoints")
|
||
net.WriteString(selectedSID)
|
||
net.WriteUInt(amount, 32)
|
||
net.SendToServer()
|
||
amountEntry:SetValue("")
|
||
end
|
||
|
||
-- Кнопка забирания очков
|
||
local takeBtn = controlPanel:Add("DButton")
|
||
takeBtn:SetSize(180, 40)
|
||
takeBtn:SetPos(410, 10)
|
||
takeBtn:SetText("Забрать очки")
|
||
takeBtn:SetFont("ixAdminShopBold")
|
||
takeBtn:SetTextColor(COLOR_TEXT)
|
||
takeBtn.Paint = function(s, w, h)
|
||
draw.RoundedBox(6, 0, 0, w, h, s:IsHovered() and Color(230, 60, 60) or COLOR_DANGER)
|
||
end
|
||
takeBtn.DoClick = function()
|
||
if not selectedSID then
|
||
surface.PlaySound("buttons/button10.wav")
|
||
return
|
||
end
|
||
local amount = tonumber(amountEntry:GetValue()) or 0
|
||
if amount < 1 then
|
||
surface.PlaySound("buttons/button10.wav")
|
||
return
|
||
end
|
||
surface.PlaySound("ui/buttonclick.wav")
|
||
net.Start("ixAdminShopPanelTakePoints")
|
||
net.WriteString(selectedSID)
|
||
net.WriteUInt(amount, 32)
|
||
net.SendToServer()
|
||
amountEntry:SetValue("")
|
||
end
|
||
|
||
-- Информация о выбранном игроке
|
||
local infoLabel = controlPanel:Add("DLabel")
|
||
infoLabel:SetSize(400, 30)
|
||
infoLabel:SetPos(10, 55)
|
||
infoLabel:SetFont("ixAdminShopSmall")
|
||
infoLabel:SetTextColor(COLOR_TEXT_DIM)
|
||
infoLabel:SetText("Выберите игрока из таблицы")
|
||
|
||
-- Подсказка
|
||
local hintLabel = controlPanel:Add("DLabel")
|
||
hintLabel:SetSize(500, 25)
|
||
hintLabel:SetPos(10, 85)
|
||
hintLabel:SetFont("ixAdminShopSmall")
|
||
hintLabel:SetTextColor(COLOR_WARN)
|
||
hintLabel:SetText("Вводите только положительные числа. Нажмите строку в таблице для выбора.")
|
||
|
||
-- Обработчик выбора строки
|
||
self.panelTable.OnRowSelected = function(tbl, rowIndex, row)
|
||
local data = self.panelPlayers and self.panelPlayers[rowIndex]
|
||
if data then
|
||
selectedSID = data.steamID
|
||
infoLabel:SetText("Выбран: " .. data.name .. " (" .. data.steamID .. ") — Очков: " .. data.points)
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
vgui.Register("ixAdminShop", PANEL, "DFrame")
|
||
|
||
net.Receive("ixAdminShopPanelData", function()
|
||
local data = net.ReadTable()
|
||
|
||
if IsValid(ixAdminShop) and ixAdminShop.panelTable then
|
||
ixAdminShop.panelTable:Clear()
|
||
ixAdminShop.panelPlayers = {}
|
||
|
||
for i, plyData in ipairs(data) do
|
||
ixAdminShop.panelTable:AddLine(plyData.name, plyData.steamID, plyData.rank, plyData.points)
|
||
ixAdminShop.panelPlayers[i] = plyData
|
||
end
|
||
end
|
||
end)
|
||
|
||
net.Receive("ixAdminShopOpen", function()
|
||
if (IsValid(ixAdminShop)) then
|
||
ixAdminShop:Close()
|
||
end
|
||
ixAdminShop = vgui.Create("ixAdminShop")
|
||
end)
|