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,371 @@
local PLUGIN = PLUGIN
ix.util.Include("sh_config.lua")
-- Цветовая схема (как в арсенале)
local COLOR_BG_DARK = Color(3, 5, 4)
local COLOR_BG_MEDIUM = Color(8, 12, 10)
local COLOR_BG_LIGHT = Color(12, 18, 14)
local COLOR_PRIMARY = Color(27, 94, 32)
local COLOR_PRIMARY_DARK = Color(15, 60, 18)
local COLOR_ACCENT = Color(56, 102, 35)
local COLOR_TEXT_PRIMARY = Color(165, 214, 167)
local COLOR_TEXT_SECONDARY = Color(174, 213, 129)
local COLOR_DANGER = Color(229, 57, 53)
local COLOR_WARNING = Color(255, 193, 7)
local COLOR_BORDER = Color(46, 125, 50, 100)
surface.CreateFont("ixShopPriceFont", {
font = "Roboto",
size = 28,
weight = 900,
antialias = true
})
surface.CreateFont("ixShopNameFont", {
font = "Roboto",
size = 20,
weight = 700,
antialias = true
})
-- Вспомогательная функция для рисования кругов
if not draw.Circle then
function draw.Circle(x, y, radius, color)
local segmentCount = math.max(16, radius)
surface.SetDrawColor(color or color_white)
local circle = {}
for i = 0, segmentCount do
local angle = math.rad((i / segmentCount) * 360)
table.insert(circle, {
x = x + math.cos(angle) * radius,
y = y + math.sin(angle) * radius
})
end
surface.DrawPoly(circle)
end
end
if not surface.DrawCircle then
function surface.DrawCircle(x, y, radius, color)
draw.Circle(x, y, radius, color)
end
end
-- Получение меню магазина
net.Receive("ixShop_OpenMenu", function()
local shopType = net.ReadString()
local shopData = net.ReadTable()
PLUGIN:OpenShopMenu(shopType, shopData)
end)
-- Создание меню магазина
function PLUGIN:OpenShopMenu(shopType, shopData)
if IsValid(self.shopFrame) then
self.shopFrame:Remove()
end
local scrW, scrH = ScrW(), ScrH()
local frame = vgui.Create("DFrame")
frame:SetSize(math.min(1200, scrW - 100), math.min(800, scrH - 100))
frame:Center()
frame:SetTitle("")
frame:SetDraggable(true)
frame:ShowCloseButton(false)
frame:MakePopup()
frame:SetDeleteOnClose(true)
self.shopFrame = frame
-- Градиентный фон с зеленым оттенком (как в арсенале)
frame.Paint = function(s, w, h)
-- Основной фон
surface.SetDrawColor(COLOR_BG_DARK)
surface.DrawRect(0, 0, w, h)
-- Градиент сверху
local gradHeight = 200
for i = 0, gradHeight do
local alpha = (1 - i/gradHeight) * 40
surface.SetDrawColor(COLOR_PRIMARY.r, COLOR_PRIMARY.g, COLOR_PRIMARY.b, alpha)
surface.DrawRect(0, i, w, 1)
end
-- Верхняя панель
surface.SetDrawColor(COLOR_BG_MEDIUM)
surface.DrawRect(0, 0, w, 80)
-- Акцентная линия
surface.SetDrawColor(COLOR_PRIMARY)
surface.DrawRect(0, 80, w, 3)
-- Декоративные элементы
surface.SetDrawColor(COLOR_BORDER)
for i = 0, w, 150 do
surface.DrawRect(i, 0, 1, 80)
end
-- Заголовок
draw.SimpleText("" .. (shopData.name or "МАГАЗИН") .. "", "ixMenuButtonFont", w/2, 28, COLOR_ACCENT, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
draw.SimpleText((shopData.description or ""), "ixSmallFont", w/2, 55, COLOR_TEXT_SECONDARY, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
-- Кнопка закрытия
local closeBtn = vgui.Create("DButton", frame)
closeBtn:SetText("")
closeBtn:SetSize(38, 38)
closeBtn:SetPos(frame:GetWide() - 50, 12)
closeBtn.Paint = function(s, w, h)
local col = s:IsHovered() and COLOR_DANGER or Color(COLOR_DANGER.r * 0.7, COLOR_DANGER.g * 0.7, COLOR_DANGER.b * 0.7)
-- Фон кнопки
draw.RoundedBox(4, 0, 0, w, h, col)
-- Внешняя рамка
surface.SetDrawColor(COLOR_BG_DARK)
surface.DrawOutlinedRect(0, 0, w, h, 2)
-- Крестик
surface.SetDrawColor(COLOR_TEXT_PRIMARY)
surface.DrawLine(w*0.3, h*0.3, w*0.7, h*0.7)
surface.DrawLine(w*0.7, h*0.3, w*0.3, h*0.7)
if s:IsHovered() then
surface.SetDrawColor(255, 255, 255, 30)
draw.RoundedBox(4, 0, 0, w, h, Color(255, 255, 255, 30))
end
end
closeBtn.DoClick = function() frame:Close() end
-- Боковая панель категорий
local sidePanel = vgui.Create("DPanel", frame)
sidePanel:SetSize(250, frame:GetTall() - 110)
sidePanel:SetPos(20, 90)
sidePanel.Paint = function(s, w, h)
draw.RoundedBox(8, 0, 0, w, h, COLOR_BG_MEDIUM)
surface.SetDrawColor(COLOR_BORDER)
surface.DrawOutlinedRect(0, 0, w, h, 2)
end
local categoryList = vgui.Create("DScrollPanel", sidePanel)
categoryList:Dock(FILL)
categoryList:DockMargin(5, 5, 5, 5)
-- Панель товаров (основная)
local contentPanel = vgui.Create("DPanel", frame)
contentPanel:SetSize(frame:GetWide() - 300, frame:GetTall() - 110)
contentPanel:SetPos(280, 90)
contentPanel.Paint = function(s, w, h)
draw.RoundedBox(8, 0, 0, w, h, COLOR_BG_MEDIUM)
surface.SetDrawColor(COLOR_BORDER)
surface.DrawOutlinedRect(0, 0, w, h, 2)
end
local itemList = vgui.Create("DScrollPanel", contentPanel)
itemList:SetSize(contentPanel:GetWide() - 20, contentPanel:GetTall() - 20)
itemList:SetPos(10, 10)
local function LoadCategory(catID)
itemList:Clear()
local data = PLUGIN.shops[catID]
if not data then return end
local items = data.items or {}
if table.Count(items) == 0 then
local noItems = vgui.Create("DLabel", itemList)
noItems:SetText("В этой категории пока нет товаров.")
noItems:SetFont("ixMediumFont")
noItems:SetTextColor(Color(200, 50, 50))
noItems:SizeToContents()
noItems:SetPos(itemList:GetWide()/2 - noItems:GetWide()/2, 100)
return
end
for itemID, itemData in SortedPairs(items) do
local ixItem = ix.item.list[itemID]
if not ixItem then continue end
local itemPanel = vgui.Create("DPanel", itemList)
itemPanel:SetSize(itemList:GetWide() - 20, 100)
itemPanel:Dock(TOP)
itemPanel:DockMargin(0, 0, 10, 8)
local isHovered = false
itemPanel.Paint = function(s, w, h)
local bgColor = COLOR_BG_LIGHT
if isHovered then
bgColor = Color(bgColor.r + 10, bgColor.g + 15, bgColor.b + 10)
end
draw.RoundedBox(6, 0, 0, w, h, bgColor)
draw.RoundedBoxEx(6, 0, 0, 5, h, COLOR_PRIMARY, true, false, true, false)
surface.SetDrawColor(COLOR_BORDER)
surface.DrawOutlinedRect(0, 0, w, h, 2)
end
itemPanel.OnCursorEntered = function() isHovered = true end
itemPanel.OnCursorExited = function() isHovered = false end
-- Иконка
local itemIcon = vgui.Create("DModelPanel", itemPanel)
itemIcon:SetSize(80, 80)
itemIcon:SetPos(15, 10)
itemIcon:SetFOV(30)
function itemIcon:LayoutEntity(ent)
return
end
if ixItem.model then
itemIcon:SetModel(ixItem.model)
itemIcon.LayoutEntity = function() end
local ent = itemIcon:GetEntity()
if IsValid(ent) then
local mins, maxs = ent:GetRenderBounds()
local size = math.max(maxs.x - mins.x, maxs.y - mins.y, maxs.z - mins.z)
local center = (mins + maxs) / 2
if shopType == "consumables" then
itemIcon:SetFOV(18)
itemIcon:SetCamPos(center + Vector(size * 0.6, size * 0.6, size * 0.4))
itemIcon:SetLookAt(center)
elseif shopType == "ammo_basic" or shopType == "ammo_special" then
itemIcon:SetFOV(45)
itemIcon:SetCamPos(center + Vector(size * 2.2, size * 2.2, size * 1.2))
itemIcon:SetLookAt(center)
else
itemIcon:SetFOV(30)
itemIcon:SetCamPos(center + Vector(size * 1.4, size * 1.4, size * 0.8))
itemIcon:SetLookAt(center)
end
end
end
-- Название
local nameLabel = vgui.Create("DLabel", itemPanel)
nameLabel:SetText(ixItem.name or itemID)
nameLabel:SetFont("ixShopNameFont")
nameLabel:SetTextColor(COLOR_TEXT_PRIMARY)
nameLabel:SetPos(110, 25)
nameLabel:SizeToContents()
-- Цена
local price = itemData.price or 0
local priceLabel = vgui.Create("DLabel", itemPanel)
priceLabel:SetText(price .. "")
priceLabel:SetFont("ixShopPriceFont")
priceLabel:SetTextColor(COLOR_ACCENT)
priceLabel:SetPos(110, 55)
priceLabel:SizeToContents()
-- Кнопка покупки
local buyButton = vgui.Create("DButton", itemPanel)
buyButton:SetSize(120, 40)
buyButton:SetPos(itemPanel:GetWide() - 135, 30)
buyButton:SetText("")
buyButton.Paint = function(s, w, h)
local col = s:IsHovered() and COLOR_ACCENT or COLOR_PRIMARY
draw.RoundedBox(6, 0, 0, w, h, col)
draw.SimpleText("КУПИТЬ ►", "ixSmallFont", w/2, h/2, COLOR_TEXT_PRIMARY, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
buyButton.DoClick = function()
net.Start("ixShop_BuyItem")
net.WriteString(catID)
net.WriteString(itemID)
net.SendToServer()
end
end
end
-- Заполнение списка категорий
for id, data in SortedPairsByMemberValue(PLUGIN.shops, "name") do
local catBtn = categoryList:Add("DButton")
catBtn:SetText(data.name or id)
catBtn:SetFont("ixSmallFont")
catBtn:Dock(TOP)
catBtn:SetTall(45)
catBtn:DockMargin(0, 0, 0, 5)
catBtn:SetTextColor(COLOR_TEXT_PRIMARY)
catBtn.Paint = function(s, w, h)
local active = (shopType == id)
local col = active and COLOR_PRIMARY or COLOR_BG_LIGHT
if s:IsHovered() then col = COLOR_ACCENT end
draw.RoundedBox(4, 0, 0, w, h, col)
if active then
draw.RoundedBox(0, 0, 0, 4, h, COLOR_TEXT_PRIMARY)
end
end
catBtn.DoClick = function()
shopType = id
LoadCategory(id)
end
end
-- Загружаем начальную категорию
LoadCategory(shopType)
end
-- Добавление опций в C-Menu (свойства сущности)
properties.Add("ixShopModel", {
MenuLabel = "Изменить модель NPC",
Order = 999,
MenuIcon = "icon16/user_edit.png",
Filter = function(self, ent, ply)
if (ent:GetClass() != "ix_shop_npc") then return false end
if (!ply:IsAdmin()) then return false end
return true
end,
Action = function(self, ent)
Derma_StringRequest(
"Изменить модель NPC",
"Введите путь к модели:",
ent:GetModel(),
function(text)
net.Start("ixShop_UpdateModel")
net.WriteEntity(ent)
net.WriteString(text)
net.SendToServer()
end
)
end
})
properties.Add("ixShopType", {
MenuLabel = "Изменить тип магазина",
Order = 1000,
MenuIcon = "icon16/cart_edit.png",
Filter = function(self, ent, ply)
if (ent:GetClass() != "ix_shop_npc") then return false end
if (!ply:IsAdmin()) then return false end
return true
end,
MenuOpen = function(self, option, ent, ply)
local plugin = ix.plugin.Get("shop")
if (not plugin) then return end
local subMenu = option:AddSubMenu()
for typeID, data in pairs(plugin.shops) do
local target = subMenu:AddOption(data.name or typeID, function()
net.Start("ixShop_UpdateType")
net.WriteEntity(ent)
net.WriteString(typeID)
net.SendToServer()
end)
target:SetIcon("icon16/tag_blue.png")
end
end,
Action = function(self, ent)
-- Опции обрабатываются в MenuOpen
end
})