```lua
-- weapon_p228.lua

--[[
    Custom P228 SWEP for Garry's Mod
    Features:
    - Improved recoil
    - Weapon lowering animation when running
    - Optimized shell ejection
    - Iron sights implementation
    - Refactored and organized code structure
    - **New Features:**
        1. Smooth weapon lowering and raising when running.
        2. Prevent firing while running.
--]]

AddCSLuaFile()

-- ============================
--        SWEP Information
-- ============================
SWEP.PrintName = "P228"
SWEP.Author = "Your Name"
SWEP.Category = "Custom Weapons" -- Убедитесь, что название категории совпадает
SWEP.Slot = 1
SWEP.SlotPos = 1
SWEP.DrawAmmo = true
SWEP.DrawCrosshair = true
SWEP.ViewModelFOV = 90
SWEP.ViewModelFlip = true -- Set to true if the weapon should appear flipped
SWEP.UseHands = true
SWEP.HoldType = "pistol"

-- **Добавлено:** Разрешение на спавн оружия
SWEP.Spawnable = true
SWEP.AdminSpawnable = true

-- ============================
--            Models
-- ============================
SWEP.ViewModel = "models/weapons/v_pist_p228.mdl"    -- First-person model
SWEP.WorldModel = "models/weapons/w_pist_p228.mdl"   -- Third-person model

-- ============================
--        Primary Fire
-- ============================
SWEP.Primary.ClipSize    = 12
SWEP.Primary.DefaultClip = 12
SWEP.Primary.Automatic   = false
SWEP.Primary.Ammo        = "Pistol"
SWEP.Primary.Damage      = 25
SWEP.Primary.NumShots    = 1
SWEP.Primary.Spread      = 0.02
SWEP.Primary.Recoil      = 1.0
SWEP.Primary.Delay       = 0.3

-- ============================
--        Secondary Fire
-- ============================
SWEP.Secondary.ClipSize    = -1
SWEP.Secondary.DefaultClip = -1
SWEP.Secondary.Automatic   = false
SWEP.Secondary.Ammo        = "none"

-- ============================
--        Recoil Settings
-- ============================
SWEP.RecoilVertical   = 0.5
SWEP.RecoilHorizontal = 0.5
SWEP.MaxRecoil        = 5
SWEP.RecoilDecay      = 3 -- How fast recoil returns to normal

-- ============================
--         Iron Sights
-- ============================
SWEP.IronSightsPos = Vector(-5.95, -10, 2.36)
SWEP.IronSightsAng = Vector(0, 0, 0)

-- ============================
--        Shell Ejection
-- ============================
SWEP.ShellEjectAngle    = Angle(0, 90, 0)
SWEP.ShellEjectVelocity = 100
SWEP.ShellModel         = "models/shells/shell_9mm.mdl"
SWEP.ShellEjectDelay    = 0.05 -- Delay to prevent multiple shells

-- ============================
--        Initialization
-- ============================
function SWEP:Initialize()
    self:SetHoldType(self.HoldType)

    if CLIENT then
        -- Initialize iron sights and lowering states
        self.Ironsights            = false
        self.IsLowered             = false
        self.IronSightsPosOffset   = Vector(0, 0, 0)
        self.IronSightsAngOffset   = Vector(0, 0, 0)
        self.LoweredPosOffset      = Vector(0, 0, 0)
        self.LoweredAngOffset      = Vector(0, 0, 0)

        -- Define Lowered Position and Angle
        self.LoweredPos      = Vector(-0, -0, -0) -- Adjust these values as needed
        self.LoweredAng      = Vector(-15, 0, 0)      -- Adjust these values as needed

        -- Smooth Transition Settings
        self.TargetLowered    = false
        self.TransitionSpeed  = 10 -- Adjust for faster or slower transitions

        -- Progress variable (0 = fully raised, 1 = fully lowered)
        self.LoweredProgress = 0
    end
end

-- ============================
--        Primary Attack
-- ============================
function SWEP:PrimaryAttack()
    local owner = self:GetOwner()
    if not IsValid(owner) then return end

    -- **Added:** Prevent firing while running
    if owner:KeyDown(IN_SPEED) and owner:GetVelocity():Length() > 200 then
        -- Не позволяет стрелять, когда бежит
        return
    end

    if not self:CanPrimaryAttack() then return end

    -- Fire the bullet
    self:ShootBullet(self.Primary.Damage, self.Primary.NumShots, self.Primary.Spread)

    -- Emit sound
    self:EmitSound("Weapon_p228.Single", 75, math.random(95, 105))

    -- Play firing animation
    self:SendWeaponAnim(ACT_VM_PRIMARYATTACK)

    -- Apply recoil
    self:ApplyRecoil()

    -- Eject shell
    self:EmitShell()

    -- Consume ammo
    self:TakePrimaryAmmo(1)

    -- Set next fire time
    self:SetNextPrimaryFire(CurTime() + self.Primary.Delay)
end

-- ============================
--        Secondary Attack
-- ============================
function SWEP:SecondaryAttack()
    local owner = self:GetOwner()
    if not IsValid(owner) then return end

    -- **Added:** Prevent secondary attack while running
    if owner:KeyDown(IN_SPEED) and owner:GetVelocity():Length() > 200 then
        return
    end

    self:SetIronsights(not self:GetIronsights())
end

-- ============================
--            Reload
-- ============================
function SWEP:Reload()
    local owner = self:GetOwner()
    if owner:GetAmmoCount(self.Primary.Ammo) <= 0 then return end
    if self:Clip1() >= self.Primary.ClipSize then return end

    self:DefaultReload(ACT_VM_RELOAD)
    self:SetIronsights(false)
    self:SetNextPrimaryFire(CurTime() + 1)
end

-- ============================
--             Think
-- ============================
function SWEP:Think()
    if CLIENT then
        self:UpdateWeaponStates()
    end
    self:NextThink(CurTime())
    return true
end

-- ============================
--        Recoil Function
-- ============================
function SWEP:ApplyRecoil()
    local owner = self:GetOwner()
    if not IsValid(owner) then return end

    -- Vertical recoil
    owner:ViewPunch(Angle(-self.RecoilVertical, 0, 0))

    -- Horizontal recoil
    local recoilDirection = math.random() < 0.5 and -self.RecoilHorizontal or self.RecoilHorizontal
    owner:SetEyeAngles(owner:EyeAngles() + Angle(0, recoilDirection, 0))

    -- Clamp recoil to prevent excessive buildup
    if not owner.RecoilAmount then owner.RecoilAmount = 0 end
    owner.RecoilAmount = math.Clamp(owner.RecoilAmount + self.RecoilVertical, 0, self.MaxRecoil)
end

-- ============================
--        Shoot Bullet
-- ============================
function SWEP:ShootBullet(damage, num_bullets, aimcone)
    local owner = self:GetOwner()
    if not IsValid(owner) then return end

    owner:LagCompensation(true)

    local bullet = {}
    bullet.Num    = num_bullets
    bullet.Src    = owner:GetShootPos()
    bullet.Dir    = owner:GetAimVector()
    bullet.Spread = Vector(aimcone, aimcone, 0)
    bullet.Tracer = 1
    bullet.Force  = damage * 2
    bullet.Damage = damage

    owner:FireBullets(bullet)

    owner:LagCompensation(false)
end

-- ============================
--        Shell Ejection
-- ============================
function SWEP:EmitShell()
    if not CLIENT then return end

    if not self.NextShellEject then self.NextShellEject = 0 end
    if CurTime() < self.NextShellEject then return end
    self.NextShellEject = CurTime() + self.ShellEjectDelay

    local vm = self:GetOwner():GetViewModel()
    if not IsValid(vm) then return end

    local attachmentIndex = vm:LookupAttachment("muzzle") -- Ensure your model has a 'muzzle' attachment
    if attachmentIndex == 0 then return end -- Attachment not found
    local attachment = vm:GetAttachment(attachmentIndex)
    if not attachment then return end

    local emitter = ParticleEmitter(attachment.Pos)
    if not emitter then return end

    local shell = emitter:Add(self.ShellModel, attachment.Pos)
    if shell then
        shell:SetVelocity(
            vm:GetForward() * -self.ShellEjectVelocity +
            vm:GetRight() * math.random(50, 100) +
            vm:GetUp() * math.random(50, 100)
        )
        shell:SetDieTime(5)
        shell:SetStartAlpha(255)
        shell:SetEndAlpha(0)
        shell:SetStartSize(1)
        shell:SetEndSize(2)
        shell:SetRoll(math.Rand(0, 360))
        shell:SetColor(255, 255, 255)
    end

    emitter:Finish()
end

-- ============================
--        Iron Sights
-- ============================
function SWEP:SetIronsights(b)
    self.Ironsights = b
    if CLIENT then
        self:SetIronsightsClient(b)
    end
end

function SWEP:GetIronsights()
    return self.Ironsights
end

if CLIENT then
    function SWEP:SetIronsightsClient(b)
        self.DrawIronSights = b
    end

    -- ============================
    --        Weapon States
    -- ============================
    function SWEP:UpdateWeaponStates()
        self:UpdateIronSights()
        self:UpdateLoweredState()
        self:SmoothTransitions()
    end

    -- ============================
    --        Update Iron Sights
    -- ============================
    function SWEP:UpdateIronSights()
        if self:GetIronsights() then
            self.IronSightsPosOffset = LerpVector(FrameTime() * self.TransitionSpeed, self.IronSightsPosOffset or Vector(0, 0, 0), self.IronSightsPos)
            self.IronSightsAngOffset = LerpVector(FrameTime() * self.TransitionSpeed, self.IronSightsAngOffset or Vector(0, 0, 0), self.IronSightsAng)
        else
            self.IronSightsPosOffset = LerpVector(FrameTime() * self.TransitionSpeed, self.IronSightsPosOffset or Vector(0, 0, 0), Vector(0, 0, 0))
            self.IronSightsAngOffset = LerpVector(FrameTime() * self.TransitionSpeed, self.IronSightsAngOffset or Vector(0, 0, 0), Vector(0, 0, 0))
        end
    end

    -- ============================
    --    Weapon Lowering When Running
    -- ============================
    function SWEP:UpdateLoweredState()
        local owner = self:GetOwner()
        if not IsValid(owner) or not owner:IsPlayer() then return end

        if owner:KeyDown(IN_SPEED) and owner:GetVelocity():Length() > 200 then
            if not self.TargetLowered then
                self.TargetLowered = true
            end
        else
            if self.TargetLowered then
                self.TargetLowered = false
            end
        end
    end

    -- Function to handle smoothing transitions
    function SWEP:SmoothTransitions()
        if self.TargetLowered then
            self.LoweredProgress = math.min(self.LoweredProgress + FrameTime() * self.TransitionSpeed, 1)
        else
            self.LoweredProgress = math.max(self.LoweredProgress - FrameTime() * self.TransitionSpeed, 0)
        end

        -- Set the offsets based on LoweredProgress
        self.LoweredPosOffset = self.LoweredPos * self.LoweredProgress
        self.LoweredAngOffset = self.LoweredAng * self.LoweredProgress

        -- Update IsLowered state based on progress
        self.IsLowered = self.LoweredProgress > 0.5
    end

    -- ============================
    --        View Model Position
    -- ============================
    function SWEP:GetViewModelPosition(pos, ang)
        -- Apply Iron Sights Offset
        if self.DrawIronSights then
            pos = pos + ang:Right() * self.IronSightsPosOffset.x
            pos = pos + ang:Forward() * self.IronSightsPosOffset.y
            pos = pos + ang:Up() * self.IronSightsPosOffset.z

            ang:RotateAroundAxis(ang:Right(), self.IronSightsAngOffset.x)
            ang:RotateAroundAxis(ang:Up(), self.IronSightsAngOffset.y)
            ang:RotateAroundAxis(ang:Forward(), self.IronSightsAngOffset.z)
        end

        -- Apply Lowered Offset
        if self.LoweredProgress > 0 then
            pos = pos + ang:Right() * self.LoweredPosOffset.x
            pos = pos + ang:Forward() * self.LoweredPosOffset.y
            pos = pos + ang:Up() * self.LoweredPosOffset.z

            ang:RotateAroundAxis(ang:Right(), self.LoweredAngOffset.x)
            ang:RotateAroundAxis(ang:Up(), self.LoweredAngOffset.y)
            ang:RotateAroundAxis(ang:Forward(), self.LoweredAngOffset.z)
        end

        return pos, ang
    end

    -- ============================
    --        Draw HUD
    -- ============================
    function SWEP:DrawHUD()
        if self:GetIronsights() then
            -- Optionally, you can draw a custom crosshair or scope here
            return
        end

        -- Default crosshair
        self:DrawCrosshairDot()
    end

    -- Draws a simple crosshair
    function SWEP:DrawCrosshairDot()
        local x = ScrW() / 2
        local y = ScrH() / 2
        surface.SetDrawColor(255, 255, 255, 255)
        surface.DrawLine(x - 5, y, x + 5, y)
        surface.DrawLine(x, y - 5, x, y + 5)
    end
end

-- ============================
--        Networking
-- ============================
function SWEP:OnRestore()
    self:SetHoldType(self.HoldType)
end

-- ============================
--            Holster
-- ============================
function SWEP:Holster()
    if CLIENT then
        self:SetIronsights(false)
        self.TargetLowered = false
    end
    return true
end

-- ============================
--            Deploy
-- ============================
function SWEP:Deploy()
    self:SetHoldType(self.HoldType)
    if CLIENT then
        self.Ironsights = false
        self.TargetLowered = false
    end
    return true
end
```
Сделай прицел на правую кнопку мыши, чтобы viewmodel оружие смещалось к центру экрану как бы имитируя прицеливание