Files
2026-03-31 10:27:04 +03:00

412 lines
16 KiB
Lua
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- Клиентские данные
local PLUGIN = PLUGIN
PLUGIN.promoCodesList = PLUGIN.promoCodesList or {}
-- Получение результата активации
net.Receive("ixPromoCodeResult", function()
local success = net.ReadBool()
local message = net.ReadString()
if success then
notification.AddLegacy(message, NOTIFY_GENERIC, 5)
surface.PlaySound("buttons/button15.wav")
else
notification.AddLegacy(message, NOTIFY_ERROR, 5)
surface.PlaySound("buttons/button10.wav")
end
LocalPlayer():Notify(message)
end)
-- Получение списка промокодов (для админов)
net.Receive("ixPromoCodeSync", function()
local jsonData = net.ReadString()
PLUGIN.promoCodesList = util.JSONToTable(jsonData) or {}
end)
-- Функция активации промокода
function PLUGIN:ActivatePromoCode(code)
net.Start("ixPromoCodeActivate")
net.WriteString(code)
net.SendToServer()
end
-- Админ-панель управления промокодами
function PLUGIN:OpenPromoCodesAdmin()
if not LocalPlayer():IsSuperAdmin() then
LocalPlayer():Notify("У вас нет доступа к этой панели")
return
end
-- Запрашиваем список промокодов
net.Start("ixPromoCodeList")
net.SendToServer()
local frame = vgui.Create("DFrame")
frame:SetSize(1200, 700)
frame:Center()
frame:SetTitle("")
frame:SetDraggable(true)
frame:ShowCloseButton(false)
frame:MakePopup()
frame.Paint = function(s, w, h)
draw.RoundedBox(8, 0, 0, w, h, Color(18, 18, 20))
surface.SetDrawColor(1, 67, 29)
surface.DrawRect(0, 0, w, 3)
draw.SimpleText("УПРАВЛЕНИЕ ПРОМОКОДАМИ", "F4Menu_DonateHero", w/2, 30, Color(1, 67, 29), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
local closeBtn = vgui.Create("DButton", frame)
closeBtn:SetPos(frame:GetWide() - 35, 5)
closeBtn:SetSize(30, 30)
closeBtn:SetText("")
closeBtn:SetFont("F4Menu_Category")
closeBtn:SetTextColor(Color(255, 255, 255))
closeBtn.Paint = function(s, w, h)
draw.RoundedBox(4, 0, 0, w, h, s:IsHovered() and Color(200, 50, 50) or Color(0, 0, 0, 0))
end
closeBtn.DoClick = function()
frame:Close()
end
-- Панель создания промокода
local createPanel = vgui.Create("DPanel", frame)
createPanel:SetPos(20, 70)
createPanel:SetSize(550, 600)
createPanel.Paint = function(s, w, h)
draw.RoundedBox(8, 0, 0, w, h, Color(25, 25, 28))
draw.SimpleText("СОЗДАТЬ ПРОМОКОД", "F4Menu_Category", w/2, 20, Color(200, 200, 200), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
local yPos = 60
-- Переменные для доступа в обработчиках
local codeEntry
local isCustomCode = false
-- Тип промокода
local typeLabel = vgui.Create("DLabel", createPanel)
typeLabel:SetPos(20, yPos)
typeLabel:SetSize(200, 25)
typeLabel:SetFont("F4Menu_Item")
typeLabel:SetTextColor(Color(200, 200, 200))
typeLabel:SetText("Тип промокода:")
local typeSelector = vgui.Create("DPanel", createPanel)
typeSelector:SetPos(20, yPos + 30)
typeSelector:SetSize(510, 40)
typeSelector.Paint = function(s, w, h)
draw.RoundedBox(6, 0, 0, w, h, Color(35, 35, 38))
end
local randomBtn = vgui.Create("DButton", typeSelector)
randomBtn:SetPos(5, 5)
randomBtn:SetSize(250, 30)
randomBtn:SetText("")
randomBtn.Paint = function(s, w, h)
local active = not isCustomCode
draw.RoundedBox(6, 0, 0, w, h, active and Color(1, 67, 29) or Color(45, 45, 48))
draw.SimpleText("Случайный код", "F4Menu_Item", w/2, h/2, Color(255, 255, 255), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
randomBtn.DoClick = function()
isCustomCode = false
codeEntry:SetEnabled(false)
codeEntry:SetValue("")
codeEntry:SetPlaceholderText("Будет сгенерирован автоматически")
end
local customBtn = vgui.Create("DButton", typeSelector)
customBtn:SetPos(255, 5)
customBtn:SetSize(250, 30)
customBtn:SetText("")
customBtn.Paint = function(s, w, h)
local active = isCustomCode
draw.RoundedBox(6, 0, 0, w, h, active and Color(1, 67, 29) or Color(45, 45, 48))
draw.SimpleText("Именной код", "F4Menu_Item", w/2, h/2, Color(255, 255, 255), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
customBtn.DoClick = function()
isCustomCode = true
codeEntry:SetEnabled(true)
codeEntry:SetValue("")
codeEntry:SetPlaceholderText("NEW_YEAR2026")
end
yPos = yPos + 85
-- Код промокода
local codeLabel = vgui.Create("DLabel", createPanel)
codeLabel:SetPos(20, yPos)
codeLabel:SetSize(400, 25)
codeLabel:SetFont("F4Menu_Item")
codeLabel:SetTextColor(Color(200, 200, 200))
codeLabel:SetText("Код промокода:")
local examplesLabel = vgui.Create("DLabel", createPanel)
examplesLabel:SetPos(20, yPos + 20)
examplesLabel:SetSize(510, 15)
examplesLabel:SetFont("F4Menu_InfoSmall")
examplesLabel:SetTextColor(Color(120, 120, 120))
examplesLabel:SetText("Примеры: NEW_YEAR2026, SUMMER_SALE, WELCOME100, STREAMER_GIFT")
codeEntry = vgui.Create("DTextEntry", createPanel)
codeEntry:SetPos(20, yPos + 40)
codeEntry:SetSize(510, 35)
codeEntry:SetFont("F4Menu_Item")
codeEntry:SetPlaceholderText("Будет сгенерирован автоматически")
codeEntry:SetEnabled(false)
codeEntry:SetUpdateOnType(true)
codeEntry.Paint = function(s, w, h)
local bgColor = s:IsEnabled() and Color(35, 35, 38) or Color(25, 25, 28)
draw.RoundedBox(6, 0, 0, w, h, bgColor)
s:DrawTextEntryText(Color(255, 255, 255), Color(100, 150, 255), Color(255, 255, 255))
-- Валидация в реальном времени
if s:IsEnabled() and s:GetValue() ~= "" then
local value = s:GetValue()
local isValid = string.match(value, "^[A-Z0-9_]+$") ~= nil
if not isValid then
draw.RoundedBox(6, 0, 0, w, h, Color(80, 20, 20, 50))
draw.SimpleText("Только A-Z, 0-9 и _", "F4Menu_InfoSmall", w - 10, h/2, Color(255, 100, 100), TEXT_ALIGN_RIGHT, TEXT_ALIGN_CENTER)
end
end
end
codeEntry.OnTextChanged = function(s)
-- Автоматическое приведение к верхнему регистру
local text = s:GetValue()
local upperText = string.upper(text)
if text ~= upperText then
s:SetValue(upperText)
s:SetCaretPos(#upperText)
end
end
yPos = yPos + 80
-- Количество IGS
local amountLabel = vgui.Create("DLabel", createPanel)
amountLabel:SetPos(20, yPos)
amountLabel:SetSize(200, 25)
amountLabel:SetFont("F4Menu_Item")
amountLabel:SetTextColor(Color(200, 200, 200))
amountLabel:SetText("Количество IGS:")
local amountEntry = vgui.Create("DTextEntry", createPanel)
amountEntry:SetPos(20, yPos + 30)
amountEntry:SetSize(510, 35)
amountEntry:SetFont("F4Menu_Item")
amountEntry:SetPlaceholderText("100")
amountEntry:SetNumeric(true)
amountEntry.Paint = function(s, w, h)
draw.RoundedBox(6, 0, 0, w, h, Color(35, 35, 38))
s:DrawTextEntryText(Color(255, 255, 255), Color(100, 150, 255), Color(255, 255, 255))
end
yPos = yPos + 80
-- Максимум использований
local usesLabel = vgui.Create("DLabel", createPanel)
usesLabel:SetPos(20, yPos)
usesLabel:SetSize(200, 25)
usesLabel:SetFont("F4Menu_Item")
usesLabel:SetTextColor(Color(200, 200, 200))
usesLabel:SetText("Максимум использований:")
local usesEntry = vgui.Create("DTextEntry", createPanel)
usesEntry:SetPos(20, yPos + 30)
usesEntry:SetSize(510, 35)
usesEntry:SetFont("F4Menu_Item")
usesEntry:SetPlaceholderText("1")
usesEntry:SetNumeric(true)
usesEntry.Paint = function(s, w, h)
draw.RoundedBox(6, 0, 0, w, h, Color(35, 35, 38))
s:DrawTextEntryText(Color(255, 255, 255), Color(100, 150, 255), Color(255, 255, 255))
end
yPos = yPos + 80
-- Срок действия (дни)
local daysLabel = vgui.Create("DLabel", createPanel)
daysLabel:SetPos(20, yPos)
daysLabel:SetSize(200, 25)
daysLabel:SetFont("F4Menu_Item")
daysLabel:SetTextColor(Color(200, 200, 200))
daysLabel:SetText("Срок действия (дней):")
local daysEntry = vgui.Create("DTextEntry", createPanel)
daysEntry:SetPos(20, yPos + 30)
daysEntry:SetSize(510, 35)
daysEntry:SetFont("F4Menu_Item")
daysEntry:SetPlaceholderText("30")
daysEntry:SetNumeric(true)
daysEntry.Paint = function(s, w, h)
draw.RoundedBox(6, 0, 0, w, h, Color(35, 35, 38))
s:DrawTextEntryText(Color(255, 255, 255), Color(100, 150, 255), Color(255, 255, 255))
end
yPos = yPos + 80
-- Доступные ранги
local ranksLabel = vgui.Create("DLabel", createPanel)
ranksLabel:SetPos(20, yPos)
ranksLabel:SetSize(300, 25)
ranksLabel:SetFont("F4Menu_Item")
ranksLabel:SetTextColor(Color(200, 200, 200))
ranksLabel:SetText("Доступно для рангов (пусто = всем):")
local ranksEntry = vgui.Create("DTextEntry", createPanel)
ranksEntry:SetPos(20, yPos + 30)
ranksEntry:SetSize(510, 35)
ranksEntry:SetFont("F4Menu_Item")
ranksEntry:SetPlaceholderText("user, vip, moderator (через запятую)")
ranksEntry.Paint = function(s, w, h)
draw.RoundedBox(6, 0, 0, w, h, Color(35, 35, 38))
s:DrawTextEntryText(Color(255, 255, 255), Color(100, 150, 255), Color(255, 255, 255))
end
yPos = yPos + 80
-- Кнопка создания
local createBtn = vgui.Create("DButton", createPanel)
createBtn:SetPos(20, yPos)
createBtn:SetSize(510, 45)
createBtn:SetText("")
createBtn.Paint = function(s, w, h)
draw.RoundedBox(8, 0, 0, w, h, s:IsHovered() and Color(1, 87, 39) or Color(1, 67, 29))
draw.SimpleText("СОЗДАТЬ ПРОМОКОД", "F4Menu_Category", w/2, h/2, Color(255, 255, 255), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
createBtn.DoClick = function()
local code = codeEntry:GetValue()
local amount = tonumber(amountEntry:GetValue()) or 100
local maxUses = tonumber(usesEntry:GetValue()) or 1
local days = tonumber(daysEntry:GetValue()) or 30
local expiresAt = os.time() + (days * 86400)
local ranksText = ranksEntry:GetValue()
local allowedRanks = {}
if ranksText ~= "" then
for rank in string.gmatch(ranksText, "([^,]+)") do
table.insert(allowedRanks, string.Trim(rank))
end
end
net.Start("ixPromoCodeCreate")
net.WriteString(code)
net.WriteUInt(amount, 32)
net.WriteUInt(maxUses, 16)
net.WriteUInt(expiresAt, 32)
net.WriteUInt(#allowedRanks, 8)
for _, rank in ipairs(allowedRanks) do
net.WriteString(rank)
end
net.SendToServer()
-- Очистка полей
codeEntry:SetValue("")
amountEntry:SetValue("")
usesEntry:SetValue("")
daysEntry:SetValue("")
ranksEntry:SetValue("")
end
-- Список промокодов
local listPanel = vgui.Create("DPanel", frame)
listPanel:SetPos(590, 70)
listPanel:SetSize(590, 600)
listPanel.Paint = function(s, w, h)
draw.RoundedBox(8, 0, 0, w, h, Color(25, 25, 28))
draw.SimpleText("АКТИВНЫЕ ПРОМОКОДЫ", "F4Menu_Category", w/2, 20, Color(200, 200, 200), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
local listScroll = vgui.Create("DScrollPanel", listPanel)
listScroll:SetPos(10, 50)
listScroll:SetSize(570, 540)
local vbar = listScroll:GetVBar()
vbar:SetWide(8)
function vbar:Paint(w, h)
draw.RoundedBox(4, 0, 0, w, h, Color(15, 15, 17))
end
function vbar.btnUp:Paint(w, h) end
function vbar.btnDown:Paint(w, h) end
function vbar.btnGrip:Paint(w, h)
draw.RoundedBox(4, 0, 0, w, h, Color(1, 67, 29))
end
local function RefreshList()
listScroll:Clear()
for _, promo in ipairs(self.promoCodesList) do
local promoPanel = vgui.Create("DPanel", listScroll)
promoPanel:Dock(TOP)
promoPanel:DockMargin(5, 5, 5, 0)
promoPanel:SetTall(100)
promoPanel.Paint = function(s, w, h)
draw.RoundedBox(6, 0, 0, w, h, Color(35, 35, 38))
-- Код
draw.SimpleText(promo.code, "F4Menu_Category", 15, 15, Color(100, 150, 255), TEXT_ALIGN_LEFT)
-- Информация
local info = string.format("IGS: %d | Использований: %d/%d", promo.amount, promo.currentUses, promo.maxUses)
draw.SimpleText(info, "F4Menu_Item", 15, 40, Color(200, 200, 200), TEXT_ALIGN_LEFT)
-- Срок действия
local expiresDate = os.date("%d.%m.%Y %H:%M", promo.expiresAt)
local expired = os.time() > promo.expiresAt
draw.SimpleText("До: " .. expiresDate, "F4Menu_InfoSmall", 15, 65, expired and Color(255, 100, 100) or Color(150, 150, 150), TEXT_ALIGN_LEFT)
-- Ранги
if promo.allowedRanks and #promo.allowedRanks > 0 then
local ranksText = "Ранги: " .. table.concat(promo.allowedRanks, ", ")
draw.SimpleText(ranksText, "F4Menu_InfoSmall", 15, 80, Color(150, 150, 150), TEXT_ALIGN_LEFT)
end
end
-- Кнопка удаления
local deleteBtn = vgui.Create("DButton", promoPanel)
deleteBtn:SetPos(promoPanel:GetWide() + 385, 30)
deleteBtn:SetSize(100, 40)
deleteBtn:SetText("")
deleteBtn.Paint = function(s, w, h)
draw.RoundedBox(6, 0, 0, w, h, s:IsHovered() and Color(200, 50, 50) or Color(150, 40, 40))
draw.SimpleText("Удалить", "F4Menu_Item", w/2, h/2, Color(255, 255, 255), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
deleteBtn.DoClick = function()
Derma_Query(
"Удалить промокод '" .. promo.code .. "'?",
"Подтверждение",
"Да",
function()
net.Start("ixPromoCodeDelete")
net.WriteString(promo.code)
net.SendToServer()
timer.Simple(0.5, RefreshList)
end,
"Нет"
)
end
end
end
-- Обновляем список каждую секунду
timer.Create("PromoCodesRefresh", 1, 0, function()
if not IsValid(frame) then
timer.Remove("PromoCodesRefresh")
return
end
RefreshList()
end)
RefreshList()
end
-- Команда для открытия админ-панели
concommand.Add("promocodes_admin", function()
local plugin = ix.plugin.Get("promocodes")
if plugin then
plugin:OpenPromoCodesAdmin()
end
end)