Files
VnUtest/garrysmod/addons/molotok/lua/weapons/weapon_builder.lua
2026-03-31 10:27:04 +03:00

1162 lines
40 KiB
Lua
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)