Сделай чтобы я мог выбрать из списка что хочу установить на оружие:
```lua
-- cl_att.lua
if CLIENT then
	SWEP._attachments = SWEP._attachments or {}
	-- Очистка вложений при удалении оружия
	function SWEP:OnRemove()
		for _, attachment in pairs(self._attachments) do
			if IsValid(attachment.csEnt) then attachment.csEnt:Remove() end
		end
	end

	local color_white = Color(255, 255, 255)
	local color_black = Color(0, 0, 0)
	-- Функция отрисовки оптических вложений
	local function drawOpticAttach(csEnt, flags)
		local weap = csEnt.parent
		if not IsValid(weap) then return end
		local ply = csEnt:GetParent()
		if not IsValid(ply) then return end
		if ply:GetActiveWeapon() ~= weap then return end
		csEnt:DrawModel(flags)
		if not qwb.ironsighted then return end
		local scopeMat = csEnt.opticMat or weap.DefaultScopeMat
		if not scopeMat then return end
		local attID = weap:LookupAttachment("muzzle")
		if not attID or attID <= 0 then return end
		local att = weap:GetAttachment(attID)
		if not att then return end
		local pos, ang = LocalToWorld(csEnt.opticPos, csEnt.opticAng or Angle(-90, 0, 0), csEnt:GetPos(), att.Ang)
		local w, h = unpack(csEnt.opticSize or {512, 512})
		local radius = math.max(w, h)
		local semiRadius = radius * 0.5
		if not draw_Circle then -- Убедитесь, что функция draw_Circle доступна
			return
		end

		qwb.fullyClearStencil()
		render.SetStencilEnable(true)
		render.SetStencilCompareFunction(STENCIL_ALWAYS)
		render.SetStencilFailOperation(STENCIL_ZERO)
		render.SetStencilZFailOperation(STENCIL_ZERO)
		render.SetStencilPassOperation(STENCIL_REPLACE)
		render.SetStencilReferenceValue(1)
		cam.Start3D2D(pos, ang, 0.01)
		draw.NoTexture()
		surface.SetDrawColor(color_black)
		draw_Circle(semiRadius, semiRadius, semiRadius, 30)
		cam.End3D2D()
		render.SetStencilCompareFunction(STENCIL_EQUAL)
		cam.Start3D2D(pos, ang, 0.01)
		local mat = weap.mat
		if not mat then return end
		surface.SetDrawColor(color_white)
		surface.SetMaterial(mat)
		surface.DrawTexturedRect(0, 0, w, h)
		surface.SetDrawColor(color_white)
		surface.SetMaterial(scopeMat)
		surface.DrawTexturedRect(0, 0, w, h)
		cam.End3D2D()
		render.SetStencilEnable(false)
	end

	-- Обработка сетевого сообщения установки вложения
	net.Receive("qwb.setAttachment", function()
		local weap = net.ReadEntity()
		if not IsValid(weap) then return end
		local attachType, attachID = net.ReadString(), net.ReadString()
		if not attachType or not attachID then return end
		local attachmentData = weap.Attachments and weap.Attachments[attachType] and weap.Attachments[attachType][attachID]
		if not attachmentData then return end
		weap._attachments = weap._attachments or {}
		weap._attachments[attachType] = weap._attachments[attachType] or {}
		local attachment = weap._attachments[attachType]
		attachment.id = attachID
		if IsValid(attachment.csEnt) then attachment.csEnt:Remove() end
		local owner = weap:GetOwner()
		attachment.csEnt = ClientsideModel(attachmentData.mdl)
		if not IsValid(attachment.csEnt) then return end
		attachment.csEnt:SetParent(weap)
		attachment.csEnt:AddEffects(EF_BONEMERGE)
		attachment.csEnt:SetLocalPos(attachmentData.pos)
		attachment.csEnt:SetLocalAngles(attachmentData.ang)
		attachment.csEnt:SetModelScale(attachmentData.scale or 1)
		attachment.csEnt.parent = weap
		if attachmentData.opticPos and owner == LocalPlayer() then
			attachment.csEnt.opticPos = attachmentData.opticPos
			attachment.csEnt.opticAng = attachmentData.opticAng
			attachment.csEnt.opticSize = attachmentData.opticSize
			attachment.csEnt.opticMat = attachmentData.opticMat or weap.DefaultScopeMat
			attachment.csEnt.RenderOverride = drawOpticAttach
		end

		attachment.csEnt:Spawn()
	end)

	-- Обработка сетевого сообщения удаления вложения
	net.Receive("qwb.removeAttachment", function()
		local weap = net.ReadEntity()
		if not IsValid(weap) then return end
		if not weap._attachments then return end
		local attachType = net.ReadString()
		if not attachType then return end
		local attachment = weap._attachments[attachType]
		if not attachment then return end
		if IsValid(attachment.csEnt) then attachment.csEnt:Remove() end
		weap._attachments[attachType] = nil
	end)
end
```
```lua
-- cl_att_menu.lua
if CLIENT then
	-- Инициализация глобальной таблицы qwb, если она ещё не определена
	qwb = qwb or {}
	local color_bg = Color(0, 0, 0, 200)
	local color_button = Color(50, 50, 50, 250)
	local color_button_hover = Color(80, 80, 80, 250)
	local color_text = Color(255, 255, 255, 255)
	-- Функция для создания кнопки
	local function CreateButton(parent, text, x, y, w, h, callback)
		local btn = vgui.Create("DButton", parent)
		btn:SetText(text)
		btn:SetPos(x, y)
		btn:SetSize(w, h)
		btn:SetFont("DermaDefault")
		btn:SetTextColor(color_text)
		btn.Paint = function(self, pw, ph)
			if self:IsHovered() then
				surface.SetDrawColor(color_button_hover)
			else
				surface.SetDrawColor(color_button)
			end

			surface.DrawRect(0, 0, pw, ph)
		end

		btn.DoClick = callback
		return btn
	end

	-- Открытие меню вложений
	function SWEP:OpenAttMenu()
		if IsValid(self._attMenu) then return end
		self._attMenu = vgui.Create("DPanel")
		local pnl = self._attMenu
		pnl:SetSize(ScrW() * 0.6, ScrH() * 0.6)
		pnl:Center()
		pnl:SetPaintBackground(false)
		pnl:MakePopup()
		-- Фоновый прямоугольник
		pnl.Paint = function(self, w, h) draw.RoundedBox(8, 0, 0, w, h, color_bg) end
		-- Заголовок
		local lbl = vgui.Create("DLabel", pnl)
		lbl:SetText("Кастомизация оружия")
		lbl:SetFont("DermaLarge")
		lbl:SetTextColor(color_text)
		lbl:SizeToContents()
		lbl:SetPos((pnl:GetWide() - lbl:GetWide()) / 2, 20)
		-- Список вложений
		local attachList = vgui.Create("DListView", pnl)
		attachList:SetPos(50, 60)
		attachList:SetSize(pnl:GetWide() - 100, pnl:GetTall() - 150)
		attachList:SetMultiSelect(false)
		attachList:AddColumn("Тип вложения")
		attachList:AddColumn("Текущее вложение")
		-- Предполагается, что SWEP.Attachments содержит список типов вложений
		if self.Attachments then
			for attachType, attachData in pairs(self.Attachments) do
				local currentAttach = "Нет"
				if self._attachments and self._attachments[attachType] and self._attachments[attachType].id then currentAttach = self._attachments[attachType].id end
				attachList:AddLine(attachType, currentAttach)
			end
		end

		-- Кнопка установки вложения
		local installBtn = CreateButton(pnl, "Установить вложение", 50, pnl:GetTall() - 80, 200, 40, function()
			local selected = attachList:GetSelectedLine()
			if not selected then return end
			local attachType = attachList:GetLine(selected):GetValue(1)
			-- Открытие диалога ввода ID вложения
			Derma_StringRequest("Установка вложения", "Введите ID вложения для типа: " .. attachType, "", function(text) if text and text ~= "" then RunConsoleCommand("attach_set", attachType, text) end end, function() end, "OK", "Отмена")
		end)

		pnl.installBtn = installBtn -- Сохранение ссылки для предотвращения предупреждений линтера
		-- Кнопка удаления вложения
		local removeBtn = CreateButton(pnl, "Удалить вложение", 260, pnl:GetTall() - 80, 200, 40, function()
			local selected = attachList:GetSelectedLine()
			if not selected then return end
			local attachType = attachList:GetLine(selected):GetValue(1)
			RunConsoleCommand("attach_remove", attachType)
		end)

		pnl.removeBtn = removeBtn -- Сохранение ссылки для предотвращения предупреждений линтера
		-- Кнопка закрытия меню
		local closeBtn = CreateButton(pnl, "Закрыть", pnl:GetWide() - 110, pnl:GetTall() - 80, 60, 40, function() self:CloseAttMenu() end)
		pnl.closeBtn = closeBtn -- Сохранение ссылки для предотвращения предупреждений линтера
		qwb.attMenuOpened = true
	end

	-- Закрытие меню вложений
	function SWEP:CloseAttMenu()
		if not IsValid(self._attMenu) then return end
		self._attMenu:Remove()
		self._attMenu = nil
		qwb.attMenuOpened = false
	end

	-- Переключение состояния меню вложений
	function SWEP:ToggleAttMenu()
		if IsValid(self._attMenu) then
			self:CloseAttMenu()
		else
			self:OpenAttMenu()
		end
	end

	-- Хук для открытия/закрытия меню по контекстному меню
	hook.Add("OnContextMenuOpen", "qwb.att.menu", function()
		local weap = LocalPlayer():GetActiveWeapon()
		if not IsValid(weap) then return end
		if not weap.IsQWB then return end
		weap:ToggleAttMenu()
	end)

	-- Обработка консольных команд для установки и удаления вложений
	concommand.Add("attach_set", function(ply, cmd, args)
		-- Проверка, что вызывающий клиент является игроком
		if not IsValid(ply) or not ply:IsPlayer() then return end
		local weap = ply:GetActiveWeapon()
		if not IsValid(weap) or not weap.IsQWB then return end
		local attachType, attachID = args[1], args[2]
		if not attachType or not attachID then return end
		weap:SetAttach(attachType, attachID)
	end)

	concommand.Add("attach_remove", function(ply, cmd, args)
		-- Проверка, что вызывающий клиент является игроком
		if not IsValid(ply) or not ply:IsPlayer() then return end
		local weap = ply:GetActiveWeapon()
		if not IsValid(weap) or not weap.IsQWB then return end
		local attachType = args[1]
		if not attachType then return end
		weap:RemoveAttach(attachType)
	end)
end
```
```lua
AddCSLuaFile()

SWEP.Base = 'weapon_base_qwb'

SWEP.WorldModel = 'models/weapons/w_smg_ump45.mdl'

SWEP.PrintName = 'UMP-45'
SWEP.Category = 'qurs\' weapons base'

SWEP.Spawnable = true
SWEP.AdminOnly = false

SWEP.IronsightOffset = Vector(-6, -0.88, 5.7)
SWEP.IronsightAngle = Angle(0, -2, 0)
SWEP.IronsightZNear = 0.4

SWEP.Primary.Sound = 'Weapon_UMP45.Single'
SWEP.Primary.Damage = 5
SWEP.Primary.NumShots = 1
SWEP.Primary.Spread = 0.01
SWEP.Primary.Delay = 0.1

SWEP.Primary.ClipSize = 30
SWEP.Primary.DefaultClip = 0
SWEP.Primary.Automatic = true
SWEP.Primary.Ammo = 'SMG1'

SWEP.Recoil = 2
SWEP.HorizontalRecoil = 6.5

SWEP.HoldType = 'smg'

SWEP.ShellType = '57'
SWEP.ShellOffset = Vector(-5, 1, 1.5)
SWEP.ShellVelocity = 65

SWEP.AimSound = Sound('weapons/ammopickup.wav')

-- Attachments
SWEP.Attachments = {
	sights = {
		holo_sight = {
			name = 'Holo sight',
			mdl = 'models/attachment/crysis_holo.mdl',
			pos = Vector(-3, 1.3, 6.95),
			ang = Angle(-10, 0, 0),
			scale = 1,

			sightCameraOffset = Vector(0, 0, 0.5),
			sightCameraAngleOffset = Angle(),
		},

		optical_sight = {
			name = 'Optical sight',
			mdl = 'models/attachment/crysis_holo.mdl',
			pos = Vector(-3, 1.3, 6.95),
			ang = Angle(-10, 0, 0),
			scale = 1,

			sightCameraOffset = Vector(0, 0, 0.5),
			sightCameraAngleOffset = Angle(),

			opticPos = Vector(3.5, -0.3, 1.75), -- the position of the rendered crosshair relative to the attachment model
			opticAng = Angle(-90, 0, 0),
			opticSize = {80, 80},
			opticFOV = 5,
		},
	},
}
```
```lua
-- sh_att.lua
-- Получение информации о вложении
function SWEP:GetAttach(att)
	return self._attachments and self._attachments[att]
end
```
```lua
-- sv_att.lua
-- Проверка возможности установки вложения
function SWEP:CanAttach(attachType, attachID)
	-- Здесь можно добавить логику проверки допустимости вложения
	return true
end

-- Установка вложения
function SWEP:SetAttach(attachType, attachID)
	if not self.Attachments[attachType] or not self.Attachments[attachType][attachID] then return end
	self._attachments = self._attachments or {}
	self._attachments[attachType] = self._attachments[attachType] or {}
	if self._attachments[attachType].id == attachID then return end
	if not self:CanAttach(attachType, attachID) then return end
	self._attachments[attachType].id = attachID
	net.Start("qwb.setAttachment")
	net.WriteEntity(self)
	net.WriteString(attachType)
	net.WriteString(attachID)
	net.Broadcast()
end

-- Удаление вложения
function SWEP:RemoveAttach(attachType)
	if not self._attachments or not self._attachments[attachType] then return end
	self._attachments[attachType] = nil
	net.Start("qwb.removeAttachment")
	net.WriteEntity(self)
	net.WriteString(attachType)
	net.Broadcast()
end

if SERVER then
	-- Обработка сетевого сообщения установки вложения
	net.Receive("qwb.setAttachment", function(len, ply)
		local weap = ply:GetActiveWeapon()
		if not IsValid(weap) or not weap.IsQWB or not weap.SetAttach then return end
		local attachType, attachID = net.ReadString(), net.ReadString()
		if not attachType or not attachID then return end
		weap:SetAttach(attachType, attachID)
	end)

	-- Обработка сетевого сообщения удаления вложения
	net.Receive("qwb.removeAttachment", function(len, ply)
		local weap = ply:GetActiveWeapon()
		if not IsValid(weap) or not weap.IsQWB or not weap.RemoveAttach then return end
		local attachType = net.ReadString()
		if not attachType then return end
		weap:RemoveAttach(attachType)
	end)
end
```
Напиши полный код для всех файлов