add sborka

This commit is contained in:
2026-03-31 10:27:04 +03:00
commit f5e5f56c84
2345 changed files with 382127 additions and 0 deletions

View File

@@ -0,0 +1,411 @@
-- Клиентские данные
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)