Files
VnUtest/garrysmod/addons/shkaf/lua/entities/wardrobe_ent/cl_init.lua
2026-03-31 10:27:04 +03:00

1880 lines
86 KiB
Lua
Raw 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.
include("shared.lua")
-- === НАСТРОЙКА ИКОНОК, НАЗВАНИЙ И ОПИСАНИЙ (ARMA STYLE) ===
local CUSTOM_ICONS_BG = {
["shirt_and_jaket"] = {
default = Material("wardrobe_ui/body.png", "noclamp smooth"),
},
["helm"] = {
-- default = Material("wardrobe_ui/helm_icon.png", "noclamp smooth"),
}
}
local CUSTOM_ICONS_SKIN = {
-- default = Material("wardrobe_ui/skin_bg.png", "noclamp smooth"),
}
local CUSTOM_NAMES_BG = {
["shirt_and_jaket"] = {
[0] = "Стандартная форма",
[1] = "Закатанные рукава",
},
["helm"] = {
[0] = "Без шлема",
}
}
local CUSTOM_DESC_BG = {
["shirt_and_jaket"] = {
[0] = "Полевая форма из плотного материала.\nОбеспечивает базовую маскировку.\n\n1.2kg",
[1] = "Облегченный вариант формы для жаркого климата.\nСнижает перегрев бойца.\n\n1.0kg",
},
["helm"] = {
[0] = "Отсутствие головного убора.\nНе предоставляет баллистической защиты.\n\n0.0kg",
}
}
local CUSTOM_NAMES_SKIN = {
[0] = "Лесной камуфляж (Woodland)",
[1] = "Пустынный камуфляж (Desert)",
}
local CUSTOM_DESC_SKIN = {
[0] = "Стандартный лесной паттерн для умеренного климата.\n\nКамуфляж",
[1] = "Песочный паттерн для засушливых регионов.\n\nКамуфляж",
}
-- ========================
local C_BG_D = Color(3, 5, 4)
local C_BG_M = Color(8, 12, 10)
local C_BG_L = Color(12, 18, 14)
local C_PRI = Color(27, 94, 32)
local C_PRI_H = Color(33, 110, 38)
local C_ACC = Color(56, 102, 35)
local C_ACC_H = Color(66, 120, 45)
local C_BDR = Color(46, 125, 50, 80)
local C_TXT_P = Color(165, 214, 167)
local C_GRAD = Color(27, 94, 32, 15)
local C_WARN = Color(150, 40, 40)
local C_WARN_H = Color(180, 50, 50)
local C_WHITE = Color(255, 255, 255, 200)
local C_ARMA_TOOLTIP_BG = Color(35, 35, 35, 240)
local C_ARMA_SLOT = Color(40, 40, 40, 255)
local C_ARMA_SLOT_HOVER = Color(60, 60, 60, 255)
local C_ARMA_SLOT_EMPTY = Color(20, 20, 20, 150)
local C_ARMA_SEL = Color(194, 138, 74, 255)
local C_ARMA_TEXT = Color(180, 180, 180, 255)
local bgMat = Material("materials/wardrobe_ui/fon.jpg", "noclamp smooth")
-- =====================================================================
-- ПАНЕЛЬ ОБЫЧНОГО ИГРОКА
-- =====================================================================
net.Receive("SandboxWardrobeOpen", function()
local model = net.ReadString()
local bodygroups = net.ReadTable()
local availableSkins = net.ReadTable()
local currentBodygroups = net.ReadTable()
local currentSkin = net.ReadUInt(8)
local unlockedBodygroups = net.ReadTable()
local serverPresets = net.ReadTable()
local scrW, scrH = ScrW(), ScrH()
local fW = math.Clamp(scrW * 0.62, 950, 1600)
local fH = math.Clamp(scrH * 0.62, 600, 1000)
local leftW = 230
local rightW = math.Clamp(fW * 0.38, 350, 550)
local midW = fW - leftW - rightW - 80
local midX = 40 + leftW
local rightX = midX + midW + 20
local frame = vgui.Create("DFrame")
frame:SetSize(fW, fH)
frame:Center()
frame:SetTitle("")
frame:ShowCloseButton(false)
frame:MakePopup()
frame:SetBackgroundBlur(true)
frame.Paint = function(s, w, h)
draw.RoundedBox(8, 0, 0, w, h, C_BG_D)
surface.SetDrawColor(C_GRAD)
surface.DrawRect(0, 0, w, 3)
draw.RoundedBoxEx(8, 0, 0, w, 50, C_BG_M, true, true, false, false)
surface.SetDrawColor(C_BDR)
surface.DrawLine(0, 50, w, 50)
draw.SimpleText("ГАРДЕРОБ", "DermaLarge", w/2, 25, C_TXT_P, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
surface.SetDrawColor(C_ACC)
surface.DrawRect(20, 48, 30, 2)
surface.DrawRect(w-50, 48, 30, 2)
surface.SetDrawColor(C_BDR)
surface.DrawOutlinedRect(0, 0, w, h, 1)
end
local closeBtn = vgui.Create("DButton", frame)
closeBtn:SetPos(frame:GetWide() - 40, 10)
closeBtn:SetSize(30, 30)
closeBtn:SetText("")
closeBtn.Paint = function(s, w, h)
local col = s:IsHovered() and C_ACC_H or C_ACC
draw.RoundedBox(4, 0, 0, w, h, col)
draw.SimpleText("X", "DermaLarge", w/2, h/2-2, C_TXT_P, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
closeBtn.DoClick = function() frame:Close() end
local btnOffset = 45
local hintsPanel = vgui.Create("DPanel", frame)
hintsPanel:SetPos(20, 60)
hintsPanel.Expanded = true
hintsPanel.CurHeight = 305
hintsPanel:SetSize(230, hintsPanel.CurHeight)
hintsPanel.Paint = function(s, w, h) draw.RoundedBox(6, 0, 0, w, h, C_BG_M); surface.SetDrawColor(C_BDR); surface.DrawOutlinedRect(0, 0, w, h, 1) end
hintsPanel.Think = function(s)
local targetHeight = s.Expanded and 305 or 35
if s.CurHeight ~= targetHeight then s.CurHeight = Lerp(FrameTime() * 15, s.CurHeight, targetHeight); if math.abs(s.CurHeight - targetHeight) < 0.5 then s.CurHeight = targetHeight end; s:SetTall(math.Round(s.CurHeight)) end
end
local hintsHeader = vgui.Create("DButton", hintsPanel)
hintsHeader:SetPos(0, 0); hintsHeader:SetSize(230, 35); hintsHeader:SetText("")
hintsHeader.Paint = function(s, w, h)
local col = s:IsHovered() and C_BG_L or C_BG_D
draw.RoundedBoxEx(6, 0, 0, w, h, col, true, true, not hintsPanel.Expanded, not hintsPanel.Expanded)
surface.SetDrawColor(C_ACC); surface.DrawRect(0, 0, 3, h)
draw.SimpleText("УПРАВЛЕНИЕ", "DermaLarge", 10, h/2, C_TXT_P, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
draw.SimpleText(hintsPanel.Expanded and "" or "", "DermaDefault", w - 15, h/2, C_TXT_P, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
hintsHeader.DoClick = function(s) hintsPanel.Expanded = not hintsPanel.Expanded end
local hintsContent = vgui.Create("DPanel", hintsPanel)
hintsContent:SetPos(0, 35); hintsContent:SetSize(230, 270); hintsContent.Paint = function() end
local binds = { {"ЛКМ / Перетаскивание", "Примерить одежду"}, {"ALT + ЛКМ", "Вращение камеры"}, {"SHIFT + ЛКМ", "Перемещение камеры"}, {"Колёсико мыши", "Приблизить / Отдалить"} }
local hy = 10
for _, bind in ipairs(binds) do
local bindItem = vgui.Create("DPanel", hintsContent)
bindItem:SetPos(10, hy); bindItem:SetSize(210, 55)
bindItem.Paint = function(s, w, h) draw.RoundedBox(4, 0, 0, w, h, C_BG_D); surface.SetDrawColor(C_BDR); surface.DrawOutlinedRect(0, 0, w, h, 1); draw.SimpleText(bind[1], "DermaDefaultBold", w/2, 16, C_WHITE, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER); draw.SimpleText(bind[2], "DermaDefault", w/2, 36, C_TXT_P, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER) end
hy = hy + 60
end
local presetsPanel = vgui.Create("DPanel", frame)
presetsPanel.Expanded = false
presetsPanel.CurHeight = 35
presetsPanel:SetSize(230, presetsPanel.CurHeight)
local presetsContent = vgui.Create("DScrollPanel", presetsPanel)
presetsContent:SetPos(0, 35)
presetsPanel.Paint = function(s, w, h)
draw.RoundedBox(6, 0, 0, w, h, C_BG_M)
surface.SetDrawColor(C_BDR)
surface.DrawOutlinedRect(0, 0, w, h, 1)
end
presetsPanel.Think = function(s)
local maxH = fH - hintsPanel.CurHeight - 80
local targetHeight = s.Expanded and maxH or 35
if s.CurHeight ~= targetHeight then
s.CurHeight = Lerp(FrameTime() * 15, s.CurHeight, targetHeight)
if math.abs(s.CurHeight - targetHeight) < 0.5 then s.CurHeight = targetHeight end
s:SetTall(math.Round(s.CurHeight))
end
local targetY = hintsPanel:GetY() + hintsPanel:GetTall() + 10
if s:GetY() ~= targetY then s:SetPos(20, targetY) end
presetsContent:SetSize(230, s:GetTall() - 35)
end
local presetsHeader = vgui.Create("DButton", presetsPanel)
presetsHeader:SetPos(0, 0); presetsHeader:SetSize(230, 35); presetsHeader:SetText("")
presetsHeader.Paint = function(s, w, h)
local col = s:IsHovered() and C_BG_L or C_BG_D
draw.RoundedBoxEx(6, 0, 0, w, h, col, true, true, not presetsPanel.Expanded, not presetsPanel.Expanded)
surface.SetDrawColor(C_ACC); surface.DrawRect(0, 0, 3, h); draw.SimpleText("СБОРКИ", "DermaLarge", 10, h/2, C_TXT_P, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
draw.SimpleText(presetsPanel.Expanded and "" or "", "DermaDefault", w - 15, h/2, C_TXT_P, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
presetsHeader.DoClick = function(s) presetsPanel.Expanded = not presetsPanel.Expanded end
local allPresets = serverPresets or {}
if not allPresets[model] then allPresets[model] = {} end
local function SavePresetsToServer()
net.Start("SandboxWardrobeSavePreset")
net.WriteTable(allPresets)
net.SendToServer()
end
-- НОРМАЛИЗАЦИЯ ключей (util.JSONToTable превращает строковые числа обратно в number type)
local norm = {}
for k, v in pairs(allPresets[model]) do norm[tostring(k)] = v end
allPresets[model] = norm
local py = 10
for i = 1, 10 do
local slotPanel = vgui.Create("DPanel", presetsContent)
slotPanel:SetPos(10, py)
slotPanel:SetSize(200, 85)
slotPanel.Paint = function(s, w, h)
draw.RoundedBox(4, 0, 0, w, h, C_BG_D)
surface.SetDrawColor(C_BDR)
surface.DrawOutlinedRect(0, 0, w, h, 1)
end
local pData = allPresets[model][i] or allPresets[model][tostring(i)]
local hasData = pData ~= nil
local defaultName = "Сборка " .. i
local iconCell = vgui.Create("DPanel", slotPanel)
iconCell:SetPos(130, 12)
iconCell:SetSize(60, 60)
iconCell.Paint = function(s, w, h)
local curData = allPresets[model][tostring(i)]
local isFilled = curData ~= nil
draw.RoundedBox(4, 0, 0, w, h, isFilled and C_ARMA_SLOT or C_ARMA_SLOT_EMPTY)
if isFilled then
local mat = Material("wardrobe_ui/body.png", "noclamp smooth")
if mat and not mat:IsError() then
surface.SetDrawColor(255, 255, 255, 200)
surface.SetMaterial(mat)
surface.DrawTexturedRect(5, 5, w-10, h-10)
end
surface.SetDrawColor(C_PRI)
surface.DrawOutlinedRect(0, 0, w, h, 1)
else
draw.SimpleText("ПУСТО", "DermaDefault", w/2, h/2, Color(100, 100, 100), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
surface.SetDrawColor(Color(0,0,0,100))
surface.DrawOutlinedRect(0, 0, w, h, 1)
end
end
local nameEntry = vgui.Create("DTextEntry", slotPanel)
nameEntry:SetPos(10, 12)
nameEntry:SetSize(110, 25)
nameEntry:SetText(hasData and (pData.name or defaultName) or defaultName)
nameEntry.Paint = function(s, w, h)
draw.RoundedBox(4, 0, 0, w, h, C_BG_M)
surface.SetDrawColor(s:IsEditing() and C_PRI or C_BDR)
surface.DrawOutlinedRect(0, 0, w, h, 1)
s:DrawTextEntryText(C_WHITE, C_PRI, C_WHITE)
end
nameEntry.OnChange = function(s)
local curData = allPresets[model][tostring(i)]
if curData then
curData.name = s:GetText()
timer.Create("WardrobePresetSave_"..i, 0.5, 1, function()
if IsValid(frame) then SavePresetsToServer() end
end)
end
end
local btnW = 33
local btnY = 47
local loadBtn = vgui.Create("DButton", slotPanel)
loadBtn:SetPos(10, btnY)
loadBtn:SetSize(btnW, 25)
loadBtn:SetText("")
loadBtn.Paint = function(s, w, h)
if not allPresets[model][tostring(i)] then
draw.RoundedBox(4, 0, 0, w, h, Color(30, 30, 30, 150))
draw.SimpleText("ЗАГР", "DermaDefault", w/2, h/2, Color(100, 100, 100), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
return
end
local col = s:IsHovered() and C_ACC_H or C_ACC
draw.RoundedBox(4, 0, 0, w, h, col)
draw.SimpleText("ЗАГР", "DermaDefault", w/2, h/2, C_TXT_P, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
loadBtn.DoClick = function()
local curData = allPresets[model][tostring(i)]
if not curData then return end
for idx, val in pairs(curData.bgs) do currentBodygroups[tonumber(idx) or idx] = val end
if curData.skin then currentSkin = curData.skin end
surface.PlaySound("buttons/button15.wav")
end
local saveBtn = vgui.Create("DButton", slotPanel)
saveBtn:SetPos(48, btnY)
saveBtn:SetSize(btnW, 25)
saveBtn:SetText("")
saveBtn.Paint = function(s, w, h)
local col = s:IsHovered() and C_PRI_H or C_PRI
draw.RoundedBox(4, 0, 0, w, h, col)
draw.SimpleText("СОХР", "DermaDefault", w/2, h/2, C_TXT_P, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
saveBtn.DoClick = function()
local finalName = nameEntry:GetText()
if finalName == "" then finalName = defaultName end
local safeBGs = {}
for k, v in pairs(currentBodygroups) do safeBGs[tostring(k)] = v end
allPresets[model][tostring(i)] = {
name = finalName,
bgs = safeBGs,
skin = currentSkin
}
SavePresetsToServer()
surface.PlaySound("buttons/button15.wav")
end
local delBtn = vgui.Create("DButton", slotPanel)
delBtn:SetPos(87, btnY)
delBtn:SetSize(btnW, 25)
delBtn:SetText("")
delBtn.Paint = function(s, w, h)
if not allPresets[model][tostring(i)] then
draw.RoundedBox(4, 0, 0, w, h, Color(30, 30, 30, 150))
draw.SimpleText("УДАЛ", "DermaDefault", w/2, h/2, Color(100, 100, 100), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
return
end
local col = s:IsHovered() and C_WARN_H or C_WARN
draw.RoundedBox(4, 0, 0, w, h, col)
draw.SimpleText("УДАЛ", "DermaDefault", w/2, h/2, C_TXT_P, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
delBtn.DoClick = function()
if allPresets[model][tostring(i)] or allPresets[model][i] then
allPresets[model][tostring(i)] = nil
allPresets[model][i] = nil
SavePresetsToServer()
nameEntry:SetText(defaultName)
surface.PlaySound("buttons/button15.wav")
end
end
py = py + 95
end
local modelPanel = vgui.Create("DModelPanel", frame)
modelPanel:SetPos(midX, 60); modelPanel:SetSize(midW, fH - 140); modelPanel:SetModel(model)
modelPanel:Receiver("wardrobe_item", function(pnl, tbl, bDoDrop, command, x, y)
if bDoDrop then local droppedCell = tbl[1]; if IsValid(droppedCell) then if droppedCell.isBodygroup then currentBodygroups[droppedCell.bgIdx] = droppedCell.bgVal elseif droppedCell.isSkin then currentSkin = droppedCell.skinVal end; surface.PlaySound("UI/buttonclick.wav") end end
end)
local oldPaint = modelPanel.Paint
modelPanel.Paint = function(s, w, h)
if bgMat and not bgMat:IsError() then surface.SetDrawColor(255, 255, 255, 255); surface.SetMaterial(bgMat); surface.DrawTexturedRect(0, 0, w, h) end
local mx, my = gui.MousePos(); local px, py = s:LocalToScreen(0, 0)
local isHoveredDrag = dragndrop.IsDragging() and (mx >= px and mx <= px + w and my >= py and my <= py + h)
if isHoveredDrag then surface.SetDrawColor(C_PRI); surface.DrawOutlinedRect(0, 0, w, h, 3); surface.SetDrawColor(C_GRAD); surface.DrawRect(0, 0, w, h) else surface.SetDrawColor(C_BDR); surface.DrawOutlinedRect(0, 0, w, h, 1) end
oldPaint(s, w, h)
end
modelPanel.camAnims = { zoom = 60, rot = Angle(0, 180, 0), offset = Vector(0, 0, 0) }; modelPanel.curZoom = 60; modelPanel.curRot = Angle(0, 180, 0); modelPanel.curOffset = Vector(0, 0, 0)
modelPanel.LayoutEntity = function(s, ent)
if not s.bAnimSet then local seq = ent:LookupSequence("idle_subtle"); if seq <= 0 then seq = ent:LookupSequence("idle_all_01") end; if seq > 0 then ent:SetSequence(seq) end; s.bAnimSet = true end
if (s.bDragging) then
local x, y = gui.MousePos(); local dx = x - s.lastX; local dy = y - s.lastY
if input.IsKeyDown(KEY_LSHIFT) or input.IsKeyDown(KEY_RSHIFT) then local right = s.curRot:Right(); local up = s.curRot:Up(); s.camAnims.offset = s.camAnims.offset - right * (dx * 0.15) + up * (dy * 0.15) elseif input.IsKeyDown(KEY_LALT) or input.IsKeyDown(KEY_RALT) then s.camAnims.rot.yaw = s.camAnims.rot.yaw - dx * 0.8; s.camAnims.rot.pitch = math.Clamp(s.camAnims.rot.pitch - dy * 0.8, -45, 45) end
s.lastX, s.lastY = x, y
end
local speed = FrameTime() * 10; s.curZoom = Lerp(speed, s.curZoom, s.camAnims.zoom); s.curRot = LerpAngle(speed, s.curRot, s.camAnims.rot); s.curOffset = LerpVector(speed, s.curOffset, s.camAnims.offset)
local targetPos = ent:GetPos() + Vector(0, 0, 40) + s.curOffset; s:SetCamPos(targetPos - s.curRot:Forward() * s.curZoom); s:SetLookAng(s.curRot)
ent:FrameAdvance((RealTime() - (s.lastTick or RealTime())) * 1); s.lastTick = RealTime()
end
local function SetCameraAngle(angle)
if angle == "front" then modelPanel.camAnims.rot = Angle(0, 180, 0) elseif angle == "side" then modelPanel.camAnims.rot = Angle(0, 90, 0) elseif angle == "back" then modelPanel.camAnims.rot = Angle(0, 0, 0) end
modelPanel.camAnims.offset = Vector(0, 0, 0)
end
modelPanel.OnMousePressed = function(s, code) if code == MOUSE_LEFT then local clickTime = SysTime(); if s.lastClickTime and (clickTime - s.lastClickTime) < 0.3 then s.camAnims.zoom = 60; s.camAnims.rot = Angle(0, 180, 0); s.camAnims.offset = Vector(0, 0, 0); s.lastClickTime = 0; return end; s.lastClickTime = clickTime; s.bDragging = true; s.lastX, s.lastY = gui.MousePos() end end
modelPanel.OnMouseReleased = function(s, code) if code == MOUSE_LEFT then s.bDragging = false end end
modelPanel.OnCursorExited = function(s) s.bDragging = false end
modelPanel.OnMouseWheeled = function(s, delta) s.camAnims.zoom = math.Clamp(s.camAnims.zoom - delta * 15, 10, 120) end
timer.Simple(0.05, function() if IsValid(modelPanel) then SetCameraAngle("front") end end)
modelPanel.Think = function(s) local entity = s:GetEntity(); if IsValid(entity) then for idx, value in pairs(currentBodygroups) do if entity:GetBodygroup(idx) ~= value then entity:SetBodygroup(idx, value) end end; if entity:GetSkin() ~= currentSkin then entity:SetSkin(currentSkin) end end end
local viewButtons = vgui.Create("DPanel", frame)
viewButtons:SetPos(midX, fH - 70); viewButtons:SetSize(midW, 50)
viewButtons.Paint = function(s, w, h) draw.RoundedBox(6, 0, 0, w, h, C_BG_M); surface.SetDrawColor(C_BDR); surface.DrawOutlinedRect(0, 0, w, h, 1) end
local angles = {{"СПЕРЕДИ", "front"}, {"СБОКУ", "side"}, {"СЗАДИ", "back"}}
for k, v in ipairs(angles) do
local btn = vgui.Create("DButton", viewButtons)
btn.Paint = function(s, w, h) local col = s:IsHovered() and C_PRI_H or C_PRI; draw.RoundedBox(4, 0, 0, w, h, col); surface.SetDrawColor(C_BDR); surface.DrawOutlinedRect(0, 0, w, h, 1); draw.SimpleText(v[1], "DermaDefault", w/2, h/2, C_TXT_P, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER) end
btn.DoClick = function() SetCameraAngle(v[2]) end
btn.PerformLayout = function(s, w, h)
local bs = (k == 2) and 20 or 10
local bw = (viewButtons:GetWide() - 40) / 3
btn:SetSize(bw, 30)
btn:SetPos(10 + (k-1)*(bw+10), 10)
end
btn:SetText("")
end
local HoveredItemName = nil
local HoveredItemDesc = nil
local tooltipPanel = vgui.Create("DPanel", frame)
tooltipPanel:SetSize(280, 100)
tooltipPanel:SetDrawOnTop(true)
tooltipPanel:SetMouseInputEnabled(false)
tooltipPanel:SetAlpha(0)
tooltipPanel.Paint = function(s, w, h)
draw.RoundedBox(0, 0, 0, w, h, C_ARMA_TOOLTIP_BG)
surface.SetDrawColor(C_ARMA_SEL)
surface.DrawRect(0, 0, w, 3)
if HoveredItemName then draw.SimpleText(HoveredItemName, "DermaDefaultBold", 10, 12, C_ARMA_SEL, TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP) end
if HoveredItemDesc then draw.DrawText(HoveredItemDesc, "DermaDefault", 10, 35, C_ARMA_TEXT, TEXT_ALIGN_LEFT) end
end
tooltipPanel.Think = function(s)
if HoveredItemName then
s:SetAlpha(math.Clamp(s:GetAlpha() + FrameTime() * 1500, 0, 255))
local mx, my = gui.MousePos()
local fx, fy = frame:GetPos()
s:SetPos(mx - fx + 15, my - fy + 15)
local _, lineCount = string.gsub(HoveredItemDesc or "", "\n", "")
s:SetTall(50 + (lineCount * 16))
else
s:SetAlpha(math.Clamp(s:GetAlpha() - FrameTime() * 1500, 0, 255))
end
end
local settingsPanel = vgui.Create("DPanel", frame)
settingsPanel:SetPos(rightX, 60)
settingsPanel:SetSize(rightW, fH - 80)
settingsPanel.Paint = function(s, w, h)
draw.RoundedBox(6, 0, 0, w, h, C_BG_M)
surface.SetDrawColor(C_BDR)
surface.DrawOutlinedRect(0, 0, w, h, 1)
end
local settingsScroll = vgui.Create("DScrollPanel", settingsPanel)
settingsScroll:Dock(FILL)
settingsScroll:DockMargin(10, 10, 10, 55)
local sbar = settingsScroll:GetVBar()
sbar:SetWide(4)
sbar.Paint = function() end
sbar.btnGrip.Paint = function(s, w, h) draw.RoundedBox(0, 0, 0, w, h, Color(100, 100, 100, 100)) end
sbar.btnUp.Paint = function() end
sbar.btnDown.Paint = function() end
local baseCellSize = 108
local bgMainHeaderLabel = vgui.Create("DLabel", settingsScroll)
bgMainHeaderLabel:Dock(TOP)
bgMainHeaderLabel:DockMargin(22, 0, 30, 15)
bgMainHeaderLabel:SetTall(20)
bgMainHeaderLabel:SetText("РЕДАКТИРОВАНИЕ БОДИГРУПП")
bgMainHeaderLabel:SetFont("DermaDefaultBold")
bgMainHeaderLabel:SetTextColor(C_ARMA_SEL)
if table.Count(bodygroups) > 0 then
local bgArray = {}
for idx, data in pairs(bodygroups) do table.insert(bgArray, data) end
table.sort(bgArray, function(a, b) return a.index < b.index end)
for _, data in ipairs(bgArray) do
local idx = data.index
local bgName = string.lower(data.name)
local allowedValues = {}
local blockedForModel = WARDROBE_BLOCKED_BGS[string.lower(model)] or {}
for i = 0, data.count - 1 do
local isBlocked = false
if not (unlockedBodygroups[tostring(idx)] and unlockedBodygroups[tostring(idx)][tostring(i)]) then
for _, blockData in ipairs(blockedForModel) do
if blockData[1] == idx and blockData[2] == i then
if isfunction(blockData[3]) then
if not blockData[3](LocalPlayer()) then
isBlocked = true
end
else
isBlocked = true
end
break
end
end
end
if not isBlocked then
table.insert(allowedValues, i)
end
end
if #allowedValues == 0 then continue end
local bgPanel = vgui.Create("DPanel", settingsScroll)
bgPanel:Dock(TOP)
bgPanel:DockMargin(10, 5, 30, 10)
bgPanel.Expanded = true
local panelExpandedHeight = 25 + baseCellSize
bgPanel.CurHeight = panelExpandedHeight
bgPanel:SetTall(panelExpandedHeight)
bgPanel.Paint = function(s, w, h) end
bgPanel.Think = function(s)
local targetHeight = s.Expanded and panelExpandedHeight or 25
if s.CurHeight ~= targetHeight then
s.CurHeight = Lerp(FrameTime() * 15, s.CurHeight, targetHeight)
if math.abs(s.CurHeight - targetHeight) < 0.5 then s.CurHeight = targetHeight end
s:SetTall(math.Round(s.CurHeight))
end
end
local headerBtn = vgui.Create("DButton", bgPanel)
headerBtn:SetPos(22, 0)
headerBtn:SetSize(336, 25)
headerBtn:SetText("")
headerBtn.Paint = function(s, w, h)
local col = s:IsHovered() and Color(255, 255, 255) or Color(200, 200, 200)
draw.SimpleText(string.upper(data.name), "DermaDefaultBold", 0, h/2, col, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
draw.SimpleText(bgPanel.Expanded and "" or "", "DermaDefault", w, h/2, col, TEXT_ALIGN_RIGHT, TEXT_ALIGN_CENTER)
end
headerBtn.DoClick = function(s)
bgPanel.Expanded = not bgPanel.Expanded
surface.PlaySound("UI/buttonclick.wav")
end
local carouselPanel = vgui.Create("DPanel", bgPanel)
carouselPanel:SetPos(22, 25)
carouselPanel:SetSize(336, baseCellSize)
carouselPanel.Paint = function() end
local startScrollIdx = 0
for k, v in ipairs(allowedValues) do
if v == (currentBodygroups[idx] or 0) then
startScrollIdx = k - 1
break
end
end
carouselPanel.TargetScroll = startScrollIdx
carouselPanel.CurScroll = carouselPanel.TargetScroll
local leftBtn = vgui.Create("DButton", bgPanel)
leftBtn:SetPos(0, 25)
leftBtn:SetSize(16, baseCellSize)
leftBtn:SetText("")
leftBtn.Paint = function(s, w, h)
local col = s:IsHovered() and C_ARMA_SLOT_HOVER or C_ARMA_SLOT_EMPTY
draw.RoundedBox(0, 0, 0, w, h, col)
draw.SimpleText("<", "DermaDefaultBold", w/2, h/2, s:IsHovered() and C_ARMA_SEL or Color(150, 150, 150), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
surface.SetDrawColor(Color(0, 0, 0, 120))
surface.DrawOutlinedRect(0, 0, w, h, 1)
end
local rightBtn = vgui.Create("DButton", bgPanel)
rightBtn:SetPos(364, 25)
rightBtn:SetSize(16, baseCellSize)
rightBtn:SetText("")
rightBtn.Paint = function(s, w, h)
local col = s:IsHovered() and C_ARMA_SLOT_HOVER or C_ARMA_SLOT_EMPTY
draw.RoundedBox(0, 0, 0, w, h, col)
draw.SimpleText(">", "DermaDefaultBold", w/2, h/2, s:IsHovered() and C_ARMA_SEL or Color(150, 150, 150), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
surface.SetDrawColor(Color(0, 0, 0, 120))
surface.DrawOutlinedRect(0, 0, w, h, 1)
end
local maxScroll = math.max(0, #allowedValues - 1)
leftBtn.DoClick = function()
carouselPanel.TargetScroll = math.max(0, carouselPanel.TargetScroll - 1)
surface.PlaySound("UI/buttonclick.wav")
end
rightBtn.DoClick = function()
carouselPanel.TargetScroll = math.min(maxScroll, carouselPanel.TargetScroll + 1)
surface.PlaySound("UI/buttonclick.wav")
end
local cells = {}
for scrollIdx, val in ipairs(allowedValues) do
local i = val
local cell = vgui.Create("DButton", carouselPanel)
cell:SetText("")
cell.isBodygroup = true
cell.bgIdx = idx
cell.bgVal = i
cell:Droppable("wardrobe_item")
cell.OnCursorEntered = function(s)
HoveredItemName = CUSTOM_NAMES_BG[bgName] and CUSTOM_NAMES_BG[bgName][i] or (string.upper(bgName) .. " #" .. i)
HoveredItemDesc = CUSTOM_DESC_BG[bgName] and CUSTOM_DESC_BG[bgName][i] or "Дополнительная экипировка.\n\n0.5kg"
end
cell.OnCursorExited = function(s) HoveredItemName = nil; HoveredItemDesc = nil end
cell.DoClick = function(s)
currentBodygroups[idx] = i
carouselPanel.TargetScroll = scrollIdx - 1
surface.PlaySound("UI/buttonclick.wav")
end
cell.Paint = function(s, w, h)
local isSelected = (currentBodygroups[idx] or 0) == i
local bgColor = C_ARMA_SLOT
if isSelected then bgColor = C_ARMA_SEL
elseif s:IsHovered() then bgColor = C_ARMA_SLOT_HOVER end
local alphaMult = math.Clamp(1 - (s.AbsOffset or 0) * 0.35, 0.2, 1)
if s:IsDragging() then surface.SetDrawColor(255, 255, 255, 20); surface.DrawRect(0, 0, w, h); return end
surface.SetDrawColor(bgColor.r, bgColor.g, bgColor.b, bgColor.a * alphaMult)
surface.DrawRect(0, 0, w, h)
local iconData = CUSTOM_ICONS_BG[bgName]
local customIcon = iconData and (iconData[i] or iconData.default)
if customIcon and not customIcon:IsError() then
surface.SetDrawColor(255, 255, 255, 255 * alphaMult)
surface.SetMaterial(customIcon)
surface.DrawTexturedRect(4, 4, w - 8, h - 8)
else
local txtColor = isSelected and Color(40, 40, 40, 255 * alphaMult) or Color(150, 150, 150, 255 * alphaMult)
draw.SimpleText(tostring(i), "DermaLarge", w/2, h/2, txtColor, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
surface.SetDrawColor(0, 0, 0, 120 * alphaMult)
surface.DrawOutlinedRect(0, 0, w, h, 1)
end
table.insert(cells, cell)
end
carouselPanel.Think = function(s)
s.CurScroll = Lerp(FrameTime() * 12, s.CurScroll, s.TargetScroll)
for i, cell in ipairs(cells) do
local index = i - 1
local offset = index - s.CurScroll
local absOffset = math.abs(offset)
cell.AbsOffset = absOffset
local scale = math.Clamp(1 - absOffset * 0.25, 0.5, 1)
local size = baseCellSize * scale
local spacing = 65
local x = (s:GetWide() / 2) - (size / 2) + (offset * spacing)
local y = (s:GetTall() / 2) - (size / 2)
cell:SetSize(size, size)
cell:SetPos(x, y)
cell:SetZPos(100 - math.floor(absOffset * 10))
end
end
end
end
if #availableSkins > 1 then
local skinPanel = vgui.Create("DPanel", settingsScroll)
skinPanel:Dock(TOP)
skinPanel:DockMargin(10, 5, 30, 10)
skinPanel.Expanded = true
local panelExpandedHeight = 25 + baseCellSize
skinPanel.CurHeight = panelExpandedHeight
skinPanel:SetTall(panelExpandedHeight)
skinPanel.Paint = function(s, w, h) end
skinPanel.Think = function(s)
local targetHeight = s.Expanded and panelExpandedHeight or 25
if s.CurHeight ~= targetHeight then
s.CurHeight = Lerp(FrameTime() * 15, s.CurHeight, targetHeight)
if math.abs(s.CurHeight - targetHeight) < 0.5 then s.CurHeight = targetHeight end
s:SetTall(math.Round(s.CurHeight))
end
end
local headerBtn = vgui.Create("DButton", skinPanel)
headerBtn:SetPos(22, 0)
headerBtn:SetSize(336, 25)
headerBtn:SetText("")
headerBtn.Paint = function(s, w, h)
local col = s:IsHovered() and Color(255, 255, 255) or Color(200, 200, 200)
draw.SimpleText("ВАРИАНТ КАМУФЛЯЖА", "DermaDefaultBold", 0, h/2, col, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
draw.SimpleText(skinPanel.Expanded and "" or "", "DermaDefault", w, h/2, col, TEXT_ALIGN_RIGHT, TEXT_ALIGN_CENTER)
end
headerBtn.DoClick = function(s)
skinPanel.Expanded = not skinPanel.Expanded
surface.PlaySound("UI/buttonclick.wav")
end
local carouselPanel = vgui.Create("DPanel", skinPanel)
carouselPanel:SetPos(22, 25)
carouselPanel:SetSize(336, baseCellSize)
carouselPanel.Paint = function() end
carouselPanel.TargetScroll = currentSkin or 0
carouselPanel.CurScroll = carouselPanel.TargetScroll
local leftBtn = vgui.Create("DButton", skinPanel)
leftBtn:SetPos(0, 25); leftBtn:SetSize(16, baseCellSize); leftBtn:SetText("")
leftBtn.Paint = function(s, w, h)
local col = s:IsHovered() and C_ARMA_SLOT_HOVER or C_ARMA_SLOT_EMPTY
draw.RoundedBox(0, 0, 0, w, h, col)
draw.SimpleText("<", "DermaDefaultBold", w/2, h/2, s:IsHovered() and C_ARMA_SEL or Color(150, 150, 150), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
surface.SetDrawColor(Color(0, 0, 0, 120)); surface.DrawOutlinedRect(0, 0, w, h, 1)
end
local rightBtn = vgui.Create("DButton", skinPanel)
rightBtn:SetPos(364, 25); rightBtn:SetSize(16, baseCellSize); rightBtn:SetText("")
rightBtn.Paint = function(s, w, h)
local col = s:IsHovered() and C_ARMA_SLOT_HOVER or C_ARMA_SLOT_EMPTY
draw.RoundedBox(0, 0, 0, w, h, col)
draw.SimpleText(">", "DermaDefaultBold", w/2, h/2, s:IsHovered() and C_ARMA_SEL or Color(150, 150, 150), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
surface.SetDrawColor(Color(0, 0, 0, 120)); surface.DrawOutlinedRect(0, 0, w, h, 1)
end
local maxScroll = math.max(0, #availableSkins - 1)
leftBtn.DoClick = function()
carouselPanel.TargetScroll = math.max(0, carouselPanel.TargetScroll - 1)
surface.PlaySound("UI/buttonclick.wav")
end
rightBtn.DoClick = function()
carouselPanel.TargetScroll = math.min(maxScroll, carouselPanel.TargetScroll + 1)
surface.PlaySound("UI/buttonclick.wav")
end
local cells = {}
for i = 0, #availableSkins - 1 do
local cell = vgui.Create("DButton", carouselPanel)
cell:SetText("")
cell.isSkin = true
cell.skinVal = i
cell:Droppable("wardrobe_item")
cell.OnCursorEntered = function(s) HoveredItemName = CUSTOM_NAMES_SKIN[i] or ("Текстура #" .. i); HoveredItemDesc = CUSTOM_DESC_SKIN[i] or "Альтернативный паттерн экипировки." end
cell.OnCursorExited = function(s) HoveredItemName = nil; HoveredItemDesc = nil end
cell.DoClick = function(s)
currentSkin = i
carouselPanel.TargetScroll = i
surface.PlaySound("UI/buttonclick.wav")
end
cell.Paint = function(s, w, h)
local isSelected = currentSkin == i
local bgColor = C_ARMA_SLOT
if isSelected then bgColor = C_ARMA_SEL elseif s:IsHovered() then bgColor = C_ARMA_SLOT_HOVER end
local alphaMult = math.Clamp(1 - (s.AbsOffset or 0) * 0.35, 0.2, 1)
if s:IsDragging() then surface.SetDrawColor(255, 255, 255, 20); surface.DrawRect(0, 0, w, h); return end
surface.SetDrawColor(bgColor.r, bgColor.g, bgColor.b, bgColor.a * alphaMult)
surface.DrawRect(0, 0, w, h)
local customIcon = CUSTOM_ICONS_SKIN[i] or CUSTOM_ICONS_SKIN.default
if customIcon and not customIcon:IsError() then
surface.SetDrawColor(255, 255, 255, 255 * alphaMult)
surface.SetMaterial(customIcon)
surface.DrawTexturedRect(4, 4, w - 8, h - 8)
else
local txtColor = isSelected and Color(40, 40, 40, 255 * alphaMult) or Color(150, 150, 150, 255 * alphaMult)
draw.SimpleText(tostring(i), "DermaLarge", w/2, h/2, txtColor, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
surface.SetDrawColor(0, 0, 0, 120 * alphaMult)
surface.DrawOutlinedRect(0, 0, w, h, 1)
end
table.insert(cells, cell)
end
carouselPanel.Think = function(s)
s.CurScroll = Lerp(FrameTime() * 12, s.CurScroll, s.TargetScroll)
for i, cell in ipairs(cells) do
local index = i - 1
local offset = index - s.CurScroll
local absOffset = math.abs(offset)
cell.AbsOffset = absOffset
local scale = math.Clamp(1 - absOffset * 0.25, 0.5, 1)
local size = baseCellSize * scale
local spacing = 65
local x = (s:GetWide() / 2) - (size / 2) + (offset * spacing)
local y = (s:GetTall() / 2) - (size / 2)
cell:SetSize(size, size)
cell:SetPos(x, y)
cell:SetZPos(100 - math.floor(absOffset * 10))
end
end
end
local applyBtn = vgui.Create("DButton", settingsPanel)
local wipeBtn = vgui.Create("DButton", settingsPanel)
wipeBtn:SetSize(135, 35); wipeBtn:SetText("")
wipeBtn.Paint = function(s, w, h)
local col = s:IsHovered() and C_WARN_H or C_WARN
draw.RoundedBox(8, 0, 0, w, h, C_BDR); draw.RoundedBox(8, 2, 2, w-4, h-4, col)
draw.SimpleText("СБРОСИТЬ", "DermaDefaultBold", w/2, h/2, C_TXT_P, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
wipeBtn.DoClick = function() net.Start("SandboxWardrobeApply"); net.WriteBool(true); net.SendToServer(); frame:Close() end
applyBtn:SetSize(135, 35); applyBtn:SetText("")
applyBtn.Paint = function(s, w, h)
local col = s:IsHovered() and C_PRI_H or C_PRI
draw.RoundedBox(8, 0, 0, w, h, C_BDR); draw.RoundedBox(8, 2, 2, w-4, h-4, col); draw.RoundedBoxEx(8, 2, 2, w-4, h/2-2, C_GRAD, true, true, false, false)
draw.SimpleText("ПРИМЕНИТЬ", "DermaDefaultBold", w/2, h/2, C_TXT_P, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
applyBtn.DoClick = function() net.Start("SandboxWardrobeApply"); net.WriteBool(false); net.WriteTable(currentBodygroups); net.WriteUInt(currentSkin, 8); net.SendToServer(); frame:Close() end
settingsPanel.PerformLayout = function(s, w, h)
applyBtn:SetPos(w - 135 - 10, h - 35 - 10)
wipeBtn:SetPos(w - 135*2 - 20, h - 35 - 10)
end
end)
-- =====================================================================
-- АДМИН ПАНЕЛЬ
-- =====================================================================
net.Receive("SandboxWardrobeAdminOpen", function()
local unlockedData = net.ReadTable()
local fW, fH = 800, 600
local frame = vgui.Create("DFrame")
frame:SetSize(fW, fH)
frame:Center()
frame:SetTitle("")
frame:ShowCloseButton(false)
frame:MakePopup()
frame:SetBackgroundBlur(true)
frame.Paint = function(s, w, h)
draw.RoundedBox(8, 0, 0, w, h, C_BG_D)
surface.SetDrawColor(C_GRAD)
surface.DrawRect(0, 0, w, 3)
draw.RoundedBoxEx(8, 0, 0, w, 50, C_BG_M, true, true, false, false)
surface.SetDrawColor(C_BDR)
surface.DrawLine(0, 50, w, 50)
draw.SimpleText("АДМИН ПАНЕЛЬ (ДОСТУП)", "DermaLarge", w/2, 25, C_TXT_P, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
surface.SetDrawColor(C_ACC)
surface.DrawRect(20, 48, 30, 2)
surface.DrawRect(w-50, 48, 30, 2)
surface.SetDrawColor(C_BDR)
surface.DrawOutlinedRect(0, 0, w, h, 1)
end
local closeBtn = vgui.Create("DButton", frame)
closeBtn:SetPos(frame:GetWide() - 40, 10)
closeBtn:SetSize(30, 30)
closeBtn:SetText("")
closeBtn.Paint = function(s, w, h)
local col = s:IsHovered() and C_ACC_H or C_ACC
draw.RoundedBox(4, 0, 0, w, h, col)
draw.SimpleText("X", "DermaLarge", w/2, h/2-2, C_TXT_P, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
closeBtn.DoClick = function() frame:Close() end
local leftPanel = vgui.Create("DPanel", frame)
leftPanel:SetPos(20, 60)
leftPanel:SetSize(230, fH - 80)
leftPanel.Paint = function(s, w, h)
draw.RoundedBox(6, 0, 0, w, h, C_BG_M)
surface.SetDrawColor(C_BDR)
surface.DrawOutlinedRect(0, 0, w, h, 1)
end
local plyList = vgui.Create("DListView", leftPanel)
plyList:Dock(FILL)
plyList:DockMargin(10, 10, 10, 10)
plyList:AddColumn("Игрок")
plyList.Paint = function(s, w, h)
draw.RoundedBox(4, 0, 0, w, h, C_BG_D)
surface.SetDrawColor(C_BDR)
surface.DrawOutlinedRect(0, 0, w, h, 1)
end
local function FormatLine(line)
for _, col in pairs(line.Columns) do
col:SetTextColor(C_WHITE)
col:SetFont("DermaDefaultBold")
end
line.Paint = function(s, w, h)
local col = C_BG_M
if s:IsSelected() then col = C_PRI
elseif s:IsHovered() then col = C_BG_L end
draw.RoundedBox(0, 0, 0, w, h, col)
surface.SetDrawColor(C_BDR)
surface.DrawOutlinedRect(0, 0, w, h, 1)
end
end
local manualBtn = vgui.Create("DButton", leftPanel)
manualBtn:Dock(BOTTOM)
manualBtn:DockMargin(10, 0, 10, 10)
manualBtn:SetTall(30)
manualBtn:SetText("")
manualBtn.Paint = function(s, w, h)
local col = s:IsHovered() and C_PRI_H or C_PRI
draw.RoundedBox(4, 0, 0, w, h, col)
surface.SetDrawColor(C_BDR)
surface.DrawOutlinedRect(0, 0, w, h, 1)
draw.SimpleText("ДОБАВИТЬ ID", "DermaDefault", w/2, h/2, C_TXT_P, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
manualBtn.DoClick = function()
Derma_StringRequest("Оффлайн игрок", "SteamID64:", "", function(text)
if string.len(text) == 17 then
local line = plyList:AddLine("Оффлайн ("..text..")")
FormatLine(line)
line.sid = text
line.mdl = "models/cwz/characters/mason_pm.mdl"
end
end)
end
local selectedSid = nil
local rightPanel = vgui.Create("DPanel", frame)
rightPanel:SetPos(260, 60)
rightPanel:SetSize(fW - 280, fH - 80)
rightPanel.Paint = function(s, w, h)
draw.RoundedBox(6, 0, 0, w, h, C_BG_M)
surface.SetDrawColor(C_BDR)
surface.DrawOutlinedRect(0, 0, w, h, 1)
end
local topRow = vgui.Create("DPanel", rightPanel)
topRow:Dock(TOP)
topRow:SetTall(45)
topRow:DockMargin(10, 10, 10, 0)
topRow.Paint = function() end
local modelEntry = vgui.Create("DTextEntry", topRow)
modelEntry:Dock(FILL)
modelEntry:DockMargin(0, 0, 10, 15)
modelEntry:SetPlaceholderText(" Путь к модели (напр. models/cwz/characters/mason_pm.mdl)")
modelEntry.Paint = function(s, w, h)
draw.RoundedBox(4, 0, 0, w, h, C_BG_D)
surface.SetDrawColor(C_BDR)
surface.DrawOutlinedRect(0, 0, w, h, 1)
s:DrawTextEntryText(C_WHITE, C_ACC, C_WHITE)
end
local loadBtn = vgui.Create("DButton", topRow)
loadBtn:Dock(RIGHT)
loadBtn:SetWide(100)
loadBtn:DockMargin(0, 0, 0, 15)
loadBtn:SetText("")
loadBtn.Paint = function(s, w, h)
local col = s:IsHovered() and C_PRI_H or C_PRI
draw.RoundedBox(4, 0, 0, w, h, col)
surface.SetDrawColor(C_BDR)
surface.DrawOutlinedRect(0, 0, w, h, 1)
draw.SimpleText("ОБНОВИТЬ", "DermaDefault", w/2, h/2, C_TXT_P, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
local bgScroll = vgui.Create("DScrollPanel", rightPanel)
bgScroll:Dock(FILL)
bgScroll:DockMargin(10, 0, 10, 10)
local sbar = bgScroll:GetVBar()
sbar:SetWide(4)
sbar.Paint = function() end
sbar.btnGrip.Paint = function(s, w, h) draw.RoundedBox(0, 0, 0, w, h, Color(100, 100, 100, 100)) end
sbar.btnUp.Paint = function() end
sbar.btnDown.Paint = function() end
local function LoadModelBGs(modelSearch)
bgScroll:Clear()
if not modelSearch or modelSearch == "" then return end
local sidData = unlockedData[selectedSid] or {}
local modelData = sidData[string.lower(modelSearch)] or {}
local blockedForModel = WARDROBE_BLOCKED_BGS[string.lower(modelSearch)]
if not blockedForModel or table.IsEmpty(blockedForModel) then
local err = bgScroll:Add("DLabel")
err:SetText(" Для этой модели нет заблокированных бодигрупп в настройках.")
err:SetTextColor(C_WARN)
err:SetFont("DermaDefaultBold")
err:SizeToContents()
err:Dock(TOP)
return
end
for _, blockData in ipairs(blockedForModel) do
local bg_id = blockData[1]
local bg_val = blockData[2]
local str_bg_id = tostring(bg_id)
local str_bg_val = tostring(bg_val)
local bg_name = blockData[4] or ("Бодигруппа " .. bg_id .. " Вариант " .. bg_val)
local linePnl = vgui.Create("DPanel", bgScroll)
linePnl:Dock(TOP)
linePnl:SetTall(35)
linePnl:DockMargin(0, 5, 10, 5)
linePnl.Paint = function(s, w, h)
draw.RoundedBox(4, 0, 0, w, h, C_BG_D)
surface.SetDrawColor(C_BDR)
surface.DrawOutlinedRect(0, 0, w, h, 1)
end
local lbl = vgui.Create("DLabel", linePnl)
lbl:Dock(LEFT)
lbl:DockMargin(10, 0, 0, 0)
lbl:SetWide(300)
lbl:SetText(bg_name)
lbl:SetFont("DermaDefaultBold")
lbl:SetTextColor(C_WHITE)
local lockBtn = vgui.Create("DButton", linePnl)
lockBtn:Dock(RIGHT)
lockBtn:SetWide(100)
lockBtn:DockMargin(0, 5, 5, 5)
lockBtn:SetText("")
local isUnlocked = (modelData[str_bg_id] and modelData[str_bg_id][str_bg_val]) and true or false
lockBtn.IsUnlocked = isUnlocked
lockBtn.Paint = function(s, w, h)
local col = s.IsUnlocked and C_PRI or C_WARN
if s:IsHovered() then
col = s.IsUnlocked and C_PRI_H or C_WARN_H
end
draw.RoundedBox(4, 0, 0, w, h, col)
draw.SimpleText(s.IsUnlocked and "ОТКРЫТ" or "ЗАКРЫТ", "DermaDefaultBold", w/2, h/2, C_TXT_P, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
lockBtn.DoClick = function(s)
s.IsUnlocked = not s.IsUnlocked
unlockedData[selectedSid] = unlockedData[selectedSid] or {}
unlockedData[selectedSid][string.lower(modelSearch)] = unlockedData[selectedSid][string.lower(modelSearch)] or {}
unlockedData[selectedSid][string.lower(modelSearch)][str_bg_id] = unlockedData[selectedSid][string.lower(modelSearch)][str_bg_id] or {}
unlockedData[selectedSid][string.lower(modelSearch)][str_bg_id][str_bg_val] = s.IsUnlocked and true or nil
net.Start("SandboxWardrobeAdminUpdate")
net.WriteString(selectedSid)
net.WriteString(modelSearch)
net.WriteUInt(bg_id, 8)
net.WriteUInt(bg_val, 8)
net.WriteBool(s.IsUnlocked)
net.SendToServer()
surface.PlaySound("UI/buttonclick.wav")
end
end
end
for _, ply in ipairs(player.GetAll()) do
local line = plyList:AddLine(ply:Nick())
FormatLine(line)
line.sid = ply:SteamID64()
line.mdl = ply:GetModel()
end
plyList.OnRowSelected = function(lst, index, pnl)
selectedSid = pnl.sid
modelEntry:SetText(pnl.mdl or "")
LoadModelBGs(pnl.mdl)
end
loadBtn.DoClick = function()
if not selectedSid then return end
LoadModelBGs(modelEntry:GetText())
end
end)
-- =====================================================================
-- ПАНЕЛЬ КОМАНДИРА
-- =====================================================================
local C_CMD = Color(40, 75, 120)
local C_CMD_H = Color(50, 90, 140)
local C_CMD_SEL = Color(70, 130, 180)
net.Receive("SandboxWardrobeCommanderOpen", function()
local membersData = net.ReadTable()
local scrW, scrH = ScrW(), ScrH()
local fW = math.Clamp(scrW * 0.65, 1000, 1600)
local fH = math.Clamp(scrH * 0.65, 650, 1000)
local frame = vgui.Create("DFrame")
frame:SetSize(fW, fH)
frame:Center()
frame:SetTitle("")
frame:ShowCloseButton(false)
frame:MakePopup()
frame:SetBackgroundBlur(true)
frame.Paint = function(s, w, h)
draw.RoundedBox(8, 0, 0, w, h, C_BG_D)
surface.SetDrawColor(C_GRAD)
surface.DrawRect(0, 0, w, 3)
draw.RoundedBoxEx(8, 0, 0, w, 50, C_BG_M, true, true, false, false)
surface.SetDrawColor(C_BDR)
surface.DrawLine(0, 50, w, 50)
draw.SimpleText("ПАНЕЛЬ КОМАНДИРА", "DermaLarge", w/2, 25, C_TXT_P, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
surface.SetDrawColor(C_CMD)
surface.DrawRect(20, 48, 30, 2)
surface.DrawRect(w - 50, 48, 30, 2)
surface.SetDrawColor(C_BDR)
surface.DrawOutlinedRect(0, 0, w, h, 1)
end
local closeBtn = vgui.Create("DButton", frame)
closeBtn:SetPos(frame:GetWide() - 40, 10)
closeBtn:SetSize(30, 30)
closeBtn:SetText("")
closeBtn.Paint = function(s, w, h)
local col = s:IsHovered() and C_ACC_H or C_ACC
draw.RoundedBox(4, 0, 0, w, h, col)
draw.SimpleText("X", "DermaLarge", w/2, h/2 - 2, C_TXT_P, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
closeBtn.DoClick = function() frame:Close() end
-- Предварительное объявление локальных функций для перестроения интерфейса
local selectedMember = nil
local cmdCurrentBodygroups = {}
local cmdCurrentSkin = 0
local cmdModel = ""
local cmdBodygroups = {}
local cmdAvailableSkins = {}
local RebuildSettings
local RebuildCommanderPresets
-- === ЛЕВАЯ ПАНЕЛЬ: СПИСОК ПОДРАЗДЕЛЕНИЯ И СБОРКИ ===
local leftW = 230
local leftContainer = vgui.Create("DPanel", frame)
leftContainer:SetPos(20, 60)
leftContainer:SetSize(leftW, fH - 80)
leftContainer.Paint = function() end
local squadPanel = vgui.Create("DPanel", leftContainer)
squadPanel:Dock(FILL)
squadPanel.Paint = function(s, w, h)
draw.RoundedBox(6, 0, 0, w, h, C_BG_M)
surface.SetDrawColor(C_BDR)
surface.DrawOutlinedRect(0, 0, w, h, 1)
end
local squadLabel = vgui.Create("DLabel", squadPanel)
squadLabel:Dock(TOP)
squadLabel:DockMargin(10, 10, 10, 5)
squadLabel:SetTall(20)
squadLabel:SetText("ПОДРАЗДЕЛЕНИЕ")
squadLabel:SetFont("DermaDefaultBold")
squadLabel:SetTextColor(C_CMD_SEL)
local memberList = vgui.Create("DListView", squadPanel)
memberList:Dock(FILL)
memberList:DockMargin(10, 5, 10, 10)
memberList:AddColumn("Боец")
memberList.Paint = function(s, w, h)
draw.RoundedBox(4, 0, 0, w, h, C_BG_D)
surface.SetDrawColor(C_BDR)
surface.DrawOutlinedRect(0, 0, w, h, 1)
end
local function FormatMemberLine(line)
for _, col in pairs(line.Columns) do
col:SetTextColor(C_WHITE)
col:SetFont("DermaDefaultBold")
end
line.Paint = function(s, w, h)
local col = C_BG_M
if s:IsSelected() then col = C_CMD
elseif s:IsHovered() then col = C_BG_L end
draw.RoundedBox(0, 0, 0, w, h, col)
surface.SetDrawColor(C_BDR)
surface.DrawOutlinedRect(0, 0, w, h, 1)
end
end
if #membersData == 0 then
local emptyLbl = vgui.Create("DLabel", squadPanel)
emptyLbl:Dock(TOP)
emptyLbl:DockMargin(10, 10, 10, 0)
emptyLbl:SetTall(40)
emptyLbl:SetText("Нет бойцов\nв подразделении")
emptyLbl:SetFont("DermaDefault")
emptyLbl:SetTextColor(Color(120, 120, 120))
emptyLbl:SetWrap(true)
end
for _, data in ipairs(membersData) do
local line = memberList:AddLine(data.nick)
FormatMemberLine(line)
line.memberData = data
end
-- === ПАНЕЛЬ СБОРОК КОМАНДИРА ===
local presetsPanel = vgui.Create("DPanel", leftContainer)
presetsPanel:Dock(BOTTOM)
presetsPanel:DockMargin(0, 10, 0, 0)
presetsPanel.Expanded = false
presetsPanel.CurHeight = 35
presetsPanel:SetTall(35)
presetsPanel.Paint = function(s, w, h)
draw.RoundedBox(6, 0, 0, w, h, C_BG_M)
surface.SetDrawColor(C_BDR)
surface.DrawOutlinedRect(0, 0, w, h, 1)
end
presetsPanel.Think = function(s)
local maxH = fH - 80 - 150 -- Резервируем хотя бы 150px для списка бойцов
local targetHeight = s.Expanded and maxH or 35
if s.CurHeight ~= targetHeight then
s.CurHeight = Lerp(FrameTime() * 15, s.CurHeight, targetHeight)
if math.abs(s.CurHeight - targetHeight) < 0.5 then s.CurHeight = targetHeight end
s:SetTall(math.Round(s.CurHeight))
end
end
local presetsHeader = vgui.Create("DButton", presetsPanel)
presetsHeader:Dock(TOP)
presetsHeader:SetTall(35)
presetsHeader:SetText("")
presetsHeader.Paint = function(s, w, h)
local col = s:IsHovered() and C_BG_L or C_BG_D
draw.RoundedBoxEx(6, 0, 0, w, h, col, true, true, not presetsPanel.Expanded, not presetsPanel.Expanded)
surface.SetDrawColor(C_CMD)
surface.DrawRect(0, 0, 3, h)
draw.SimpleText("СБОРКИ", "DermaLarge", 10, h/2, C_TXT_P, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
draw.SimpleText(presetsPanel.Expanded and "" or "", "DermaDefault", w - 15, h/2, C_TXT_P, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
presetsHeader.DoClick = function(s) presetsPanel.Expanded = not presetsPanel.Expanded; surface.PlaySound("UI/buttonclick.wav") end
local presetsContent = vgui.Create("DScrollPanel", presetsPanel)
presetsContent:Dock(FILL)
RebuildCommanderPresets = function()
presetsContent:Clear()
if not selectedMember or cmdModel == "" then return end
local allPresets = {}
if file.Exists("wardrobe_presets.txt", "DATA") then allPresets = util.JSONToTable(file.Read("wardrobe_presets.txt", "DATA")) or {} end
if not allPresets[cmdModel] then allPresets[cmdModel] = {} end
local py = 10
for i = 1, 10 do
local slotPanel = vgui.Create("DPanel", presetsContent)
slotPanel:SetPos(10, py)
slotPanel:SetSize(200, 85)
slotPanel.Paint = function(s, w, h)
draw.RoundedBox(4, 0, 0, w, h, C_BG_D)
surface.SetDrawColor(C_BDR)
surface.DrawOutlinedRect(0, 0, w, h, 1)
end
local pData = allPresets[cmdModel][i] or allPresets[cmdModel][tostring(i)]
local hasData = pData ~= nil
local defaultName = "Сборка " .. i
local iconCell = vgui.Create("DPanel", slotPanel)
iconCell:SetPos(130, 12)
iconCell:SetSize(60, 60)
iconCell.Paint = function(s, w, h)
local curData = allPresets[cmdModel][tostring(i)]
local isFilled = curData ~= nil
draw.RoundedBox(4, 0, 0, w, h, isFilled and C_ARMA_SLOT or C_ARMA_SLOT_EMPTY)
if isFilled then
local mat = Material("wardrobe_ui/body.png", "noclamp smooth")
if mat and not mat:IsError() then
surface.SetDrawColor(255, 255, 255, 200)
surface.SetMaterial(mat)
surface.DrawTexturedRect(5, 5, w-10, h-10)
end
surface.SetDrawColor(C_CMD)
surface.DrawOutlinedRect(0, 0, w, h, 1)
else
draw.SimpleText("ПУСТО", "DermaDefault", w/2, h/2, Color(100, 100, 100), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
surface.SetDrawColor(Color(0,0,0,100))
surface.DrawOutlinedRect(0, 0, w, h, 1)
end
end
local nameEntry = vgui.Create("DTextEntry", slotPanel)
nameEntry:SetPos(10, 12)
nameEntry:SetSize(110, 25)
nameEntry:SetText(hasData and (pData.name or defaultName) or defaultName)
nameEntry.Paint = function(s, w, h)
draw.RoundedBox(4, 0, 0, w, h, C_BG_M)
surface.SetDrawColor(s:IsEditing() and C_CMD or C_BDR)
surface.DrawOutlinedRect(0, 0, w, h, 1)
s:DrawTextEntryText(C_WHITE, C_CMD, C_WHITE)
end
nameEntry.OnChange = function(s)
local curData = allPresets[cmdModel][tostring(i)]
if curData then
curData.name = s:GetText()
file.Write("wardrobe_presets.txt", util.TableToJSON(allPresets))
end
end
local btnW = 33
local btnY = 47
local loadBtn = vgui.Create("DButton", slotPanel)
loadBtn:SetPos(10, btnY)
loadBtn:SetSize(btnW, 25)
loadBtn:SetText("")
loadBtn.Paint = function(s, w, h)
if not allPresets[cmdModel][tostring(i)] then
draw.RoundedBox(4, 0, 0, w, h, Color(30, 30, 30, 150))
draw.SimpleText("ЗАГР", "DermaDefault", w/2, h/2, Color(100, 100, 100), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
return
end
local col = s:IsHovered() and C_CMD_H or C_CMD
draw.RoundedBox(4, 0, 0, w, h, col)
draw.SimpleText("ЗАГР", "DermaDefault", w/2, h/2, C_TXT_P, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
loadBtn.DoClick = function()
local curData = allPresets[cmdModel][tostring(i)]
if not curData then return end
for idx, val in pairs(curData.bgs) do cmdCurrentBodygroups[tonumber(idx) or idx] = val end
if curData.skin then cmdCurrentSkin = curData.skin end
surface.PlaySound("buttons/button15.wav")
-- Обновляем правую панель чтобы отразить загруженный пресет
RebuildSettings()
end
local saveBtn = vgui.Create("DButton", slotPanel)
saveBtn:SetPos(48, btnY)
saveBtn:SetSize(btnW, 25)
saveBtn:SetText("")
saveBtn.Paint = function(s, w, h)
local col = s:IsHovered() and C_CMD_H or C_CMD
draw.RoundedBox(4, 0, 0, w, h, col)
draw.SimpleText("СОХР", "DermaDefault", w/2, h/2, C_TXT_P, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
saveBtn.DoClick = function()
local finalName = nameEntry:GetText()
if finalName == "" then finalName = defaultName end
local safeBGs = {}
for k, v in pairs(cmdCurrentBodygroups) do safeBGs[tostring(k)] = v end
allPresets[cmdModel][tostring(i)] = {
name = finalName,
bgs = safeBGs,
skin = cmdCurrentSkin
}
file.Write("wardrobe_presets.txt", util.TableToJSON(allPresets))
surface.PlaySound("buttons/button15.wav")
end
local delBtn = vgui.Create("DButton", slotPanel)
delBtn:SetPos(87, btnY)
delBtn:SetSize(btnW, 25)
delBtn:SetText("")
delBtn.Paint = function(s, w, h)
if not allPresets[cmdModel][tostring(i)] then
draw.RoundedBox(4, 0, 0, w, h, Color(30, 30, 30, 150))
draw.SimpleText("УДАЛ", "DermaDefault", w/2, h/2, Color(100, 100, 100), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
return
end
local col = s:IsHovered() and C_WARN_H or C_WARN
draw.RoundedBox(4, 0, 0, w, h, col)
draw.SimpleText("УДАЛ", "DermaDefault", w/2, h/2, C_TXT_P, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
delBtn.DoClick = function()
if allPresets[cmdModel][tostring(i)] then
allPresets[cmdModel][tostring(i)] = nil
file.Write("wardrobe_presets.txt", util.TableToJSON(allPresets))
nameEntry:SetText(defaultName)
surface.PlaySound("buttons/button15.wav")
end
end
py = py + 95
end
end
-- === ЦЕНТРАЛЬНАЯ ПАНЕЛЬ: МОДЕЛЬ ===
local rightW = math.Clamp(fW * 0.38, 350, 550)
local midW = fW - leftW - rightW - 80
local midX = 40 + leftW
local rightX = midX + midW + 20
local modelPanel = vgui.Create("DModelPanel", frame)
modelPanel:SetPos(midX, 60)
modelPanel:SetSize(midW, fH - 140)
modelPanel.bModelReady = false
local oldCmdPaint = modelPanel.Paint
modelPanel.Paint = function(s, w, h)
if bgMat and not bgMat:IsError() then
surface.SetDrawColor(255, 255, 255, 255)
surface.SetMaterial(bgMat)
surface.DrawTexturedRect(0, 0, w, h)
end
surface.SetDrawColor(C_BDR)
surface.DrawOutlinedRect(0, 0, w, h, 1)
if not selectedMember then
draw.SimpleText("Выберите бойца", "DermaLarge", w/2, h/2, Color(100, 100, 100), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
elseif s.bModelReady then
oldCmdPaint(s, w, h)
end
end
modelPanel.camAnims = { zoom = 60, rot = Angle(0, 180, 0), offset = Vector(0, 0, 0) }
modelPanel.curZoom = 60
modelPanel.curRot = Angle(0, 180, 0)
modelPanel.curOffset = Vector(0, 0, 0)
modelPanel.LayoutEntity = function(s, ent)
if not s.bAnimSet then
local seq = ent:LookupSequence("idle_subtle")
if seq <= 0 then seq = ent:LookupSequence("idle_all_01") end
if seq > 0 then ent:SetSequence(seq) end
s.bAnimSet = true
end
if s.bDragging then
local x, y = gui.MousePos()
local dx = x - s.lastX
local dy = y - s.lastY
if input.IsKeyDown(KEY_LSHIFT) or input.IsKeyDown(KEY_RSHIFT) then
local right = s.curRot:Right()
local up = s.curRot:Up()
s.camAnims.offset = s.camAnims.offset - right * (dx * 0.15) + up * (dy * 0.15)
elseif input.IsKeyDown(KEY_LALT) or input.IsKeyDown(KEY_RALT) then
s.camAnims.rot.yaw = s.camAnims.rot.yaw - dx * 0.8
s.camAnims.rot.pitch = math.Clamp(s.camAnims.rot.pitch - dy * 0.8, -45, 45)
end
s.lastX, s.lastY = x, y
end
local speed = FrameTime() * 10
s.curZoom = Lerp(speed, s.curZoom, s.camAnims.zoom)
s.curRot = LerpAngle(speed, s.curRot, s.camAnims.rot)
s.curOffset = LerpVector(speed, s.curOffset, s.camAnims.offset)
local targetPos = ent:GetPos() + Vector(0, 0, 40) + s.curOffset
s:SetCamPos(targetPos - s.curRot:Forward() * s.curZoom)
s:SetLookAng(s.curRot)
ent:FrameAdvance((RealTime() - (s.lastTick or RealTime())) * 1)
s.lastTick = RealTime()
end
modelPanel.OnMousePressed = function(s, code)
if code == MOUSE_LEFT then
local clickTime = SysTime()
if s.lastClickTime and (clickTime - s.lastClickTime) < 0.3 then
s.camAnims.zoom = 60
s.camAnims.rot = Angle(0, 180, 0)
s.camAnims.offset = Vector(0, 0, 0)
s.lastClickTime = 0
return
end
s.lastClickTime = clickTime
s.bDragging = true
s.lastX, s.lastY = gui.MousePos()
end
end
modelPanel.OnMouseReleased = function(s, code) if code == MOUSE_LEFT then s.bDragging = false end end
modelPanel.OnCursorExited = function(s) s.bDragging = false end
modelPanel.OnMouseWheeled = function(s, delta) s.camAnims.zoom = math.Clamp(s.camAnims.zoom - delta * 15, 10, 120) end
modelPanel.Think = function(s)
local entity = s:GetEntity()
if IsValid(entity) then
for idx, value in pairs(cmdCurrentBodygroups) do
if entity:GetBodygroup(idx) ~= value then entity:SetBodygroup(idx, value) end
end
if entity:GetSkin() ~= cmdCurrentSkin then entity:SetSkin(cmdCurrentSkin) end
end
end
-- === КНОПКИ РАКУРСОВ ===
local viewButtons = vgui.Create("DPanel", frame)
viewButtons:SetPos(midX, fH - 70)
viewButtons:SetSize(midW, 50)
viewButtons.Paint = function(s, w, h)
draw.RoundedBox(6, 0, 0, w, h, C_BG_M)
surface.SetDrawColor(C_BDR)
surface.DrawOutlinedRect(0, 0, w, h, 1)
end
local angles = {{"СПЕРЕДИ", "front"}, {"СБОКУ", "side"}, {"СЗАДИ", "back"}}
for k, v in ipairs(angles) do
local btn = vgui.Create("DButton", viewButtons)
btn:SetText("")
btn.Paint = function(s, w, h)
local col = s:IsHovered() and C_CMD_H or C_CMD
draw.RoundedBox(4, 0, 0, w, h, col)
surface.SetDrawColor(C_BDR)
surface.DrawOutlinedRect(0, 0, w, h, 1)
draw.SimpleText(v[1], "DermaDefault", w/2, h/2, C_TXT_P, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
btn.DoClick = function()
if v[2] == "front" then modelPanel.camAnims.rot = Angle(0, 180, 0)
elseif v[2] == "side" then modelPanel.camAnims.rot = Angle(0, 90, 0)
elseif v[2] == "back" then modelPanel.camAnims.rot = Angle(0, 0, 0) end
modelPanel.camAnims.offset = Vector(0, 0, 0)
end
btn.PerformLayout = function(s, w, h)
local bw = (viewButtons:GetWide() - 40) / 3
btn:SetSize(bw, 30)
btn:SetPos(10 + (k - 1) * (bw + 10), 10)
end
end
-- === ПРАВАЯ ПАНЕЛЬ: НАСТРОЙКИ БОДИГРУПП И СКИНОВ ===
local settingsPanel = vgui.Create("DPanel", frame)
settingsPanel:SetPos(rightX, 60)
settingsPanel:SetSize(rightW, fH - 80)
settingsPanel.Paint = function(s, w, h)
draw.RoundedBox(6, 0, 0, w, h, C_BG_M)
surface.SetDrawColor(C_BDR)
surface.DrawOutlinedRect(0, 0, w, h, 1)
end
local settingsScroll = vgui.Create("DScrollPanel", settingsPanel)
settingsScroll:Dock(FILL)
settingsScroll:DockMargin(10, 10, 10, 55)
local sbar = settingsScroll:GetVBar()
sbar:SetWide(4)
sbar.Paint = function() end
sbar.btnGrip.Paint = function(s, w, h) draw.RoundedBox(0, 0, 0, w, h, Color(100, 100, 100, 100)) end
sbar.btnUp.Paint = function() end
sbar.btnDown.Paint = function() end
local baseCellSize = 108
RebuildSettings = function()
settingsScroll:Clear()
if not selectedMember then
local lbl = vgui.Create("DLabel", settingsScroll)
lbl:Dock(TOP)
lbl:DockMargin(10, 20, 10, 0)
lbl:SetTall(30)
lbl:SetText("Выберите бойца из списка слева")
lbl:SetFont("DermaDefaultBold")
lbl:SetTextColor(Color(120, 120, 120))
return
end
local headerLabel = vgui.Create("DLabel", settingsScroll)
headerLabel:Dock(TOP)
headerLabel:DockMargin(22, 0, 30, 15)
headerLabel:SetTall(20)
headerLabel:SetText("ЭКИПИРОВКА: " .. string.upper(selectedMember.nick))
headerLabel:SetFont("DermaDefaultBold")
headerLabel:SetTextColor(C_CMD_SEL)
-- === БОДИГРУППЫ ===
if table.Count(cmdBodygroups) > 0 then
local bgArray = {}
for idx, data in pairs(cmdBodygroups) do table.insert(bgArray, data) end
table.sort(bgArray, function(a, b) return a.index < b.index end)
for _, data in ipairs(bgArray) do
local idx = data.index
local bgName = string.lower(data.name)
local bgPanel = vgui.Create("DPanel", settingsScroll)
bgPanel:Dock(TOP)
bgPanel:DockMargin(10, 5, 30, 10)
bgPanel.Expanded = true
local panelExpandedHeight = 25 + baseCellSize
bgPanel.CurHeight = panelExpandedHeight
bgPanel:SetTall(panelExpandedHeight)
bgPanel.Paint = function() end
bgPanel.Think = function(s)
local targetHeight = s.Expanded and panelExpandedHeight or 25
if s.CurHeight ~= targetHeight then
s.CurHeight = Lerp(FrameTime() * 15, s.CurHeight, targetHeight)
if math.abs(s.CurHeight - targetHeight) < 0.5 then s.CurHeight = targetHeight end
s:SetTall(math.Round(s.CurHeight))
end
end
local headerBtn = vgui.Create("DButton", bgPanel)
headerBtn:SetPos(22, 0)
headerBtn:SetSize(336, 25)
headerBtn:SetText("")
headerBtn.Paint = function(s, w, h)
local col = s:IsHovered() and Color(255, 255, 255) or Color(200, 200, 200)
draw.SimpleText(string.upper(data.name), "DermaDefaultBold", 0, h/2, col, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
draw.SimpleText(bgPanel.Expanded and "" or "", "DermaDefault", w, h/2, col, TEXT_ALIGN_RIGHT, TEXT_ALIGN_CENTER)
end
headerBtn.DoClick = function() bgPanel.Expanded = not bgPanel.Expanded; surface.PlaySound("UI/buttonclick.wav") end
local carouselPanel = vgui.Create("DPanel", bgPanel)
carouselPanel:SetPos(22, 25)
carouselPanel:SetSize(336, baseCellSize)
carouselPanel.Paint = function() end
local startIdx = 0
for k2 = 0, data.count - 1 do
if k2 == (cmdCurrentBodygroups[idx] or 0) then startIdx = k2; break end
end
carouselPanel.TargetScroll = startIdx
carouselPanel.CurScroll = startIdx
local leftArrow = vgui.Create("DButton", bgPanel)
leftArrow:SetPos(0, 25); leftArrow:SetSize(16, baseCellSize); leftArrow:SetText("")
leftArrow.Paint = function(s, w, h)
local col = s:IsHovered() and C_ARMA_SLOT_HOVER or C_ARMA_SLOT_EMPTY
draw.RoundedBox(0, 0, 0, w, h, col)
draw.SimpleText("<", "DermaDefaultBold", w/2, h/2, s:IsHovered() and C_CMD_SEL or Color(150, 150, 150), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
surface.SetDrawColor(Color(0, 0, 0, 120)); surface.DrawOutlinedRect(0, 0, w, h, 1)
end
local rightArrow = vgui.Create("DButton", bgPanel)
rightArrow:SetPos(364, 25); rightArrow:SetSize(16, baseCellSize); rightArrow:SetText("")
rightArrow.Paint = function(s, w, h)
local col = s:IsHovered() and C_ARMA_SLOT_HOVER or C_ARMA_SLOT_EMPTY
draw.RoundedBox(0, 0, 0, w, h, col)
draw.SimpleText(">", "DermaDefaultBold", w/2, h/2, s:IsHovered() and C_CMD_SEL or Color(150, 150, 150), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
surface.SetDrawColor(Color(0, 0, 0, 120)); surface.DrawOutlinedRect(0, 0, w, h, 1)
end
local maxScroll = math.max(0, data.count - 1)
leftArrow.DoClick = function() carouselPanel.TargetScroll = math.max(0, carouselPanel.TargetScroll - 1); surface.PlaySound("UI/buttonclick.wav") end
rightArrow.DoClick = function() carouselPanel.TargetScroll = math.min(maxScroll, carouselPanel.TargetScroll + 1); surface.PlaySound("UI/buttonclick.wav") end
local cells = {}
for i = 0, data.count - 1 do
local cell = vgui.Create("DButton", carouselPanel)
cell:SetText("")
cell.DoClick = function()
cmdCurrentBodygroups[idx] = i
carouselPanel.TargetScroll = i
surface.PlaySound("UI/buttonclick.wav")
end
cell.Paint = function(s, w, h)
local isSelected = (cmdCurrentBodygroups[idx] or 0) == i
local bgColor = C_ARMA_SLOT
if isSelected then bgColor = C_CMD_SEL
elseif s:IsHovered() then bgColor = C_ARMA_SLOT_HOVER end
local alphaMult = math.Clamp(1 - (s.AbsOffset or 0) * 0.35, 0.2, 1)
surface.SetDrawColor(bgColor.r, bgColor.g, bgColor.b, bgColor.a * alphaMult)
surface.DrawRect(0, 0, w, h)
local iconData = CUSTOM_ICONS_BG[bgName]
local customIcon = iconData and (iconData[i] or iconData.default)
if customIcon and not customIcon:IsError() then
surface.SetDrawColor(255, 255, 255, 255 * alphaMult)
surface.SetMaterial(customIcon)
surface.DrawTexturedRect(4, 4, w - 8, h - 8)
else
local txtColor = isSelected and Color(40, 40, 40, 255 * alphaMult) or Color(150, 150, 150, 255 * alphaMult)
draw.SimpleText(tostring(i), "DermaLarge", w/2, h/2, txtColor, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
surface.SetDrawColor(0, 0, 0, 120 * alphaMult)
surface.DrawOutlinedRect(0, 0, w, h, 1)
end
table.insert(cells, cell)
end
carouselPanel.Think = function(s)
s.CurScroll = Lerp(FrameTime() * 12, s.CurScroll, s.TargetScroll)
for ci, cell in ipairs(cells) do
local index = ci - 1
local offset = index - s.CurScroll
local absOffset = math.abs(offset)
cell.AbsOffset = absOffset
local scale = math.Clamp(1 - absOffset * 0.25, 0.5, 1)
local size = baseCellSize * scale
local spacing = 65
local x = (s:GetWide() / 2) - (size / 2) + (offset * spacing)
local y = (s:GetTall() / 2) - (size / 2)
cell:SetSize(size, size)
cell:SetPos(x, y)
cell:SetZPos(100 - math.floor(absOffset * 10))
end
end
end
end
-- === СКИНЫ ===
if #cmdAvailableSkins > 1 then
local skinPanel = vgui.Create("DPanel", settingsScroll)
skinPanel:Dock(TOP)
skinPanel:DockMargin(10, 5, 30, 10)
skinPanel.Expanded = true
local panelExpandedHeight = 25 + baseCellSize
skinPanel.CurHeight = panelExpandedHeight
skinPanel:SetTall(panelExpandedHeight)
skinPanel.Paint = function() end
skinPanel.Think = function(s)
local targetHeight = s.Expanded and panelExpandedHeight or 25
if s.CurHeight ~= targetHeight then
s.CurHeight = Lerp(FrameTime() * 15, s.CurHeight, targetHeight)
if math.abs(s.CurHeight - targetHeight) < 0.5 then s.CurHeight = targetHeight end
s:SetTall(math.Round(s.CurHeight))
end
end
local skinHeader = vgui.Create("DButton", skinPanel)
skinHeader:SetPos(22, 0); skinHeader:SetSize(336, 25); skinHeader:SetText("")
skinHeader.Paint = function(s, w, h)
local col = s:IsHovered() and Color(255, 255, 255) or Color(200, 200, 200)
draw.SimpleText("ВАРИАНТ КАМУФЛЯЖА", "DermaDefaultBold", 0, h/2, col, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
draw.SimpleText(skinPanel.Expanded and "" or "", "DermaDefault", w, h/2, col, TEXT_ALIGN_RIGHT, TEXT_ALIGN_CENTER)
end
skinHeader.DoClick = function() skinPanel.Expanded = not skinPanel.Expanded; surface.PlaySound("UI/buttonclick.wav") end
local skinCarousel = vgui.Create("DPanel", skinPanel)
skinCarousel:SetPos(22, 25); skinCarousel:SetSize(336, baseCellSize)
skinCarousel.Paint = function() end
skinCarousel.TargetScroll = cmdCurrentSkin or 0
skinCarousel.CurScroll = skinCarousel.TargetScroll
-- ИСПРАВЛЕНА ОПЕЧАТКА ТУТ:
local skinLeft = vgui.Create("DButton", skinPanel)
skinLeft:SetPos(0, 25); skinLeft:SetSize(16, baseCellSize); skinLeft:SetText("")
skinLeft.Paint = function(s, w, h)
local col = s:IsHovered() and C_ARMA_SLOT_HOVER or C_ARMA_SLOT_EMPTY
draw.RoundedBox(0, 0, 0, w, h, col)
draw.SimpleText("<", "DermaDefaultBold", w/2, h/2, s:IsHovered() and C_CMD_SEL or Color(150, 150, 150), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
surface.SetDrawColor(Color(0, 0, 0, 120)); surface.DrawOutlinedRect(0, 0, w, h, 1)
end
local skinRight = vgui.Create("DButton", skinPanel)
skinRight:SetPos(364, 25); skinRight:SetSize(16, baseCellSize); skinRight:SetText("")
skinRight.Paint = function(s, w, h)
local col = s:IsHovered() and C_ARMA_SLOT_HOVER or C_ARMA_SLOT_EMPTY
draw.RoundedBox(0, 0, 0, w, h, col)
draw.SimpleText(">", "DermaDefaultBold", w/2, h/2, s:IsHovered() and C_CMD_SEL or Color(150, 150, 150), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
surface.SetDrawColor(Color(0, 0, 0, 120)); surface.DrawOutlinedRect(0, 0, w, h, 1)
end
local skinMaxScroll = math.max(0, #cmdAvailableSkins - 1)
skinLeft.DoClick = function() skinCarousel.TargetScroll = math.max(0, skinCarousel.TargetScroll - 1); surface.PlaySound("UI/buttonclick.wav") end
skinRight.DoClick = function() skinCarousel.TargetScroll = math.min(skinMaxScroll, skinCarousel.TargetScroll + 1); surface.PlaySound("UI/buttonclick.wav") end
local skinCells = {}
for i = 0, #cmdAvailableSkins - 1 do
local cell = vgui.Create("DButton", skinCarousel)
cell:SetText("")
cell.DoClick = function()
cmdCurrentSkin = i
skinCarousel.TargetScroll = i
surface.PlaySound("UI/buttonclick.wav")
end
cell.Paint = function(s, w, h)
local isSelected = cmdCurrentSkin == i
local bgColor = C_ARMA_SLOT
if isSelected then bgColor = C_CMD_SEL
elseif s:IsHovered() then bgColor = C_ARMA_SLOT_HOVER end
local alphaMult = math.Clamp(1 - (s.AbsOffset or 0) * 0.35, 0.2, 1)
surface.SetDrawColor(bgColor.r, bgColor.g, bgColor.b, bgColor.a * alphaMult)
surface.DrawRect(0, 0, w, h)
local txtColor = isSelected and Color(40, 40, 40, 255 * alphaMult) or Color(150, 150, 150, 255 * alphaMult)
draw.SimpleText(tostring(i), "DermaLarge", w/2, h/2, txtColor, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
surface.SetDrawColor(0, 0, 0, 120 * alphaMult)
surface.DrawOutlinedRect(0, 0, w, h, 1)
end
table.insert(skinCells, cell)
end
skinCarousel.Think = function(s)
s.CurScroll = Lerp(FrameTime() * 12, s.CurScroll, s.TargetScroll)
for ci, cell in ipairs(skinCells) do
local index = ci - 1
local offset = index - s.CurScroll
local absOffset = math.abs(offset)
cell.AbsOffset = absOffset
local scale = math.Clamp(1 - absOffset * 0.25, 0.5, 1)
local size = baseCellSize * scale
local spacing = 65
local x = (s:GetWide() / 2) - (size / 2) + (offset * spacing)
local y = (s:GetTall() / 2) - (size / 2)
cell:SetSize(size, size)
cell:SetPos(x, y)
cell:SetZPos(100 - math.floor(absOffset * 10))
end
end
end
end
-- === КНОПКИ ПРИМЕНИТЬ / СБРОС ===
local applyBtn = vgui.Create("DButton", settingsPanel)
local wipeBtn = vgui.Create("DButton", settingsPanel)
wipeBtn:SetSize(135, 35); wipeBtn:SetText("")
wipeBtn.Paint = function(s, w, h)
local col = s:IsHovered() and C_WARN_H or C_WARN
draw.RoundedBox(8, 0, 0, w, h, C_BDR)
draw.RoundedBox(8, 2, 2, w - 4, h - 4, col)
draw.SimpleText("СБРОСИТЬ", "DermaDefaultBold", w/2, h/2, C_TXT_P, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
wipeBtn.DoClick = function()
if not selectedMember then return end
net.Start("SandboxWardrobeCommanderApply")
net.WriteUInt(selectedMember.entIndex, 16)
net.WriteBool(true)
net.SendToServer()
surface.PlaySound("UI/buttonclick.wav")
end
applyBtn:SetSize(135, 35); applyBtn:SetText("")
applyBtn.Paint = function(s, w, h)
local col = s:IsHovered() and C_CMD_H or C_CMD
draw.RoundedBox(8, 0, 0, w, h, C_BDR)
draw.RoundedBox(8, 2, 2, w - 4, h - 4, col)
draw.RoundedBoxEx(8, 2, 2, w - 4, h/2 - 2, C_GRAD, true, true, false, false)
draw.SimpleText("ПРИМЕНИТЬ", "DermaDefaultBold", w/2, h/2, C_TXT_P, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
end
applyBtn.DoClick = function()
if not selectedMember then return end
net.Start("SandboxWardrobeCommanderApply")
net.WriteUInt(selectedMember.entIndex, 16)
net.WriteBool(false)
net.WriteTable(cmdCurrentBodygroups)
net.WriteUInt(cmdCurrentSkin, 8)
net.SendToServer()
surface.PlaySound("UI/buttonclick.wav")
end
settingsPanel.PerformLayout = function(s, w, h)
applyBtn:SetPos(w - 135 - 10, h - 35 - 10)
wipeBtn:SetPos(w - 135 * 2 - 20, h - 35 - 10)
end
-- Инициальный rebuild
RebuildSettings()
RebuildCommanderPresets()
-- === Выбор бойца ===
memberList.OnRowSelected = function(lst, index, pnl)
selectedMember = pnl.memberData
if not selectedMember then return end
cmdModel = selectedMember.model
cmdCurrentBodygroups = {}
cmdCurrentSkin = 0
cmdBodygroups = {}
cmdAvailableSkins = {}
-- Обновляем модель
modelPanel:SetModel(cmdModel)
modelPanel.bAnimSet = false
modelPanel.bModelReady = false
modelPanel.camAnims.zoom = 60
modelPanel.camAnims.rot = Angle(0, 180, 0)
modelPanel.camAnims.offset = Vector(0, 0, 0)
-- Задержка нужна чтобы DModelPanel успел обновить Entity после SetModel
timer.Simple(0.1, function()
if not IsValid(modelPanel) then return end
modelPanel.bModelReady = true
local ent = modelPanel:GetEntity()
if IsValid(ent) then
for i = 0, ent:GetNumBodyGroups() - 1 do
local name = ent:GetBodygroupName(i)
local count = ent:GetBodygroupCount(i)
if count > 1 then
cmdBodygroups[i] = { name = name, count = count, index = i }
cmdCurrentBodygroups[i] = 0
end
end
local skinCount = ent:SkinCount() or 0
for i = 0, skinCount - 1 do
table.insert(cmdAvailableSkins, i)
end
end
-- Пробуем получить текущие бодигруппы от серверного игрока
local target = Entity(selectedMember.entIndex)
if IsValid(target) then
for idx in pairs(cmdBodygroups) do
cmdCurrentBodygroups[idx] = target:GetBodygroup(idx)
end
cmdCurrentSkin = target:GetSkin()
end
RebuildSettings()
RebuildCommanderPresets()
end)
end
end)