add sborka
This commit is contained in:
@@ -0,0 +1 @@
|
||||
include("shared.lua")
|
||||
@@ -0,0 +1,763 @@
|
||||
AddCSLuaFile("shared.lua")
|
||||
include("shared.lua")
|
||||
|
||||
util.AddNetworkString("MuR_DroneCam")
|
||||
util.AddNetworkString("MuR_DroneLost")
|
||||
util.AddNetworkString("MuR_DroneEmergency") -- экстренное самоуничтожение
|
||||
|
||||
local rotorSounds = {
|
||||
"ambient/machines/spin_loop.wav",
|
||||
"ambient/machines/machine1_hit1.wav"
|
||||
}
|
||||
|
||||
local damageSounds = {
|
||||
"physics/metal/metal_box_impact_bullet1.wav",
|
||||
"physics/metal/metal_box_impact_bullet2.wav",
|
||||
"physics/metal/metal_box_impact_bullet3.wav",
|
||||
"physics/metal/metal_computer_impact_bullet1.wav",
|
||||
"physics/metal/metal_computer_impact_bullet2.wav"
|
||||
}
|
||||
|
||||
local startupSounds = {
|
||||
"buttons/button9.wav",
|
||||
"buttons/button17.wav"
|
||||
}
|
||||
|
||||
function ENT:Initialize()
|
||||
self:SetModel("models/murdered/weapons/drone_ex.mdl")
|
||||
self:ResetSequence("idle")
|
||||
self:SetHealth(self.HealthCount)
|
||||
self:SetGravity(0)
|
||||
self.Velo = {x = 0, y = 0, z = 0}
|
||||
|
||||
self:SetSolid(SOLID_VPHYSICS)
|
||||
self:PhysicsInit(SOLID_VPHYSICS)
|
||||
|
||||
self.MultSpeed = 900
|
||||
self.MaxSpeed = 1000
|
||||
self.BoostSpeed = 1500
|
||||
self.MaxPitch = 18
|
||||
self.MaxRoll = 22
|
||||
self.LinearDrag = 0.04
|
||||
self.GrenadeDelay = CurTime() + 5
|
||||
self.TakeDamageWall = 0
|
||||
self.DeltaTime = 0
|
||||
self.LastBoostSound = 0
|
||||
self.LastWindSound = 0
|
||||
|
||||
-- самоуничтожение
|
||||
self.EmergencyKill = false
|
||||
self.EmergencyKillTime = 0
|
||||
self._Exploding = false
|
||||
self:SetNW2Float("EmergencyCountdown", 0)
|
||||
|
||||
self:SetNW2Float("RemoveTime", CurTime() + self.BatteryCount)
|
||||
|
||||
self:EmitSound(startupSounds[math.random(#startupSounds)], 60, 100, 0.5)
|
||||
|
||||
local ply = self:GetCreator()
|
||||
if IsValid(ply) then
|
||||
local forward = ply:GetForward()
|
||||
forward.z = 0
|
||||
forward = forward:GetNormalized()
|
||||
|
||||
local spawnDist = 80
|
||||
local spawnHeight = 40
|
||||
|
||||
local spawnPos = ply:GetPos() + forward * spawnDist + Vector(0, 0, spawnHeight)
|
||||
|
||||
local tr = util.TraceHull({
|
||||
start = spawnPos,
|
||||
endpos = spawnPos,
|
||||
mins = Vector(-20, -20, -20),
|
||||
maxs = Vector(20, 20, 20),
|
||||
filter = {ply, self}
|
||||
})
|
||||
|
||||
if tr.Hit then
|
||||
spawnPos = spawnPos + forward * 60
|
||||
end
|
||||
|
||||
local ground = util.TraceLine({
|
||||
start = spawnPos,
|
||||
endpos = spawnPos - Vector(0, 0, 5000),
|
||||
filter = self
|
||||
})
|
||||
|
||||
if spawnPos.z < ground.HitPos.z + 30 then
|
||||
spawnPos.z = ground.HitPos.z + 30
|
||||
end
|
||||
|
||||
self:SetPos(spawnPos)
|
||||
self:SetAngles(Angle(0, ply:EyeAngles().y, 0))
|
||||
end
|
||||
|
||||
-- визуальная ПГ-26 под дроном
|
||||
local rocket = ents.Create("prop_dynamic")
|
||||
if IsValid(rocket) then
|
||||
rocket:SetModel("models/sw/rus/rockets/pg26.mdl")
|
||||
rocket:SetParent(self)
|
||||
rocket:SetLocalPos(Vector(-15, 0, 2))
|
||||
rocket:SetLocalAngles(Angle(0, 0, 0))
|
||||
rocket:Spawn()
|
||||
rocket:SetMoveType(MOVETYPE_NONE)
|
||||
rocket:SetSolid(SOLID_NONE)
|
||||
rocket:PhysicsDestroy()
|
||||
self.RocketModel = rocket
|
||||
end
|
||||
|
||||
if CreateSound then
|
||||
self.RotorSound = CreateSound(self, rotorSounds[1])
|
||||
if self.RotorSound then
|
||||
self.RotorSound:PlayEx(0.2, 80)
|
||||
end
|
||||
|
||||
self.WindSound = CreateSound(self, "ambient/wind/wind_snippet2.wav")
|
||||
if self.WindSound then
|
||||
self.WindSound:PlayEx(0, 100)
|
||||
end
|
||||
end
|
||||
|
||||
timer.Simple(0.1, function()
|
||||
if not IsValid(self) then return end
|
||||
local ply2 = self:GetCreator()
|
||||
if not IsValid(ply2) then return end
|
||||
|
||||
ply2.ControlDrone = self
|
||||
|
||||
net.Start("MuR_DroneCam")
|
||||
net.WriteEntity(self)
|
||||
net.Send(ply2)
|
||||
end)
|
||||
end
|
||||
|
||||
function ENT:OnRemove()
|
||||
local ply = self:GetCreator()
|
||||
if IsValid(ply) and ply.ControlDrone == self then
|
||||
ply.ControlDrone = nil
|
||||
end
|
||||
|
||||
if self.RotorSound then self.RotorSound:Stop() end
|
||||
if self.WindSound then self.WindSound:Stop() end
|
||||
self:StopSound("ambient/machines/spin_loop.wav")
|
||||
self:StopSound("ambient/wind/wind_snippet2.wav")
|
||||
self:StopSound("vehicles/airboat/fan_blade_fullthrottle_loop1.wav")
|
||||
end
|
||||
|
||||
function ENT:GetForwardAbs()
|
||||
local ang = self:GetAngles()
|
||||
ang.x = 0
|
||||
ang.z = 0
|
||||
return ang:Forward()
|
||||
end
|
||||
|
||||
function ENT:GetRightAbs()
|
||||
local ang = self:GetAngles()
|
||||
ang.x = 0
|
||||
ang.z = 0
|
||||
return ang:Right()
|
||||
end
|
||||
|
||||
function ENT:GetUpAbs()
|
||||
local ang = self:GetAngles()
|
||||
ang.x = 0
|
||||
ang.z = 0
|
||||
return ang:Up()
|
||||
end
|
||||
|
||||
function ENT:Think()
|
||||
-- самоуничтожение
|
||||
if self.EmergencyKill then
|
||||
local phys = self:GetPhysicsObject()
|
||||
if IsValid(phys) then
|
||||
phys:EnableMotion(false)
|
||||
end
|
||||
|
||||
if CurTime() >= (self.EmergencyKillTime or 0) then
|
||||
self:Explode()
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
-- Ограничение дистанции как у spy-дрона
|
||||
local ply = self:GetCreator()
|
||||
if IsValid(ply) then
|
||||
local maxDist = 12000 * 12000 -- 12к юнитов
|
||||
local dist2 = ply:GetPos():DistToSqr(self:GetPos())
|
||||
|
||||
if dist2 > maxDist then
|
||||
net.Start("MuR_DroneLost")
|
||||
net.Send(ply)
|
||||
|
||||
if ply.ControlDrone == self then
|
||||
ply.ControlDrone = nil
|
||||
end
|
||||
|
||||
self:Explode()
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
-- === РЭБ (глушилка) ===
|
||||
if self:GetNWBool("Jammed") then
|
||||
|
||||
-- если впервые попали в РЭБ — запускаем таймер
|
||||
if not self.JamStart then
|
||||
self.JamStart = CurTime()
|
||||
self.JamEnd = CurTime() + 5 -- 5 секунд на выход
|
||||
end
|
||||
|
||||
-- ВАЖНО: управление НЕ отключаем
|
||||
-- ВАЖНО: скорость НЕ сбрасываем
|
||||
-- Дрон продолжает лететь как обычно
|
||||
|
||||
-- если время вышло — взрываем
|
||||
if CurTime() >= self.JamEnd then
|
||||
local ply = self:GetCreator()
|
||||
if IsValid(ply) then
|
||||
net.Start("MuR_DroneLost")
|
||||
net.Send(ply)
|
||||
if ply.ControlDrone == self then
|
||||
ply.ControlDrone = nil
|
||||
end
|
||||
end
|
||||
|
||||
self:Explode()
|
||||
return true
|
||||
end
|
||||
|
||||
else
|
||||
-- если вышли из РЭБ — сбрасываем таймер
|
||||
self.JamStart = nil
|
||||
self.JamEnd = nil
|
||||
end
|
||||
|
||||
|
||||
|
||||
local ply = self:GetCreator()
|
||||
if not IsValid(ply) then
|
||||
self:Remove()
|
||||
return
|
||||
end
|
||||
|
||||
local phys = self:GetPhysicsObject()
|
||||
local mu = self.MultSpeed
|
||||
local ms = self.MaxSpeed
|
||||
local tick = FrameTime()
|
||||
local ang = ply:EyeAngles().y
|
||||
local sang = Angle(0, ang, 0)
|
||||
|
||||
ply:SetActiveWeapon(nil)
|
||||
|
||||
local tr = util.TraceEntity({
|
||||
start = self:GetPos(),
|
||||
endpos = self:GetPos(),
|
||||
filter = self
|
||||
}, self)
|
||||
|
||||
if self:GetNW2Bool("Boost") then
|
||||
local fwd = self:GetForward()
|
||||
phys:SetVelocityInstantaneous(fwd * self.Velo.x)
|
||||
else
|
||||
if tr.Hit then
|
||||
phys:SetVelocityInstantaneous(
|
||||
(self:GetForwardAbs() * self.Velo.x +
|
||||
self:GetRightAbs() * self.Velo.y +
|
||||
self:GetUpAbs() * self.Velo.z) / 2
|
||||
)
|
||||
if self.TakeDamageWall < CurTime() then
|
||||
self:TakeDamage(1)
|
||||
self.TakeDamageWall = CurTime() + 0.2
|
||||
end
|
||||
else
|
||||
phys:SetVelocityInstantaneous(
|
||||
self:GetForwardAbs() * self.Velo.x +
|
||||
self:GetRightAbs() * self.Velo.y +
|
||||
self:GetUpAbs() * self.Velo.z +
|
||||
Vector(0, 0, 15)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
local ratioX = math.Clamp(self.Velo.x / ms, -1, 1)
|
||||
local ratioY = math.Clamp(self.Velo.y / ms, -1, 1)
|
||||
local targetPitch = ratioX * self.MaxPitch
|
||||
local targetRoll = ratioY * self.MaxRoll
|
||||
sang = Angle(targetPitch, ang, targetRoll)
|
||||
|
||||
local b = {}
|
||||
b.secondstoarrive = 1
|
||||
b.pos = self:GetPos()
|
||||
b.angle = sang
|
||||
b.maxangular = 120
|
||||
b.maxangulardamp = 50
|
||||
b.maxspeed = 1000
|
||||
b.maxspeeddamp = 150
|
||||
b.dampfactor = 0.4
|
||||
b.teleportdistance= 0
|
||||
b.deltatime = CurTime() - self.DeltaTime
|
||||
phys:ComputeShadowControl(b)
|
||||
|
||||
if ply:KeyDown(IN_FORWARD) or self:GetNW2Bool("Boost") then
|
||||
if self:GetNW2Bool("Boost") then
|
||||
self.Velo.x = math.Clamp(self.Velo.x + tick * mu * 2, -ms, self.BoostSpeed)
|
||||
else
|
||||
self.Velo.x = math.Clamp(self.Velo.x + tick * mu, -ms, ms)
|
||||
end
|
||||
elseif ply:KeyDown(IN_BACK) then
|
||||
self.Velo.x = math.Clamp(self.Velo.x - tick * mu, -ms, ms)
|
||||
end
|
||||
|
||||
if ply:KeyDown(IN_MOVELEFT) then
|
||||
self.Velo.y = math.Clamp(self.Velo.y - tick * mu, -ms, ms)
|
||||
elseif ply:KeyDown(IN_MOVERIGHT) then
|
||||
self.Velo.y = math.Clamp(self.Velo.y + tick * mu, -ms, ms)
|
||||
end
|
||||
|
||||
if ply:KeyDown(IN_DUCK) then
|
||||
self.Velo.z = math.Clamp(self.Velo.z - tick * mu, -ms, ms)
|
||||
elseif ply:KeyDown(IN_SPEED) then
|
||||
self.Velo.z = math.Clamp(self.Velo.z + tick * mu, -ms, ms)
|
||||
end
|
||||
|
||||
local drag = 1 - math.Clamp(self.LinearDrag * tick, 0, 0.2)
|
||||
self.Velo.x = self.Velo.x * drag
|
||||
self.Velo.y = self.Velo.y * drag
|
||||
self.Velo.z = self.Velo.z * (0.98 * drag)
|
||||
|
||||
if not ply:KeyDown(IN_FORWARD) and not ply:KeyDown(IN_BACK) and not self:GetNW2Bool("Boost") then
|
||||
if math.abs(self.Velo.x) > 5 then
|
||||
if self.Velo.x > 0 then
|
||||
self.Velo.x = math.Clamp(self.Velo.x - tick * mu, -ms, ms)
|
||||
else
|
||||
self.Velo.x = math.Clamp(self.Velo.x + tick * mu, -ms, ms)
|
||||
end
|
||||
else
|
||||
self.Velo.x = 0
|
||||
end
|
||||
end
|
||||
|
||||
if not ply:KeyDown(IN_SPEED) and not ply:KeyDown(IN_DUCK) then
|
||||
if math.abs(self.Velo.z) > 5 then
|
||||
if self.Velo.z > 0 then
|
||||
self.Velo.z = math.Clamp(self.Velo.z - tick * mu, -ms, ms)
|
||||
else
|
||||
self.Velo.z = math.Clamp(self.Velo.z + tick * mu, -ms, ms)
|
||||
end
|
||||
else
|
||||
self.Velo.z = 0
|
||||
end
|
||||
end
|
||||
|
||||
if not ply:KeyDown(IN_MOVELEFT) and not ply:KeyDown(IN_MOVERIGHT) then
|
||||
if math.abs(self.Velo.y) > 5 then
|
||||
if self.Velo.y > 0 then
|
||||
self.Velo.y = math.Clamp(self.Velo.y - tick * mu, -ms, ms)
|
||||
else
|
||||
self.Velo.y = math.Clamp(self.Velo.y + tick * mu, -ms, ms)
|
||||
end
|
||||
else
|
||||
self.Velo.y = 0
|
||||
end
|
||||
end
|
||||
|
||||
local wasBoost = self:GetNW2Bool("Boost")
|
||||
self:SetNW2Bool("Boost", ply:KeyDown(IN_ATTACK))
|
||||
self:SetNW2Bool("Light", ply:KeyDown(IN_ATTACK2))
|
||||
|
||||
if ply:KeyDown(IN_ATTACK) and not wasBoost then
|
||||
self:EmitSound("vehicles/airboat/fan_blade_fullthrottle_loop1.wav", 70, 150, 0.4)
|
||||
self.LastBoostSound = CurTime()
|
||||
elseif not ply:KeyDown(IN_ATTACK) and wasBoost then
|
||||
self:StopSound("vehicles/airboat/fan_blade_fullthrottle_loop1.wav")
|
||||
end
|
||||
|
||||
if self.RotorSound then
|
||||
local spd = phys:GetVelocity():Length()
|
||||
local load = math.Clamp(
|
||||
(math.abs(self.Velo.x) + math.abs(self.Velo.y) + math.abs(self.Velo.z)) / (self.BoostSpeed * 0.6),
|
||||
0, 1
|
||||
)
|
||||
local timeLeft = math.max(self:GetNW2Float("RemoveTime") - CurTime(), 0)
|
||||
local battFrac = math.Clamp(timeLeft / self.BatteryCount, 0, 1)
|
||||
local basePitch = 80 + 45 * load + math.Clamp(spd * 0.025, 0, 35)
|
||||
local baseVol = 0.15 + 0.35 * load
|
||||
basePitch = basePitch * (0.85 + 0.15 * battFrac)
|
||||
|
||||
if battFrac < 0.15 then
|
||||
basePitch = basePitch + math.sin(CurTime() * 15) * 5
|
||||
end
|
||||
|
||||
self.RotorSound:ChangePitch(math.Clamp(basePitch, 55, 180), 0.08)
|
||||
self.RotorSound:ChangeVolume(math.Clamp(baseVol, 0.05, 0.55), 0.08)
|
||||
end
|
||||
|
||||
if self.WindSound then
|
||||
local spd = phys:GetVelocity():Length()
|
||||
local windVol = math.Clamp(spd / 800, 0, 0.4)
|
||||
local windPitch = 80 + math.Clamp(spd * 0.05, 0, 40)
|
||||
self.WindSound:ChangeVolume(windVol, 0.15)
|
||||
self.WindSound:ChangePitch(windPitch, 0.15)
|
||||
end
|
||||
|
||||
if self:Health() <= 0 then
|
||||
local ply2 = self:GetCreator()
|
||||
if IsValid(ply2) then
|
||||
net.Start("MuR_DroneLost")
|
||||
net.Send(ply2)
|
||||
end
|
||||
self:Explode()
|
||||
return true
|
||||
end
|
||||
|
||||
local ply2 = self:GetCreator()
|
||||
if self:GetNW2Float("RemoveTime") < CurTime() or not IsValid(ply2) or not ply2:Alive() or (ply2.GetSVAnim and ply2:GetSVAnim() ~= "") then
|
||||
if IsValid(ply2) then
|
||||
net.Start("MuR_DroneLost")
|
||||
net.Send(ply2)
|
||||
if ply2.ControlDrone == self then
|
||||
ply2.ControlDrone = nil
|
||||
end
|
||||
end
|
||||
self:EmitSound("ambient/machines/machine1_hit2.wav", 70, 80, 0.6)
|
||||
self:Remove()
|
||||
elseif IsValid(ply2) and ply2:KeyDown(IN_RELOAD) and ply2:GetPos():DistToSqr(self:GetPos()) < 50000 then
|
||||
ply2:Give("swep_drone")
|
||||
ply2:SelectWeapon("swep_drone")
|
||||
self:EmitSound("buttons/button14.wav", 60, 100, 0.5)
|
||||
if ply2.ControlDrone == self then
|
||||
ply2.ControlDrone = nil
|
||||
end
|
||||
self:Remove()
|
||||
end
|
||||
|
||||
self.DeltaTime = CurTime()
|
||||
self:NextThink(CurTime())
|
||||
return true
|
||||
end
|
||||
|
||||
function ENT:PhysicsCollide(col)
|
||||
if self.Velo.x > self.MaxSpeed and self:GetNW2Bool("Boost") then
|
||||
self:EmitSound("physics/metal/metal_solid_impact_hard"..math.random(1,5)..".wav", 80, 100, 0.8)
|
||||
self:TakeDamage(self:Health())
|
||||
end
|
||||
end
|
||||
|
||||
-- ВЗРЫВ С HEAT-ПРОБИТИЕМ, ПОРТНУТ ИЗ КРОКУСА (исправлен для самоуничтожения)
|
||||
function ENT:Explode()
|
||||
if self._Exploding then return end
|
||||
self._Exploding = true
|
||||
|
||||
local ply = self:GetCreator()
|
||||
|
||||
if IsValid(ply) then
|
||||
net.Start("MuR_DroneLost")
|
||||
net.Send(ply)
|
||||
|
||||
if ply.ControlDrone == self then
|
||||
ply.ControlDrone = nil
|
||||
end
|
||||
end
|
||||
|
||||
self:OnFinishExplosion()
|
||||
|
||||
timer.Simple(0, function()
|
||||
if IsValid(self) then
|
||||
self:Remove()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function ENT:OnFinishExplosion()
|
||||
local angle_reg = Angle(0,0,0)
|
||||
local angle_dif = Angle(-90,0,0)
|
||||
local pos = self:LocalToWorld(self:OBBCenter())
|
||||
|
||||
if self:WaterLevel() >= 1 then
|
||||
local tr = util.TraceLine({
|
||||
start = pos,
|
||||
endpos = pos + Vector(0,0,9000),
|
||||
filter = self,
|
||||
})
|
||||
local tr2 = util.TraceLine({
|
||||
start = tr.HitPos,
|
||||
endpos = tr.HitPos - Vector(0,0,9000),
|
||||
filter = self,
|
||||
mask = MASK_WATER + CONTENTS_TRANSLUCENT,
|
||||
})
|
||||
if tr2.Hit then
|
||||
ParticleEffect(self.EffectWater, tr2.HitPos,(self.AngEffect and angle_dif or angle_reg), nil)
|
||||
end
|
||||
else
|
||||
local tr = util.TraceLine({
|
||||
start = pos,
|
||||
endpos = pos - Vector(0, 0, self.TraceLength or 150),
|
||||
filter = self,
|
||||
})
|
||||
if tr.HitWorld then
|
||||
ParticleEffect(self.Effect, pos, (self.AngEffect and angle_dif or angle_reg), nil)
|
||||
else
|
||||
ParticleEffect(self.EffectAir, pos, (self.AngEffect and angle_dif or angle_reg), nil)
|
||||
end
|
||||
end
|
||||
|
||||
if self.HEAT then
|
||||
local hitPos = self:GetPos() + self:GetForward() * 20
|
||||
|
||||
local heat = {}
|
||||
heat.Src = hitPos
|
||||
heat.Dir = self:GetForward()
|
||||
heat.Spread = Vector(0,0,0)
|
||||
heat.Force = self.ArmorPenetration + math.Rand(-self.ArmorPenetration * 0.05, self.ArmorPenetration * 0.05)
|
||||
heat.HullSize = self.HEATRadius * 2
|
||||
heat.Damage = self.PenetrationDamage + math.Rand(-self.PenetrationDamage * 0.1, self.PenetrationDamage * 0.1)
|
||||
heat.Velocity = 30000
|
||||
heat.Attacker = self.Owner or self:GetCreator() or self
|
||||
|
||||
if LVS and LVS.FireBullet then
|
||||
LVS:FireBullet(heat)
|
||||
end
|
||||
|
||||
if self.ExplosionRadius > 0 then
|
||||
util.BlastDamage(
|
||||
self,
|
||||
(IsValid(self:GetCreator()) and self:GetCreator()) or self.Attacker or game.GetWorld(),
|
||||
pos,
|
||||
self.ExplosionRadius,
|
||||
self.ExplosionDamage
|
||||
)
|
||||
self:BlastDoors(self, pos, 10)
|
||||
end
|
||||
|
||||
if self.FragCount > 0 then
|
||||
self:Fragmentation(
|
||||
self,
|
||||
pos,
|
||||
10000,
|
||||
self.FragDamage,
|
||||
self.FragRadius,
|
||||
(IsValid(self:GetCreator()) and self:GetCreator()) or self.Attacker or game.GetWorld()
|
||||
)
|
||||
end
|
||||
else
|
||||
if self.ExplosionRadius > 0 then
|
||||
for _, v in pairs(ents.FindInSphere(pos, self.ExplosionRadius)) do
|
||||
if IsValid(v) and not v.SWBombV3 then
|
||||
local dmg = DamageInfo()
|
||||
dmg:SetInflictor(self)
|
||||
dmg:SetDamage(self.ExplosionDamage)
|
||||
dmg:SetDamageType(self.DamageType or DMG_BLAST)
|
||||
dmg:SetAttacker((IsValid(self:GetCreator()) and self:GetCreator()) or self.Attacker or game.GetWorld())
|
||||
v:TakeDamageInfo(dmg)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if self.BlastRadius > 0 then
|
||||
util.BlastDamage(
|
||||
self,
|
||||
(IsValid(self:GetCreator()) and self:GetCreator()) or self.Attacker or game.GetWorld(),
|
||||
pos,
|
||||
self.BlastRadius,
|
||||
self.ExplosionDamage / 2
|
||||
)
|
||||
self:BlastDoors(self, pos, 10)
|
||||
end
|
||||
|
||||
if self.FragCount > 0 then
|
||||
self:Fragmentation(
|
||||
self,
|
||||
pos,
|
||||
10000,
|
||||
self.FragDamage,
|
||||
self.FragRadius,
|
||||
(IsValid(self:GetCreator()) and self:GetCreator()) or self.Attacker or game.GetWorld()
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
if swv3 and swv3.CreateSound then
|
||||
swv3.CreateSound(pos, false, self.ExplosionSound, self.FarExplosionSound, self.DistExplosionSound)
|
||||
else
|
||||
sound.Play(self.ExplosionSound or "ambient/explosions/explode_4.wav", pos, 100, 100, 1)
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:IsDoor(ent)
|
||||
local Class = ent:GetClass()
|
||||
return (Class == "prop_door") or (Class == "prop_door_rotating") or (Class == "func_door") or (Class == "func_door_rotating")
|
||||
end
|
||||
|
||||
function ENT:BlastDoors(blaster, pos, power, range, ignoreVisChecks)
|
||||
for _, door in pairs(ents.FindInSphere(pos, 40 * power * (range or 1))) do
|
||||
if self:IsDoor(door) then
|
||||
local proceed = ignoreVisChecks
|
||||
|
||||
if not proceed then
|
||||
local tr = util.QuickTrace(pos, door:LocalToWorld(door:OBBCenter()) - pos, blaster)
|
||||
proceed = IsValid(tr.Entity) and (tr.Entity == door)
|
||||
end
|
||||
|
||||
if proceed then
|
||||
self:BlastDoor(door, (door:LocalToWorld(door:OBBCenter()) - pos):GetNormalized() * 1000)
|
||||
end
|
||||
end
|
||||
|
||||
if door:GetClass() == "func_breakable_surf" then
|
||||
door:Fire("Break")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:BlastDoor(ent, vel)
|
||||
local Moddel, Pozishun, Ayngul, Muteeriul, Skin = ent:GetModel(), ent:GetPos(), ent:GetAngles(), ent:GetMaterial(), ent:GetSkin()
|
||||
sound.Play("Wood_Crate.Break", Pozishun, 60, 100)
|
||||
sound.Play("Wood_Furniture.Break", Pozishun, 60, 100)
|
||||
ent:Fire("unlock", "", 0)
|
||||
ent:Fire("open", "", 0)
|
||||
ent:SetNoDraw(true)
|
||||
ent:SetNotSolid(true)
|
||||
|
||||
if Moddel and Pozishun and Ayngul then
|
||||
local Replacement = ents.Create("prop_physics")
|
||||
Replacement:SetModel(Moddel)
|
||||
Replacement:SetPos(Pozishun + Vector(0, 0, 1))
|
||||
Replacement:SetAngles(Ayngul)
|
||||
|
||||
if Muteeriul then
|
||||
Replacement:SetMaterial(Muteeriul)
|
||||
end
|
||||
|
||||
if Skin then
|
||||
Replacement:SetSkin(Skin)
|
||||
end
|
||||
|
||||
Replacement:SetModelScale(.9, 0)
|
||||
Replacement:Spawn()
|
||||
Replacement:Activate()
|
||||
|
||||
if vel then
|
||||
Replacement:GetPhysicsObject():SetVelocity(vel)
|
||||
|
||||
timer.Simple(0, function()
|
||||
if IsValid(Replacement) then
|
||||
Replacement:GetPhysicsObject():ApplyForceCenter(vel * 100)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
timer.Simple(3, function()
|
||||
if IsValid(Replacement) then
|
||||
Replacement:SetCollisionGroup(COLLISION_GROUP_WEAPON)
|
||||
end
|
||||
end)
|
||||
|
||||
timer.Simple(30, function()
|
||||
if IsValid(ent) then
|
||||
ent:SetNotSolid(false)
|
||||
ent:SetNoDraw(false)
|
||||
end
|
||||
|
||||
if IsValid(Replacement) then
|
||||
Replacement:Remove()
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:Fragmentation(shooter, origin, fragNum, fragDmg, fragMaxDist, attacker, direction, spread, zReduction)
|
||||
shooter = shooter or game.GetWorld()
|
||||
zReduction = zReduction or 2
|
||||
|
||||
local Spred = Vector(0, 0, 0)
|
||||
local BulletsFired, MaxBullets, disperseTime = 0, self.FragCount, .5
|
||||
|
||||
if fragNum >= 12000 then
|
||||
disperseTime = 2
|
||||
elseif fragNum >= 6000 then
|
||||
disperseTime = 1
|
||||
end
|
||||
|
||||
for i = 1, fragNum do
|
||||
timer.Simple((i / fragNum) * disperseTime, function()
|
||||
local Dir
|
||||
|
||||
if direction and spread then
|
||||
Dir = Vector(direction.x, direction.y, direction.z)
|
||||
Dir = Dir + VectorRand() * math.Rand(0, spread)
|
||||
Dir:Normalize()
|
||||
else
|
||||
Dir = VectorRand()
|
||||
end
|
||||
|
||||
if zReduction then
|
||||
Dir.z = Dir.z / zReduction
|
||||
Dir:Normalize()
|
||||
end
|
||||
|
||||
local Tr = util.QuickTrace(origin, Dir * fragMaxDist, shooter)
|
||||
|
||||
if Tr.Hit and not Tr.HitSky and not Tr.HitWorld and (BulletsFired < MaxBullets) then
|
||||
local LowFrag = (Tr.Entity.IsVehicle and Tr.Entity:IsVehicle()) or Tr.Entity.LFS or Tr.Entity.LVS or Tr.Entity.EZlowFragPlease
|
||||
|
||||
if (not LowFrag) or (LowFrag and math.random(1, 4) == 2) then
|
||||
|
||||
local firer = (IsValid(shooter) and shooter) or game.GetWorld()
|
||||
|
||||
firer:FireBullets({
|
||||
Attacker = attacker,
|
||||
Damage = fragDmg,
|
||||
Force = fragDmg / 8,
|
||||
Num = 1,
|
||||
Src = origin,
|
||||
Tracer = 0,
|
||||
Dir = Dir,
|
||||
Spread = Spred,
|
||||
AmmoType = "Buckshot"
|
||||
})
|
||||
BulletsFired = BulletsFired + 1
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:OnTakeDamage(dmgt)
|
||||
local dmg = dmgt:GetDamage()
|
||||
self:SetHealth(self:Health() - dmg)
|
||||
|
||||
local owner = self:GetCreator()
|
||||
if IsValid(owner) then
|
||||
owner:ViewPunch(AngleRand(-1, 1))
|
||||
local snd = damageSounds[math.random(#damageSounds)]
|
||||
self:EmitSound(snd, 75, math.random(90, 110))
|
||||
|
||||
if self:Health() < self.HealthCount * 0.3 and math.random() < 0.3 then
|
||||
self:EmitSound("ambient/machines/machine1_hit1.wav", 65, math.random(120, 140), 0.4)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
hook.Add("SetupPlayerVisibility", "GpincDroneCam", function(ply, viewEntity)
|
||||
local drone = ply.ControlDrone
|
||||
if IsValid(drone) and drone:GetClass() == "mur_drone_entity" then
|
||||
AddOriginToPVS(drone:GetPos())
|
||||
end
|
||||
end)
|
||||
|
||||
-- приём команды экстренного самоуничтожения
|
||||
net.Receive("MuR_DroneEmergency", function(len, ply)
|
||||
local drone = ply.ControlDrone
|
||||
|
||||
if not IsValid(drone) or drone:GetClass() ~= "mur_drone_entity" then
|
||||
ply.ControlDrone = nil
|
||||
return
|
||||
end
|
||||
|
||||
if not drone.EmergencyKill then
|
||||
drone.EmergencyKill = true
|
||||
drone.EmergencyKillTime = CurTime() + 3
|
||||
drone:SetNWFloat("EmergencyCountdown", drone.EmergencyKillTime)
|
||||
drone:EmitSound("buttons/button17.wav", 75, 100, 0.6)
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,541 @@
|
||||
ENT.Base = "base_gmodentity"
|
||||
ENT.Type = "anim"
|
||||
ENT.PrintName = "FPV дрон"
|
||||
ENT.Spawnable = true
|
||||
ENT.AutomaticFrameAdvance = true
|
||||
|
||||
-- === ПАРАМЕТРЫ, НУЖНЫЕ И СЕРВЕРУ, И КЛИЕНТУ ===
|
||||
ENT.BatteryCount = 60
|
||||
ENT.HealthCount = 2
|
||||
|
||||
-- HEAT / Кумулятивная боеголовка
|
||||
ENT.HEAT = true
|
||||
ENT.HEATRadius = 2
|
||||
ENT.ArmorPenetration = 50000
|
||||
ENT.PenetrationDamage = 2500
|
||||
|
||||
-- Взрыв
|
||||
ENT.ExplosionDamage = 300
|
||||
ENT.ExplosionRadius = 75
|
||||
ENT.BlastRadius = 0
|
||||
|
||||
-- Фрагментация
|
||||
ENT.FragDamage = 25
|
||||
ENT.FragRadius = 300
|
||||
ENT.FragCount = 100
|
||||
|
||||
-- Эффекты
|
||||
ENT.TraceLength = 150
|
||||
ENT.AngEffect = true
|
||||
|
||||
-- LVS эффекты
|
||||
ENT.Effect = "lvs_explosion_small"
|
||||
ENT.EffectAir = "lvs_explosion_small"
|
||||
ENT.EffectWater = "lvs_explosion_small"
|
||||
|
||||
-- LVS звуки
|
||||
ENT.ExplosionSound = "lvs/explosion_small.wav"
|
||||
ENT.FarExplosionSound = "lvs/explosion_small.wav"
|
||||
ENT.DistExplosionSound = "lvs/explosion_small.wav"
|
||||
|
||||
if CLIENT then
|
||||
local droneMat = Material("vgui/gradient-l")
|
||||
local scanlineMat = Material("pp/texturize")
|
||||
local noiseChars = {"█", "▓", "▒", "░", "▄", "▀", "■", "▪"}
|
||||
|
||||
-- Ссылка на активный дрон (локально)
|
||||
local DRONE = nil
|
||||
|
||||
-- Уникальные имена хуков, чтобы можно было безопасно удалять
|
||||
local HOOK_RENDER_SSE = "FPV_Drone_RenderScreenspaceEffects"
|
||||
local HOOK_HUDPAINT = "FPV_Drone_HUDPaint"
|
||||
local HOOK_CREATEMOVE = "FPV_Drone_CreateMove"
|
||||
local HOOK_CALCVIEW = "FPV_Drone_CalcView"
|
||||
local HOOK_CLEANUP_THINK = "FPV_Drone_CleanupThink"
|
||||
local HOOK_EMERGENCY = "FPV_Drone_EmergencyKey"
|
||||
|
||||
local function DrawScanlines(sw, sh, intensity)
|
||||
surface.SetDrawColor(0, 0, 0, intensity * 15)
|
||||
for i = 0, sh, 4 do
|
||||
surface.DrawRect(0, i, sw, 1)
|
||||
end
|
||||
end
|
||||
|
||||
local function DrawGlitchNoise(sw, sh, intensity)
|
||||
if intensity < 0.3 then return end
|
||||
local glitchCount = math.floor((1 - intensity) * 50)
|
||||
surface.SetDrawColor(255, 255, 255, (1 - intensity) * 100)
|
||||
for i = 1, glitchCount do
|
||||
local x = math.random(0, sw)
|
||||
local y = math.random(0, sh)
|
||||
local w = math.random(20, 200)
|
||||
surface.DrawRect(x, y, w, 2)
|
||||
end
|
||||
end
|
||||
|
||||
local function DrawCornerBrackets(x, y, w, h, size, thickness, col)
|
||||
surface.SetDrawColor(col)
|
||||
surface.DrawRect(x, y, size, thickness)
|
||||
surface.DrawRect(x, y, thickness, size)
|
||||
surface.DrawRect(x + w - size, y, size, thickness)
|
||||
surface.DrawRect(x + w - thickness, y, thickness, size)
|
||||
surface.DrawRect(x, y + h - thickness, size, thickness)
|
||||
surface.DrawRect(x, y + h - size, thickness, size)
|
||||
surface.DrawRect(x + w - size, y + h - thickness, size, thickness)
|
||||
surface.DrawRect(x + w - thickness, y + h - size, thickness, size)
|
||||
end
|
||||
|
||||
local function DrawCrosshair(cx, cy, size, gap, thickness, col)
|
||||
surface.SetDrawColor(col)
|
||||
surface.DrawRect(cx - size, cy - thickness/2, size - gap, thickness)
|
||||
surface.DrawRect(cx + gap, cy - thickness/2, size - gap, thickness)
|
||||
surface.DrawRect(cx - thickness/2, cy - size, thickness, size - gap)
|
||||
surface.DrawRect(cx - thickness/2, cy + gap, thickness, size - gap)
|
||||
end
|
||||
|
||||
local function DrawArcSegment(cx, cy, radius, startAng, endAng, thickness, segments, col)
|
||||
surface.SetDrawColor(col)
|
||||
local step = (endAng - startAng) / segments
|
||||
for i = 0, segments - 1 do
|
||||
local a1 = math.rad(startAng + i * step)
|
||||
local a2 = math.rad(startAng + (i + 1) * step)
|
||||
local x1 = cx + math.cos(a1) * radius
|
||||
local y1 = cy + math.sin(a1) * radius
|
||||
local x2 = cx + math.cos(a2) * radius
|
||||
local y2 = cy + math.sin(a2) * radius
|
||||
surface.DrawLine(x1, y1, x2, y2)
|
||||
end
|
||||
end
|
||||
|
||||
local function DrawCircularBar(cx, cy, radius, frac, thickness, bgCol, fgCol)
|
||||
DrawArcSegment(cx, cy, radius, -90, 270, thickness, 32, bgCol)
|
||||
if frac > 0 then
|
||||
DrawArcSegment(cx, cy, radius, -90, -90 + 360 * frac, thickness, math.floor(32 * frac), fgCol)
|
||||
end
|
||||
end
|
||||
|
||||
local function GetDroneRT()
|
||||
local name = "MuR_Drone_LastFrame"
|
||||
return GetRenderTarget(name, ScrW(), ScrH())
|
||||
end
|
||||
|
||||
local lastFrameMat = CreateMaterial("MuR_Drone_LastFrame_Mat", "UnlitGeneric", {
|
||||
["$basetexture"] = "MuR_Drone_LastFrame",
|
||||
["$ignorez"] = 1,
|
||||
["$vertexcolor"] = 1,
|
||||
["$vertexalpha"] = 1
|
||||
})
|
||||
|
||||
local function LoseDrone()
|
||||
surface.PlaySound("ambient/levels/prison/radio_random"..math.random(1,15)..".wav")
|
||||
local startTime = CurTime()
|
||||
|
||||
lastFrameMat:SetTexture("$basetexture", GetDroneRT())
|
||||
|
||||
hook.Add("HUDPaint", "FPV_Drone_LostHUD", function()
|
||||
if not LocalPlayer():Alive() then return end
|
||||
local sw, sh = ScrW(), ScrH()
|
||||
local elapsed = CurTime() - startTime
|
||||
local flicker = math.sin(elapsed * 30) > 0
|
||||
|
||||
surface.SetDrawColor(0, 0, 0, 255)
|
||||
surface.DrawRect(0, 0, sw, sh)
|
||||
|
||||
surface.SetDrawColor(255, 255, 255, 255)
|
||||
surface.SetMaterial(lastFrameMat)
|
||||
surface.DrawTexturedRect(0, 0, sw, sh)
|
||||
|
||||
surface.SetDrawColor(0, 0, 0, math.min(elapsed * 100, 200))
|
||||
surface.DrawRect(0, 0, sw, sh)
|
||||
|
||||
for i = 1, 150 do
|
||||
local px = math.random(0, sw)
|
||||
local py = math.random(0, sh)
|
||||
local ps = math.random(2, 8)
|
||||
local c = math.random(30, 80)
|
||||
surface.SetDrawColor(c, c, c, 255)
|
||||
surface.DrawRect(px, py, ps, ps)
|
||||
end
|
||||
|
||||
DrawScanlines(sw, sh, 3)
|
||||
|
||||
for i = 1, 20 do
|
||||
local y = math.random(0, sh)
|
||||
local w = math.random(sw * 0.3, sw)
|
||||
local x = math.random(0, sw - w)
|
||||
surface.SetDrawColor(40, 40, 45, 200)
|
||||
surface.DrawRect(x, y, w, math.random(1, 5))
|
||||
end
|
||||
|
||||
for i = 1, 2000 do
|
||||
surface.SetDrawColor(255, 255, 255, math.random(10, 80))
|
||||
surface.DrawRect(math.random(0, sw), math.random(0, sh), 1, 1)
|
||||
end
|
||||
|
||||
|
||||
local textCol = flicker and Color(255, 50, 50, 255) or Color(200, 40, 40, 200)
|
||||
draw.SimpleText("▌▌ СОЕДИНЕНИЕ ПОТЕРЯНО ▐▐", "MuR_Font5", sw/2, sh/2 - 30, textCol, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
draw.SimpleText("СВЯЗЬ ПРЕКРАЩЕНА", "MuR_Font2", sw/2, sh/2 + 30, Color(150, 150, 150, 200), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end)
|
||||
|
||||
|
||||
timer.Simple(2, function()
|
||||
hook.Remove("HUDPaint", "FPV_Drone_LostHUD")
|
||||
end)
|
||||
end
|
||||
net.Receive("MuR_DroneLost", LoseDrone)
|
||||
|
||||
local function FPV_ClearHooks()
|
||||
hook.Remove("HUDPaint", HOOK_HUDPAINT)
|
||||
hook.Remove("RenderScreenspaceEffects", HOOK_RENDER_SSE)
|
||||
hook.Remove("CalcView", HOOK_CALCVIEW)
|
||||
hook.Remove("CreateMove", HOOK_CREATEMOVE)
|
||||
end
|
||||
|
||||
|
||||
-- Основной приём дрона: ставим локальную ссылку и создаём HUD/CalcView хуки
|
||||
net.Receive("MuR_DroneCam", function()
|
||||
FPV_ClearHooks()
|
||||
local ent = net.ReadEntity()
|
||||
DRONE = ent
|
||||
|
||||
-- Если дрон невалиден — ничего не делаем
|
||||
if not IsValid(DRONE) then return end
|
||||
|
||||
-- Переменные HUD
|
||||
local pulseAlpha = 0
|
||||
local pulseDir = 1
|
||||
local lastBattBeep = 0
|
||||
local lastDmgBeep = 0
|
||||
local startTime = CurTime()
|
||||
local smoothBatt = 1
|
||||
local smoothHP = 1
|
||||
|
||||
local ppTab = {
|
||||
["$pp_colour_addr"] = 0,
|
||||
["$pp_colour_addg"] = 0.02,
|
||||
["$pp_colour_addb"] = 0,
|
||||
["$pp_colour_brightness"] = -0.03,
|
||||
["$pp_colour_contrast"] = 1.15,
|
||||
["$pp_colour_colour"] = 0.4,
|
||||
["$pp_colour_mulr"] = 0,
|
||||
["$pp_colour_mulg"] = 0.05,
|
||||
["$pp_colour_mulb"] = 0
|
||||
}
|
||||
|
||||
-- RenderScreenspaceEffects
|
||||
hook.Add("RenderScreenspaceEffects", HOOK_RENDER_SSE, function()
|
||||
if not IsValid(DRONE) then return end
|
||||
|
||||
local rt = GetDroneRT()
|
||||
render.CopyRenderTargetToTexture(rt)
|
||||
|
||||
local timeLeft = math.max(DRONE:GetNW2Float("RemoveTime") - CurTime(), 0)
|
||||
local battFrac = math.Clamp(timeLeft / DRONE.BatteryCount, 0, 1)
|
||||
|
||||
if battFrac < 0.2 then
|
||||
ppTab["$pp_colour_addr"] = 0.1 * (1 - battFrac / 0.2)
|
||||
ppTab["$pp_colour_contrast"] = 1.15 + 0.2 * (1 - battFrac / 0.2)
|
||||
else
|
||||
ppTab["$pp_colour_addr"] = 0
|
||||
ppTab["$pp_colour_contrast"] = 1.15
|
||||
end
|
||||
|
||||
DrawColorModify(ppTab)
|
||||
DrawSharpen(0.8, 0.8)
|
||||
end)
|
||||
|
||||
-- HUDPaint
|
||||
hook.Add("HUDPaint", HOOK_HUDPAINT, function()
|
||||
if not IsValid(DRONE) then return end
|
||||
local ent = DRONE
|
||||
local sw, sh = ScrW(), ScrH()
|
||||
local cx, cy = sw / 2, sh / 2
|
||||
local time = CurTime()
|
||||
local elapsed = time - startTime
|
||||
|
||||
pulseAlpha = pulseAlpha + pulseDir * FrameTime() * 400
|
||||
if pulseAlpha >= 255 then pulseDir = -1 pulseAlpha = 255
|
||||
elseif pulseAlpha <= 100 then pulseDir = 1 pulseAlpha = 100 end
|
||||
|
||||
local timeLeft = math.max(ent:GetNW2Float("RemoveTime") - CurTime(), 0)
|
||||
local battFrac = math.Clamp(timeLeft / ent.BatteryCount, 0, 1)
|
||||
local hpFrac = math.Clamp(ent:Health() / ent.HealthCount, 0, 1)
|
||||
|
||||
smoothBatt = Lerp(FrameTime() * 3, smoothBatt, battFrac)
|
||||
smoothHP = Lerp(FrameTime() * 5, smoothHP, hpFrac)
|
||||
|
||||
local vel = ent:GetVelocity():Length()
|
||||
local spd = math.Round(vel * 0.068)
|
||||
local tr = util.TraceLine({start = ent:GetPos(), endpos = ent:GetPos() - Vector(0, 0, 10000), filter = ent})
|
||||
local agl = math.Round(ent:GetPos().z - tr.HitPos.z)
|
||||
local dist2 = LocalPlayer():GetPos():DistToSqr(ent:GetPos())
|
||||
local signal = math.Clamp(1 - (dist2 / (5000 * 5000)), 0, 1)
|
||||
local canDisarm = dist2 < 50000
|
||||
local jamStrength = signal
|
||||
|
||||
if jamStrength < 0.4 then
|
||||
DrawGlitchNoise(sw, sh, jamStrength)
|
||||
end
|
||||
|
||||
if jamStrength < 0.2 then
|
||||
surface.SetDrawColor(255, 255, 255, math.random(20, 60))
|
||||
surface.DrawRect(0, 0, sw, sh)
|
||||
end
|
||||
|
||||
if jamStrength < 0.1 then
|
||||
local shift = math.random(-10, 10)
|
||||
surface.SetDrawColor(255, 255, 255, 40)
|
||||
surface.DrawRect(shift, 0, sw, sh)
|
||||
end
|
||||
|
||||
|
||||
local killTime = ent:GetNWFloat("EmergencyCountdown", 0)
|
||||
|
||||
DrawScanlines(sw, sh, 32)
|
||||
DrawGlitchNoise(sw, sh, signal)
|
||||
|
||||
local frameCol = Color(40, 255, 120, 120)
|
||||
DrawCornerBrackets(40, 40, sw - 80, sh - 80, 60, 3, frameCol)
|
||||
|
||||
local crossCol = Color(40, 255, 120, pulseAlpha)
|
||||
DrawCrosshair(cx, cy, 25, 8, 2, crossCol)
|
||||
|
||||
surface.SetDrawColor(40, 255, 120, 60)
|
||||
DrawArcSegment(cx, cy, 45, 0, 360, 1, 64, Color(40, 255, 120, 40))
|
||||
|
||||
local targetDist = tr.HitPos:Distance(ent:GetPos())
|
||||
if targetDist < 500 then
|
||||
local lockCol = Color(255, 80, 60, pulseAlpha)
|
||||
DrawCrosshair(cx, cy, 35, 5, 3, lockCol)
|
||||
draw.SimpleText(string.format("%.1fm", targetDist * 0.0254), "MuR_Font2", cx, cy + 50, lockCol, TEXT_ALIGN_CENTER, TEXT_ALIGN_TOP)
|
||||
end
|
||||
|
||||
draw.SimpleText("◈ FPV ", "MuR_Font3", 80, 60, Color(40, 255, 120, 255), TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP)
|
||||
draw.SimpleText(string.format("ВРЕМЯ СВЯЗИ %.1fs", elapsed), "MuR_Font1", 80, 95, Color(150, 150, 150, 200), TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP)
|
||||
draw.SimpleText(os.date("%H:%M:%S"), "MuR_Font1", 80, 115, Color(150, 150, 150, 200), TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP)
|
||||
|
||||
local telemX = sw - 80
|
||||
local spdCol = spd > 60 and Color(255, 200, 60) or Color(40, 255, 120)
|
||||
draw.SimpleText(string.format("%03d km/h", spd), "MuR_Font3", telemX, 60, spdCol, TEXT_ALIGN_RIGHT, TEXT_ALIGN_TOP)
|
||||
draw.SimpleText("СКОР", "MuR_Font1", telemX, 95, Color(100, 100, 100), TEXT_ALIGN_RIGHT, TEXT_ALIGN_TOP)
|
||||
|
||||
draw.SimpleText(string.format("%04d m", agl), "MuR_Font3", telemX, 120, Color(40, 255, 120), TEXT_ALIGN_RIGHT, TEXT_ALIGN_TOP)
|
||||
draw.SimpleText("ВЫС", "MuR_Font1", telemX, 155, Color(100, 100, 100), TEXT_ALIGN_RIGHT, TEXT_ALIGN_TOP)
|
||||
|
||||
local sigCol = signal < 0.25 and Color(255, 60, 60) or (signal < 0.5 and Color(255, 200, 60) or Color(40, 255, 120))
|
||||
local sigBars = math.floor(signal * 5)
|
||||
local sigStr = string.rep("▮", sigBars) .. string.rep("▯", 5 - sigBars)
|
||||
draw.SimpleText(sigStr, "MuR_Font2", telemX, 180, sigCol, TEXT_ALIGN_RIGHT, TEXT_ALIGN_TOP)
|
||||
draw.SimpleText(string.format("%d%%", math.Round(signal * 100)), "MuR_Font1", telemX, 210, sigCol, TEXT_ALIGN_RIGHT, TEXT_ALIGN_TOP)
|
||||
|
||||
local barW, barH = 250, 12
|
||||
local barX, barY = 80, sh - 180
|
||||
|
||||
local battCol = smoothBatt < 0.2 and Color(255, 60, 60) or (smoothBatt < 0.4 and Color(255, 200, 60) or Color(40, 255, 120))
|
||||
surface.SetDrawColor(30, 30, 30, 200)
|
||||
surface.DrawRect(barX, barY, barW, barH)
|
||||
surface.SetDrawColor(battCol.r, battCol.g, battCol.b, 255)
|
||||
surface.DrawRect(barX + 2, barY + 2, (barW - 4) * smoothBatt, barH - 4)
|
||||
surface.SetDrawColor(battCol.r, battCol.g, battCol.b, 100)
|
||||
surface.DrawOutlinedRect(barX, barY, barW, barH, 1)
|
||||
draw.SimpleText(string.format("ЗАРЯД %d%%", math.Round(smoothBatt * 100)), "MuR_Font1", barX, barY - 20, battCol, TEXT_ALIGN_LEFT, TEXT_ALIGN_BOTTOM)
|
||||
draw.SimpleText(string.format("%.0fs", timeLeft), "MuR_Font1", barX + barW, barY - 20, Color(150, 150, 150), TEXT_ALIGN_RIGHT, TEXT_ALIGN_BOTTOM)
|
||||
|
||||
barY = sh - 130
|
||||
local hpCol = smoothHP < 0.3 and Color(255, 60, 60) or (smoothHP < 0.6 and Color(255, 200, 60) or Color(40, 255, 120))
|
||||
surface.SetDrawColor(30, 30, 30, 200)
|
||||
surface.DrawRect(barX, barY, barW, barH)
|
||||
surface.SetDrawColor(hpCol.r, hpCol.g, hpCol.b, 255)
|
||||
surface.DrawRect(barX + 2, barY + 2, (barW - 4) * smoothHP, barH - 4)
|
||||
surface.SetDrawColor(hpCol.r, hpCol.g, hpCol.b, 100)
|
||||
surface.DrawOutlinedRect(barX, barY, barW, barH, 1)
|
||||
draw.SimpleText(string.format("ПРОЧНОСТЬ %d%%", math.Round(smoothHP * 100)), "MuR_Font1", barX, barY - 20, hpCol, TEXT_ALIGN_LEFT, TEXT_ALIGN_BOTTOM)
|
||||
|
||||
local ctrlX, ctrlY = sw - 80, sh - 200
|
||||
local ctrlCol = Color(150, 150, 150, 200)
|
||||
|
||||
draw.SimpleText("▶ [J] САМОУНИЧТОЖЕНИЕ", "MuR_Font1", ctrlX, ctrlY, Color(255, 80, 80, 200), TEXT_ALIGN_RIGHT)
|
||||
draw.SimpleText("▶ [ЛКМ] УСКОРЕНИЕ", "MuR_Font1", ctrlX, ctrlY + 22, ctrlCol, TEXT_ALIGN_RIGHT)
|
||||
draw.SimpleText("▲ [SHIFT] ВВЕРХ", "MuR_Font1", ctrlX, ctrlY + 44, ctrlCol, TEXT_ALIGN_RIGHT)
|
||||
draw.SimpleText("▼ [CTRL] ВНИЗ", "MuR_Font1", ctrlX, ctrlY + 66, ctrlCol, TEXT_ALIGN_RIGHT)
|
||||
|
||||
if canDisarm then
|
||||
draw.SimpleText("◉ [R] ВЕРНУТЬ ДРОН", "MuR_Font1", ctrlX, ctrlY + 88, Color(40, 255, 120, 200), TEXT_ALIGN_RIGHT)
|
||||
else
|
||||
draw.SimpleText("✖ СЛИШКОМ ДАЛЕКО", "MuR_Font1", ctrlX, ctrlY + 88, Color(255, 100, 60, 200), TEXT_ALIGN_RIGHT)
|
||||
end
|
||||
|
||||
if killTime > CurTime() then
|
||||
local left = math.max(0, killTime - CurTime())
|
||||
local flash = math.sin(CurTime() * 10) > 0 and 255 or 120
|
||||
|
||||
draw.SimpleText(
|
||||
string.format("САМОУНИЧТОЖЕНИЕ: %.1f", left),
|
||||
"MuR_Font3",
|
||||
cx,
|
||||
260,
|
||||
Color(255, 50, 50, flash),
|
||||
TEXT_ALIGN_CENTER
|
||||
)
|
||||
end
|
||||
|
||||
if ent:GetNW2Bool("Boost") then
|
||||
local boostFlash = math.sin(time * 15) > 0 and 255 or 180
|
||||
draw.SimpleText("◀◀ УСКОРЕНИЕ ▶▶", "MuR_Font2", cx, sh - 100, Color(255, 200, 60, boostFlash), TEXT_ALIGN_CENTER)
|
||||
end
|
||||
|
||||
if smoothBatt < 0.15 then
|
||||
local warnFlash = math.sin(time * 8) > 0 and 255 or 100
|
||||
draw.SimpleText("⚠ НИЗКИЙ ЗАРЯД ⚠", "MuR_Font4", cx, 80, Color(255, 60, 60, warnFlash), TEXT_ALIGN_CENTER)
|
||||
if time > lastBattBeep then
|
||||
surface.PlaySound("buttons/button17.wav")
|
||||
lastBattBeep = time + 0.8
|
||||
end
|
||||
end
|
||||
|
||||
if smoothHP < 0.25 then
|
||||
local dmgFlash = math.sin(time * 6) > 0 and 255 or 100
|
||||
draw.SimpleText("⚠ КРИТИЧЕСКИЕ ПОВРЕЖДЕНИЯ ⚠", "MuR_Font3", cx, 130, Color(255, 120, 60, dmgFlash), TEXT_ALIGN_CENTER)
|
||||
if time > lastDmgBeep then
|
||||
surface.PlaySound("buttons/button10.wav")
|
||||
lastDmgBeep = time + 1.5
|
||||
end
|
||||
end
|
||||
|
||||
if signal < 0.2 then
|
||||
local sigFlash = math.sin(time * 10) > 0 and 255 or 100
|
||||
draw.SimpleText("◢◤ СЛАБЫЙ СИГНАЛ ◢◤", "MuR_Font3", cx, 180, Color(255, 80, 60, sigFlash), TEXT_ALIGN_CENTER)
|
||||
end
|
||||
|
||||
if ent:GetNWBool("Jammed") then
|
||||
local jamStrength = ent:GetNWFloat("JamStrength", 1)
|
||||
if DrawHeavySignalNoise then
|
||||
DrawHeavySignalNoise(jamStrength)
|
||||
end
|
||||
local jamFlash = math.sin(time * 12) > 0 and 255 or 120
|
||||
draw.SimpleText("◢◤ ОБНАРУЖЕНЫ ПОМЕХИ!!! ◢◤", "MuR_Font3", cx, 220, Color(255, 50, 50, jamFlash), TEXT_ALIGN_CENTER)
|
||||
end
|
||||
end)
|
||||
|
||||
-- CreateMove: блокируем прыжок/использование, но не трогаем селектор
|
||||
hook.Add("CreateMove", HOOK_CREATEMOVE, function(cmd)
|
||||
if not IsValid(DRONE) then
|
||||
hook.Remove("CreateMove", HOOK_CREATEMOVE)
|
||||
return
|
||||
end
|
||||
cmd:SetForwardMove(0)
|
||||
cmd:SetSideMove(0)
|
||||
cmd:RemoveKey(IN_JUMP)
|
||||
cmd:RemoveKey(IN_USE)
|
||||
end)
|
||||
|
||||
-- CalcView: камера FPV
|
||||
hook.Add("CalcView", HOOK_CALCVIEW, function(ply, pos, angles, fov)
|
||||
if not IsValid(DRONE) then
|
||||
hook.Remove("CalcView", HOOK_CALCVIEW)
|
||||
hook.Remove("CreateMove", HOOK_CREATEMOVE)
|
||||
hook.Remove("RenderScreenspaceEffects", HOOK_RENDER_SSE)
|
||||
hook.Remove("HUDPaint", HOOK_HUDPAINT)
|
||||
return
|
||||
end
|
||||
|
||||
local ent = DRONE
|
||||
local dronePos = ent:GetPos()
|
||||
local droneAng = ent:GetAngles()
|
||||
local droneVel = ent:GetVelocity()
|
||||
local speed = droneVel:Length()
|
||||
|
||||
local timeLeft = math.max(ent:GetNW2Float("RemoveTime") - CurTime(), 0)
|
||||
local battFrac = math.Clamp(timeLeft / ent.BatteryCount, 0, 1)
|
||||
local hpFrac = math.Clamp(ent:Health() / ent.HealthCount, 0, 1)
|
||||
|
||||
local baseOffset = Vector(-8, 0, -2)
|
||||
local speedOffset = math.Clamp(speed * 0.015, 0, 5)
|
||||
baseOffset.x = baseOffset.x - speedOffset
|
||||
|
||||
local camPos = dronePos + ent:GetForward() * baseOffset.x + ent:GetUp() * baseOffset.z
|
||||
|
||||
local camAng = Angle(droneAng.p, droneAng.y, droneAng.r)
|
||||
local eyeAng = ply:EyeAngles()
|
||||
camAng.p = eyeAng.p
|
||||
|
||||
local time = CurTime()
|
||||
local bobScale = 0.3 + (1 - battFrac) * 0.5
|
||||
camAng.p = camAng.p + math.sin(time * 2.5) * bobScale
|
||||
camAng.r = camAng.r + math.cos(time * 1.8) * bobScale * 0.7
|
||||
|
||||
if hpFrac < 0.3 then
|
||||
local shake = (0.3 - hpFrac) * 3
|
||||
camAng.p = camAng.p + math.Rand(-shake, shake)
|
||||
camAng.r = camAng.r + math.Rand(-shake, shake)
|
||||
end
|
||||
|
||||
if battFrac < 0.15 then
|
||||
local flicker = (0.15 - battFrac) * 10
|
||||
camAng.p = camAng.p + math.sin(time * 20) * flicker
|
||||
end
|
||||
|
||||
local baseFov = 40
|
||||
local speedFovBoost = math.Clamp(speed * 0.025, 0, 15)
|
||||
local dynamicFov = baseFov + speedFovBoost
|
||||
|
||||
if ent:GetNW2Bool("Boost") then
|
||||
dynamicFov = dynamicFov + 10
|
||||
end
|
||||
|
||||
return {
|
||||
origin = camPos,
|
||||
angles = camAng,
|
||||
fov = math.Clamp(dynamicFov, 70, 120),
|
||||
drawviewer = true
|
||||
}
|
||||
end)
|
||||
end)
|
||||
|
||||
local KillKeyHeld = false
|
||||
|
||||
hook.Add("Think", "FPV_Drone_EmergencyKey", function()
|
||||
if not IsValid(DRONE) then KillKeyHeld = false return end
|
||||
|
||||
if input.IsKeyDown(KEY_J) and not KillKeyHeld then
|
||||
KillKeyHeld = true
|
||||
net.Start("MuR_DroneEmergency")
|
||||
net.SendToServer()
|
||||
end
|
||||
|
||||
if not input.IsKeyDown(KEY_J) then
|
||||
KillKeyHeld = false
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
-- Подстраховка: если дрон удалён — очистим ссылку и хуки
|
||||
hook.Add("Think", HOOK_CLEANUP_THINK, function()
|
||||
if DRONE and not IsValid(DRONE) then
|
||||
DRONE = nil
|
||||
-- Удаляем наши хуки, если они остались
|
||||
hook.Remove("CalcView", HOOK_CALCVIEW)
|
||||
hook.Remove("CreateMove", HOOK_CREATEMOVE)
|
||||
hook.Remove("RenderScreenspaceEffects", HOOK_RENDER_SSE)
|
||||
hook.Remove("HUDPaint", HOOK_HUDPAINT)
|
||||
hook.Remove("Think", HOOK_EMERGENCY)
|
||||
hook.Remove("Think", HOOK_CLEANUP_THINK)
|
||||
end
|
||||
end)
|
||||
local function FPV_ClearHooks()
|
||||
hook.Remove("Think", "FPV_DroneEmergencyKey")
|
||||
hook.Remove("Think", "fpv_drone_cleanup_on_remove")
|
||||
hook.Remove("HUDPaint", "FPV_Drone_HUDPaint")
|
||||
hook.Remove("RenderScreenspaceEffects", "FPV_Drone_RenderScreenspaceEffects")
|
||||
hook.Remove("CalcView", "FPV_Drone_CalcView")
|
||||
hook.Remove("CreateMove", "FPV_Drone_CreateMove")
|
||||
end
|
||||
|
||||
hook.Add("EntityRemoved", "FPV_Drone_GlobalCleanup", function(ent)
|
||||
if ent == DRONE then
|
||||
DRONE = nil
|
||||
FPV_ClearHooks()
|
||||
end
|
||||
end)
|
||||
end
|
||||
@@ -0,0 +1 @@
|
||||
include("shared.lua")
|
||||
@@ -0,0 +1,467 @@
|
||||
AddCSLuaFile("shared.lua")
|
||||
include("shared.lua")
|
||||
|
||||
util.AddNetworkString("MuR_DroneCam_Grenade")
|
||||
util.AddNetworkString("MuR_DroneLost_Grenade")
|
||||
util.AddNetworkString("MuR_GrenadeDropped")
|
||||
util.AddNetworkString("MuR_ThermalState")
|
||||
util.AddNetworkString("MuR_DroneEmergency")
|
||||
|
||||
local rotorSounds = {
|
||||
"ambient/machines/spin_loop.wav",
|
||||
"ambient/machines/machine1_hit1.wav"
|
||||
}
|
||||
|
||||
local damageSounds = {
|
||||
"physics/metal/metal_box_impact_bullet1.wav",
|
||||
"physics/metal/metal_box_impact_bullet2.wav",
|
||||
"physics/metal/metal_box_impact_bullet3.wav",
|
||||
"physics/metal/metal_computer_impact_bullet1.wav",
|
||||
"physics/metal/metal_computer_impact_bullet2.wav"
|
||||
}
|
||||
|
||||
local startupSounds = {
|
||||
"buttons/button9.wav",
|
||||
"buttons/button17.wav"
|
||||
}
|
||||
|
||||
function ENT:Initialize()
|
||||
self:SetModel("models/murdered/weapons/drone_ex.mdl")
|
||||
self:ResetSequence("idle")
|
||||
self:SetHealth(self.HealthCount)
|
||||
self:SetGravity(0)
|
||||
self.Velo = {x = 0, y = 0, z = 0}
|
||||
self:SetSolid(SOLID_VPHYSICS)
|
||||
self:PhysicsInit(SOLID_VPHYSICS)
|
||||
self.MultSpeed = 700
|
||||
self.MaxSpeed = 800
|
||||
self.BoostSpeed = 1250
|
||||
self.MaxPitch = 18
|
||||
self.MaxRoll = 22
|
||||
self.LinearDrag = 0.04
|
||||
self.GrenadeDropDelay = 0
|
||||
self.GrenadesLeft = 3
|
||||
self.TakeDamageWall = 0
|
||||
self.DeltaTime = 0
|
||||
self.LastBoostSound = 0
|
||||
self.LastWindSound = 0
|
||||
self:SetNW2Float('RemoveTime', CurTime() + self.BatteryCount)
|
||||
self:SetNW2Int("Grenades", self.GrenadesLeft)
|
||||
|
||||
self:EmitSound(startupSounds[math.random(#startupSounds)], 60, 100, 0.5)
|
||||
|
||||
if CreateSound then
|
||||
self.RotorSound = CreateSound(self, rotorSounds[1])
|
||||
if self.RotorSound then
|
||||
self.RotorSound:PlayEx(0.2, 80)
|
||||
end
|
||||
|
||||
self.WindSound = CreateSound(self, "ambient/wind/wind_snippet2.wav")
|
||||
if self.WindSound then
|
||||
self.WindSound:PlayEx(0, 100)
|
||||
end
|
||||
end
|
||||
|
||||
local ply = self:GetCreator()
|
||||
if IsValid(ply) then
|
||||
local forward = ply:GetForward()
|
||||
forward.z = 0
|
||||
forward = forward:GetNormalized()
|
||||
|
||||
local spawnDist = 80
|
||||
local spawnHeight = 40
|
||||
|
||||
local spawnPos = ply:GetPos() + forward * spawnDist + Vector(0, 0, spawnHeight)
|
||||
|
||||
local tr = util.TraceHull({
|
||||
start = spawnPos,
|
||||
endpos = spawnPos,
|
||||
mins = Vector(-20, -20, -20),
|
||||
maxs = Vector(20, 20, 20),
|
||||
filter = {ply, self}
|
||||
})
|
||||
|
||||
if tr.Hit then
|
||||
spawnPos = spawnPos + forward * 60
|
||||
end
|
||||
|
||||
local ground = util.TraceLine({
|
||||
start = spawnPos,
|
||||
endpos = spawnPos - Vector(0, 0, 5000),
|
||||
filter = self
|
||||
})
|
||||
|
||||
if spawnPos.z < ground.HitPos.z + 30 then
|
||||
spawnPos.z = ground.HitPos.z + 30
|
||||
end
|
||||
|
||||
self:SetPos(spawnPos)
|
||||
self:SetAngles(Angle(0, ply:EyeAngles().y, 0))
|
||||
end
|
||||
|
||||
|
||||
|
||||
timer.Simple(0.1, function()
|
||||
if not IsValid(self) then return end
|
||||
local ply = self:GetCreator()
|
||||
if not IsValid(ply) then return end
|
||||
ply.ControlDrone = self
|
||||
net.Start("MuR_DroneCam_Grenade")
|
||||
net.WriteEntity(self)
|
||||
net.Send(ply)
|
||||
end)
|
||||
end
|
||||
|
||||
function ENT:OnRemove()
|
||||
if self.RotorSound then
|
||||
self.RotorSound:Stop()
|
||||
end
|
||||
if self.WindSound then
|
||||
self.WindSound:Stop()
|
||||
end
|
||||
self:StopSound("ambient/machines/spin_loop.wav")
|
||||
self:StopSound("ambient/wind/wind_snippet2.wav")
|
||||
self:StopSound("vehicles/airboat/fan_blade_fullthrottle_loop1.wav")
|
||||
end
|
||||
|
||||
function ENT:GetForwardAbs()
|
||||
local ang = self:GetAngles()
|
||||
ang.x = 0
|
||||
ang.z = 0
|
||||
return ang:Forward()
|
||||
end
|
||||
|
||||
function ENT:GetRightAbs()
|
||||
local ang = self:GetAngles()
|
||||
ang.x = 0
|
||||
ang.z = 0
|
||||
return ang:Right()
|
||||
end
|
||||
|
||||
function ENT:GetUpAbs()
|
||||
local ang = self:GetAngles()
|
||||
ang.x = 0
|
||||
ang.z = 0
|
||||
return ang:Up()
|
||||
end
|
||||
|
||||
function ENT:Think()
|
||||
if self.EmergencyKill then
|
||||
local phys = self:GetPhysicsObject()
|
||||
if IsValid(phys) then
|
||||
phys:EnableMotion(false)
|
||||
end
|
||||
|
||||
if CurTime() >= (self.EmergencyKillTime or 0) then
|
||||
self:Explode()
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if self:GetNWBool("Jammed") then
|
||||
|
||||
if not self.JamKillTime then
|
||||
self.JamKillTime = CurTime() + 5
|
||||
end
|
||||
|
||||
if CurTime() >= self.JamKillTime then
|
||||
local effect = EffectData()
|
||||
effect:SetOrigin(self:GetPos())
|
||||
util.Effect("Explosion", effect)
|
||||
self:Remove()
|
||||
return true
|
||||
end
|
||||
|
||||
-- ВАЖНО: НЕ ДЕЛАТЬ return здесь!
|
||||
-- Просто продолжаем Think(), чтобы таймер мог дойти до взрыва
|
||||
else
|
||||
-- вышел из зоны — сбрасываем таймер
|
||||
self.JamKillTime = nil
|
||||
end
|
||||
|
||||
local ply = self:GetCreator()
|
||||
if not IsValid(ply) then
|
||||
self:Remove()
|
||||
else
|
||||
local phys = self:GetPhysicsObject()
|
||||
local mu = self.MultSpeed
|
||||
local ms = self.MaxSpeed
|
||||
local deltatime = self.DeltaTime
|
||||
local vel = phys:GetVelocity()
|
||||
local pos = self:GetPos()
|
||||
local ang = ply:EyeAngles().y
|
||||
local sang = Angle(0, ang, 0)
|
||||
ply:SetActiveWeapon(nil)
|
||||
local trace = {start = self:GetPos(), endpos = self:GetPos(), filter = self}
|
||||
local tr = util.TraceEntity(trace, self)
|
||||
|
||||
-- Кнопка G — экстренный выход
|
||||
if not self.EmergencyKill and ply:KeyDown(IN_GRENADE1) then
|
||||
self.EmergencyKill = true
|
||||
self.EmergencyKillTime = CurTime() + 3 -- 3 секунды до взрыва
|
||||
|
||||
-- отправляем время на клиент для HUD
|
||||
self:SetNWFloat("EmergencyCountdown", self.EmergencyKillTime)
|
||||
|
||||
self:EmitSound("buttons/button17.wav", 75, 100, 0.6)
|
||||
end
|
||||
|
||||
|
||||
-- ускоренный расход батареи при включённом теплаке
|
||||
if self:GetNW2Bool("ThermalActive") then
|
||||
local drainMul = 2 -- во сколько раз быстрее жрёт
|
||||
local newRemove = self:GetNW2Float("RemoveTime") - FrameTime() * (drainMul - 1)
|
||||
self:SetNW2Float("RemoveTime", newRemove)
|
||||
end
|
||||
|
||||
if self:GetNW2Bool('Boost') then
|
||||
local fwd = self:GetForward()
|
||||
phys:SetVelocityInstantaneous(fwd * self.Velo.x)
|
||||
else
|
||||
if tr.Hit then
|
||||
phys:SetVelocityInstantaneous((self:GetForwardAbs() * self.Velo.x + self:GetRightAbs() * self.Velo.y + self:GetUpAbs() * self.Velo.z) / 2)
|
||||
if self.TakeDamageWall < CurTime() then
|
||||
self:TakeDamage(1)
|
||||
self.TakeDamageWall = CurTime() + 0.2
|
||||
end
|
||||
else
|
||||
phys:SetVelocityInstantaneous(self:GetForwardAbs() * self.Velo.x + self:GetRightAbs() * self.Velo.y + self:GetUpAbs() * self.Velo.z + Vector(0,0,15))
|
||||
end
|
||||
end
|
||||
|
||||
local ratioX = math.Clamp(self.Velo.x / ms, -1, 1)
|
||||
local ratioY = math.Clamp(self.Velo.y / ms, -1, 1)
|
||||
local targetPitch = ratioX * self.MaxPitch
|
||||
local targetRoll = ratioY * self.MaxRoll
|
||||
sang = Angle(targetPitch, ang, targetRoll)
|
||||
|
||||
local b = {}
|
||||
b.secondstoarrive = 1
|
||||
b.pos = self:GetPos()
|
||||
b.angle = sang
|
||||
b.maxangular = 120
|
||||
b.maxangulardamp = 50
|
||||
b.maxspeed = 1000
|
||||
b.maxspeeddamp = 150
|
||||
b.dampfactor = 0.4
|
||||
b.teleportdistance = 0
|
||||
b.deltatime = CurTime() - self.DeltaTime
|
||||
phys:ComputeShadowControl(b)
|
||||
|
||||
local tick = FrameTime()
|
||||
if ply:KeyDown(IN_FORWARD) then
|
||||
self.Velo.x = math.Clamp(self.Velo.x + tick * mu, -ms, ms)
|
||||
elseif ply:KeyDown(IN_BACK) then
|
||||
self.Velo.x = math.Clamp(self.Velo.x - tick * mu, -ms, ms)
|
||||
end
|
||||
if ply:KeyDown(IN_MOVELEFT) then
|
||||
self.Velo.y = math.Clamp(self.Velo.y - tick * mu, -ms, ms)
|
||||
elseif ply:KeyDown(IN_MOVERIGHT) then
|
||||
self.Velo.y = math.Clamp(self.Velo.y + tick * mu, -ms, ms)
|
||||
end
|
||||
if ply:KeyDown(IN_DUCK) then
|
||||
self.Velo.z = math.Clamp(self.Velo.z - tick * mu, -ms, ms)
|
||||
elseif ply:KeyDown(IN_SPEED) then
|
||||
self.Velo.z = math.Clamp(self.Velo.z + tick * mu, -ms, ms)
|
||||
end
|
||||
|
||||
local drag = 1 - math.Clamp(self.LinearDrag * tick, 0, 0.2)
|
||||
self.Velo.x = self.Velo.x * drag
|
||||
self.Velo.y = self.Velo.y * drag
|
||||
self.Velo.z = self.Velo.z * (0.98 * drag)
|
||||
|
||||
if not ply:KeyDown(IN_FORWARD) and not ply:KeyDown(IN_BACK) and not self:GetNW2Bool('Boost') then
|
||||
if self.Velo.x > 5 or self.Velo.x < -5 then
|
||||
if self.Velo.x > 0 then
|
||||
self.Velo.x = math.Clamp(self.Velo.x - tick * mu, -ms, ms)
|
||||
elseif self.Velo.x < 0 then
|
||||
self.Velo.x = math.Clamp(self.Velo.x + tick * mu, -ms, ms)
|
||||
end
|
||||
else
|
||||
self.Velo.x = 0
|
||||
end
|
||||
end
|
||||
if not ply:KeyDown(IN_SPEED) and not ply:KeyDown(IN_DUCK) then
|
||||
if self.Velo.z > 5 or self.Velo.z < -5 then
|
||||
if self.Velo.z > 0 then
|
||||
self.Velo.z = math.Clamp(self.Velo.z - tick * mu, -ms, ms)
|
||||
elseif self.Velo.z < 0 then
|
||||
self.Velo.z = math.Clamp(self.Velo.z + tick * mu, -ms, ms)
|
||||
end
|
||||
else
|
||||
self.Velo.z = 0
|
||||
end
|
||||
end
|
||||
if not ply:KeyDown(IN_MOVELEFT) and not ply:KeyDown(IN_MOVERIGHT) then
|
||||
if self.Velo.y > 5 or self.Velo.y < -5 then
|
||||
if self.Velo.y > 0 then
|
||||
self.Velo.y = math.Clamp(self.Velo.y - tick * mu, -ms, ms)
|
||||
elseif self.Velo.y < 0 then
|
||||
self.Velo.y = math.Clamp(self.Velo.y + tick * mu, -ms, ms)
|
||||
end
|
||||
else
|
||||
self.Velo.y = 0
|
||||
end
|
||||
end
|
||||
|
||||
-- Removed Boost Logic for Bomber Drone
|
||||
-- local wasBoost = self:GetNW2Bool('Boost')
|
||||
-- self:SetNW2Bool('Boost', ply:KeyDown(IN_ATTACK))
|
||||
|
||||
-- Drop Grenade Logic
|
||||
if ply:KeyDown(IN_ATTACK2) and self.GrenadesLeft > 0 and CurTime() > self.GrenadeDropDelay then
|
||||
self:DropGrenade()
|
||||
self.GrenadeDropDelay = CurTime() + 1.5
|
||||
end
|
||||
|
||||
-- Removed boost sound logic
|
||||
|
||||
|
||||
if self.RotorSound then
|
||||
local spd = phys:GetVelocity():Length()
|
||||
local load = math.Clamp((math.abs(self.Velo.x) + math.abs(self.Velo.y) + math.abs(self.Velo.z)) / (self.BoostSpeed * 0.6), 0, 1)
|
||||
local timeLeft = math.max(self:GetNW2Float('RemoveTime') - CurTime(), 0)
|
||||
local battFrac = math.Clamp(timeLeft / self.BatteryCount, 0, 1)
|
||||
local basePitch = 80 + 45 * load + math.Clamp(spd * 0.025, 0, 35)
|
||||
local baseVol = 0.15 + 0.35 * load
|
||||
basePitch = basePitch * (0.85 + 0.15 * battFrac)
|
||||
|
||||
if battFrac < 0.15 then
|
||||
basePitch = basePitch + math.sin(CurTime() * 15) * 5
|
||||
end
|
||||
|
||||
self.RotorSound:ChangePitch(math.Clamp(basePitch, 55, 180), 0.08)
|
||||
self.RotorSound:ChangeVolume(math.Clamp(baseVol, 0.05, 0.55), 0.08)
|
||||
end
|
||||
|
||||
if self.WindSound then
|
||||
local spd = phys:GetVelocity():Length()
|
||||
local windVol = math.Clamp(spd / 800, 0, 0.4)
|
||||
local windPitch = 80 + math.Clamp(spd * 0.05, 0, 40)
|
||||
self.WindSound:ChangeVolume(windVol, 0.15)
|
||||
self.WindSound:ChangePitch(windPitch, 0.15)
|
||||
end
|
||||
|
||||
local maxDist = 13000 * 13000 -- дальность БИБА ТВОЮ МАТЬ ДАЛЬНОСТЬ!!!
|
||||
local dist2 = ply:GetPos():DistToSqr(self:GetPos())
|
||||
|
||||
if dist2 > maxDist then
|
||||
net.Start("MuR_DroneLost_Grenade")
|
||||
net.Send(ply)
|
||||
|
||||
self:Explode()
|
||||
return true
|
||||
end
|
||||
|
||||
if self:Health() <= 0 then
|
||||
net.Start("MuR_DroneLost_Grenade")
|
||||
net.Send(ply)
|
||||
self:Explode()
|
||||
end
|
||||
if self:GetNW2Float('RemoveTime') < CurTime() or not ply:Alive() or (ply.GetSVAnim and ply:GetSVAnim() ~= "") then
|
||||
net.Start("MuR_DroneLost_Grenade")
|
||||
net.Send(ply)
|
||||
self:EmitSound("ambient/machines/machine1_hit2.wav", 70, 80, 0.6)
|
||||
self:Remove()
|
||||
elseif (ply:KeyDown(IN_RELOAD) and ply:GetPos():DistToSqr(self:GetPos()) < 50000) then
|
||||
ply:Give("swep_drone_grenade")
|
||||
ply:SelectWeapon("swep_drone_grenade")
|
||||
self:EmitSound("buttons/button14.wav", 60, 100, 0.5)
|
||||
self:Remove()
|
||||
end
|
||||
self.DeltaTime = CurTime()
|
||||
self:NextThink(CurTime())
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:PhysicsCollide(col)
|
||||
if self.Velo.x > self.MaxSpeed and self:GetNW2Bool('Boost') then
|
||||
self:EmitSound("physics/metal/metal_solid_impact_hard"..math.random(1,5)..".wav", 80, 100, 0.8)
|
||||
self:TakeDamage(self:Health())
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:Explode()
|
||||
local effect = EffectData()
|
||||
effect:SetOrigin(self:GetPos())
|
||||
util.Effect("Explosion", effect)
|
||||
|
||||
self:EmitSound("ambient/explosions/explode_4.wav", 100, 100, 1)
|
||||
|
||||
self:Remove()
|
||||
end
|
||||
|
||||
function ENT:DropGrenade()
|
||||
if self.GrenadesLeft <= 0 then return end
|
||||
|
||||
self.GrenadesLeft = self.GrenadesLeft - 1
|
||||
self:SetNW2Int("Grenades", self.GrenadesLeft)
|
||||
|
||||
self:EmitSound("weapons/smg1/switch_single.wav", 80, 100)
|
||||
|
||||
local grenade = ents.Create("lvs_item_explosive")
|
||||
grenade:SetPos(self:GetPos() - Vector(0,0,10))
|
||||
grenade:SetAngles(self:GetAngles())
|
||||
grenade:Spawn()
|
||||
grenade:Activate()
|
||||
grenade:Fire("SetTimer", "3")
|
||||
|
||||
local phys = grenade:GetPhysicsObject()
|
||||
if IsValid(phys) then
|
||||
phys:SetVelocity(self:GetVelocity() + Vector(0,0,-50))
|
||||
phys:AddAngleVelocity(VectorRand() * 200)
|
||||
end
|
||||
local ply = self:GetCreator()
|
||||
if IsValid(ply) then
|
||||
net.Start("MuR_GrenadeDropped")
|
||||
net.Send(ply)
|
||||
end
|
||||
|
||||
local physDrone = self:GetPhysicsObject()
|
||||
if IsValid(physDrone) then
|
||||
physDrone:ApplyForceCenter(Vector(0,0,2000))
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:OnTakeDamage(dmgt)
|
||||
local dmg = dmgt:GetDamage()
|
||||
local att = dmgt:GetAttacker()
|
||||
self:SetHealth(self:Health() - dmg)
|
||||
if IsValid(self:GetCreator()) then
|
||||
self:GetCreator():ViewPunch(AngleRand(-1, 1))
|
||||
local snd = damageSounds[math.random(#damageSounds)]
|
||||
self:EmitSound(snd, 75, math.random(90, 110))
|
||||
|
||||
if self:Health() < self.HealthCount * 0.3 and math.random() < 0.3 then
|
||||
self:EmitSound("ambient/machines/machine1_hit1.wav", 65, math.random(120, 140), 0.4)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
net.Receive("MuR_DroneEmergency", function(len, ply)
|
||||
local drone = ply.ControlDrone
|
||||
if not IsValid(drone) then return end
|
||||
|
||||
if not drone.EmergencyKill then
|
||||
drone.EmergencyKill = true
|
||||
drone.EmergencyKillTime = CurTime() + 3
|
||||
drone:SetNWFloat("EmergencyCountdown", drone.EmergencyKillTime)
|
||||
drone:EmitSound("buttons/button17.wav", 75, 100, 0.6)
|
||||
end
|
||||
end)
|
||||
|
||||
net.Receive("MuR_ThermalState", function(len, ply)
|
||||
local drone = ply.ControlDrone
|
||||
if IsValid(drone) then
|
||||
drone:SetNW2Bool("ThermalActive", net.ReadBool())
|
||||
end
|
||||
end)
|
||||
|
||||
hook.Add("SetupPlayerVisibility", "GpincDroneCam_Grenade", function(ply, viewEntity)
|
||||
local drone = ply.ControlDrone
|
||||
if IsValid(drone) and drone:GetClass() == "mur_drone_grenade" then
|
||||
AddOriginToPVS(drone:GetPos())
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,569 @@
|
||||
ENT.Base = "base_gmodentity"
|
||||
ENT.Type = "anim"
|
||||
ENT.PrintName = "Grenade Drone"
|
||||
ENT.Spawnable = true
|
||||
ENT.AutomaticFrameAdvance = true
|
||||
|
||||
ENT.BatteryCount = 300
|
||||
ENT.HealthCount = 10
|
||||
ENT.GrenadeCount = 3
|
||||
|
||||
if CLIENT then
|
||||
local droneRT = nil
|
||||
local droneRTMat = nil
|
||||
|
||||
function GetDroneRT()
|
||||
if not droneRT then
|
||||
droneRT = GetRenderTarget("mur_drone_grenade_rt", ScrW(), ScrH(), false)
|
||||
droneRTMat = CreateMaterial("mur_drone_grenade_rt_mat", "UnlitGeneric", {
|
||||
["$basetexture"] = droneRT:GetName(),
|
||||
["$ignorez"] = 1,
|
||||
["$vertexcolor"] = 1,
|
||||
["$vertexalpha"] = 1
|
||||
})
|
||||
end
|
||||
return droneRT
|
||||
end
|
||||
|
||||
local GrenadeNotifyEnd = 0
|
||||
local GrenadeNotifyMat = Material("posvetka/nizhniya_posvedka.png")
|
||||
|
||||
net.Receive("MuR_GrenadeDropped", function()
|
||||
GrenadeNotifyEnd = CurTime() + 1.5 -- показывать 1.5 секунды
|
||||
end)
|
||||
|
||||
local function DrawSignalNoise(strength)
|
||||
if strength > 1 then return end
|
||||
|
||||
local sw, sh = ScrW(), ScrH()
|
||||
local noise = (1 - strength) * 200
|
||||
local lines = (1 - strength) * 40
|
||||
|
||||
for i = 1, noise do
|
||||
surface.SetDrawColor(255, 255, 255, math.random(10, 40))
|
||||
surface.DrawRect(
|
||||
math.random(0, sw),
|
||||
math.random(0, sh),
|
||||
math.random(1, 3),
|
||||
math.random(1, 3)
|
||||
)
|
||||
end
|
||||
|
||||
for i = 1, lines do
|
||||
local y = math.random(0, sh)
|
||||
surface.SetDrawColor(255, 255, 255, math.random(10, 40))
|
||||
surface.DrawRect(0, y, sw, math.random(1, 3))
|
||||
end
|
||||
|
||||
if strength < 0.3 then
|
||||
local shake = (0.3 - strength) * 4
|
||||
surface.SetDrawColor(255, 255, 255, 20)
|
||||
surface.DrawRect(
|
||||
math.random(-shake, shake),
|
||||
math.random(-shake, shake),
|
||||
sw,
|
||||
sh
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
hook.Add("HUDPaint", "MuR_GrenadeDropImage", function()
|
||||
if GrenadeNotifyEnd <= CurTime() then return end
|
||||
|
||||
local sw, sh = ScrW(), ScrH()
|
||||
local w, h = 282, 152 -- размер картинки
|
||||
|
||||
local timeLeft = GrenadeNotifyEnd - CurTime()
|
||||
local alpha = math.Clamp(timeLeft * 255, 0, 255)
|
||||
|
||||
surface.SetMaterial(GrenadeNotifyMat)
|
||||
surface.SetDrawColor(255, 255, 255, alpha)
|
||||
surface.DrawTexturedRect(sw/2 - w/2, sh/2 - h/2, w, h)
|
||||
end)
|
||||
|
||||
function DrawScanlines(sw, sh, spacing)
|
||||
spacing = spacing or 3
|
||||
surface.SetDrawColor(0, 0, 0, 40)
|
||||
for y = 0, sh, spacing do
|
||||
surface.DrawLine(0, y, sw, y)
|
||||
end
|
||||
end
|
||||
|
||||
function DrawGlitchNoise(sw, sh, strength)
|
||||
strength = strength or 1
|
||||
local count = math.floor(80 * strength)
|
||||
for i = 1, count do
|
||||
local x = math.random(0, sw)
|
||||
local y = math.random(0, sh)
|
||||
local w = math.random(1, 4)
|
||||
local h = math.random(1, 4)
|
||||
local c = math.random(80, 140)
|
||||
surface.SetDrawColor(c, c, c, 80)
|
||||
surface.DrawRect(x, y, w, h)
|
||||
end
|
||||
end
|
||||
|
||||
function DrawCornerBrackets(x, y, w, h, len, thick, col)
|
||||
col = col or color_white
|
||||
thick = thick or 2
|
||||
len = len or 20
|
||||
surface.SetDrawColor(col)
|
||||
|
||||
surface.DrawRect(x, y, len, thick)
|
||||
surface.DrawRect(x, y, thick, len)
|
||||
surface.DrawRect(x + w - len, y, len, thick)
|
||||
surface.DrawRect(x + w - thick, y, thick, len)
|
||||
surface.DrawRect(x, y + h - thick, len, thick)
|
||||
surface.DrawRect(x, y + h - len, thick, len)
|
||||
surface.DrawRect(x + w - len, y + h - thick, len, thick)
|
||||
surface.DrawRect(x + w - thick, y + h - len, thick, len)
|
||||
end
|
||||
|
||||
function DrawCrosshair(cx, cy, size, gap, thick, col)
|
||||
col = col or color_white
|
||||
size = size or 20
|
||||
gap = gap or 4
|
||||
thick = thick or 2
|
||||
surface.SetDrawColor(col)
|
||||
|
||||
surface.DrawRect(cx - size, cy - thick / 2, size - gap, thick)
|
||||
surface.DrawRect(cx + gap, cy - thick / 2, size - gap, thick)
|
||||
surface.DrawRect(cx - thick / 2, cy - size, thick, size - gap)
|
||||
surface.DrawRect(cx - thick / 2, cy + gap, thick, size - gap)
|
||||
end
|
||||
|
||||
function DrawArcSegment(cx, cy, radius, startAng, endAng, thick, steps, col)
|
||||
col = col or color_white
|
||||
thick = thick or 2
|
||||
steps = steps or 32
|
||||
surface.SetDrawColor(col)
|
||||
|
||||
local step = (endAng - startAng) / steps
|
||||
for i = 0, steps - 1 do
|
||||
local a1 = math.rad(startAng + step * i)
|
||||
local a2 = math.rad(startAng + step * (i + 1))
|
||||
local x1, y1 = cx + math.cos(a1) * radius, cy + math.sin(a1) * radius
|
||||
local x2, y2 = cx + math.cos(a2) * radius, cy + math.sin(a2) * radius
|
||||
surface.DrawLine(x1, y1, x2, y2)
|
||||
end
|
||||
end
|
||||
|
||||
local DRONE = nil
|
||||
local Thermal = false
|
||||
local ThermalKeyHeld = false
|
||||
local Zoom = 1
|
||||
|
||||
local DefMats = {}
|
||||
local DefClrs = {}
|
||||
local DoXRay = false
|
||||
|
||||
local FLIR = {
|
||||
["$pp_colour_addr"] = -.3,
|
||||
["$pp_colour_addg"] = -.4,
|
||||
["$pp_colour_addb"] = -.4,
|
||||
["$pp_colour_brightness"] = .1,
|
||||
["$pp_colour_contrast"] = 1, -- да-да, мой флир, биба
|
||||
["$pp_colour_colour"] = 0,
|
||||
["$pp_colour_mulr"] = 0,
|
||||
["$pp_colour_mulg"] = 0,
|
||||
["$pp_colour_mulb"] = 0,
|
||||
}
|
||||
|
||||
net.Receive("MuR_DroneCam_Grenade", function()
|
||||
local ent = net.ReadEntity()
|
||||
DRONE = ent
|
||||
Thermal = false
|
||||
Zoom = 1
|
||||
|
||||
local pulseAlpha = 0
|
||||
local pulseDir = 1
|
||||
local lastBattBeep = 0
|
||||
local lastDmgBeep = 0
|
||||
local startTime = CurTime()
|
||||
local smoothBatt = 1
|
||||
local smoothHP = 1
|
||||
local ppTab = {
|
||||
["$pp_colour_addr"] = 0.05,
|
||||
["$pp_colour_addg"] = 0.02,
|
||||
["$pp_colour_addb"] = 0,
|
||||
["$pp_colour_brightness"] = -0.03,
|
||||
["$pp_colour_contrast"] = 1.15,
|
||||
["$pp_colour_colour"] = 0.5,
|
||||
["$pp_colour_mulr"] = 0.05,
|
||||
["$pp_colour_mulg"] = 0.05,
|
||||
["$pp_colour_mulb"] = 0
|
||||
}
|
||||
|
||||
hook.Add("RenderScreenspaceEffects", "DroneCam", function()
|
||||
if not IsValid(ent) then return end
|
||||
|
||||
local rt = GetDroneRT()
|
||||
render.CopyRenderTargetToTexture(rt)
|
||||
|
||||
local timeLeft = math.max(ent:GetNW2Float('RemoveTime') - CurTime(), 0)
|
||||
local battFrac = math.Clamp(timeLeft / ent.BatteryCount, 0, 1)
|
||||
|
||||
if battFrac < 0.2 then
|
||||
ppTab["$pp_colour_addr"] = 0.1 * (1 - battFrac / 0.2)
|
||||
ppTab["$pp_colour_contrast"] = 1.15 + 0.2 * (1 - battFrac / 0.2)
|
||||
else
|
||||
ppTab["$pp_colour_addr"] = 0.05
|
||||
ppTab["$pp_colour_contrast"] = 1.15
|
||||
end
|
||||
|
||||
DrawColorModify(ppTab)
|
||||
DrawSharpen(0.8, 0.8)
|
||||
end)
|
||||
|
||||
hook.Add("HUDPaint", "DroneCam", function()
|
||||
if not IsValid(ent) then return end
|
||||
local sw, sh = ScrW(), ScrH()
|
||||
local cx, cy = sw / 2, sh / 2
|
||||
local time = CurTime()
|
||||
local elapsed = time - startTime
|
||||
|
||||
pulseAlpha = pulseAlpha + pulseDir * FrameTime() * 400
|
||||
if pulseAlpha >= 255 then pulseDir = -1 pulseAlpha = 255
|
||||
elseif pulseAlpha <= 100 then pulseDir = 1 pulseAlpha = 100 end
|
||||
|
||||
local timeLeft = math.max(ent:GetNW2Float('RemoveTime') - CurTime(), 0)
|
||||
local battFrac = math.Clamp(timeLeft / ent.BatteryCount, 0, 1)
|
||||
local hpFrac = math.Clamp(ent:Health() / ent.HealthCount, 0, 1)
|
||||
|
||||
smoothBatt = Lerp(FrameTime() * 3, smoothBatt, battFrac)
|
||||
smoothHP = Lerp(FrameTime() * 5, smoothHP, hpFrac)
|
||||
|
||||
local vel = ent:GetVelocity():Length()
|
||||
local spd = math.Round(vel * 0.068)
|
||||
local tr = util.TraceLine({start = ent:GetPos(), endpos = ent:GetPos() - Vector(0, 0, 10000), filter = ent})
|
||||
local agl = math.Round(ent:GetPos().z - tr.HitPos.z)
|
||||
local dist2 = LocalPlayer():GetPos():DistToSqr(ent:GetPos())
|
||||
local signal = math.Clamp(1 - (dist2 / (10000 * 10000)), 0, 1)
|
||||
local canDisarm = dist2 < 50000
|
||||
local killTime = ent:GetNWFloat("EmergencyCountdown", 0)
|
||||
|
||||
DrawScanlines(sw, sh, 32)
|
||||
DrawGlitchNoise(sw, sh, signal)
|
||||
|
||||
local frameCol = Color(255, 150, 40, 120)
|
||||
DrawCornerBrackets(40, 40, sw - 80, sh - 80, 60, 3, frameCol)
|
||||
|
||||
local crossCol = Color(255, 150, 40, pulseAlpha)
|
||||
DrawCrosshair(cx, cy, 25, 8, 2, crossCol)
|
||||
|
||||
surface.SetDrawColor(255, 50, 50, 100)
|
||||
DrawArcSegment(cx, cy, 60, 0, 360, 2, 64, Color(255, 50, 50, 50))
|
||||
if tr.Hit then
|
||||
local screenData = tr.HitPos:ToScreen()
|
||||
if screenData.visible then
|
||||
surface.SetDrawColor(255, 0, 0, 200)
|
||||
surface.DrawRect(screenData.x - 2, screenData.y - 2, 4, 4)
|
||||
draw.SimpleText("⚠ МЕСТО ПАДЕНИЯ", "MuR_Font1", screenData.x, screenData.y - 15, Color(255, 50, 50, 200), TEXT_ALIGN_CENTER)
|
||||
end
|
||||
end
|
||||
|
||||
local targetDist = tr.HitPos:Distance(ent:GetPos())
|
||||
if targetDist < 500 then
|
||||
local lockCol = Color(255, 80, 60, pulseAlpha)
|
||||
DrawCrosshair(cx, cy, 35, 5, 3, lockCol)
|
||||
draw.SimpleText(string.format("%.1fm", targetDist * 0.0254), "MuR_Font2", cx, cy + 50, lockCol, TEXT_ALIGN_CENTER, TEXT_ALIGN_TOP)
|
||||
end
|
||||
|
||||
draw.SimpleText("◈ ДРОН СО СБРОСОМ", "MuR_Font3", 80, 60, Color(255, 150, 40, 255), TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP)
|
||||
draw.SimpleText(string.format("ВРЕМЯ РАБОТЫ %.1fs", elapsed), "MuR_Font1", 80, 95, Color(150, 150, 150, 200), TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP)
|
||||
draw.SimpleText(os.date("%H:%M:%S"), "MuR_Font1", 80, 115, Color(150, 150, 150, 200), TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP)
|
||||
|
||||
local grenadesLeft = ent:GetNW2Int("Grenades", 3)
|
||||
draw.SimpleText(string.format("БОЕКОМПЛЕКТ: %d/3", grenadesLeft), "MuR_Font3", 80, 140, grenadesLeft > 0 and Color(255, 50, 50) or Color(100, 100, 100), TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP)
|
||||
|
||||
local telemX = sw - 80
|
||||
local spdCol = spd > 60 and Color(255, 200, 60) or Color(255, 150, 40)
|
||||
draw.SimpleText(string.format("%03d km/h", spd), "MuR_Font3", telemX, 60, spdCol, TEXT_ALIGN_RIGHT, TEXT_ALIGN_TOP)
|
||||
draw.SimpleText("СКОР", "MuR_Font1", telemX, 95, Color(100, 100, 100), TEXT_ALIGN_RIGHT, TEXT_ALIGN_TOP)
|
||||
|
||||
draw.SimpleText(string.format("%04d m", agl), "MuR_Font3", telemX, 120, Color(255, 150, 40), TEXT_ALIGN_RIGHT, TEXT_ALIGN_TOP)
|
||||
draw.SimpleText("ВЫС", "MuR_Font1", telemX, 155, Color(100, 100, 100), TEXT_ALIGN_RIGHT, TEXT_ALIGN_TOP)
|
||||
|
||||
local sigCol = signal < 0.25 and Color(255, 60, 60) or (signal < 0.5 and Color(255, 200, 60) or Color(255, 150, 40))
|
||||
local sigBars = math.floor(signal * 5)
|
||||
local sigStr = string.rep("▮", sigBars) .. string.rep("▯", 5 - sigBars)
|
||||
draw.SimpleText(sigStr, "MuR_Font2", telemX, 180, sigCol, TEXT_ALIGN_RIGHT, TEXT_ALIGN_TOP)
|
||||
draw.SimpleText(string.format("%d%%", math.Round(signal * 100)), "MuR_Font1", telemX, 210, sigCol, TEXT_ALIGN_RIGHT, TEXT_ALIGN_TOP)
|
||||
|
||||
local barW, barH = 250, 12
|
||||
local barX, barY = 80, sh - 180
|
||||
|
||||
local battCol = smoothBatt < 0.2 and Color(255, 60, 60) or (smoothBatt < 0.4 and Color(255, 200, 60) or Color(255, 150, 40))
|
||||
surface.SetDrawColor(30, 30, 30, 200)
|
||||
surface.DrawRect(barX, barY, barW, barH)
|
||||
surface.SetDrawColor(battCol.r, battCol.g, battCol.b, 255)
|
||||
surface.DrawRect(barX + 2, barY + 2, (barW - 4) * smoothBatt, barH - 4)
|
||||
surface.SetDrawColor(battCol.r, battCol.g, battCol.b, 100)
|
||||
surface.DrawOutlinedRect(barX, barY, barW, barH, 1)
|
||||
draw.SimpleText(string.format("ЗАРЯД %d%%", math.Round(smoothBatt * 100)), "MuR_Font1", barX, barY - 20, battCol, TEXT_ALIGN_LEFT, TEXT_ALIGN_BOTTOM)
|
||||
draw.SimpleText(string.format("%.0fs", timeLeft), "MuR_Font1", barX + barW, barY - 20, Color(150, 150, 150), TEXT_ALIGN_RIGHT, TEXT_ALIGN_BOTTOM)
|
||||
|
||||
barY = sh - 130
|
||||
local hpCol = smoothHP < 0.3 and Color(255, 60, 60) or (smoothHP < 0.6 and Color(255, 200, 60) or Color(255, 150, 40))
|
||||
surface.SetDrawColor(30, 30, 30, 200)
|
||||
surface.DrawRect(barX, barY, barW, barH)
|
||||
surface.SetDrawColor(hpCol.r, hpCol.g, hpCol.b, 255)
|
||||
surface.DrawRect(barX + 2, barY + 2, (barW - 4) * smoothHP, barH - 4)
|
||||
surface.SetDrawColor(hpCol.r, hpCol.g, hpCol.b, 100)
|
||||
surface.DrawOutlinedRect(barX, barY, barW, barH, 1)
|
||||
draw.SimpleText(string.format("ПРОЧНОСТЬ %d%%", math.Round(smoothHP * 100)), "MuR_Font1", barX, barY - 20, hpCol, TEXT_ALIGN_LEFT, TEXT_ALIGN_BOTTOM)
|
||||
|
||||
local ctrlX, ctrlY = sw - 80, sh - 200
|
||||
local ctrlCol = Color(150, 150, 150, 200)
|
||||
draw.SimpleText("▶ [J] САМОУНИЧТОЖЕНИЕ", "MuR_Font1", ctrlX, ctrlY, Color(255, 80, 80, 200), TEXT_ALIGN_RIGHT)
|
||||
draw.SimpleText("▶ [ALT] ТЕПЛОВИЗОР", "MuR_Font1", ctrlX, ctrlY + 22, ctrlCol, TEXT_ALIGN_RIGHT)
|
||||
draw.SimpleText("▼ [ПКМ] СБРОСИТЬ ГРАНАТУ", "MuR_Font1", ctrlX, ctrlY + 44, Color(255, 80, 60, 200), TEXT_ALIGN_RIGHT)
|
||||
draw.SimpleText("▲ [SHIFT] ВВЕРХ", "MuR_Font1", ctrlX, ctrlY + 66, ctrlCol, TEXT_ALIGN_RIGHT)
|
||||
draw.SimpleText("▼ [CTRL] ВНИЗ", "MuR_Font1", ctrlX, ctrlY + 88, ctrlCol, TEXT_ALIGN_RIGHT)
|
||||
|
||||
local rY = ctrlY + 110
|
||||
if canDisarm then
|
||||
draw.SimpleText("◉ [R] ВЕРНУТЬ ДРОН", "MuR_Font1", ctrlX, rY, Color(40, 255, 120, 200), TEXT_ALIGN_RIGHT)
|
||||
else
|
||||
draw.SimpleText("✖ СЛИШКОМ ДАЛЕКО", "MuR_Font1", ctrlX, rY, Color(255, 100, 60, 200), TEXT_ALIGN_RIGHT)
|
||||
end
|
||||
|
||||
if ent:GetNW2Bool('Boost') then
|
||||
local boostFlash = math.sin(time * 15) > 0 and 255 or 180
|
||||
draw.SimpleText("◀◀ УСКОРЕНИЕ ▶▶", "MuR_Font2", cx, sh - 100, Color(255, 200, 60, boostFlash), TEXT_ALIGN_CENTER)
|
||||
end
|
||||
|
||||
if smoothBatt < 0.15 then
|
||||
local warnFlash = math.sin(time * 8) > 0 and 255 or 100
|
||||
draw.SimpleText("⚠ НИЗКИЙ ЗАРЯД ⚠", "MuR_Font4", cx, 80, Color(255, 60, 60, warnFlash), TEXT_ALIGN_CENTER)
|
||||
if time > lastBattBeep then
|
||||
surface.PlaySound("buttons/button17.wav")
|
||||
lastBattBeep = time + 0.8
|
||||
end
|
||||
end
|
||||
|
||||
if smoothHP < 0.25 then
|
||||
local dmgFlash = math.sin(time * 6) > 0 and 255 or 100
|
||||
draw.SimpleText("⚠ КРИТИЧЕСКИЕ ПОВРЕЖДЕНИЯ ⚠", "MuR_Font3", cx, 130, Color(255, 120, 60, dmgFlash), TEXT_ALIGN_CENTER)
|
||||
if time > lastDmgBeep then
|
||||
surface.PlaySound("buttons/button10.wav")
|
||||
lastDmgBeep = time + 1.5
|
||||
end
|
||||
end
|
||||
|
||||
if signal < 0.2 then
|
||||
local sigFlash = math.sin(time * 10) > 0 and 255 or 100
|
||||
draw.SimpleText("◢◤ СЛАБЫЙ СИГНАЛ ◢◤", "MuR_Font3", cx, 180, Color(255, 80, 60, sigFlash), TEXT_ALIGN_CENTER)
|
||||
end
|
||||
DrawSignalNoise(signal)
|
||||
if ent:GetNWBool("Jammed") then
|
||||
local jamStrength = ent:GetNWFloat("JamStrength", 1)
|
||||
|
||||
DrawHeavySignalNoise(jamStrength)
|
||||
|
||||
local jamFlash = math.sin(time * 8) > 0 and 255 or 120
|
||||
draw.SimpleText(
|
||||
"◢◤ ОБНАРУЖЕНЫ ПОМЕХИ ◢◤",
|
||||
"MuR_Font3",
|
||||
cx,
|
||||
220,
|
||||
Color(255, 80, 60, jamFlash),
|
||||
TEXT_ALIGN_CENTER
|
||||
)
|
||||
end
|
||||
|
||||
if killTime > CurTime() then
|
||||
local left = math.max(0, killTime - CurTime())
|
||||
local flash = math.sin(CurTime() * 10) > 0 and 255 or 120
|
||||
|
||||
draw.SimpleText(
|
||||
string.format("САМОУНИЧТОЖЕНИЕ: %.1f", left),
|
||||
"MuR_Font3",
|
||||
cx,
|
||||
260,
|
||||
Color(255, 50, 50, flash),
|
||||
TEXT_ALIGN_CENTER
|
||||
)
|
||||
end
|
||||
end)
|
||||
|
||||
hook.Add("CreateMove", "DroneCam", function(cmd)
|
||||
cmd:SetForwardMove(0)
|
||||
cmd:SetSideMove(0)
|
||||
cmd:RemoveKey(IN_JUMP)
|
||||
cmd:RemoveKey(IN_USE)
|
||||
end)
|
||||
|
||||
hook.Add("CalcView", "DroneCam", function(ply, pos, angles, fov)
|
||||
if not IsValid(ent) then
|
||||
hook.Remove("CalcView", "DroneCam")
|
||||
hook.Remove("CreateMove", "DroneCam")
|
||||
hook.Remove("RenderScreenspaceEffects", "DroneCam")
|
||||
hook.Remove("HUDPaint", "DroneCam")
|
||||
return
|
||||
end
|
||||
|
||||
local dPos = ent:GetPos()
|
||||
local dAng = ent:GetAngles()
|
||||
local vel = ent:GetVelocity():Length()
|
||||
|
||||
local offset = Vector(-8 - math.Clamp(vel * 0.015, 0, 5), 0, -2)
|
||||
local camPos = dPos + ent:GetForward() * offset.x + ent:GetUp() * offset.z
|
||||
|
||||
local camAng = Angle(dAng.p, dAng.y, dAng.r)
|
||||
camAng.p = ply:EyeAngles().p
|
||||
|
||||
local dynFov = 40 + math.Clamp(vel * 0.025, 0, 15)
|
||||
dynFov = Lerp(Zoom, 20, dynFov)
|
||||
|
||||
return {
|
||||
origin = camPos,
|
||||
angles = camAng,
|
||||
fov = math.Clamp(dynFov, 20, 120),
|
||||
drawviewer = true
|
||||
}
|
||||
end)
|
||||
end)
|
||||
|
||||
hook.Add("Think", "mur_drone_ThermalToggle", function()
|
||||
if not IsValid(DRONE) then return end
|
||||
if DRONE:GetClass() ~= "mur_drone_grenade" then return end
|
||||
|
||||
if input.IsKeyDown(KEY_LALT) and not ThermalKeyHeld then
|
||||
ThermalKeyHeld = true
|
||||
Thermal = not Thermal
|
||||
|
||||
surface.PlaySound(Thermal and "sw/misc/nv_on.wav" or "sw/misc/nv_off.wav")
|
||||
net.Start("MuR_ThermalState")
|
||||
net.WriteBool(Thermal)
|
||||
net.SendToServer()
|
||||
end
|
||||
|
||||
if not input.IsKeyDown(KEY_LALT) then
|
||||
ThermalKeyHeld = false
|
||||
end
|
||||
end)
|
||||
|
||||
local function ApplyXRay()
|
||||
for _, v in ipairs(ents.GetAll()) do
|
||||
if not v:GetModel() then continue end
|
||||
if string.sub(v:GetModel(), -3) ~= "mdl" then continue end
|
||||
|
||||
if v:IsPlayer() then
|
||||
elseif v:IsNPC() then
|
||||
elseif v.LVS or v.LFS then
|
||||
else
|
||||
continue
|
||||
end
|
||||
|
||||
local col = v:GetColor()
|
||||
if col.a <= 0 then continue end
|
||||
|
||||
if not DefClrs[v] then
|
||||
DefClrs[v] = Color(col.r, col.g, col.b, col.a)
|
||||
end
|
||||
|
||||
local entmat = v:GetMaterial()
|
||||
if not DefMats[v] then
|
||||
DefMats[v] = entmat
|
||||
end
|
||||
|
||||
v:SetColor(Color(255,255,255,255))
|
||||
v:SetMaterial("xray/living")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
hook.Add("RenderScene", "mur_drone_FLIR_Apply", function()
|
||||
if not Thermal then return end
|
||||
if not IsValid(DRONE) then return end
|
||||
if DRONE:GetClass() ~= "mur_drone_grenade" then return end
|
||||
|
||||
if not DoXRay then
|
||||
DoXRay = true
|
||||
ApplyXRay()
|
||||
end
|
||||
end)
|
||||
|
||||
local function RenderFLIR()
|
||||
DrawColorModify(FLIR)
|
||||
DrawBloom(0,1,1,1,0,0,0,0,0)
|
||||
DrawMotionBlur(FrameTime() * 50, 0.5, 0.05)
|
||||
DrawBokehDOF(0.15, 1, 12)
|
||||
end
|
||||
|
||||
hook.Add("HUDPaintBackground", "mur_drone_FLIR_Effects", function()
|
||||
if not Thermal then return end
|
||||
if not IsValid(DRONE) then return end
|
||||
if DRONE:GetClass() ~= "mur_drone_grenade" then return end
|
||||
|
||||
RenderFLIR()
|
||||
end)
|
||||
|
||||
hook.Add("Think", "mur_drone_FLIR_AutoOff", function()
|
||||
if Thermal and not IsValid(DRONE) then
|
||||
Thermal = false
|
||||
end
|
||||
|
||||
if not Thermal and DoXRay then
|
||||
DoXRay = false
|
||||
|
||||
for ent, mat in pairs(DefMats) do
|
||||
if IsValid(ent) then ent:SetMaterial(mat) end
|
||||
end
|
||||
for ent, clr in pairs(DefClrs) do
|
||||
if IsValid(ent) then
|
||||
ent:SetRenderMode(RENDERMODE_TRANSALPHA)
|
||||
ent:SetColor(Color(clr.r, clr.g, clr.b, clr.a))
|
||||
end
|
||||
end
|
||||
|
||||
DefMats = {}
|
||||
DefClrs = {}
|
||||
end
|
||||
end)
|
||||
|
||||
hook.Add("Think", "mur_drone_Zoom", function()
|
||||
if not IsValid(DRONE) then return end
|
||||
if DRONE:GetClass() ~= "mur_drone_grenade" then return end
|
||||
|
||||
local delta = input.GetAnalogValue(ANALOG_MOUSE_WHEEL)
|
||||
|
||||
if delta ~= 0 then
|
||||
Zoom = Zoom - delta * 0.05
|
||||
|
||||
Zoom = math.Clamp(Zoom, 0, 1)
|
||||
end
|
||||
end)
|
||||
|
||||
hook.Add("Think", "DroneEmergencyKey", function()
|
||||
if not IsValid(DRONE) then return end
|
||||
if DRONE:GetClass() ~= "mur_drone_grenade" then return end
|
||||
|
||||
if input.IsKeyDown(KEY_J) and not DRONE._KillKeyHeld then
|
||||
DRONE._KillKeyHeld = true
|
||||
|
||||
net.Start("MuR_DroneEmergency")
|
||||
net.SendToServer()
|
||||
end
|
||||
|
||||
if not input.IsKeyDown(KEY_J) then
|
||||
DRONE._KillKeyHeld = false
|
||||
end
|
||||
end)
|
||||
|
||||
hook.Add("PlayerBindPress", "mur_drone_ZoomBlock", function(ply, bind, pressed)
|
||||
if not IsValid(DRONE) then return end
|
||||
if DRONE:GetClass() ~= "mur_drone_grenade" then return end
|
||||
|
||||
if bind == "invprev" then
|
||||
Zoom = math.Clamp(Zoom - 0.1, 0, 1)
|
||||
return true
|
||||
end
|
||||
|
||||
if bind == "invnext" then
|
||||
Zoom = math.Clamp(Zoom + 0.1, 0, 1)
|
||||
return true
|
||||
end
|
||||
end)
|
||||
end
|
||||
@@ -0,0 +1 @@
|
||||
include("shared.lua")
|
||||
417
garrysmod/addons/ft_drones/lua/entities/mur_drone_spy/init.lua
Normal file
417
garrysmod/addons/ft_drones/lua/entities/mur_drone_spy/init.lua
Normal file
@@ -0,0 +1,417 @@
|
||||
AddCSLuaFile("shared.lua")
|
||||
include("shared.lua")
|
||||
|
||||
util.AddNetworkString("MuR_DroneCam_Spy")
|
||||
util.AddNetworkString("MuR_DroneLost_Spy")
|
||||
util.AddNetworkString("MuR_ThermalState_Spy")
|
||||
util.AddNetworkString("MuR_DroneEmergency_Spy")
|
||||
|
||||
local rotorSounds = {
|
||||
"ambient/machines/spin_loop.wav",
|
||||
"ambient/machines/machine1_hit1.wav"
|
||||
}
|
||||
|
||||
local damageSounds = {
|
||||
"physics/metal/metal_box_impact_bullet1.wav",
|
||||
"physics/metal/metal_box_impact_bullet2.wav",
|
||||
"physics/metal/metal_box_impact_bullet3.wav",
|
||||
"physics/metal/metal_computer_impact_bullet1.wav",
|
||||
"physics/metal/metal_computer_impact_bullet2.wav"
|
||||
}
|
||||
|
||||
local startupSounds = {
|
||||
"buttons/button9.wav",
|
||||
"buttons/button17.wav"
|
||||
}
|
||||
|
||||
function ENT:Initialize()
|
||||
self:SetModel("models/murdered/weapons/drone_ex.mdl")
|
||||
self:ResetSequence("idle")
|
||||
self:SetHealth(self.HealthCount)
|
||||
self:SetGravity(0)
|
||||
self.Velo = {x = 0, y = 0, z = 0}
|
||||
|
||||
self:SetSolid(SOLID_VPHYSICS)
|
||||
self:PhysicsInit(SOLID_VPHYSICS)
|
||||
|
||||
self.MultSpeed = 300
|
||||
self.MaxSpeed = 500
|
||||
self.BoostSpeed = 1600
|
||||
self.MaxPitch = 25
|
||||
self.MaxRoll = 30
|
||||
self.LinearDrag = 0.04
|
||||
self.TakeDamageWall = 0
|
||||
self.DeltaTime = 0
|
||||
self.LastBoostSound = 0
|
||||
self.LastWindSound = 0
|
||||
|
||||
self:SetNW2Float("RemoveTime", CurTime() + self.BatteryCount)
|
||||
|
||||
local ply = self:GetCreator()
|
||||
if IsValid(ply) then
|
||||
local forward = ply:GetForward()
|
||||
forward.z = 0
|
||||
forward = forward:GetNormalized()
|
||||
|
||||
local spawnDist = 80
|
||||
local spawnHeight = 40
|
||||
|
||||
local spawnPos = ply:GetPos() + forward * spawnDist + Vector(0, 0, spawnHeight)
|
||||
|
||||
local tr = util.TraceHull({
|
||||
start = spawnPos,
|
||||
endpos = spawnPos,
|
||||
mins = Vector(-20, -20, -20),
|
||||
maxs = Vector(20, 20, 20),
|
||||
filter = {ply, self}
|
||||
})
|
||||
|
||||
if tr.Hit then
|
||||
spawnPos = spawnPos + forward * 60
|
||||
end
|
||||
|
||||
local ground = util.TraceLine({
|
||||
start = spawnPos,
|
||||
endpos = spawnPos - Vector(0, 0, 5000),
|
||||
filter = self
|
||||
})
|
||||
|
||||
if spawnPos.z < ground.HitPos.z + 30 then
|
||||
spawnPos.z = ground.HitPos.z + 30
|
||||
end
|
||||
|
||||
self:SetPos(spawnPos)
|
||||
self:SetAngles(Angle(0, ply:EyeAngles().y, 0))
|
||||
end
|
||||
|
||||
self:EmitSound(startupSounds[math.random(#startupSounds)], 60, 100, 0.5)
|
||||
|
||||
if CreateSound then
|
||||
self.RotorSound = CreateSound(self, rotorSounds[1])
|
||||
if self.RotorSound then
|
||||
self.RotorSound:PlayEx(0.2, 90)
|
||||
end
|
||||
|
||||
self.WindSound = CreateSound(self, "ambient/wind/wind_snippet2.wav")
|
||||
if self.WindSound then
|
||||
self.WindSound:PlayEx(0, 100)
|
||||
end
|
||||
end
|
||||
|
||||
timer.Simple(0.1, function()
|
||||
if not IsValid(self) then return end
|
||||
local ply2 = self:GetCreator()
|
||||
if not IsValid(ply2) then return end
|
||||
|
||||
ply2.ControlDrone = self
|
||||
|
||||
net.Start("MuR_DroneCam_Spy")
|
||||
net.WriteEntity(self)
|
||||
net.Send(ply2)
|
||||
end)
|
||||
end
|
||||
|
||||
function ENT:OnRemove()
|
||||
if self.RotorSound then self.RotorSound:Stop() end
|
||||
if self.WindSound then self.WindSound:Stop() end
|
||||
self:StopSound("ambient/machines/spin_loop.wav")
|
||||
self:StopSound("ambient/wind/wind_snippet2.wav")
|
||||
end
|
||||
|
||||
function ENT:GetForwardAbs()
|
||||
local ang = self:GetAngles()
|
||||
ang.x = 0
|
||||
ang.z = 0
|
||||
return ang:Forward()
|
||||
end
|
||||
|
||||
function ENT:GetRightAbs()
|
||||
local ang = self:GetAngles()
|
||||
ang.x = 0
|
||||
ang.z = 0
|
||||
return ang:Right()
|
||||
end
|
||||
|
||||
function ENT:GetUpAbs()
|
||||
local ang = self:GetAngles()
|
||||
ang.x = 0
|
||||
ang.z = 0
|
||||
return ang:Up()
|
||||
end
|
||||
|
||||
function ENT:Think()
|
||||
if self.EmergencyKill then
|
||||
local phys = self:GetPhysicsObject()
|
||||
if IsValid(phys) then
|
||||
phys:EnableMotion(false)
|
||||
phys:SetVelocityInstantaneous(vector_origin)
|
||||
end
|
||||
|
||||
if CurTime() >= (self.EmergencyKillTime or 0) then
|
||||
self:Explode()
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
if self:GetNW2Bool("ThermalActive") then
|
||||
local drainMul = 2 -- во сколько раз быстрее тратится батарея (подбери значение по вкусу, биба)
|
||||
local newRemove = self:GetNW2Float("RemoveTime") - FrameTime() * (drainMul - 1)
|
||||
self:SetNW2Float("RemoveTime", newRemove)
|
||||
end
|
||||
|
||||
|
||||
local ply = self:GetCreator()
|
||||
if not IsValid(ply) then
|
||||
self:Remove()
|
||||
return
|
||||
end
|
||||
|
||||
ply:SetActiveWeapon(nil)
|
||||
|
||||
if self:GetNWBool("Jammed") then
|
||||
|
||||
if not self.JamStart then
|
||||
self.JamStart = CurTime()
|
||||
self.JamEnd = CurTime() + 5
|
||||
end
|
||||
if CurTime() >= self.JamEnd then
|
||||
local ply = self:GetCreator()
|
||||
if IsValid(ply) then
|
||||
net.Start("MuR_DroneLost_Spy")
|
||||
net.Send(ply)
|
||||
if ply.ControlDrone == self then
|
||||
ply.ControlDrone = nil
|
||||
end
|
||||
end
|
||||
|
||||
self:Remove()
|
||||
return true
|
||||
end
|
||||
|
||||
else
|
||||
self.JamStart = nil
|
||||
self.JamEnd = nil
|
||||
end
|
||||
|
||||
|
||||
local phys = self:GetPhysicsObject()
|
||||
local mu = self.MultSpeed
|
||||
local ms = self.MaxSpeed
|
||||
local vel = phys:GetVelocity()
|
||||
local pos = self:GetPos()
|
||||
|
||||
local angYaw = ply:EyeAngles().y
|
||||
local sang = Angle(0, angYaw, 0)
|
||||
|
||||
local trace = {start = self:GetPos(), endpos = self:GetPos(), filter = self}
|
||||
local tr = util.TraceEntity(trace, self)
|
||||
|
||||
if self:GetNW2Bool("Boost") then
|
||||
local fwd = self:GetForward()
|
||||
phys:SetVelocityInstantaneous(fwd * self.Velo.x)
|
||||
else
|
||||
if tr.Hit then
|
||||
phys:SetVelocityInstantaneous(
|
||||
(self:GetForwardAbs() * self.Velo.x +
|
||||
self:GetRightAbs() * self.Velo.y +
|
||||
self:GetUpAbs() * self.Velo.z) / 2
|
||||
)
|
||||
if self.TakeDamageWall < CurTime() then
|
||||
self:TakeDamage(1)
|
||||
self.TakeDamageWall = CurTime() + 0.2
|
||||
end
|
||||
else
|
||||
phys:SetVelocityInstantaneous(
|
||||
self:GetForwardAbs() * self.Velo.x +
|
||||
self:GetRightAbs() * self.Velo.y +
|
||||
self:GetUpAbs() * self.Velo.z +
|
||||
Vector(0, 0, 15)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
local ratioX = math.Clamp(self.Velo.x / ms, -1, 1)
|
||||
local ratioY = math.Clamp(self.Velo.y / ms, -1, 1)
|
||||
local targetPitch = ratioX * self.MaxPitch
|
||||
local targetRoll = ratioY * self.MaxRoll
|
||||
sang = Angle(targetPitch, angYaw, targetRoll)
|
||||
|
||||
local b = {}
|
||||
b.secondstoarrive = 1
|
||||
b.pos = self:GetPos()
|
||||
b.angle = sang
|
||||
b.maxangular = 140
|
||||
b.maxangulardamp = 60
|
||||
b.maxspeed = 1200
|
||||
b.maxspeeddamp = 180
|
||||
b.dampfactor = 0.3
|
||||
b.teleportdistance= 0
|
||||
b.deltatime = CurTime() - self.DeltaTime
|
||||
phys:ComputeShadowControl(b)
|
||||
|
||||
local tick = FrameTime()
|
||||
|
||||
if ply:KeyDown(IN_FORWARD) or self:GetNW2Bool("Boost") then
|
||||
if self:GetNW2Bool("Boost") then
|
||||
self.Velo.x = math.Clamp(self.Velo.x + tick * mu * 2, -ms, self.BoostSpeed)
|
||||
else
|
||||
self.Velo.x = math.Clamp(self.Velo.x + tick * mu, -ms, ms)
|
||||
end
|
||||
elseif ply:KeyDown(IN_BACK) then
|
||||
self.Velo.x = math.Clamp(self.Velo.x - tick * mu, -ms, ms)
|
||||
end
|
||||
|
||||
if ply:KeyDown(IN_MOVELEFT) then
|
||||
self.Velo.y = math.Clamp(self.Velo.y - tick * mu, -ms, ms)
|
||||
elseif ply:KeyDown(IN_MOVERIGHT) then
|
||||
self.Velo.y = math.Clamp(self.Velo.y + tick * mu, -ms, ms)
|
||||
end
|
||||
|
||||
if ply:KeyDown(IN_DUCK) then
|
||||
self.Velo.z = math.Clamp(self.Velo.z - tick * mu, -ms, ms)
|
||||
elseif ply:KeyDown(IN_SPEED) then
|
||||
self.Velo.z = math.Clamp(self.Velo.z + tick * mu, -ms, ms)
|
||||
end
|
||||
|
||||
local drag = 1 - math.Clamp(self.LinearDrag * tick, 0, 0.2)
|
||||
self.Velo.x = self.Velo.x * drag
|
||||
self.Velo.y = self.Velo.y * drag
|
||||
self.Velo.z = self.Velo.z * (0.98 * drag)
|
||||
|
||||
if not ply:KeyDown(IN_FORWARD) and not ply:KeyDown(IN_BACK) and not self:GetNW2Bool("Boost") then
|
||||
if math.abs(self.Velo.x) > 5 then
|
||||
self.Velo.x = math.Approach(self.Velo.x, 0, tick * mu * (self.Velo.x > 0 and 1 or -1))
|
||||
else
|
||||
self.Velo.x = 0
|
||||
end
|
||||
end
|
||||
if not ply:KeyDown(IN_SPEED) and not ply:KeyDown(IN_DUCK) then
|
||||
if math.abs(self.Velo.z) > 5 then
|
||||
self.Velo.z = math.Approach(self.Velo.z, 0, tick * mu * (self.Velo.z > 0 and 1 or -1))
|
||||
else
|
||||
self.Velo.z = 0
|
||||
end
|
||||
end
|
||||
if not ply:KeyDown(IN_MOVELEFT) and not ply:KeyDown(IN_MOVERIGHT) then
|
||||
if math.abs(self.Velo.y) > 5 then
|
||||
self.Velo.y = math.Approach(self.Velo.y, 0, tick * mu * (self.Velo.y > 0 and 1 or -1))
|
||||
else
|
||||
self.Velo.y = 0
|
||||
end
|
||||
end
|
||||
|
||||
if self.RotorSound then
|
||||
local spd = phys:GetVelocity():Length()
|
||||
local load = math.Clamp((math.abs(self.Velo.x) + math.abs(self.Velo.y) + math.abs(self.Velo.z)) / (self.BoostSpeed * 0.6), 0, 1)
|
||||
local timeLeft = math.max(self:GetNW2Float("RemoveTime") - CurTime(), 0)
|
||||
local battFrac = math.Clamp(timeLeft / self.BatteryCount, 0, 1)
|
||||
local basePitch = 80 + 45 * load + math.Clamp(spd * 0.025, 0, 35)
|
||||
local baseVol = 0.15 + 0.35 * load
|
||||
basePitch = basePitch * (0.85 + 0.15 * battFrac)
|
||||
|
||||
if battFrac < 0.15 then
|
||||
basePitch = basePitch + math.sin(CurTime() * 15) * 5
|
||||
end
|
||||
|
||||
self.RotorSound:ChangePitch(math.Clamp(basePitch, 55, 180), 0.08)
|
||||
self.RotorSound:ChangeVolume(math.Clamp(baseVol, 0.05, 0.55), 0.08)
|
||||
end
|
||||
|
||||
if self.WindSound then
|
||||
local spd = phys:GetVelocity():Length()
|
||||
local windVol = math.Clamp(spd / 800, 0, 0.4)
|
||||
local windPitch = 80 + math.Clamp(spd * 0.05, 0, 40)
|
||||
self.WindSound:ChangeVolume(windVol, 0.15)
|
||||
self.WindSound:ChangePitch(windPitch, 0.15)
|
||||
end
|
||||
|
||||
local maxDist = 12000 * 12000
|
||||
local dist2 = ply:GetPos():DistToSqr(self:GetPos())
|
||||
|
||||
if dist2 > maxDist then
|
||||
net.Start("MuR_DroneLost_Spy")
|
||||
net.Send(ply)
|
||||
self:Break()
|
||||
return true
|
||||
end
|
||||
|
||||
if self:Health() <= 0 then
|
||||
net.Start("MuR_DroneLost_Spy")
|
||||
net.Send(ply)
|
||||
self:Break()
|
||||
end
|
||||
|
||||
if self:GetNW2Float("RemoveTime") < CurTime() or not ply:Alive() then
|
||||
net.Start("MuR_DroneLost_Spy")
|
||||
net.Send(ply)
|
||||
self:EmitSound("ambient/machines/machine1_hit2.wav", 70, 80, 0.6)
|
||||
self:Remove()
|
||||
elseif ply:KeyDown(IN_RELOAD) and ply:GetPos():DistToSqr(self:GetPos()) < 50000 then
|
||||
ply:Give("swep_drone_spy")
|
||||
ply:SelectWeapon("swep_drone_spy")
|
||||
self:EmitSound("buttons/button14.wav", 60, 100, 0.5)
|
||||
self:Remove()
|
||||
end
|
||||
|
||||
self.DeltaTime = CurTime()
|
||||
self:NextThink(CurTime())
|
||||
return true
|
||||
end
|
||||
|
||||
function ENT:PhysicsCollide(col)
|
||||
if self.Velo.x > self.MaxSpeed and self:GetNW2Bool("Boost") then
|
||||
self:EmitSound("physics/metal/metal_solid_impact_hard"..math.random(1,5)..".wav", 80, 100, 0.8)
|
||||
self:TakeDamage(self:Health())
|
||||
end
|
||||
end
|
||||
|
||||
-- Визуальный взрыв без урона для EmergencyKill
|
||||
function ENT:Explode()
|
||||
local effectdata = EffectData()
|
||||
effectdata:SetOrigin(self:GetPos())
|
||||
util.Effect("Explosion", effectdata)
|
||||
|
||||
self:EmitSound("ambient/explosions/explode_4.wav", 100, 100, 1)
|
||||
self:Remove()
|
||||
end
|
||||
|
||||
function ENT:Break()
|
||||
local effectdata = EffectData()
|
||||
effectdata:SetOrigin(self:GetPos())
|
||||
util.Effect("ManhackSparks", effectdata)
|
||||
self:EmitSound("physics/metal/metal_box_break"..math.random(1,2)..".wav", 100, 100, 1)
|
||||
self:Remove()
|
||||
end
|
||||
|
||||
function ENT:OnTakeDamage(dmgt)
|
||||
local dmg = dmgt:GetDamage()
|
||||
self:SetHealth(self:Health() - dmg)
|
||||
end
|
||||
|
||||
hook.Add("SetupPlayerVisibility", "GpincDroneCam_Spy", function(ply)
|
||||
local drone = ply.ControlDrone
|
||||
if IsValid(drone) and drone:GetClass() == "mur_drone_spy" then
|
||||
AddOriginToPVS(drone:GetPos())
|
||||
end
|
||||
end)
|
||||
|
||||
net.Receive("MuR_ThermalState_Spy", function(len, ply)
|
||||
local drone = ply.ControlDrone
|
||||
if IsValid(drone) then
|
||||
drone:SetNW2Bool("ThermalActive", net.ReadBool())
|
||||
end
|
||||
end)
|
||||
|
||||
-- Команда экстренного самоуничтожения от клиента
|
||||
net.Receive("MuR_DroneEmergency_Spy", function(len, ply)
|
||||
local drone = ply.ControlDrone
|
||||
if not IsValid(drone) then return end
|
||||
if drone:GetClass() ~= "mur_drone_spy" then return end
|
||||
|
||||
if not drone.EmergencyKill then
|
||||
drone.EmergencyKill = true
|
||||
drone.EmergencyKillTime = CurTime() + 3
|
||||
drone:SetNWFloat("EmergencyCountdown", drone.EmergencyKillTime)
|
||||
drone:EmitSound("buttons/button17.wav", 75, 100, 0.6)
|
||||
end
|
||||
end)
|
||||
695
garrysmod/addons/ft_drones/lua/entities/mur_drone_spy/shared.lua
Normal file
695
garrysmod/addons/ft_drones/lua/entities/mur_drone_spy/shared.lua
Normal file
@@ -0,0 +1,695 @@
|
||||
ENT.Base = "base_gmodentity"
|
||||
ENT.Type = "anim"
|
||||
ENT.PrintName = "Recon Drone"
|
||||
ENT.Spawnable = true
|
||||
ENT.AutomaticFrameAdvance = true
|
||||
|
||||
ENT.BatteryCount = 600
|
||||
ENT.HealthCount = 20
|
||||
|
||||
if CLIENT then
|
||||
|
||||
---------------------------------------------------------
|
||||
-- ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ HUD
|
||||
---------------------------------------------------------
|
||||
|
||||
local function DrawScanlines(sw, sh, intensity)
|
||||
surface.SetDrawColor(0, 0, 0, intensity * 15)
|
||||
for i = 0, sh, 4 do
|
||||
surface.DrawRect(0, i, sw, 1)
|
||||
end
|
||||
end
|
||||
|
||||
local function DrawGlitchNoise(sw, sh, intensity)
|
||||
if intensity < 0.3 then return end
|
||||
local glitchCount = math.floor((1 - intensity) * 50)
|
||||
surface.SetDrawColor(255, 255, 255, (1 - intensity) * 100)
|
||||
for i = 1, glitchCount do
|
||||
local x = math.random(0, sw)
|
||||
local y = math.random(0, sh)
|
||||
local w = math.random(20, 200)
|
||||
surface.DrawRect(x, y, w, 2)
|
||||
end
|
||||
end
|
||||
|
||||
local function DrawCornerBrackets(x, y, w, h, size, thickness, col)
|
||||
surface.SetDrawColor(col)
|
||||
surface.DrawRect(x, y, size, thickness)
|
||||
surface.DrawRect(x, y, thickness, size)
|
||||
surface.DrawRect(x + w - size, y, size, thickness)
|
||||
surface.DrawRect(x + w - thickness, y, thickness, size)
|
||||
surface.DrawRect(x, y + h - thickness, size, thickness)
|
||||
surface.DrawRect(x, y + h - size, thickness, size)
|
||||
surface.DrawRect(x + w - size, y + h - thickness, size, thickness)
|
||||
surface.DrawRect(x + w - thickness, y + h - size, thickness, size)
|
||||
end
|
||||
|
||||
local function DrawCrosshair(cx, cy, size, gap, thickness, col)
|
||||
surface.SetDrawColor(col)
|
||||
surface.DrawRect(cx - size, cy - thickness/2, size - gap, thickness)
|
||||
surface.DrawRect(cx + gap, cy - thickness/2, size - gap, thickness)
|
||||
surface.DrawRect(cx - thickness/2, cy - size, thickness, size - gap)
|
||||
surface.DrawRect(cx - thickness/2, cy + gap, thickness, size - gap)
|
||||
end
|
||||
|
||||
local function DrawArcSegment(cx, cy, radius, startAng, endAng, thickness, segments, col)
|
||||
surface.SetDrawColor(col)
|
||||
local step = (endAng - startAng) / segments
|
||||
for i = 0, segments - 1 do
|
||||
local a1 = math.rad(startAng + i * step)
|
||||
local a2 = math.rad(startAng + (i + 1) * step)
|
||||
local x1 = cx + math.cos(a1) * radius
|
||||
local y1 = cy + math.sin(a1) * radius
|
||||
local x2 = cx + math.cos(a2) * radius
|
||||
local y2 = cy + math.sin(a2) * radius
|
||||
surface.DrawLine(x1, y1, x2, y2)
|
||||
end
|
||||
end
|
||||
|
||||
---------------------------------------------------------
|
||||
-- РЕНДЕР ПОСЛЕДНЕГО КАДРА
|
||||
---------------------------------------------------------
|
||||
|
||||
local function GetDroneRT()
|
||||
local name = "MuR_Drone_LastFrame"
|
||||
return GetRenderTarget(name, ScrW(), ScrH())
|
||||
end
|
||||
|
||||
local lastFrameMat = CreateMaterial("MuR_Drone_LastFrame_Mat", "UnlitGeneric", {
|
||||
["$basetexture"] = "MuR_Drone_LastFrame",
|
||||
["$ignorez"] = 1,
|
||||
["$vertexcolor"] = 1,
|
||||
["$vertexalpha"] = 1
|
||||
})
|
||||
|
||||
---------------------------------------------------------
|
||||
-- МОЩНЫЕ ПОМЕХИ
|
||||
---------------------------------------------------------
|
||||
|
||||
local function DrawHeavySignalNoise(strength)
|
||||
if not strength then return end
|
||||
if strength > 0.9 then return end
|
||||
|
||||
local sw, sh = ScrW(), ScrH()
|
||||
|
||||
local noiseCount = (1 - strength) * 400
|
||||
for i = 1, noiseCount do
|
||||
surface.SetDrawColor(255, 255, 255, math.random(10, 60))
|
||||
surface.DrawRect(math.random(0, sw), math.random(0, sh), 1, 1)
|
||||
end
|
||||
|
||||
local lineCount = (1 - strength) * 60
|
||||
for i = 1, lineCount do
|
||||
local y = math.random(0, sh)
|
||||
local w = math.random(sw * 0.2, sw)
|
||||
local x = math.random(0, sw - w)
|
||||
surface.SetDrawColor(255, 255, 255, math.random(20, 80))
|
||||
surface.DrawRect(x, y, w, math.random(1, 3))
|
||||
end
|
||||
|
||||
if strength < 0.4 then
|
||||
local shift = math.random(-8, 8) * (1 - strength)
|
||||
surface.SetDrawColor(255, 255, 255, 40)
|
||||
surface.DrawRect(shift, 0, sw, sh)
|
||||
end
|
||||
|
||||
if strength < 0.3 then
|
||||
for i = 1, 10 do
|
||||
local x = math.random(0, sw)
|
||||
surface.SetDrawColor(255, 255, 255, math.random(30, 80))
|
||||
surface.DrawRect(x, 0, math.random(2, 6), sh)
|
||||
end
|
||||
end
|
||||
|
||||
if strength < 0.2 then
|
||||
local shake = (0.2 - strength) * 6
|
||||
surface.SetDrawColor(255, 255, 255, 25)
|
||||
surface.DrawRect(
|
||||
math.random(-shake, shake),
|
||||
math.random(-shake, shake),
|
||||
sw, sh
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
---------------------------------------------------------
|
||||
-- ПЕРЕМЕННЫЕ КАМЕРЫ / FLIR
|
||||
---------------------------------------------------------
|
||||
|
||||
local DRONE = nil
|
||||
local Thermal = false
|
||||
local ThermalKeyHeld = false
|
||||
local Zoom = 1
|
||||
|
||||
local FPV_Yaw = 0
|
||||
local FPV_Pitch = 0
|
||||
|
||||
local DefMats = {}
|
||||
local DefClrs = {}
|
||||
local DoXRay = false
|
||||
|
||||
local FLIR = {
|
||||
["$pp_colour_addr"] = -.3,
|
||||
["$pp_colour_addg"] = -.4,
|
||||
["$pp_colour_addb"] = -.4,
|
||||
["$pp_colour_brightness"] = .1,
|
||||
["$pp_colour_contrast"] = 1,
|
||||
["$pp_colour_colour"] = 0,
|
||||
["$pp_colour_mulr"] = 0,
|
||||
["$pp_colour_mulg"] = 0,
|
||||
["$pp_colour_mulb"] = 0,
|
||||
}
|
||||
|
||||
---------------------------------------------------------
|
||||
-- X-RAY ДЛЯ FLIR
|
||||
---------------------------------------------------------
|
||||
|
||||
local function ApplyXRay()
|
||||
for _, v in ipairs(ents.GetAll()) do
|
||||
if not v:GetModel() then continue end
|
||||
if string.sub(v:GetModel(), -3) ~= "mdl" then continue end
|
||||
|
||||
if v:IsPlayer() then
|
||||
elseif v:IsNPC() then
|
||||
elseif v.LVS or v.LFS then
|
||||
else
|
||||
continue
|
||||
end
|
||||
|
||||
local col = v:GetColor()
|
||||
if col.a <= 0 then continue end
|
||||
|
||||
if not DefClrs[v] then
|
||||
DefClrs[v] = Color(col.r, col.g, col.b, col.a)
|
||||
end
|
||||
|
||||
local entmat = v:GetMaterial()
|
||||
if not DefMats[v] then
|
||||
DefMats[v] = entmat
|
||||
end
|
||||
|
||||
v:SetColor(Color(255,255,255,255))
|
||||
v:SetMaterial("xray/living")
|
||||
end
|
||||
end
|
||||
|
||||
local function RestoreXRayMaterials()
|
||||
for ent2, mat in pairs(DefMats) do
|
||||
if IsValid(ent2) then ent2:SetMaterial(mat) end
|
||||
end
|
||||
for ent2, clr in pairs(DefClrs) do
|
||||
if IsValid(ent2) then
|
||||
ent2:SetRenderMode(RENDERMODE_TRANSALPHA)
|
||||
ent2:SetColor(Color(clr.r, clr.g, clr.b, clr.a))
|
||||
end
|
||||
end
|
||||
DefMats = {}
|
||||
DefClrs = {}
|
||||
DoXRay = false
|
||||
end
|
||||
|
||||
local function RenderFLIR()
|
||||
DrawColorModify(FLIR)
|
||||
DrawBloom(0,1,1,1,0,0,0,0,0)
|
||||
DrawMotionBlur(FrameTime() * 50, 0.5, 0.05)
|
||||
DrawBokehDOF(0.15, 1, 12)
|
||||
end
|
||||
|
||||
---------------------------------------------------------
|
||||
-- ЭКРАН ПОТЕРИ СВЯЗИ
|
||||
---------------------------------------------------------
|
||||
|
||||
local function LoseDrone()
|
||||
surface.PlaySound("ambient/levels/prison/radio_random"..math.random(1,15)..".wav")
|
||||
|
||||
local startTime = CurTime()
|
||||
lastFrameMat:SetTexture("$basetexture", GetDroneRT())
|
||||
|
||||
hook.Add("HUDPaint", "Spy_DroneCamLost", function()
|
||||
local sw, sh = ScrW(), ScrH()
|
||||
local elapsed = CurTime() - startTime
|
||||
local flicker = math.sin(elapsed * 30) > 0
|
||||
|
||||
surface.SetDrawColor(0, 0, 0, 255)
|
||||
surface.DrawRect(0, 0, sw, sh)
|
||||
|
||||
surface.SetDrawColor(255, 255, 255, 255)
|
||||
surface.SetMaterial(lastFrameMat)
|
||||
surface.DrawTexturedRect(0, 0, sw, sh)
|
||||
|
||||
surface.SetDrawColor(0, 0, 0, math.min(elapsed * 100, 200))
|
||||
surface.DrawRect(0, 0, sw, sh)
|
||||
|
||||
for i = 1, 150 do
|
||||
local px = math.random(0, sw)
|
||||
local py = math.random(0, sh)
|
||||
local ps = math.random(2, 8)
|
||||
local c = math.random(30, 80)
|
||||
surface.SetDrawColor(c, c, c, 255)
|
||||
surface.DrawRect(px, py, ps, ps)
|
||||
end
|
||||
|
||||
DrawScanlines(sw, sh, 3)
|
||||
|
||||
for i = 1, 20 do
|
||||
local y = math.random(0, sh)
|
||||
local w = math.random(sw * 0.3, sw)
|
||||
local x = math.random(0, sw - w)
|
||||
surface.SetDrawColor(40, 40, 45, 200)
|
||||
surface.DrawRect(x, y, w, math.random(1, 5))
|
||||
end
|
||||
|
||||
local textCol = flicker and Color(255, 50, 50, 255) or Color(200, 40, 40, 200)
|
||||
draw.SimpleText("▌▌ СОЕДИНЕНИЕ ПОТЕРЯНО ▐▐", "MuR_Font5", sw/2, sh/2 - 30, textCol, TEXT_ALIGN_CENTER)
|
||||
draw.SimpleText("СВЯЗЬ ПРЕКРАЩЕНА", "MuR_Font2", sw/2, sh/2 + 30, Color(150,150,150,200), TEXT_ALIGN_CENTER)
|
||||
|
||||
DrawHeavySignalNoise(0.05)
|
||||
end)
|
||||
|
||||
timer.Simple(2, function()
|
||||
hook.Remove("HUDPaint", "Spy_DroneCamLost")
|
||||
end)
|
||||
end
|
||||
|
||||
net.Receive("MuR_DroneLost_Spy", LoseDrone)
|
||||
|
||||
---------------------------------------------------------
|
||||
-- Синхронизация состояния тепловизора от сервера
|
||||
-- (перенесено сюда чтобы менять локальную переменную Thermal)
|
||||
---------------------------------------------------------
|
||||
net.Receive("MuR_ThermalState_Spy", function()
|
||||
local state = net.ReadBool()
|
||||
Thermal = state
|
||||
|
||||
if not Thermal then
|
||||
-- гарантированный сброс XRay и возврат материалов
|
||||
if DoXRay then
|
||||
RestoreXRayMaterials()
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
---------------------------------------------------------
|
||||
-- ПОЛУЧЕНИЕ ДРОНА / ОСНОВНОЙ HUD
|
||||
---------------------------------------------------------
|
||||
|
||||
net.Receive("MuR_DroneCam_Spy", function()
|
||||
local ent = net.ReadEntity()
|
||||
DRONE = ent
|
||||
Thermal = false
|
||||
Zoom = 1
|
||||
|
||||
if IsValid(ent) then
|
||||
FPV_Yaw = ent:GetAngles().y
|
||||
FPV_Pitch = 0
|
||||
end
|
||||
|
||||
local pulseAlpha = 0
|
||||
local pulseDir = 1
|
||||
local startTime = CurTime()
|
||||
local smoothBatt = 1
|
||||
local smoothHP = 1
|
||||
|
||||
local ppTab = {
|
||||
["$pp_colour_addr"] = 0,
|
||||
["$pp_colour_addg"] = 0.02,
|
||||
["$pp_colour_addb"] = 0.05,
|
||||
["$pp_colour_brightness"] = -0.01,
|
||||
["$pp_colour_contrast"] = 1.1,
|
||||
["$pp_colour_colour"] = 0.6,
|
||||
["$pp_colour_mulr"] = 0,
|
||||
["$pp_colour_mulg"] = 0.05,
|
||||
["$pp_colour_mulb"] = 0.05
|
||||
}
|
||||
|
||||
hook.Add("RenderScreenspaceEffects", "Spy_DroneCam", function()
|
||||
if not IsValid(ent) then return end
|
||||
|
||||
local rt = GetDroneRT()
|
||||
render.CopyRenderTargetToTexture(rt)
|
||||
|
||||
local timeLeft = math.max(ent:GetNW2Float("RemoveTime") - CurTime(), 0)
|
||||
local battFrac = math.Clamp(timeLeft / ent.BatteryCount, 0, 1)
|
||||
|
||||
if battFrac < 0.2 then
|
||||
ppTab["$pp_colour_addr"] = 0.1 * (1 - battFrac / 0.2)
|
||||
ppTab["$pp_colour_contrast"] = 1.15 + 0.2 * (1 - battFrac / 0.2)
|
||||
else
|
||||
ppTab["$pp_colour_addr"] = 0
|
||||
ppTab["$pp_colour_contrast"] = 1.15
|
||||
end
|
||||
|
||||
DrawColorModify(ppTab)
|
||||
DrawSharpen(0.8, 0.8)
|
||||
end)
|
||||
|
||||
-----------------------------------------------------
|
||||
-- FLIR / X-RAY
|
||||
-----------------------------------------------------
|
||||
|
||||
hook.Add("RenderScene", "spy_drone_FLIR_Apply", function()
|
||||
if not IsValid(DRONE) then return end
|
||||
if DRONE:GetClass() ~= "mur_drone_spy" then return end
|
||||
|
||||
if not Thermal then return end
|
||||
if not DRONE:GetNW2Bool("ThermalActive") then return end
|
||||
|
||||
if not DoXRay then
|
||||
DoXRay = true
|
||||
ApplyXRay()
|
||||
end
|
||||
end)
|
||||
|
||||
hook.Add("HUDPaintBackground", "spy_drone_FLIR_Effects", function()
|
||||
if not Thermal then return end
|
||||
if not IsValid(DRONE) then return end
|
||||
if DRONE:GetClass() ~= "mur_drone_spy" then return end
|
||||
|
||||
RenderFLIR()
|
||||
end)
|
||||
|
||||
hook.Add("Think", "spy_drone_FLIR_AutoOff", function()
|
||||
-- если тепловизор включён и дрон валиден — ничего не делаем
|
||||
if Thermal and IsValid(DRONE) then return end
|
||||
|
||||
-- иначе — сбрасываем XRay и возвращаем материалы
|
||||
if DoXRay then
|
||||
DoXRay = false
|
||||
|
||||
for ent2, mat in pairs(DefMats) do
|
||||
if IsValid(ent2) then ent2:SetMaterial(mat) end
|
||||
end
|
||||
for ent2, clr in pairs(DefClrs) do
|
||||
if IsValid(ent2) then
|
||||
ent2:SetRenderMode(RENDERMODE_TRANSALPHA)
|
||||
ent2:SetColor(Color(clr.r, clr.g, clr.b, clr.a))
|
||||
end
|
||||
end
|
||||
|
||||
DefMats = {}
|
||||
DefClrs = {}
|
||||
end
|
||||
end)
|
||||
|
||||
---------------------------------------------------------
|
||||
-- HUD КАМЕРЫ (ОСНОВНОЙ)
|
||||
---------------------------------------------------------
|
||||
|
||||
hook.Add("HUDPaint", "Spy_DroneCam", function()
|
||||
if not IsValid(ent) then return end
|
||||
|
||||
local sw, sh = ScrW(), ScrH()
|
||||
local cx, cy = sw / 2, sh / 2
|
||||
local time = CurTime()
|
||||
local elapsed = time - startTime
|
||||
|
||||
pulseAlpha = pulseAlpha + pulseDir * FrameTime() * 400
|
||||
if pulseAlpha >= 255 then pulseDir = -1 pulseAlpha = 255
|
||||
elseif pulseAlpha <= 100 then pulseDir = 1 pulseAlpha = 100 end
|
||||
|
||||
local timeLeft = math.max(ent:GetNW2Float("RemoveTime") - CurTime(), 0)
|
||||
local battFrac = math.Clamp(timeLeft / ent.BatteryCount, 0, 1)
|
||||
local hpFrac = math.Clamp(ent:Health() / ent.HealthCount, 0, 1)
|
||||
|
||||
smoothBatt = Lerp(FrameTime() * 3, smoothBatt, battFrac)
|
||||
smoothHP = Lerp(FrameTime() * 5, smoothHP, hpFrac)
|
||||
|
||||
local vel = ent:GetVelocity():Length()
|
||||
local spd = math.Round(vel * 0.068)
|
||||
|
||||
local tr = util.TraceLine({
|
||||
start = ent:GetPos(),
|
||||
endpos = ent:GetPos() - Vector(0,0,10000),
|
||||
filter = ent
|
||||
})
|
||||
|
||||
local agl = math.Round(ent:GetPos().z - tr.HitPos.z)
|
||||
|
||||
local dist2 = LocalPlayer():GetPos():DistToSqr(ent:GetPos())
|
||||
local signal = math.Clamp(1 - (dist2 / (8000 * 8000)), 0, 1)
|
||||
|
||||
|
||||
local killTime = ent:GetNWFloat("EmergencyCountdown", 0)
|
||||
|
||||
DrawScanlines(sw, sh, 20)
|
||||
DrawGlitchNoise(sw, sh, signal)
|
||||
|
||||
local frameCol = Color(100, 200, 255, 120)
|
||||
DrawCornerBrackets(40, 40, sw - 80, sh - 80, 60, 3, frameCol)
|
||||
|
||||
local crossCol = Color(100, 200, 255, pulseAlpha)
|
||||
DrawCrosshair(cx, cy, 25, 8, 2, crossCol)
|
||||
|
||||
surface.SetDrawColor(100, 200, 255, 60)
|
||||
DrawArcSegment(cx, cy, 45, 0, 360, 1, 64, Color(100,200,255,40))
|
||||
|
||||
draw.SimpleText("◈ РАЗВЕДЫВАТЕЛЬНЫЙ ДРОН", "MuR_Font3", 80, 60, Color(100,200,255), TEXT_ALIGN_LEFT)
|
||||
draw.SimpleText(string.format("ВРЕМЯ РАБОТЫ %.1fs", elapsed), "MuR_Font1", 80, 95, Color(150,150,150), TEXT_ALIGN_LEFT)
|
||||
draw.SimpleText(os.date("%H:%M:%S"), "MuR_Font1", 80, 115, Color(150,150,150), TEXT_ALIGN_LEFT)
|
||||
|
||||
local telemX = sw - 80
|
||||
local spdCol = spd > 100 and Color(255, 200, 60) or Color(100, 200, 255)
|
||||
draw.SimpleText(string.format("%03d km/h", spd), "MuR_Font3", telemX, 60, spdCol, TEXT_ALIGN_RIGHT)
|
||||
draw.SimpleText("СКОР", "MuR_Font1", telemX, 95, Color(100,100,100), TEXT_ALIGN_RIGHT)
|
||||
|
||||
draw.SimpleText(string.format("%04d m", agl), "MuR_Font3", telemX, 120, Color(100,200,255), TEXT_ALIGN_RIGHT)
|
||||
draw.SimpleText("ВЫС", "MuR_Font1", telemX, 155, Color(100,100,100), TEXT_ALIGN_RIGHT)
|
||||
|
||||
local sigCol = signal < 0.25 and Color(255, 60, 60)
|
||||
or (signal < 0.5 and Color(255, 200, 60) or Color(100, 200, 255))
|
||||
local sigBars = math.floor(signal * 5)
|
||||
local sigStr = string.rep("▮", sigBars) .. string.rep("▯", 5 - sigBars)
|
||||
draw.SimpleText(sigStr, "MuR_Font2", telemX, 180, sigCol, TEXT_ALIGN_RIGHT)
|
||||
draw.SimpleText(string.format("%d%%", math.Round(signal * 100)), "MuR_Font1", telemX, 210, sigCol, TEXT_ALIGN_RIGHT)
|
||||
|
||||
local barW, barH = 250, 12
|
||||
local barX, barY = 80, sh - 180
|
||||
|
||||
local battCol = smoothBatt < 0.2 and Color(255,60,60)
|
||||
or (smoothBatt < 0.4 and Color(255,200,60) or Color(100,200,255))
|
||||
surface.SetDrawColor(30,30,30,200)
|
||||
surface.DrawRect(barX, barY, barW, barH)
|
||||
surface.SetDrawColor(battCol.r, battCol.g, battCol.b, 255)
|
||||
surface.DrawRect(barX + 2, barY + 2, (barW - 4) * smoothBatt, barH - 4)
|
||||
surface.SetDrawColor(battCol.r, battCol.g, battCol.b, 100)
|
||||
surface.DrawOutlinedRect(barX, barY, barW, barH, 1)
|
||||
draw.SimpleText(string.format("ЗАРЯД %d%%", math.Round(smoothBatt * 100)), "MuR_Font1", barX, barY - 20, battCol, TEXT_ALIGN_LEFT)
|
||||
draw.SimpleText(string.format("%.0fs", timeLeft), "MuR_Font1", barX + barW, barY - 20, Color(150,150,150), TEXT_ALIGN_RIGHT)
|
||||
|
||||
barY = sh - 130
|
||||
local hpCol = smoothHP < 0.3 and Color(255,60,60)
|
||||
or (smoothHP < 0.6 and Color(255,200,60) or Color(100,200,255))
|
||||
surface.SetDrawColor(30,30,30,200)
|
||||
surface.DrawRect(barX, barY, barW, barH)
|
||||
surface.SetDrawColor(hpCol.r, hpCol.g, hpCol.b, 255)
|
||||
surface.DrawRect(barX + 2, barY + 2, (barW - 4) * smoothHP, barH - 4)
|
||||
surface.SetDrawColor(hpCol.r, hpCol.g, hpCol.b, 100)
|
||||
surface.DrawOutlinedRect(barX, barY, barW, barH, 1)
|
||||
draw.SimpleText(string.format("ПРОЧНОСТЬ %d%%", math.Round(smoothHP * 100)), "MuR_Font1", barX, barY - 20, hpCol, TEXT_ALIGN_LEFT)
|
||||
|
||||
-- Блок управления
|
||||
local ctrlX, ctrlY = sw - 80, sh - 200
|
||||
local ctrlCol = Color(150,150,150,200)
|
||||
draw.SimpleText("▶ [J] САМОУНИЧТОЖЕНИЕ", "MuR_Font1", ctrlX, ctrlY, Color(255,80,80,200), TEXT_ALIGN_RIGHT)
|
||||
draw.SimpleText("▲ [SHIFT] ВВЕРХ", "MuR_Font1", ctrlX, ctrlY + 22, ctrlCol, TEXT_ALIGN_RIGHT)
|
||||
draw.SimpleText("▼ [CTRL] ВНИЗ", "MuR_Font1", ctrlX, ctrlY + 44, ctrlCol, TEXT_ALIGN_RIGHT)
|
||||
draw.SimpleText("▶ [ALT] ТЕПЛОВИЗОР", "MuR_Font1", ctrlX, ctrlY + 66, Color(100,200,255), TEXT_ALIGN_RIGHT)
|
||||
|
||||
if smoothBatt < 0.15 then
|
||||
local warnFlash = math.sin(time * 8) > 0 and 255 or 100
|
||||
draw.SimpleText("⚠ НИЗКИЙ ЗАРЯД ⚠", "MuR_Font4", cx, 80, Color(255,60,60,warnFlash), TEXT_ALIGN_CENTER)
|
||||
end
|
||||
|
||||
if smoothHP < 0.25 then
|
||||
local dmgFlash = math.sin(time * 6) > 0 and 255 or 100
|
||||
draw.SimpleText("⚠ КРИТИЧЕСКИЕ ПОВРЕЖДЕНИЯ ⚠", "MuR_Font3", cx, 130, Color(255,120,60,dmgFlash), TEXT_ALIGN_CENTER)
|
||||
end
|
||||
|
||||
if signal < 0.2 then
|
||||
local sigFlash = math.sin(time * 10) > 0 and 255 or 100
|
||||
draw.SimpleText("◢◤ СЛАБЫЙ СИГНАЛ ◢◤", "MuR_Font3", cx, 180, Color(255, 80, 60, sigFlash), TEXT_ALIGN_CENTER)
|
||||
end
|
||||
DrawGlitchNoise(sw, sh, signal)
|
||||
if ent:GetNWBool("Jammed") then
|
||||
local jamStrength = ent:GetNWFloat("JamStrength", 1)
|
||||
|
||||
DrawHeavySignalNoise(jamStrength)
|
||||
|
||||
local jamFlash = math.sin(time * 8) > 0 and 255 or 120
|
||||
draw.SimpleText(
|
||||
"◢◤ ОБНАРУЖЕНЫ ПОМЕХИ ◢◤",
|
||||
"MuR_Font3",
|
||||
cx,
|
||||
220,
|
||||
Color(255, 80, 60, jamFlash),
|
||||
TEXT_ALIGN_CENTER
|
||||
)
|
||||
end
|
||||
|
||||
-- Обратный отсчёт самоуничтожения
|
||||
if killTime > CurTime() then
|
||||
local left = math.max(0, killTime - CurTime())
|
||||
local flash = math.sin(CurTime() * 10) > 0 and 255 or 120
|
||||
|
||||
draw.SimpleText(
|
||||
string.format("САМОУНИЧТОЖЕНИЕ: %.1f", left),
|
||||
"MuR_Font3",
|
||||
cx,
|
||||
260,
|
||||
Color(255, 50, 50, flash),
|
||||
TEXT_ALIGN_CENTER
|
||||
)
|
||||
end
|
||||
|
||||
DrawHeavySignalNoise(signal)
|
||||
end)
|
||||
hook.Add("CreateMove", "Spy_Drone_BlockWeapons", function(cmd)
|
||||
if not IsValid(DRONE) then return end
|
||||
if DRONE:GetClass() ~= "mur_drone_spy" then return end
|
||||
|
||||
cmd:RemoveKey(IN_ATTACK)
|
||||
cmd:RemoveKey(IN_ATTACK2)
|
||||
cmd:RemoveKey(IN_RELOAD)
|
||||
cmd:RemoveKey(IN_USE)
|
||||
cmd:SetImpulse(0)
|
||||
end)
|
||||
end)
|
||||
|
||||
---------------------------------------------------------
|
||||
-- КАМЕРА (CalcView)
|
||||
---------------------------------------------------------
|
||||
|
||||
hook.Add("CalcView", "Spy_DroneCam_Global", function(ply, pos, angles, fov)
|
||||
if not IsValid(DRONE) then return end
|
||||
if DRONE:GetClass() ~= "mur_drone_spy" then return end
|
||||
|
||||
local dPos = DRONE:GetPos()
|
||||
local vel = DRONE:GetVelocity():Length()
|
||||
|
||||
local offset = Vector(-8 - math.Clamp(vel * 0.015, 0, 5), 0, -2)
|
||||
local camPos = dPos + DRONE:GetForward() * offset.x + DRONE:GetUp() * offset.z
|
||||
|
||||
local camAng = Angle(FPV_Pitch, FPV_Yaw, 0)
|
||||
|
||||
local baseFov = 60
|
||||
local speedBoost = math.Clamp(vel * 0.025, 0, 15)
|
||||
local dynFov = baseFov + speedBoost
|
||||
dynFov = Lerp(Zoom, 20, dynFov)
|
||||
|
||||
dynFov = Lerp(Zoom, 20, dynFov)
|
||||
|
||||
return {
|
||||
origin = camPos,
|
||||
angles = camAng,
|
||||
fov = math.Clamp(dynFov, 20, 120),
|
||||
drawviewer = true
|
||||
}
|
||||
end)
|
||||
|
||||
hook.Add("Think", "spy_drone_Zoom", function()
|
||||
if not IsValid(DRONE) then return end
|
||||
if DRONE:GetClass() ~= "mur_drone_spy" then return end
|
||||
|
||||
local delta = input.GetAnalogValue(ANALOG_MOUSE_WHEEL)
|
||||
if delta ~= 0 then
|
||||
Zoom = Zoom - delta * 0.05
|
||||
Zoom = math.Clamp(Zoom, 0, 1)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Перехват invprev/invnext чтобы колесо и кнопки не переключали оружие
|
||||
hook.Add("PlayerBindPress", "spy_drone_ZoomBlock", function(ply, bind, pressed)
|
||||
if not IsValid(DRONE) then return end
|
||||
if DRONE:GetClass() ~= "mur_drone_spy" then return end
|
||||
|
||||
if bind == "invprev" then
|
||||
Zoom = math.Clamp(Zoom - 0.1, 0, 1)
|
||||
return true
|
||||
end
|
||||
|
||||
if bind == "invnext" then
|
||||
Zoom = math.Clamp(Zoom + 0.1, 0, 1)
|
||||
return true
|
||||
end
|
||||
end)
|
||||
|
||||
---------------------------------------------------------
|
||||
-- ТЕПЛОВИЗОР (кнопка)
|
||||
---------------------------------------------------------
|
||||
|
||||
hook.Add("Think", "spy_drone_ThermalToggle", function()
|
||||
if not IsValid(DRONE) then return end
|
||||
if DRONE:GetClass() ~= "mur_drone_spy" then return end
|
||||
|
||||
if input.IsKeyDown(KEY_LALT) and not ThermalKeyHeld then
|
||||
ThermalKeyHeld = true
|
||||
Thermal = not Thermal
|
||||
|
||||
surface.PlaySound(Thermal and "sw/misc/nv_on.wav" or "sw/misc/nv_off.wav")
|
||||
|
||||
net.Start("MuR_ThermalState_Spy")
|
||||
net.WriteBool(Thermal)
|
||||
net.SendToServer()
|
||||
end
|
||||
|
||||
if not input.IsKeyDown(KEY_LALT) then
|
||||
ThermalKeyHeld = false
|
||||
end
|
||||
end)
|
||||
|
||||
---------------------------------------------------------
|
||||
-- FPV МЫШЬ
|
||||
---------------------------------------------------------
|
||||
|
||||
hook.Add("CreateMove", "Spy_Drone_FPV_Mouse", function(cmd)
|
||||
if not IsValid(DRONE) then return end
|
||||
if DRONE:GetClass() ~= "mur_drone_spy" then return end
|
||||
|
||||
local mx = cmd:GetMouseX()
|
||||
local my = cmd:GetMouseY()
|
||||
|
||||
FPV_Yaw = FPV_Yaw - mx * 0.03
|
||||
FPV_Pitch = FPV_Pitch + my * 0.03
|
||||
FPV_Pitch = math.Clamp(FPV_Pitch, -45, 45)
|
||||
|
||||
cmd:SetMouseX(0)
|
||||
cmd:SetMouseY(0)
|
||||
cmd:SetForwardMove(0)
|
||||
cmd:SetSideMove(0)
|
||||
|
||||
DRONE:SetNW2Float("FPV_Yaw", FPV_Yaw)
|
||||
DRONE:SetNW2Float("FPV_Pitch", FPV_Pitch)
|
||||
end)
|
||||
|
||||
---------------------------------------------------------
|
||||
-- КЛАВИША G ДЛЯ САМОУНИЧТОЖЕНИЯ
|
||||
---------------------------------------------------------
|
||||
|
||||
hook.Add("Think", "spy_drone_EmergencyKey", function()
|
||||
if not IsValid(DRONE) then return end
|
||||
if DRONE:GetClass() ~= "mur_drone_spy" then return end
|
||||
|
||||
DRONE._KillKeyHeld = DRONE._KillKeyHeld or false
|
||||
|
||||
if input.IsKeyDown(KEY_J) and not DRONE._KillKeyHeld then
|
||||
DRONE._KillKeyHeld = true
|
||||
|
||||
net.Start("MuR_DroneEmergency_Spy")
|
||||
net.SendToServer()
|
||||
end
|
||||
|
||||
if not input.IsKeyDown(KEY_J) then
|
||||
DRONE._KillKeyHeld = false
|
||||
end
|
||||
end)
|
||||
|
||||
-- Подстраховка: если дрон удалён — вернуть материалы и очистить переменные
|
||||
hook.Add("Think", "spy_drone_cleanup_on_remove", function()
|
||||
if DRONE and not IsValid(DRONE) then
|
||||
DRONE = nil
|
||||
Thermal = false
|
||||
RestoreXRayMaterials()
|
||||
end
|
||||
end)
|
||||
|
||||
end
|
||||
49
garrysmod/addons/ft_drones/lua/entities/sw_sania/cl_init.lua
Normal file
49
garrysmod/addons/ft_drones/lua/entities/sw_sania/cl_init.lua
Normal file
@@ -0,0 +1,49 @@
|
||||
|
||||
include("shared.lua")
|
||||
if CLIENT then
|
||||
local jammat = Material( "models/shtormer/sania/jam.png" )
|
||||
local flag1 = false
|
||||
|
||||
function ENT:Think()
|
||||
if LocalPlayer():lvsGetVehicle():IsValid() then
|
||||
if LocalPlayer():lvsGetVehicle():GetNWEntity("UAVControl"):IsValid() then
|
||||
self:SetNWEntity("Jamming", LocalPlayer():lvsGetVehicle())
|
||||
jammer = ents.FindByClass( "sw_sania" )
|
||||
if jammer != nil then
|
||||
for i =1, #jammer do
|
||||
if jammer[i]:IsValid() then
|
||||
if LocalPlayer():lvsGetVehicle():GetPos():Distance(jammer[i]:GetPos()) < jammer[i].JamDistance then
|
||||
flag1 = true
|
||||
end
|
||||
end
|
||||
end
|
||||
if LocalPlayer():lvsGetVehicle():GetNWBool("Jammed") and !flag1 then
|
||||
LocalPlayer():lvsGetVehicle():SetNWBool("Jammed", false)
|
||||
flag1 = false
|
||||
elseif flag1 then
|
||||
LocalPlayer():lvsGetVehicle():SetNWBool("Jammed", true)
|
||||
end
|
||||
else
|
||||
flag1 = false
|
||||
end
|
||||
end
|
||||
if LocalPlayer():lvsGetVehicle():GetNWBool("Jammed") and LocalPlayer():lvsGetVehicle():GetPos():Distance(self:GetPos()) < self.JamDistance and self:IsValid() and LocalPlayer():lvsGetVehicle():IsValid() and self != NULL then
|
||||
print(LocalPlayer():lvsGetVehicle():GetNWEntity("UAVControl"):IsValid())
|
||||
hook.Add( "HUDPaint", "uavjamsania", function()
|
||||
if self != NULL and LocalPlayer():lvsGetVehicle() != NULL then
|
||||
surface.SetDrawColor( 255, 255, 255, 255/(LocalPlayer():lvsGetVehicle():GetPos():Distance(self:GetPos())/self.FullJamDistance) ) -- Set the drawing color
|
||||
surface.SetMaterial( jammat ) -- Use our cached material
|
||||
surface.DrawTexturedRect( 0, 0, ScrW(), ScrH() ) -- Actually draw the rectangle
|
||||
end
|
||||
end )
|
||||
elseif !LocalPlayer():lvsGetVehicle():GetNWBool("Jammed") or !LocalPlayer():lvsGetVehicle():IsValid() or !self:IsValid() or self == NULL or LocalPlayer():lvsGetVehicle() == NULL or LocalPlayer():lvsGetVehicle():GetNWEntity("UAVControl"):IsValid() == NULL then
|
||||
hook.Remove( "HUDPaint", "uavjamsania" )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:Draw()
|
||||
self:DrawModel()
|
||||
end
|
||||
|
||||
end
|
||||
158
garrysmod/addons/ft_drones/lua/entities/sw_sania/init.lua
Normal file
158
garrysmod/addons/ft_drones/lua/entities/sw_sania/init.lua
Normal file
@@ -0,0 +1,158 @@
|
||||
AddCSLuaFile("shared.lua")
|
||||
AddCSLuaFile("cl_init.lua")
|
||||
include("shared.lua")
|
||||
AddCSLuaFile("autorun/mur_drone_fx.lua")
|
||||
|
||||
if SERVER then
|
||||
|
||||
function ENT:Initialize()
|
||||
self:SetModel("models/shtormer/sania.mdl")
|
||||
self:PhysicsInit(SOLID_VPHYSICS)
|
||||
self:SetMoveType(MOVETYPE_VPHYSICS)
|
||||
self:SetSolid(SOLID_VPHYSICS)
|
||||
self:SetNWBool("JamEnabled", true)
|
||||
|
||||
local phys = self:GetPhysicsObject()
|
||||
if IsValid(phys) then phys:Wake() end
|
||||
|
||||
self.HP = self.HP or 500
|
||||
end
|
||||
|
||||
function ENT:OnTakeDamage(dmginfo)
|
||||
local dmg = dmginfo:GetDamage()
|
||||
self.HP = self.HP - dmg
|
||||
if self.HP <= 0 then
|
||||
self:Remove()
|
||||
end
|
||||
end
|
||||
|
||||
-- Глушение дронов
|
||||
function ENT:JamDrone(drone)
|
||||
if not IsValid(drone) then return end
|
||||
|
||||
local dist = drone:GetPos():Distance(self:GetPos())
|
||||
local jammed = dist <= self.JamDistance
|
||||
|
||||
drone:SetNWBool("Jammed", jammed)
|
||||
|
||||
if jammed then
|
||||
drone:SetNWFloat("JamStrength", 1 - dist / self.JamDistance)
|
||||
else
|
||||
drone:SetNWFloat("JamStrength", 0)
|
||||
end
|
||||
end
|
||||
|
||||
hook.Add("KeyPress", "SaniaToggle", function(ply, key)
|
||||
if key ~= IN_USE then return end
|
||||
|
||||
local tr = ply:GetEyeTrace()
|
||||
local ent = tr.Entity
|
||||
|
||||
if not IsValid(ent) then return end
|
||||
if ent:GetClass() ~= "sw_sania" then return end
|
||||
if tr.HitPos:Distance(ply:GetPos()) > 150 then return end
|
||||
|
||||
local newState = not ent:GetNWBool("JamEnabled")
|
||||
ent:SetNWBool("JamEnabled", newState)
|
||||
|
||||
if newState then
|
||||
ply:ChatPrint("РЭБ: ВКЛЮЧЕНА")
|
||||
ent:EmitSound("buttons/button3.wav", 75, 120)
|
||||
else
|
||||
ply:ChatPrint("РЭБ: ВЫКЛЮЧЕНА")
|
||||
ent:EmitSound("buttons/button19.wav", 75, 80)
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
|
||||
function ENT:Think()
|
||||
local sph = ents.FindInSphere(self:GetPos(), self.JamDistance)
|
||||
if not self:GetNWBool("JamEnabled") then
|
||||
for drone, _ in pairs(self.JammedDrones or {}) do
|
||||
if IsValid(drone) then
|
||||
drone:SetNWBool("Jammed", false)
|
||||
drone:SetNWFloat("JamStrength", 0)
|
||||
end
|
||||
end
|
||||
self.JammedDrones = {}
|
||||
self:NextThink(CurTime() + 0.1)
|
||||
return true
|
||||
end
|
||||
self.JammedDrones = self.JammedDrones or {}
|
||||
|
||||
-- временная таблица для отметки "кто сейчас в зоне"
|
||||
local inZone = {}
|
||||
|
||||
for i = 1, #sph do
|
||||
local ent = sph[i]
|
||||
if not IsValid(ent) then continue end
|
||||
|
||||
local class = ent:GetClass()
|
||||
|
||||
-- 1) Игрок в UAV
|
||||
if ent:IsPlayer() then
|
||||
local veh = ent:lvsGetVehicle()
|
||||
if IsValid(veh) then
|
||||
local uav = veh:GetNWEntity("UAVControl")
|
||||
if IsValid(uav) then
|
||||
local dist = veh:GetPos():Distance(self:GetPos())
|
||||
|
||||
if dist < self.JamDistance then
|
||||
if not self._savedRates then
|
||||
self._savedRates = {
|
||||
pitch = veh.TurnRatePitch,
|
||||
yaw = veh.TurnRateYaw,
|
||||
roll = veh.TurnRateRoll
|
||||
}
|
||||
end
|
||||
|
||||
local mul = math.max(0.1, dist / self.FullJamDistance)
|
||||
|
||||
veh.TurnRatePitch = self._savedRates.pitch * mul
|
||||
veh.TurnRateYaw = self._savedRates.yaw * mul
|
||||
veh.TurnRateRoll = self._savedRates.roll * mul
|
||||
else
|
||||
if self._savedRates then
|
||||
veh.TurnRatePitch = self._savedRates.pitch
|
||||
veh.TurnRateYaw = self._savedRates.yaw
|
||||
veh.TurnRateRoll = self._savedRates.roll
|
||||
self._savedRates = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- 2) Дроны
|
||||
if class == "mur_drone_entity"
|
||||
or class == "mur_drone_grenade"
|
||||
or class == "mur_drone_spy" then
|
||||
|
||||
inZone[ent] = true
|
||||
self.JammedDrones[ent] = true
|
||||
|
||||
local dist = ent:GetPos():Distance(self:GetPos())
|
||||
ent:SetNWBool("Jammed", true)
|
||||
ent:SetNWFloat("JamStrength", 1 - dist / self.JamDistance)
|
||||
end
|
||||
end
|
||||
|
||||
for drone, _ in pairs(self.JammedDrones) do
|
||||
if not IsValid(drone) then
|
||||
self.JammedDrones[drone] = nil
|
||||
continue
|
||||
end
|
||||
|
||||
if not inZone[drone] then
|
||||
drone:SetNWBool("Jammed", false)
|
||||
drone:SetNWFloat("JamStrength", 0)
|
||||
self.JammedDrones[drone] = nil
|
||||
end
|
||||
end
|
||||
|
||||
self:NextThink(CurTime() + 0.1)
|
||||
return true
|
||||
end
|
||||
|
||||
end
|
||||
15
garrysmod/addons/ft_drones/lua/entities/sw_sania/shared.lua
Normal file
15
garrysmod/addons/ft_drones/lua/entities/sw_sania/shared.lua
Normal file
@@ -0,0 +1,15 @@
|
||||
ENT.Base = "base_gmodentity"
|
||||
ENT.Type = "anim"
|
||||
|
||||
ENT.PrintName = "Sania UAV Jammer" -- название энтити в меню Q
|
||||
ENT.Author = "Shtormer" -- автор
|
||||
ENT.Contact = "" -- контакты с автором
|
||||
ENT.Information = "" -- описание энтити
|
||||
ENT.Category = "SW Weapons Factory" -- категория в меню Q
|
||||
ENT.Model = "models/shtormer/sania.mdl"
|
||||
ENT.Spawnable = true -- разрешается спавнить через спавн-меню Q
|
||||
ENT.AdminSpawnable = false -- разрешается спавнить только админам
|
||||
ENT.JamDistance = 5000
|
||||
ENT.FullJamDistance = 2000
|
||||
ENT.HP = 200
|
||||
ENT.JamEnabled = true
|
||||
Reference in New Issue
Block a user