AddCSLuaFile()

if CLIENT then
    if buildermenu then buildermenu:Remove() end
    SWEP.BounceWeaponIcon = false 
    language.Add("weapon_builder", "Builder")
end

if SERVER then
    util.AddNetworkString("builder_hitprop")
end

nextreload = 0

Builder_EntitiesTBL = {
    ["sw_sania"] = {
        cost = 40,
        Name = "sw_sania",
        PrintName = "РЭБ‑модуль",
        mdl = "models/shtormer/sania.mdl",
        lua = true,
        attachToVehicle = true
    }
}

Builder_EntitiesNum = 0
Builder_EntitiesLoaded = false

timer.Simple(1, function()
    local tab, ent = list.Get("SpawnableEntities"), scripted_ents.GetList()
    local tab2 = list.Get("NPC")

    for k, v in pairs(Builder_EntitiesTBL) do
        if v.Name == "prop_physics" then
            Builder_EntitiesNum = Builder_EntitiesNum + 1
            continue
        end
        
        if not istable(tab[v.Name]) and not istable(tab2[v.Name]) then
            Builder_EntitiesTBL[k] = nil
            continue
        end
        if v.lua and not istable(ent[v.Name]) then
            Builder_EntitiesTBL[k] = nil
            continue
        end
        Builder_EntitiesNum = Builder_EntitiesNum + 1
    end

    Builder_EntitiesLoaded = true
end)

function BuilderIsCreature(ent)
    return (ent:IsPlayer() or ent:IsNPC() or ent:IsNextBot() or ent:GetClass():find("prop_ragdoll"))
end

SWEP.PrintName = "Builder"
SWEP.Slot = 5
SWEP.SlotPos = 10
SWEP.DrawAmmo = false
SWEP.HoldType = "melee"
SWEP.DrawCrosshair = false
SWEP.Spawnable = true
SWEP.AdminSpawnable = true
SWEP.ViewModelFOV = 65
SWEP.ViewModelFlip = false
SWEP.UseHands = true

SWEP.ViewModel = "models/weapons/c_hammer1.mdl"
SWEP.WorldModel = "models/weapons/w_hammer1.mdl"

SWEP.Primary.ClipSize = -1
SWEP.Primary.DefaultClip = -1
SWEP.Primary.Automatic = true
SWEP.Primary.Ammo = "none"
SWEP.Primary.Delay = 0.5
SWEP.Primary.Force = 150
SWEP.Primary.Damage = 30

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

function SWEP:Initialize()
    self:SetHoldType(self.HoldType)
    self:SetWeaponHoldType(self.HoldType)

    self.buildEnt = NULL
    self.cooldwn = CurTime()
    self.ToBuildCost = 10
    self.ToBuild = "combine_mine"
    self.ToBuildmdl = "models/props_combine/combine_mine01.mdl"
    self.ToBuildname = "Combine Mine"

    self.Idle = 0
    self.IdleTimer = CurTime() + 1

    if CLIENT then
        self.Ghost = nil
    end
end

function SWEP:Deploy()
    self:SendWeaponAnim(ACT_VM_DRAW)
    self:SetNextPrimaryFire(CurTime() + self:SequenceDuration())
    self:SetNextSecondaryFire(CurTime() + self:SequenceDuration())

    self.buildEnt = NULL
    self.cooldwn = CurTime()

    self.Idle = 0
    self.IdleTimer = CurTime() + self.Owner:GetViewModel():SequenceDuration()
    self:NextThink(CurTime() + self:SequenceDuration())

    if CLIENT then
        if IsValid(self.Ghost) then self.Ghost:Remove() end
        self.Ghost = nil
    end

    return true
end

function SWEP:Holster()
    if CLIENT and IsValid(self.Ghost) then
        self.Ghost:Remove()
        self.Ghost = nil
    end
    return true
end

function SWEP:OnRemove()
    if CLIENT and IsValid(self.Ghost) then
        self.Ghost:Remove()
        self.Ghost = nil
    end
end

function SWEP:Think()
    if SERVER then return end

    local ply = self.Owner
    if not IsValid(ply) then return end

    local tr = util.TraceLine({
        start = ply:GetShootPos(),
        endpos = ply:GetShootPos() + ply:GetAimVector() * 200,
        filter = ply
    })

    if not IsValid(self.Ghost) or self.Ghost:GetModel() ~= self.ToBuildmdl then
        if IsValid(self.Ghost) then self.Ghost:Remove() end
        self.Ghost = ClientsideModel(self.ToBuildmdl, RENDERGROUP_TRANSLUCENT)
        self.Ghost:SetRenderMode(RENDERMODE_TRANSCOLOR)
        self.Ghost:SetColor(Color(255,255,255,80))
    end

    if IsValid(self.Ghost) then
        self.Ghost:SetPos(tr.HitPos)
        self.Ghost:SetAngles(Angle(0, ply:EyeAngles().y, 0))
    end
end
function SWEP:PrimaryAttack()
    self.Owner:LagCompensation(true)
    if self._nextBuild and self._nextBuild > CurTime() then
        self.Owner:LagCompensation(false)
        return
    end
    self._nextBuild = CurTime() + 0.3

    local tr = util.TraceLine({
        start  = self.Owner:GetShootPos(),
        endpos = self.Owner:GetShootPos() + self.Owner:GetAimVector() * 200,
        filter = self.Owner,
        mask   = MASK_SHOT_HULL,
    })

    if not IsValid(tr.Entity) then
        tr = util.TraceHull({
            start  = self.Owner:GetShootPos(),
            endpos = self.Owner:GetShootPos() + self.Owner:GetAimVector() * 200,
            filter = self.Owner,
            mins   = Vector(-16, -16, 0),
            maxs   = Vector(16, 16, 0),
            mask   = MASK_SHOT_HULL,
        })
    end

    self.Owner:SetAnimation(PLAYER_ATTACK1)

    ----------------------------------------------------------------
    -- ОСОБЫЙ СЛУЧАЙ: постройка sw_sania напрямую (без sent_construction)
    ----------------------------------------------------------------
    if SERVER and self.ToBuild == "sw_sania" then
        self:SetNextPrimaryFire(CurTime() + 0.5)

        if self.Owner:GetNWInt("Scrap") < 300 then
            self.Owner:PrintMessage(HUD_PRINTCENTER, "Not enough scrap (need 300)")
            self.Owner:EmitSound("weapons/wrench_hit_build_fail.wav", 80, 100)
            self.Owner:LagCompensation(false)
            goto do_anim -- чтобы всё равно сыграть анимацию
        end

        self.Owner:SetNWInt("Scrap", self.Owner:GetNWInt("Scrap") - 300)

        local hitPos    = tr.HitPos
        local hitNormal = tr.HitNormal

        local placePos  = hitPos + hitNormal * 5
        local placeAng  = hitNormal:Angle()

        local reb = ents.Create("sw_sania")
        if IsValid(reb) then
            reb:SetPos(placePos)
            reb:SetAngles(placeAng)
            reb:Spawn()
            reb:Activate()

            timer.Simple(0, function()
                if not IsValid(reb) then return end

                local veh
                for _, e in ipairs(ents.FindInSphere(placePos, 200)) do
                    if IsValid(e) and (e.LVS or (e.GetBase and IsValid(e:GetBase()) and e:GetBase().LVS)) then
                        veh = e.LVS and e or e:GetBase()
                        break
                    end
                end

                if IsValid(veh) then
                    local vphys = veh:GetPhysicsObject()
                    local rphys = reb:GetPhysicsObject()

                    if IsValid(vphys) and IsValid(rphys) then
                        constraint.Weld(reb, veh, 0, 0, 0, true, false)

                        timer.Create("REB_AutoRemove_" .. reb:EntIndex(), 1, 0, function()
                            if not IsValid(reb) then return end
                            if not IsValid(veh) then reb:Remove() end
                        end)

                        self.Owner:EmitSound("weapons/building.wav", 80, 120)
                        return
                    end
                end

                local phys = reb:GetPhysicsObject()
                if IsValid(phys) then
                    phys:EnableMotion(false)
                    phys:Sleep()
                end

                self.Owner:EmitSound("weapons/building.wav", 80, 120)
            end)
        end

        goto do_anim
    end

    ----------------------------------------------------------------
    -- ПОСТРОЙКА ЧЕРЕЗ sent_construction
    ----------------------------------------------------------------
    if SERVER and tr.Hit then
        local entData = Builder_EntitiesTBL[self.ToBuild]
        if entData then
            local existing = ents.FindInSphere(tr.HitPos, 25)
            local hasConstruction = false

            for _, e in ipairs(existing) do
                if IsValid(e) and e:GetClass() == "sent_construction" then
                    hasConstruction = true
                    break
                end
            end

            if hasConstruction then
                self.Owner:EmitSound("weapons/wrench_hit_build_fail.wav", 75, 100)
            else
                local build = ents.Create("sent_construction")
                build:SetPos(tr.HitPos)
                build:SetAngles(Angle(0, self.Owner:EyeAngles().y, 0))

                build.EntToBuild      = entData.Name
                build.EntToBuildCost  = entData.cost or 10
                build.EntModel        = entData.mdl
                build.EntHealth       = entData.health or 10000
                build:SetNick(entData.PrintName)
                build:SetOwned(self.Owner)
                build:SetPlayer(self.Owner)

                build:Spawn()
                build:Activate()

                local phys = build:GetPhysicsObject()
                if IsValid(phys) then
                    phys:EnableMotion(false)
                    phys:Sleep()
                end

                self.Owner:EmitSound("weapons/building.wav", 80, 120)
            end
        end
    end

    ----------------------------------------------------------------
    -- УДАР ПО УЖЕ СУЩЕСТВУЮЩЕМУ ОБЪЕКТУ
    ----------------------------------------------------------------
    if SERVER and (IsValid(tr.Entity) or tr.Entity == Entity(0)) then
        local v = tr.Entity
        local entClass = v:GetClass()

        -- 1. Пытаемся понять, это ли наша постройка из Builder_EntitiesTBL
        local is_built_entity = false
        local entity_data = nil

        if IsValid(v) then
            for _, entData in pairs(Builder_EntitiesTBL) do
                local is_match = false

                -- обычные энтити (turrets, mines и т.п.)
                if entData.Name == entClass then
                    is_match = true
                -- наши фортификации, которые после строительства становятся constructor_prop с нужной моделью
                elseif entClass == "constructor_prop" and entData.mdl == v:GetModel() then
                    is_match = true
                -- NPC из таблицы
                elseif list.Get("NPC")[entData.Name] and entData.Name == entClass then
                    is_match = true
                end

                if is_match then
                    is_built_entity = true
                    entity_data = entData
                    break
                end
            end
        end

        -- 2. Если это наша постройка — ломаем её и возвращаем половину цены
        if is_built_entity and entity_data and entClass ~= "sent_construction" then
            if not v.BuilderHealth then
                v.BuilderHealth     = entity_data.health or 500
                v.BuilderMaxHealth  = v.BuilderHealth
                v.BuilderScrapValue = (entity_data.cost or 0) * 5 -- полная цена в скрапе
            end

            v.BuilderHealth = v.BuilderHealth - self.Primary.Damage
            local hp = math.max(v.BuilderHealth, 0)

            self.Owner:PrintMessage(
                HUD_PRINTCENTER,
                "Ломаю постройку (" .. hp .. " HP)"
            )

            self.Owner:EmitSound("weapons/cbar_hit2.wav", 80, 100)

            net.Start("builder_hitprop")
                net.WriteVector(tr.HitPos)
                net.WriteInt(hp, 16)
            net.Send(self.Owner)

            if v.BuilderHealth <= 0 then
                local full_cost = v.BuilderScrapValue or ((entity_data.cost or 0) * 5)
                local refund = math.floor(full_cost / 2)

                if refund > 0 then
                    self.Owner:SetNWInt("Scrap", self.Owner:GetNWInt("Scrap") + refund)
                end

                v:Remove()
            end
        else
            -- СТАРОЕ ПОВЕДЕНИЕ: урон/ремонт всего остального
            if entClass ~= "sent_construction" then
                if BuilderIsCreature(v) or v:Health() <= 0 or v:GetMaxHealth() <= 0 then
                    local dmginfo = DamageInfo()
                    dmginfo:SetAttacker(self.Owner)
                    dmginfo:SetInflictor(self)
                    dmginfo:SetDamage(self.Primary.Damage)
                    dmginfo:SetDamageType(DMG_CLUB)
                    dmginfo:SetDamagePosition(self.Owner:GetShootPos())
                    dmginfo:SetDamageForce(self.Owner:EyeAngles():Forward() * self.Primary.Damage * self.Primary.Force)
                    SuppressHostEvents(NULL)
                    v:DispatchTraceAttack(dmginfo, tr)
                    SuppressHostEvents(self.Owner)

                    if v ~= Entity(0) and (not BuilderIsCreature(v) or not (v:GetClass():find("manhack") or v:GetClass():find("scanner") or v:GetClass():find("robot") or v:GetClass():find("turret") or v:GetClass():find("roller") or v:GetClass():find("sentry"))) then
                        self.Owner:EmitSound(")weapons/cbar_hitbod" .. math.random(1, 3) .. ".wav", 80, math.random(95, 100))
                    else
                        self.Owner:EmitSound(")weapons/cbar_hit" .. math.random(1, 2) .. ".wav", 75, math.random(95, 100))
                    end
                else
                    if v:GetMaxHealth() * 10 <= v:Health() then
                        self.Owner:EmitSound("weapons/wrench_hit_build_fail.wav", 80, math.random(95, 100))
                    elseif self.Owner:GetNWInt("Scrap") < 5 then
                        self.Owner:PrintMessage(HUD_PRINTCENTER, "Not enough scrap")
                        self.Owner:EmitSound("weapons/wrench_hit_build_fail.wav", 80, math.random(95, 100))
                    else
                        v:SetHealth(math.min(v:GetMaxHealth() * 10, v:Health() + 5))
                        self.Owner:SetNWInt("Scrap", self.Owner:GetNWInt("Scrap") - 5)
                        self.Owner:EmitSound(")weapons/cbar_hit" .. math.random(1, 2) .. ".wav", 75, math.random(95, 100))
                    end
                end
            end

            if entClass == "sent_construction" and self.Owner:GetNWInt("Scrap") >= 5 then
                v:SetProgress(math.Round(v:GetProgress() + 100 / v.EntToBuildCost, 2))
                self.Owner:SetNWInt("Scrap", self.Owner:GetNWInt("Scrap") - 5)
                self.Owner:EmitSound(")weapons/cbar_hit" .. math.random(1, 2) .. ".wav", 80, math.random(95, 100))

                local pos = tr.HitPos
                timer.Simple(0, function()
                    local ef = EffectData()
                    ef:SetOrigin(pos)
                    util.Effect("builderef1", ef)
                end)
            elseif entClass == "sent_construction" then
                self.Owner:PrintMessage(HUD_PRINTCENTER, "Not enough scrap")
                self.Owner:EmitSound("weapons/wrench_hit_build_fail.wav", 80, math.random(95, 100))
            end
        end

        self.cooldwn = CurTime() + 2
        timer.Create("swing" .. self:EntIndex(), 2, 1, function() end)
    else
        self.cooldwn = CurTime() + 1
    end

    ::do_anim::

    if tr.Hit then
        self.Owner:ViewPunch(Angle(-1, -1, 0))
        self.Weapon:SendWeaponAnim(ACT_VM_HITCENTER)
        self:SetNextPrimaryFire(CurTime() + self.Primary.Delay)
        self:SetNextSecondaryFire(CurTime() + self.Primary.Delay)
    else
        self.Owner:ViewPunch(Angle(1, 1, 0))
        self:EmitSound(")weapons/wrench_swing.wav", 80, math.random(95, 105))
        self.Weapon:SendWeaponAnim(ACT_VM_MISSCENTER)
        self:SetNextPrimaryFire(CurTime() + self.Primary.Delay)
        self:SetNextSecondaryFire(CurTime() + self.Primary.Delay)
    end

    self.Idle = 0
    self.IdleTimer = CurTime() + self.Owner:GetViewModel():SequenceDuration()
    self.Owner:LagCompensation(false)
end

function SWEP:SecondaryAttack()
    self.Owner:LagCompensation(true)

    if CLIENT then
        local tr = util.TraceLine({
            start  = self.Owner:GetShootPos(),
            endpos = self.Owner:GetShootPos() + self.Owner:GetAimVector() * 200,
            filter = self.Owner,
            mask   = MASK_SHOT_HULL,
        })

        if not IsValid(tr.Entity) then
            tr = util.TraceHull({
                start  = self.Owner:GetShootPos(),
                endpos = self.Owner:GetShootPos() + self.Owner:GetAimVector() * 200,
                filter = self.Owner,
                mins   = Vector(-16, -16, 0),
                maxs   = Vector(16, 16, 0),
                mask   = MASK_SHOT_HULL,
            })
        end

        if tr.Hit then
            local ef = EffectData()
            ef:SetOrigin(tr.HitPos)
            util.Effect("cball_bounce", ef)
        end
    end

    if SERVER then
        local ply = self.Owner
        local eyePos = ply:GetShootPos()
        local eyeDir = ply:GetAimVector()

        local bestEnt
        local bestDist = 200

        for _, ent in ipairs(ents.FindByClass("sw_sania")) do
            if not IsValid(ent) then continue end

            local toEnt = ent:GetPos() - eyePos
            local dist = toEnt:Length()
            if dist > 200 then continue end

            local dot = eyeDir:Dot(toEnt:GetNormalized())
            if dot < 0.8 then continue end

            if dist < bestDist then
                bestDist = dist
                bestEnt = ent
            end
        end

        if IsValid(bestEnt) then
            ply:SetNWInt("Scrap", ply:GetNWInt("Scrap") + 150)

            bestEnt:Remove()
            ply:EmitSound("buttons/button19.wav", 75, 90)
            ply:LagCompensation(false)
            return
        end
    end

    local tr = util.TraceLine({
        start = self.Owner:GetShootPos(),
        endpos = self.Owner:GetShootPos() + self.Owner:GetAimVector() * 200, -- Дистанция постройки
        filter = self.Owner,
        mask = MASK_SHOT_HULL,
    })

    if not IsValid(tr.Entity) then
        tr = util.TraceHull({
            start = self.Owner:GetShootPos(),
            endpos = self.Owner:GetShootPos() + self.Owner:GetAimVector() * 200,  -- Дистанция постройки
            filter = self.Owner,
            mins = Vector(-16, -16, 0),
            maxs = Vector(16, 16, 0),
            mask = MASK_SHOT_HULL,
        })
    end

    self.Owner:SetAnimation(PLAYER_ATTACK1)

    if SERVER and (IsValid(tr.Entity) or tr.Entity == Entity(0)) then
        local v = tr.Entity
        local entClass = v:GetClass()

        local is_built_entity = false
        local entity_data = nil

        for k, entData in pairs(Builder_EntitiesTBL) do
            if entData.Name == entClass then
                is_built_entity = true
                entity_data = entData
                break
            elseif entClass == "constructor_prop" and entData.mdl == v:GetModel() then
                is_built_entity = true
                entity_data = entData
                break
            end
        end

        -- если это наша постройка — ломаем и возвращаем половину стоимости
        if is_built_entity and entClass ~= "sent_construction" then

            -- инициализация данных постройки (один раз)
            if not v.BuilderHealth then
                v.BuilderHealth = entity_data.health or 500
                v.BuilderMaxHealth = v.BuilderHealth

                local full_cost = (entity_data.cost or 0) * 5
                v.BuilderScrapValue = math.floor(full_cost / 2)   -- половина цены
            end

            -- наносим урон
            v.BuilderHealth = v.BuilderHealth - self.Primary.Damage
            local hp = math.max(v.BuilderHealth, 0)

            self.Owner:PrintMessage(HUD_PRINTCENTER, "Ломаю постройку (" .. hp .. " HP)")
            self.Owner:EmitSound("weapons/cbar_hit2.wav", 80, 100)

            net.Start("builder_hitprop")
                net.WriteVector(tr.HitPos)
                net.WriteInt(hp, 16)
            net.Send(self.Owner)

            -- уничтожение и возврат
            if v.BuilderHealth <= 0 then
                local refund = v.BuilderScrapValue or 0
                self.Owner:SetNWInt("Scrap", self.Owner:GetNWInt("Scrap") + refund)
                v:Remove()
            end

            return  -- ВАЖНО: чтобы не выполнялся старый код ниже
        end

        if entClass == "sent_construction" then
            local spent_scrap = math.Round(v:GetProgress() / 100 * v.EntToBuildCost * 5)
            self.Owner:SetNWInt("Scrap", self.Owner:GetNWInt("Scrap") + spent_scrap)
            v:Remove()
            self.Owner:EmitSound(")weapons/cbar_hit" .. math.random(1, 2) .. ".wav", 75, math.random(95, 100))
        else
            local is_built_entity = false
            local entity_data = nil

            for k, entData in pairs(Builder_EntitiesTBL) do
                local is_match = false

                if entData.Name == entClass then
                    is_match = true
                elseif entClass == "constructor_prop" and entData.mdl == v:GetModel() then
                    is_match = true
                elseif list.Get("NPC")[entData.Name] and entData.Name == entClass then
                    is_match = true
                end

                if is_match then
                    is_built_entity = true
                    entity_data = entData
                    break
                end
            end

            if is_built_entity and entity_data then
                if not v.BuilderHealth then
                    v.BuilderHealth      = entity_data.health or 500
                    v.BuilderMaxHealth   = v.BuilderHealth

                    -- сразу считаем ВОЗВРАТ (половина цены из таблицы, с учётом *5)
                    local full_cost = (entity_data.cost or 0) * 5
                    v.BuilderScrapValue = math.floor(full_cost / 2)
                end

                v.BuilderHealth = v.BuilderHealth - self.Primary.Damage
                local hp = math.max(v.BuilderHealth, 0)

                if SERVER then
                    self.Owner:PrintMessage(
                        HUD_PRINTCENTER,
                        "Ломаю постройку (" .. hp .. " HP)"
                    )

                    self.Owner:EmitSound("weapons/cbar_hit2.wav", 80, 100)

                    net.Start("builder_hitprop")
                        net.WriteVector(tr.HitPos)
                        net.WriteInt(hp, 16)
                    net.Send(self.Owner)

                    if v.BuilderHealth <= 0 then
                        local refund = v.BuilderScrapValue or 0
                        self.Owner:SetNWInt("Scrap", self.Owner:GetNWInt("Scrap") + refund)
                        v:Remove()
                    end
                end
            else
                local dmginfo = DamageInfo()
                dmginfo:SetAttacker(self.Owner)
                dmginfo:SetInflictor(self)
                dmginfo:SetDamage(self.Primary.Damage)
                dmginfo:SetDamageType(DMG_CLUB)
                dmginfo:SetDamagePosition(self.Owner:GetShootPos())
                dmginfo:SetDamageForce(self.Owner:EyeAngles():Forward() * self.Primary.Damage * self.Primary.Force)
                SuppressHostEvents(NULL)
                v:DispatchTraceAttack(dmginfo, tr)
                SuppressHostEvents(self.Owner)

                if BuilderIsCreature(v) then
                    if not (v:GetClass():find("manhack") or v:GetClass():find("scanner") or v:GetClass():find("robot") or v:GetClass():find("turret") or v:GetClass():find("roller") or v:GetClass():find("sentry")) then
                        self.Owner:EmitSound(")weapons/cbar_hitbod" .. math.random(1, 3) .. ".wav", 80, math.random(95, 100))
                    else
                        self.Owner:EmitSound(")weapons/cbar_hit" .. math.random(1, 2) .. ".wav", 75, math.random(95, 100))
                    end
                else
                    self.Owner:EmitSound(")weapons/cbar_hit" .. math.random(1, 2) .. ".wav", 75, math.random(95, 100))
                end
            end
        end
    end

    if tr.Hit then
        self.Owner:ViewPunch(Angle(-1, -1, 0))
        self.Weapon:SendWeaponAnim(ACT_VM_HITCENTER)
        self:SetNextPrimaryFire(CurTime() + self.Primary.Delay)
        self:SetNextSecondaryFire(CurTime() + self.Primary.Delay)
    else
        self.Owner:ViewPunch(Angle(1, 1, 0))
        self:EmitSound(")weapons/wrench_swing.wav", 80, math.random(95, 105))
        self.Weapon:SendWeaponAnim(ACT_VM_MISSCENTER)
        self:SetNextPrimaryFire(CurTime() + self.Primary.Delay)
        self:SetNextSecondaryFire(CurTime() + self.Primary.Delay)
    end

    self.Idle = 0
    self.IdleTimer = CurTime() + self.Owner:GetViewModel():SequenceDuration()
    self.Owner:LagCompensation(false)
end

if CLIENT then
    net.Receive("sendToBuilder", function()
        local wep = LocalPlayer():GetActiveWeapon()
        if IsValid(wep) then
            if IsValid(wep.Ghost) then
                wep.Ghost:Remove()
            end
            wep.Ghost = nil
        end
    end)

    net.Receive("builder_hitprop", function()
        local hitPos = net.ReadVector()
        local hp     = net.ReadInt(16)

        local ef = EffectData()
        ef:SetOrigin(hitPos)
        util.Effect("cball_bounce", ef)

        local spark = EffectData()
        spark:SetOrigin(hitPos)
        util.Effect("ManhackSparks", spark)

        -- чат‑индикатор
        chat.AddText(
            Color(255, 200, 50), "[Молоток] ",
            color_white, "Урон по постройке (" .. hp .. " HP)"
        )
    end)
end

if CLIENT then
    net.Receive("builderMenu", function()
        local ply = LocalPlayer()
        local wep = ply:GetActiveWeapon()
        if IsValid(wep) and wep:GetClass() == "weapon_builder" then
            wep:Reload()
        end
    end)
end

function SWEP:Reload()
    if not self:IsValid() or not Builder_EntitiesLoaded then return false end
    if CurTime() > nextreload then
        nextreload = CurTime() + 1

        if SERVER then
            net.Start("builderMenu")
            net.Send(self.Owner)
            return
        end

        local new = false
        if not IsValid(buildermenu) then
            new = true
            buildermenu = vgui.Create("DFrame")
            buildermenu:SetSize(ScrW() / 2.2, ScrH() / 1.5)
            buildermenu:SetTitle("")
            buildermenu:SetIcon("icon16/script_gear.png")
            buildermenu:SetDraggable(false)
            buildermenu:SetAlpha(1)
            buildermenu:ShowCloseButton(false)
        end

        local frame = buildermenu
        frame:AlphaTo(255, 0.25)
        frame:MakePopup()
        frame:Center()
        frame:Show()
        frame.B_Close = false
        frame:SetKeyboardInputEnabled(true)
        frame:SetMouseInputEnabled(true)
        frame.N_Scrap = 0

        if not new then return end

        function frame:Paint(w, h)
            if IsValid(LocalPlayer()) then
                frame.N_Scrap = LocalPlayer():GetNWInt("Scrap")
            end

            Derma_DrawBackgroundBlur(self, self.m_fCreateTime)
            draw.RoundedBox(0, 0, 0, w, h, Color(0, 0, 0, 200))
            surface.SetDrawColor(255, 255, 255, (150 + math.sin(RealTime() * 5.2) * 100) * .8)
            surface.DrawOutlinedRect(0, 0, w, h)
            surface.DrawOutlinedRect(0, 0, w, 25)

            draw.TextShadow({
                text = "Молоток    [Металлолом:" .. frame.N_Scrap .. "]",
                pos = {26, 12},
                font = "TargetID",
                xalign = TEXT_ALIGN_LEFT,
                yalign = TEXT_ALIGN_CENTER,
                color = Color(255, 255, 255)
            }, 1, 255)
        end

        function frame:DoClose()
            if frame.B_Close then return end
            frame.B_Close = true
            frame:AlphaTo(1, 0.25)
            frame:SetKeyboardInputEnabled(false)
            frame:SetMouseInputEnabled(false)
            timer.Simple(0.25, function()
                if IsValid(frame) and frame.B_Close then frame:Hide() end
            end)
        end

        local CloseButton = frame:Add("DButton")
        local pax = CloseButton
        pax:SetText("")
        pax:SetPos(810, 3)
        pax:SetSize(60, 18)
        pax.B_Hover = false

        function pax:Paint(w, h)
            draw.TextShadow({
                text = "Close",
                pos = {w / 2, h / 2},
                font = "TargetID",
                xalign = TEXT_ALIGN_CENTER,
                yalign = TEXT_ALIGN_CENTER,
                color = (pax.B_Hover and Color(255, 0, 0) or Color(255, 255, 255))
            }, 1, 255)
        end

        function pax:DoClick()
            frame:DoClose()
        end

        function pax:OnCursorEntered()
            pax.B_Hover = true
        end

        function pax:OnCursorExited()
            pax.B_Hover = false
        end

        local PropPanel = vgui.Create("ContentContainer", frame)
        PropPanel:SetTriggerSpawnlistChange(false)
        PropPanel:Dock(FILL)

        local Categorised = {}
        Categorised["Фортификации (" .. Builder_EntitiesNum .. ")"] = Builder_EntitiesTBL

        for CategoryName, v in SortedPairs(Categorised) do
            local Header = vgui.Create("ContentHeader", PropPanel)
            Header:SetText(CategoryName)
            PropPanel:Add(Header)

            for k, WeaponTable in SortedPairsByMemberValue(v, "Name") do
                if WeaponTable.PrintName ~= nil then
                    local icon = vgui.Create("ContentIcon", PropPanel)
                    icon.ClassName = k

                    local iconpath = "entities/" .. WeaponTable.Name .. ".png"
                    if not file.Exists("materials/" .. iconpath, "game") then
                        iconpath = "vgui/entities/" .. WeaponTable.Name .. ".vmt"
                    end

                    icon:SetMaterial(iconpath)
                    icon:SetName(WeaponTable.PrintName)
                    icon:SetToolTip("Cost: " .. (WeaponTable.cost and WeaponTable.cost * 5 or "???"))

                    icon.DoClick = function()
                        frame:DoClose()
                        net.Start("sendToBuilder")
                        net.WriteString(icon.ClassName)
                        net.SendToServer()
                    end

                    PropPanel:Add(icon)
                end
            end
        end
    end
end

if CLIENT then
    surface.CreateFont("ScrapFont", {
        font = "Digital-7",
        size = 50,
        weight = 400,
        scanlines = true,
        antialias = true
    })

    surface.CreateFont("ScrapFont2", {
        font = "Digital-7",
        size = 36,
        weight = 400,
        scanlines = true,
        antialias = true
    })

    local scrap = Material("icon/refined_metal.png")

    hook.Add("HUDPaint", "BuilderHud", function()
        local fade = LocalPlayer().UIScrapFade or 0
        local negative = 0
        local w = 50
        local h = ScrH() / 3
        local wep = LocalPlayer():GetActiveWeapon()

        if IsValid(wep) and wep:GetClass() == "weapon_builder" then
            fade = 200
        end

        if fade > negative then
            local alpha = math.Clamp(fade, negative, 100) / 100
            if fade > negative then
                LocalPlayer().UIScrapFade = math.Clamp(fade, negative, 400) - 1
                w = math.Clamp(fade, negative, 100) / 2
            end

            surface.SetDrawColor(255, 200, 25, (150 + math.sin(RealTime() * 5.2) * 100) * .8 * alpha)
            surface.DrawOutlinedRect(w, h, 128, 128)

            surface.SetDrawColor(0, 0, 0, 128 * alpha)
            surface.DrawRect(w, h, 128, 128)

            surface.SetDrawColor(255, 255, 255, 255 * alpha)
            surface.SetMaterial(scrap)
            surface.DrawTexturedRect(w, h - 20, 128, 128)

            draw.SimpleText(
                tostring(math.Clamp(LocalPlayer():GetNWInt("Scrap", 0), 0, 99999)),
                "ScrapFont",
                w + 65,
                h + 78,
                Color(255, 200, 25, 255 * alpha),
                1,
                0
            )
        end
    end)
end
if CLIENT then
    local Mat = Material("sprites/light_ignorez")

    if true then 
        local nam = "builderef1"
        local EFFECT = {}

        function EFFECT:Init(data)
            local pos = data:GetOrigin()
            self:SetRenderBounds(-Vector(32, 32, 32), Vector(32, 32, 32))
            self.Emitter = ParticleEmitter(pos)

            for i = 1, math.random(8, 16) do
                local particle = self.Emitter:Add("effects/spark", pos)
                particle:SetVelocity(VectorRand():GetNormalized() * math.random(48, 96))
                particle:SetLifeTime(0)
                particle:SetDieTime(0.5)
                local Siz = math.Rand(1, 3)
                particle:SetStartSize(Siz)
                particle:SetEndSize(0)
                particle:SetStartLength(Siz * 2)
                particle:SetEndLength(Siz)
                particle:SetStartAlpha(128)
                particle:SetEndAlpha(0)
                particle:SetColor(255, 255, 128)
                particle:SetLighting(false)
                particle:SetCollide(true)
                particle:SetGravity(Vector(0, 0, -128))
                particle:SetBounce(1)
            end

            for i = 1, 4 do
                local particle = self.Emitter:Add("particle/particle_smokegrenade1", pos)
                if particle then
                    particle:SetVelocity(VectorRand():GetNormalized() * math.random(32, 64))
                    particle:SetLifeTime(0)
                    particle:SetDieTime(math.Rand(0.5, 1))
                    particle:SetStartAlpha(32)
                    particle:SetEndAlpha(0)
                    local Siz = math.Rand(12, 24)
                    particle:SetStartSize(Siz / 4)
                    particle:SetEndSize(Siz)
                    particle:SetRoll(math.random(0, 360))
                    particle:SetColor(128, 128, 128)
                    particle:SetGravity(Vector(0, 0, math.random(32, 64)))
                    particle:SetAirResistance(256)
                    particle:SetCollide(true)
                    particle:SetBounce(1)
                end
            end

            local dlight = DynamicLight(0)
            if dlight then
                dlight.pos = pos
                dlight.r = 255
                dlight.g = 255
                dlight.b = 128
                dlight.brightness = 1
                dlight.decay = 128
                dlight.size = 64
                dlight.dietime = CurTime() + 0.5
            end

            for i = 1, math.random(1, 4) do
                local ef = EffectData()
                ef:SetOrigin(pos)
                util.Effect("builderef2", ef)
            end
        end

        function EFFECT:Think() return false end
        function EFFECT:Render() end

        effects.Register(EFFECT, nam)
    end

    if true then 
        local nam = "builderef2"
        local EFFECT = {}

        function EFFECT:Init(data)
            self.Entity:SetModel("models/gibs/metal_gib" .. math.random(1, 5) .. ".mdl")
            self.Entity:PhysicsInit(SOLID_VPHYSICS)
            self.Entity:SetRenderMode(RENDERMODE_TRANSCOLOR)
            self.Entity:SetCollisionGroup(COLLISION_GROUP_DEBRIS)
            self.Entity:SetModelScale(math.Rand(0.25, 0.5))
            self.Entity:SetRenderMode(RENDERMODE_TRANSCOLOR)

            local phys = self.Entity:GetPhysicsObject()
            if IsValid(phys) then
                phys:Wake()
                phys:EnableMotion(true)
                phys:SetMaterial("gmod_silent")
                phys:SetAngles(Angle(math.Rand(0, 360), math.Rand(0, 360), math.Rand(0, 360)))
                local vel = VectorRand():GetNormalized() * math.Rand(64, 128)
                vel = Vector(vel.x, vel.y, math.abs(vel.z))
                phys:SetVelocity(vel)
                phys:Wake()
            end

            self.LifeTime = CurTime() + math.Rand(0.5, 1)
            self.LifeAlp = 255
        end

        function EFFECT:PhysicsCollide(data, physobj) end

        function EFFECT:Think()
            if self.LifeTime < CurTime() then
                self.LifeAlp = Lerp(0.05, self.LifeAlp, 0)
                self.Entity:SetColor(Color(255, 255, 255, self.LifeAlp))
                if self.LifeAlp <= 1 then return false end
            end
            return true
        end

        function EFFECT:Render()
            self.Entity:DrawModel()
        end

        effects.Register(EFFECT, nam)
    end
end

if SERVER then
    util.AddNetworkString("sendToBuilder")
    util.AddNetworkString("builderMenu")
    util.AddNetworkString("builder_sync")

    net.Receive("sendToBuilder", function(len, ply)
        local cls = net.ReadString()
        if not istable(Builder_EntitiesTBL[cls]) then return end

        local weapon = ply:GetActiveWeapon()
        if IsValid(weapon) and weapon:GetClass() == "weapon_builder" then
            local tab = Builder_EntitiesTBL[cls]

            weapon.ToBuildCost = tab.cost or 10
            weapon.ToBuild = cls
            weapon.ToBuildmdl = tab.mdl
            weapon.ToBuildname = tab.PrintName

            if IsValid(weapon.buildEnt) then
                weapon.buildEnt:Remove()
                weapon.buildEnt = nil
            end

            net.Start("builder_sync")
                net.WriteString(cls)
            net.Send(ply)
        end
    end)
end

if CLIENT then
    net.Receive("builder_sync", function()
        local cls = net.ReadString()
        local wep = LocalPlayer():GetActiveWeapon()
        if not IsValid(wep) or wep:GetClass() ~= "weapon_builder" then return end

        local tab = Builder_EntitiesTBL[cls]
        if not tab then return end

        wep.ToBuild = cls
        wep.ToBuildmdl = tab.mdl
        wep.ToBuildname = tab.PrintName
        wep.ToBuildCost = tab.cost or 10

        if IsValid(wep.Ghost) then
            wep.Ghost:Remove()
        end
        wep.Ghost = nil
    end)
end


    hook.Add("PlayerSpawn", "BuilderForceResetScrap", function(ply)
        ply:SetNWInt("Scrap", 500)
    end)

if CLIENT then
    local EFFECT = {}

    function EFFECT:Init(data)
        local pos = data:GetOrigin()

        self:SetRenderBounds(-Vector(32, 32, 32), Vector(32, 32, 32))
        self.Emitter = ParticleEmitter(pos)

        -- искры
        for i = 1, math.random(8, 16) do
            local particle = self.Emitter:Add("effects/spark", pos)
            if not particle then continue end

            particle:SetVelocity(VectorRand():GetNormalized() * math.random(48, 96))
            particle:SetLifeTime(0)
            particle:SetDieTime(0.5)

            local siz = math.Rand(1, 3)
            particle:SetStartSize(siz)
            particle:SetEndSize(0)
            particle:SetStartLength(siz * 2)
            particle:SetEndLength(siz)

            particle:SetStartAlpha(255)
            particle:SetEndAlpha(0)

            particle:SetColor(255, 255, 128)
            particle:SetLighting(false)
            particle:SetCollide(true)
            particle:SetGravity(Vector(0, 0, -128))
            particle:SetBounce(1)
        end

        if self.Emitter then
            self.Emitter:Finish()
        end
    end

    function EFFECT:Think()
        return false
    end

    function EFFECT:Render()
    end

    effects.Register(EFFECT, "builderef1")
end

hook.Add("PlayerDeath", "BuilderCancelConstructionOnDeath", function(ply)
    for _, ent in ipairs(ents.FindByClass("sent_construction")) do
        if ent:GetOwned() == ply or ent:GetPlayer() == ply then
            ent:Remove()
        end
    end
end)

    hook.Add("OnNPCKilled", "ScrapDrop", function() end)
    hook.Add("PlayerDeath", "ScrapDrop", function() end)
