AddCSLuaFile("cl_init.lua") AddCSLuaFile("shared.lua") include("shared.lua") resource.AddFile("materials/wardrobe_ui/body.png") resource.AddFile("materials/wardrobe_ui/fon.png") util.AddNetworkString("SandboxWardrobeOpen") util.AddNetworkString("SandboxWardrobeApply") util.AddNetworkString("SandboxWardrobeAdminOpen") util.AddNetworkString("SandboxWardrobeAdminUpdate") util.AddNetworkString("SandboxWardrobeCommanderOpen") util.AddNetworkString("SandboxWardrobeCommanderApply") util.AddNetworkString("SandboxWardrobeSavePreset") if not file.Exists("wardrobe_presets", "DATA") then file.CreateDir("wardrobe_presets") end net.Receive("SandboxWardrobeSavePreset", function(len, ply) if not IsValid(ply) then return end local presets = net.ReadTable() local steamID64 = ply:SteamID64() if steamID64 then local json = util.TableToJSON(presets, true) if json then file.Write("wardrobe_presets/" .. steamID64 .. ".txt", json) end end end) -- ===================================================================== -- ПСЕВДО-ФУНКЦИИ: Замените на свои реальные проверки! -- ===================================================================== -- Проверяет, является ли игрок командиром подразделения. -- Замените тело функции на свою логику, например: -- return ply:GetNWBool("IsSquadLeader", false) -- return ply:Team() == TEAM_COMMANDER -- return ply.isCommander == true local function IsCommander(ply) -- ЗАГЛУШКА: Сейчас возвращает true для суперадминов. -- Замените на реальную проверку! return IsValid(ply) and ply:IsSuperAdmin() end -- Возвращает таблицу игроков, которые находятся в подразделении этого командира. -- Замените тело функции на свою логику, например: -- return GetSquadByLeader(ply) -- return team.GetPlayers(ply:Team()) -- return ply:GetSquadMembers() local function GetSquadMembers(commander) -- ЗАГЛУШКА: Сейчас возвращает всех игроков на сервере кроме самого командира. -- Замените на реальную логику получения списка подразделения! local members = {} for _, ply in ipairs(player.GetAll()) do if ply ~= commander then table.insert(members, ply) end end return members end -- ===================================================================== -- СИСТЕМА РАЗБЛОКИРОВКИ БОДИГРУПП (АДМИН) -- ===================================================================== local function SanitizeData(tbl, depth) depth = depth or 1 local out = {} for k, v in pairs(tbl) do local keyStr = tostring(k) if depth == 1 then if type(k) == "number" and k > 1e15 then continue end if type(k) == "string" and string.find(keyStr, "e%+") then continue end end if type(v) == "table" then out[keyStr] = SanitizeData(v, depth + 1) else out[keyStr] = v end end return out end function LoadWardrobeUnlocked() if file.Exists("wardrobe_unlocked.txt", "DATA") then local content = file.Read("wardrobe_unlocked.txt", "DATA") local parsed = util.JSONToTable(content, true, true) if parsed then WARDROBE_UNLOCKED = SanitizeData(parsed) else WARDROBE_UNLOCKED = {} end else WARDROBE_UNLOCKED = {} end end LoadWardrobeUnlocked() function SaveWardrobeUnlocked() local json = util.TableToJSON(WARDROBE_UNLOCKED, true) if json then file.Write("wardrobe_unlocked.txt", json) end end -- ===================================================================== -- АДМИН ПАНЕЛЬ -- ===================================================================== concommand.Add("wardrobe_admin", function(ply, cmd, args) if IsValid(ply) and not ply:IsSuperAdmin() then ply:ChatPrint("У вас нет прав (нужна группа superadmin).") return end net.Start("SandboxWardrobeAdminOpen") net.WriteTable(WARDROBE_UNLOCKED) net.Send(ply) end) net.Receive("SandboxWardrobeAdminUpdate", function(len, ply) if not IsValid(ply) or not ply:IsSuperAdmin() then return end local sid64 = net.ReadString() local model = string.lower(net.ReadString()) local bg_id = tostring(net.ReadUInt(8)) local bg_val = tostring(net.ReadUInt(8)) local state = net.ReadBool() WARDROBE_UNLOCKED[sid64] = WARDROBE_UNLOCKED[sid64] or {} WARDROBE_UNLOCKED[sid64][model] = WARDROBE_UNLOCKED[sid64][model] or {} WARDROBE_UNLOCKED[sid64][model][bg_id] = WARDROBE_UNLOCKED[sid64][model][bg_id] or {} if state then WARDROBE_UNLOCKED[sid64][model][bg_id][bg_val] = true else WARDROBE_UNLOCKED[sid64][model][bg_id][bg_val] = nil if table.IsEmpty(WARDROBE_UNLOCKED[sid64][model][bg_id]) then WARDROBE_UNLOCKED[sid64][model][bg_id] = nil end if table.IsEmpty(WARDROBE_UNLOCKED[sid64][model]) then WARDROBE_UNLOCKED[sid64][model] = nil end if table.IsEmpty(WARDROBE_UNLOCKED[sid64]) then WARDROBE_UNLOCKED[sid64] = nil end end SaveWardrobeUnlocked() end) -- ===================================================================== -- ПАНЕЛЬ КОМАНДИРА -- ===================================================================== concommand.Add("wardrobe_commander", function(ply, cmd, args) if not IsValid(ply) then return end if not IsCommander(ply) then ply:ChatPrint("Вы не являетесь командиром подразделения.") return end local members = GetSquadMembers(ply) local membersData = {} for _, member in ipairs(members) do if IsValid(member) then table.insert(membersData, { nick = member:Nick(), sid64 = member:SteamID64(), model = member:GetModel(), entIndex = member:EntIndex(), }) end end net.Start("SandboxWardrobeCommanderOpen") net.WriteTable(membersData) net.Send(ply) end) net.Receive("SandboxWardrobeCommanderApply", function(len, ply) if not IsValid(ply) then return end if not IsCommander(ply) then return end local targetEntIndex = net.ReadUInt(16) local isWipe = net.ReadBool() local target = Entity(targetEntIndex) if not IsValid(target) or not target:IsPlayer() then ply:ChatPrint("Целевой игрок не найден.") return end -- Проверяем что целевой игрок находится в подразделении командира local members = GetSquadMembers(ply) local isMember = false for _, member in ipairs(members) do if member == target then isMember = true break end end if not isMember then ply:ChatPrint("Этот игрок не в вашем подразделении.") return end if isWipe then target:SetSkin(0) local bgs = target:GetBodyGroups() for _, bg in ipairs(bgs) do target:SetBodygroup(bg.id, 0) end ply:ChatPrint("Сброшена экипировка для: " .. target:Nick()) return end local bodygroups = net.ReadTable() local skin = net.ReadUInt(8) for idx, value in pairs(bodygroups) do target:SetBodygroup(idx, value) end target:SetSkin(skin) ply:ChatPrint("Экипировка применена для: " .. target:Nick()) end) -- ===================================================================== -- ЛОГИКА ЭНТИТИ ГАРДЕРОБА -- ===================================================================== function ENT:Initialize() self:SetModel("models/props_wasteland/controlroom_storagecloset001a.mdl") self:PhysicsInit(SOLID_VPHYSICS) self:SetMoveType(MOVETYPE_VPHYSICS) self:SetSolid(SOLID_VPHYSICS) self:SetUseType(SIMPLE_USE) local phys = self:GetPhysicsObject() if IsValid(phys) then phys:Wake() end self.playerCooldowns = {} end function ENT:GetModelBodygroups(model) local tempEnt = ents.Create("prop_dynamic") if not IsValid(tempEnt) then return {} end tempEnt:SetModel(model) tempEnt:Spawn() local bodygroups = {} for i = 0, tempEnt:GetNumBodyGroups() - 1 do local name = tempEnt:GetBodygroupName(i) local count = tempEnt:GetBodygroupCount(i) if count > 1 then bodygroups[i] = {name = name, count = count, index = i} end end tempEnt:Remove() return bodygroups end function ENT:GetModelSkinCount(model) local tempEnt = ents.Create("prop_dynamic") if not IsValid(tempEnt) then return 0 end tempEnt:SetModel(model) tempEnt:Spawn() local skinCount = tempEnt:SkinCount() or 0 tempEnt:Remove() return skinCount end function ENT:Use(activator) if not IsValid(activator) or not activator:IsPlayer() then return end local steamID = activator:SteamID() local now = CurTime() if self.playerCooldowns[steamID] and (now - self.playerCooldowns[steamID]) < 2 then return end local model = activator:GetModel() if not model then return end local bodygroups = self:GetModelBodygroups(model) local skinCount = self:GetModelSkinCount(model) local availableBodygroups = {} for idx, data in pairs(bodygroups) do availableBodygroups[idx] = data end local availableSkins = {} for i = 0, skinCount - 1 do table.insert(availableSkins, i) end local currentBodygroups = {} for idx in pairs(availableBodygroups) do currentBodygroups[idx] = activator:GetBodygroup(idx) end local currentSkin = activator:GetSkin() local steamID64 = activator:SteamID64() local unlocked = {} if steamID64 and WARDROBE_UNLOCKED[steamID64] and WARDROBE_UNLOCKED[steamID64][string.lower(model)] then unlocked = WARDROBE_UNLOCKED[steamID64][string.lower(model)] end local presets = {} if steamID64 and file.Exists("wardrobe_presets/" .. steamID64 .. ".txt", "DATA") then local content = file.Read("wardrobe_presets/" .. steamID64 .. ".txt", "DATA") if content and content ~= "" then presets = util.JSONToTable(content) or {} end end self.playerCooldowns[steamID] = now net.Start("SandboxWardrobeOpen") net.WriteString(model) net.WriteTable(availableBodygroups) net.WriteTable(availableSkins) net.WriteTable(currentBodygroups) net.WriteUInt(currentSkin, 8) net.WriteTable(unlocked) net.WriteTable(presets) net.Send(activator) end net.Receive("SandboxWardrobeApply", function(len, client) if not IsValid(client) then return end local isWipe = net.ReadBool() if isWipe then client:SetSkin(0) local bgs = client:GetBodyGroups() for _, bg in ipairs(bgs) do client:SetBodygroup(bg.id, 0) end return end local bodygroups = net.ReadTable() local skin = net.ReadUInt(8) for idx, value in pairs(bodygroups) do client:SetBodygroup(idx, value) end client:SetSkin(skin) end)