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)