1880 lines
86 KiB
Lua
1880 lines
86 KiB
Lua
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)
|