```lua
-- Файл: lua/weapons/custom_weapon.lua
SWEP.PrintName = "Кастомное Оружие"
SWEP.Author = "ВашеИмя"
SWEP.Instructions = "Левый клик для стрельбы."
SWEP.Spawnable = true
SWEP.AdminOnly = false
-- Настройка боеприпасов
SWEP.Primary = {
    ClipSize = 7, -- Размер магазина
    DefaultClip = 8, -- 7 в магазине + 1 в патронник
    Automatic = false,
    Ammo = "Pistol" -- Тип боеприпасов
}

SWEP.Secondary = {
    ClipSize = -1,
    DefaultClip = -1,
    Automatic = false,
    Ammo = "none"
}

-- Включаем стандартные руки и назначаем стандартный ViewModel
SWEP.UseHands = true
SWEP.ViewModel = "models/weapons/c_pistol.mdl" -- Стандартный ViewModel (можно изменить при необходимости)
SWEP.ViewModelFOV = 54
-- Указываем модель для мира (что видят другие игроки)
SWEP.WorldModel = "models/deadfall/weapons/fb wz,35 vis.mdl" -- Убедитесь, что путь к модели указан правильно
-- Настройки отображения патронов
SWEP.AmmoDisplay = {
    showStartTime = 0, -- Время начала отображения текста
    displayDuration = 3, -- Время, в течение которого текст полностью видим (без учета фейдов)
    fadeTime = 1 -- Время фейда (плавного появления и исчезновения)
}

-- Тип хватки по умолчанию
SWEP.DefaultHoldType = "pistol"
SWEP.AlternateHoldType = "revolver"
-- Таблица смещений модели оружия относительно кости руки
SWEP.ModelOffset = {
    pos = Vector(1, 1, -2), -- Смещение по позициям (регулируйте по необходимости)
    ang = Angle(0, 180, 180) -- Смещение по углам (регулируйте по необходимости)
}

if CLIENT then
    -- Инициализация клиентских переменных
    function SWEP:Initialize()
        self:SetHoldType(self.DefaultHoldType)
        -- Инициализация переменных для отображения патронов
        self.AmmoDisplay.showStartTime = 0
    end

    -- Функция Think для отслеживания нажатия клавиши R (перезарядки) и изменения типа хватки
    function SWEP:Think()
        local owner = self:GetOwner()
        if not IsValid(owner) then return end
        -- Обработка отображения патронов при перезарядке
        if owner:KeyPressed(IN_RELOAD) then self.AmmoDisplay.showStartTime = CurTime() end
        -- Изменение типа хватки при нажатии правой кнопки мыши
        if owner:KeyDown(IN_ATTACK2) then
            if self:GetHoldType() ~= self.AlternateHoldType then self:SetHoldType(self.AlternateHoldType) end
        else
            if self:GetHoldType() ~= self.DefaultHoldType then self:SetHoldType(self.DefaultHoldType) end
        end

        self:NextThink(CurTime())
        return true
    end

    -- Отрисовка стандартной модели оружия и 3D текста с количеством патронов
    function SWEP:DrawWorldModel()
        local owner = self:GetOwner()
        if IsValid(owner) then
            local boneIndex = owner:LookupBone("ValveBiped.Bip01_R_Hand")
            if boneIndex then
                local bonePos, boneAng = owner:GetBonePosition(boneIndex)
                if bonePos and boneAng then
                    -- Применяем смещение из таблицы ModelOffset
                    local pos = bonePos + boneAng:Right() * self.ModelOffset.pos.x + boneAng:Forward() * self.ModelOffset.pos.y + boneAng:Up() * self.ModelOffset.pos.z
                    local ang = boneAng
                    ang:RotateAroundAxis(ang:Up(), self.ModelOffset.ang.y)
                    ang:RotateAroundAxis(ang:Forward(), self.ModelOffset.ang.p)
                    ang:RotateAroundAxis(ang:Right(), self.ModelOffset.ang.r)
                    -- Устанавливаем позицию и угол модели
                    self:SetRenderOrigin(pos)
                    self:SetRenderAngles(ang)
                    -- Отрисовываем модель
                    self:DrawModel()
                    -- Рисуем 3D-текст поверх оружия
                    self:DrawAmmoCount(bonePos, boneAng)
                    return
                end
            end
        end

        -- Если нет владельца или не удалось найти кость, рисуем модель стандартным образом
        self:DrawModel()
        self:DrawAmmoCount()
    end

    -- Функция отрисовки 3D текста с количеством патронов
    function SWEP:DrawAmmoCount(bonePos, boneAng)
        local currentTime = CurTime()
        local elapsedTime = currentTime - self.AmmoDisplay.showStartTime
        local totalDisplayTime = self.AmmoDisplay.displayDuration + 2 * self.AmmoDisplay.fadeTime
        if elapsedTime < 0 or elapsedTime > totalDisplayTime then return end
        -- Вычисляем альфа-канал для плавного появления и исчезновения
        local alpha = 255
        if elapsedTime < self.AmmoDisplay.fadeTime then
            alpha = Lerp(elapsedTime / self.AmmoDisplay.fadeTime, 0, 255)
        elseif elapsedTime > (self.AmmoDisplay.displayDuration + self.AmmoDisplay.fadeTime) then
            local fadeOutTime = elapsedTime - (self.AmmoDisplay.displayDuration + self.AmmoDisplay.fadeTime)
            alpha = Lerp(fadeOutTime / self.AmmoDisplay.fadeTime, 255, 0)
        end

        local owner = self:GetOwner()
        if not IsValid(owner) then return end
        -- Получаем позицию и угол для текста относительно кости руки
        local boneID = owner:LookupBone("ValveBiped.Bip01_R_Hand")
        if not boneID then return end
        local bonePos, boneAng = owner:GetBonePosition(boneID)
        if not bonePos or not boneAng then return end
        -- Настройка позиции текста относительно руки
        local offset = Vector(0, 10, -5) -- Регулируйте значения для правильного позиционирования
        local textPos = bonePos + boneAng:Right() * offset.x + boneAng:Forward() * offset.y + boneAng:Up() * offset.z
        -- Настройка угла поворота текста
        local textAng = boneAng
        textAng:RotateAroundAxis(textAng:Right(), 90)
        textAng:RotateAroundAxis(textAng:Up(), 90)
        cam.Start3D2D(textPos, textAng, 0.1)
        draw.SimpleTextOutlined("Патроны: " .. self:Clip1() .. "/" .. owner:GetAmmoCount(self.Primary.Ammo), "DermaLarge", 0, 0, Color(255, 255, 255, alpha), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER, 2, Color(0, 0, 0, alpha))
        cam.End3D2D()
    end

    -- Удаление клиентской модели оружия не требуется, так как мы не создаём отдельную клиентскую модель для WorldModel
    function SWEP:OnRemove()
        -- Ничего не делаем
    end

    -- Очистка при холостере
    function SWEP:Holster()
        return true
    end
end

-- Деплой оружия
function SWEP:Deploy()
    self:SetHoldType(self.DefaultHoldType)
    self:SendWeaponAnim(ACT_VM_DRAW)
    self:SetNextPrimaryFire(CurTime() + 0.5) -- Добавляем небольшую задержку после деплоя
    return true
end

-- Основная логика при первичной атаке (стрельба)
function SWEP:PrimaryAttack()
    if not self:CanPrimaryAttack() then return end
    self:SetNextPrimaryFire(CurTime() + self:GetPrimaryFireDelay())
    local owner = self:GetOwner()
    if not IsValid(owner) then return end
    -- Воспроизвести звуковой эффект
    owner:EmitSound("Weapon_Pistol.Single")
    -- Воспроизвести анимацию выстрела
    self:SendWeaponAnim(ACT_VM_PRIMARYATTACK)
    owner:SetAnimation(PLAYER_ATTACK1) -- Анимация игрока при выстреле
    -- Уменьшаем количество патронов в магазине
    self:TakePrimaryAmmo(1)
    -- Создать эффект выстрела (Muzzle Flash) на клиенте
    if CLIENT then
        local vm = owner:GetViewModel()
        if IsValid(vm) then
            local attachmentID = vm:LookupAttachment("1") -- Обычно "1" это muzzle
            if attachmentID > 0 then
                local attachment = vm:GetAttachment(attachmentID)
                if attachment then
                    local effect = EffectData()
                    effect:SetOrigin(attachment.Pos)
                    effect:SetAngles(attachment.Ang)
                    effect:SetEntity(vm)
                    util.Effect("MuzzleFlash", effect)
                end
            end
        end
    end

    -- Логика выстрела пули со всеми эффектами на сервере
    if SERVER then
        local bullet = {
            Num = 1,
            Src = owner:GetShootPos(), -- Позиция вылета пули
            Dir = owner:GetAimVector(), -- Направление вылета пули
            Spread = Vector(0, 0, 0), -- Разброс (измените при необходимости)
            Tracer = 1, -- Частота появления трассеров (1 = каждый выстрел)
            TracerName = "Tracer", -- Эффект трассера ("Tracer" - стандартный)
            Force = 5, -- Сила попадания
            Damage = 10, -- Урон
            AmmoType = self.Primary.Ammo
        }

        owner:FireBullets(bullet)
    end
end

-- Задержка между выстрелами
function SWEP:GetPrimaryFireDelay()
    return 0.5 -- Задержка между выстрелами в секундах
end

-- Проверка возможности атаки
function SWEP:CanPrimaryAttack()
    if self:Clip1() <= 0 then
        self:EmitSound("Weapon_Pistol.Empty") -- Звук пустого магазина
        self:SetNextPrimaryFire(CurTime() + 0.2) -- Небольшая задержка перед следующим действием
        return false
    end
    return true
end

-- Действие при вторичной атаке
function SWEP:SecondaryAttack()
    -- Вторичная атака не используется, поэтому ничего не делаем
    -- Тип хватки изменяется динамически в функции Think()
end

-- Функция перезарядки оружия
function SWEP:Reload()
    local owner = self:GetOwner()
    if not IsValid(owner) then return end
    if owner:KeyDown(IN_ATTACK) then -- Не перезаряжаться при атаке
        return
    end

    if self:Clip1() >= self.Primary.ClipSize then -- Магазин полон
        return
    end

    if owner:GetAmmoCount(self.Primary.Ammo) <= 0 then -- Нет боеприпасов
        return
    end

    self:DefaultReload(ACT_VM_RELOAD)
    owner:EmitSound("Weapon_Pistol.Reload") -- Звук перезарядки
    self:SetNextPrimaryFire(CurTime() + 2) -- Устанавливаем задержку на время перезарядки
    owner:SetAnimation(PLAYER_RELOAD) -- Анимация перезарядки для игрока
    -- Обновляем отображение патронов
    if CLIENT then self.AmmoDisplay.showStartTime = CurTime() end
end
```
[{
	"resource": "/C:/Program Files (x86)/Steam/steamapps/common/GarrysMod/garrysmod/lua/weapons/custom_weapon.lua",
	"owner": "glua",
	"severity": 4,
	"message": "Variable 'bonePos' shadows existing binding, defined at line 101, column 33",
	"startLineNumber": 120,
	"startColumn": 15,
	"endLineNumber": 120,
	"endColumn": 22
},{
	"resource": "/C:/Program Files (x86)/Steam/steamapps/common/GarrysMod/garrysmod/lua/weapons/custom_weapon.lua",
	"owner": "glua",
	"severity": 4,
	"message": "Variable 'boneAng' shadows existing binding, defined at line 101, column 42",
	"startLineNumber": 120,
	"startColumn": 24,
	"endLineNumber": 120,
	"endColumn": 31
}]

Максимально оптимизируй и улучши код