Files
VnUtest/garrysmod/addons/lvs_base/lua/entities/lvs_missile.lua
2026-03-31 10:27:04 +03:00

433 lines
10 KiB
Lua

AddCSLuaFile()
ENT.Type = "anim"
ENT.PrintName = "Missile"
ENT.Author = "Luna"
ENT.Information = "LVS Missile"
ENT.Category = "[LVS]"
ENT.Spawnable = true
ENT.AdminOnly = true
ENT.ExplosionEffect = "lvs_explosion_small"
ENT.lvsProjectile = true
ENT.VJ_ID_Danger = true
function ENT:SetupDataTables()
self:NetworkVar( "Bool", 0, "Active" )
self:NetworkVar( "Entity", 0, "NWTarget" )
end
if SERVER then
util.AddNetworkString( "lvs_missile_hud" )
function ENT:GetAvailableTargets()
local targets = {
[1] = player.GetAll(),
[2] = LVS:GetVehicles(),
[3] = LVS:GetNPCs(),
}
return targets
end
function ENT:FindTarget( pos, forward, cone_ang, cone_len )
local targets = self:GetAvailableTargets()
local Attacker = self:GetAttacker()
local Parent = self:GetParent()
local Owner = self:GetOwner()
local Target = NULL
local DistToTarget = 0
for _, tbl in ipairs( targets ) do
for _, ent in pairs( tbl ) do
if not IsValid( ent ) or ent == Parent or ent == Owner or Target == ent or Attacker == ent then continue end
local pos_ent = ent:GetPos()
local dir = (pos_ent - pos):GetNormalized()
local ang = math.deg( math.acos( math.Clamp( forward:Dot( dir ) ,-1,1) ) )
if ang > cone_ang then continue end
local dist, _, _ = util.DistanceToLine( pos, pos + forward * cone_len, pos_ent )
if not IsValid( Target ) then
Target = ent
DistToTarget = dist
continue
end
if dist < DistToTarget then
Target = ent
DistToTarget = dist
end
end
end
self:SetTarget( Target )
local ply = self:GetAttacker()
if not IsValid( ply ) or not ply:IsPlayer() then return end
net.Start( "lvs_missile_hud", true )
net.WriteEntity( self )
net.Send( ply )
end
function ENT:SetEntityFilter( filter )
if not istable( filter ) then return end
self._FilterEnts = {}
for _, ent in pairs( filter ) do
self._FilterEnts[ ent ] = true
end
end
function ENT:SetTarget( ent ) self:SetNWTarget( ent ) end
function ENT:SetDamage( num ) self._dmg = num end
function ENT:SetForce( num ) self._force = num end
function ENT:SetThrust( num ) self._thrust = num end
function ENT:SetSpeed( num ) self._speed = num end
function ENT:SetTurnSpeed( num ) self._turnspeed = num end
function ENT:SetRadius( num ) self._radius = num end
function ENT:SetAttacker( ent ) self._attacker = ent end
function ENT:GetAttacker() return self._attacker or NULL end
function ENT:GetDamage() return (self._dmg or 100) end
function ENT:GetForce() return (self._force or 4000) end
function ENT:GetRadius() return (self._radius or 250) end
function ENT:GetSpeed() return (self._speed or 4000) end
function ENT:GetTurnSpeed() return (self._turnspeed or 1) * 100 end
function ENT:GetThrust() return (self._thrust or 500) end
function ENT:GetTarget()
if IsValid( self:GetNWTarget() ) then
local Pos = self:GetPos()
local tPos = self:GetTargetPos()
local Sub = tPos - Pos
local Len = Sub:Length()
local Dir = Sub:GetNormalized()
local Forward = self:GetForward()
local AngToTarget = math.deg( math.acos( math.Clamp( Forward:Dot( Dir ) ,-1,1) ) )
local LooseAng = math.min( Len / 100, 90 )
if AngToTarget > LooseAng then
self:SetNWTarget( NULL )
end
end
return self:GetNWTarget()
end
function ENT:GetTargetPos()
local Target = self:GetNWTarget()
if not IsValid( Target ) then return Vector(0,0,0) end
if isfunction( Target.GetMissileOffset ) then
return Target:LocalToWorld( Target:GetMissileOffset() )
end
return Target:GetPos()
end
function ENT:SpawnFunction( ply, tr, ClassName )
local ent = ents.Create( ClassName )
ent:SetPos( ply:GetShootPos() )
ent:SetAngles( ply:EyeAngles() )
ent:Spawn()
ent:Activate()
ent:SetAttacker( ply )
ent:Enable()
return ent
end
function ENT:Initialize()
self:SetModel( "models/weapons/w_missile_launch.mdl" )
self:SetMoveType( MOVETYPE_NONE )
self:SetRenderMode( RENDERMODE_TRANSALPHA )
end
function ENT:Enable()
if self.IsEnabled then return end
local Parent = self:GetParent()
if IsValid( Parent ) then
self:SetOwner( Parent )
self:SetParent( NULL )
end
self:PhysicsInit( SOLID_VPHYSICS )
self:SetMoveType( MOVETYPE_VPHYSICS )
self:SetSolid( SOLID_VPHYSICS )
self:SetCollisionGroup( COLLISION_GROUP_NONE )
self:PhysWake()
self.IsEnabled = true
local pObj = self:GetPhysicsObject()
if not IsValid( pObj ) then
self:Remove()
print("LVS: missing model. Missile terminated.")
return
end
pObj:SetMass( 1 )
pObj:EnableGravity( false )
pObj:EnableMotion( true )
pObj:EnableDrag( false )
self:SetTrigger( true )
self:StartMotionController()
self:PhysWake()
self.SpawnTime = CurTime()
self:SetActive( true )
end
function ENT:PhysicsSimulate( phys, deltatime )
phys:Wake()
local Thrust = self:GetThrust()
local Speed = self:GetSpeed()
local Pos = self:GetPos()
local velL = self:WorldToLocal( Pos + self:GetVelocity() )
local ForceLinear = (Vector( Speed * Thrust,0,0) - velL) * deltatime
local Target = self:GetTarget()
if not IsValid( Target ) then
return (-phys:GetAngleVelocity() * 250 * deltatime), ForceLinear, SIM_LOCAL_ACCELERATION
end
local AngForce = -self:WorldToLocalAngles( (self:GetTargetPos() - Pos):Angle() )
local ForceAngle = (Vector(AngForce.r,-AngForce.p,-AngForce.y) * self:GetTurnSpeed() - phys:GetAngleVelocity() * 5 ) * 250 * deltatime
return ForceAngle, ForceLinear, SIM_LOCAL_ACCELERATION
end
function ENT:Think()
local T = CurTime()
self:NextThink( T + 1 )
if not self.SpawnTime then return true end
if (self.SpawnTime + 12) < T then
self:Detonate()
end
return true
end
ENT.IgnoreCollisionGroup = {
[COLLISION_GROUP_NONE] = true,
[COLLISION_GROUP_WORLD] = true,
[COLLISION_GROUP_INTERACTIVE_DEBRIS] = true,
}
function ENT:StartTouch( entity )
if entity == self:GetAttacker() then return end
if istable( self._FilterEnts ) and self._FilterEnts[ entity ] then return end
if entity.GetCollisionGroup and self.IgnoreCollisionGroup[ entity:GetCollisionGroup() ] then return end
if entity.lvsProjectile then return end
self:Detonate( entity )
end
function ENT:EndTouch( entity )
end
function ENT:Touch( entity )
end
function ENT:PhysicsCollide( data )
if istable( self._FilterEnts ) and self._FilterEnts[ data.HitEntity ] then return end
self:Detonate( data.HitEntity )
end
function ENT:OnTakeDamage( dmginfo )
end
function ENT:Detonate( target )
if not self.IsEnabled or self.IsDetonated then return end
self.IsDetonated = true
local Pos = self:GetPos()
local effectdata = EffectData()
effectdata:SetOrigin( Pos )
util.Effect( self.ExplosionEffect, effectdata, true, true )
local attacker = self:GetAttacker()
LVS:BlastDamage( Pos, self:GetForward(), IsValid( attacker ) and attacker or game.GetWorld(), self, self:GetDamage(), DMG_BLAST, self:GetRadius(), self:GetForce() )
SafeRemoveEntityDelayed( self, FrameTime() )
end
else
function ENT:Initialize()
end
function ENT:Enable()
if self.IsEnabled then return end
self.IsEnabled = true
self.snd = CreateSound(self, "weapons/rpg/rocket1.wav")
self.snd:SetSoundLevel( 80 )
self.snd:Play()
local effectdata = EffectData()
effectdata:SetOrigin( self:GetPos() )
effectdata:SetEntity( self )
util.Effect( "lvs_missiletrail", effectdata )
end
function ENT:CalcDoppler()
local Ent = LocalPlayer()
local ViewEnt = Ent:GetViewEntity()
if Ent:lvsGetVehicle() == self then
if ViewEnt == Ent then
Ent = self
else
Ent = ViewEnt
end
else
Ent = ViewEnt
end
local sVel = self:GetVelocity()
local oVel = Ent:GetVelocity()
local SubVel = oVel - sVel
local SubPos = self:GetPos() - Ent:GetPos()
local DirPos = SubPos:GetNormalized()
local DirVel = SubVel:GetNormalized()
local A = math.acos( math.Clamp( DirVel:Dot( DirPos ) ,-1,1) )
return (1 + math.cos( A ) * SubVel:Length() / 13503.9)
end
function ENT:Draw()
if not self:GetActive() then return end
self:DrawModel()
end
function ENT:Think()
if self.snd then
self.snd:ChangePitch( 100 * self:CalcDoppler() )
end
if self.IsEnabled then return end
if self:GetActive() then
self:Enable()
end
end
function ENT:SoundStop()
if self.snd then
self.snd:Stop()
end
end
function ENT:OnRemove()
self:SoundStop()
end
local function DrawDiamond( X, Y, radius, angoffset )
angoffset = angoffset or 0
local segmentdist = 90
local radius2 = radius + 1
for ang = 0, 360, segmentdist do
local a = ang + angoffset
surface.DrawLine( X + math.cos( math.rad( a ) ) * radius, Y - math.sin( math.rad( a ) ) * radius, X + math.cos( math.rad( a + segmentdist ) ) * radius, Y - math.sin( math.rad( a + segmentdist ) ) * radius )
surface.DrawLine( X + math.cos( math.rad( a ) ) * radius2, Y - math.sin( math.rad( a ) ) * radius2, X + math.cos( math.rad( a + segmentdist ) ) * radius2, Y - math.sin( math.rad( a + segmentdist ) ) * radius2 )
end
end
local color_red = Color(255,0,0,255)
local HudTargets = {}
hook.Add( "HUDPaint", "!!!!lvs_missile_hud", function()
local T = CurTime()
local Index = 0
surface.SetDrawColor( 255, 0, 0, 255 )
for ID, _ in pairs( HudTargets ) do
local Missile = Entity( ID )
if not IsValid( Missile ) then
HudTargets[ ID ] = nil
continue
end
local Target = Missile:GetNWTarget()
if not IsValid( Target ) then
HudTargets[ ID ] = nil
continue
end
local MissilePos = Missile:GetPos():ToScreen()
local TargetPos = Target:LocalToWorld( Target:OBBCenter() ):ToScreen()
Index = Index + 1
if not TargetPos.visible then continue end
DrawDiamond( TargetPos.x, TargetPos.y, 40, ID * 1337 - T * 100 )
draw.DrawText("LOCK", "LVS_FONT", TargetPos.x + 20, TargetPos.y + 20, color_red, TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP )
if not MissilePos.visible then continue end
DrawDiamond( MissilePos.x, MissilePos.y, 16, ID * 1337 - T * 100 )
draw.DrawText( Index, "LVS_FONT", MissilePos.x + 10, MissilePos.y + 10, color_red, TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP )
surface.DrawLine( MissilePos.x, MissilePos.y, TargetPos.x, TargetPos.y )
end
end )
net.Receive( "lvs_missile_hud", function( len )
local ent = net.ReadEntity()
if not IsValid( ent ) then return end
HudTargets[ ent:EntIndex() ] = true
end )
end