diff --git a/lua/entities/glide_missile/cl_init.lua b/lua/entities/glide_missile/cl_init.lua new file mode 100644 index 0000000..aad8c6c --- /dev/null +++ b/lua/entities/glide_missile/cl_init.lua @@ -0,0 +1,61 @@ +include( "shared.lua" ) + +function ENT:Initialize() + self.smokeSpinSpeed = math.random( 60, 110 ) + + -- Create a RangedFeature to handle missile sounds + self.missileSounds = Glide.CreateRangedFeature( self, 4000 ) + self.missileSounds:SetActivateCallback( "ActivateSound" ) + self.missileSounds:SetDeactivateCallback( "DeactivateSound" ) + + -- Assume we have one for now, to avoid issues with the lock-on warnings clientside + self:SetHasTarget( true ) +end + +function ENT:OnRemove() + if self.missileSounds then + self.missileSounds:Destroy() + self.missileSounds = nil + end +end + +function ENT:ActivateSound() + if not self.missileLoop then + self.missileLoop = CreateSound( self, "glide/weapons/missile_loop.wav" ) + self.missileLoop:SetSoundLevel( 80 ) + self.missileLoop:Play() + end +end + +function ENT:DeactivateSound() + if self.missileLoop then + self.missileLoop:Stop() + self.missileLoop = nil + end +end + +local Effect = util.Effect +local EffectData = EffectData +local CurTime = CurTime + +function ENT:Think() + if self.missileSounds then + self.missileSounds:Think() + end + + if self:WaterLevel() > 0 then + self.smokeSpinSpeed = nil + + elseif self.smokeSpinSpeed then + local eff = EffectData() + eff:SetOrigin( self:GetPos() ) + eff:SetNormal( -self:GetForward() ) + eff:SetColor( self.smokeSpinSpeed ) + eff:SetScale( self:GetEffectiveness() ) + Effect( "glide_missile", eff ) + end + + self:SetNextClientThink( CurTime() + 0.02 ) + + return true +end diff --git a/lua/entities/glide_missile.lua b/lua/entities/glide_missile/init.lua similarity index 59% rename from lua/entities/glide_missile.lua rename to lua/entities/glide_missile/init.lua index b5d979b..2cf53f0 100644 --- a/lua/entities/glide_missile.lua +++ b/lua/entities/glide_missile/init.lua @@ -1,86 +1,7 @@ -AddCSLuaFile() +AddCSLuaFile( "shared.lua" ) +AddCSLuaFile( "cl_init.lua" ) -ENT.Type = "anim" -ENT.Base = "base_anim" -ENT.PrintName = "Missile" - -ENT.Spawnable = false -ENT.AdminOnly = false -ENT.VJTag_ID_Danger = true - -ENT.PhysgunDisabled = true -ENT.DoNotDuplicate = true -ENT.DisableDuplicator = true - -function ENT:SetupDataTables() - self:NetworkVar( "Float", "Effectiveness" ) - self:NetworkVar( "Bool", "HasTarget" ) -end - -local CurTime = CurTime - -if CLIENT then - function ENT:Initialize() - self.smokeSpinSpeed = math.random( 60, 110 ) - - -- Create a RangedFeature to handle missile sounds - self.missileSounds = Glide.CreateRangedFeature( self, 4000 ) - self.missileSounds:SetActivateCallback( "ActivateSound" ) - self.missileSounds:SetDeactivateCallback( "DeactivateSound" ) - - -- Assume we have one for now, to avoid issues with the lock-on warnings clientside - self:SetHasTarget( true ) - end - - function ENT:OnRemove() - if self.missileSounds then - self.missileSounds:Destroy() - self.missileSounds = nil - end - end - - function ENT:ActivateSound() - if not self.missileLoop then - self.missileLoop = CreateSound( self, "glide/weapons/missile_loop.wav" ) - self.missileLoop:SetSoundLevel( 80 ) - self.missileLoop:Play() - end - end - - function ENT:DeactivateSound() - if self.missileLoop then - self.missileLoop:Stop() - self.missileLoop = nil - end - end - - local Effect = util.Effect - local EffectData = EffectData - - function ENT:Think() - if self.missileSounds then - self.missileSounds:Think() - end - - if self:WaterLevel() > 0 then - self.smokeSpinSpeed = nil - - elseif self.smokeSpinSpeed then - local eff = EffectData() - eff:SetOrigin( self:GetPos() ) - eff:SetNormal( -self:GetForward() ) - eff:SetColor( self.smokeSpinSpeed ) - eff:SetScale( self:GetEffectiveness() ) - Effect( "glide_missile", eff ) - end - - self:SetNextClientThink( CurTime() + 0.02 ) - - return true - end -end - -if not SERVER then return end +include( "shared.lua" ) function ENT:Initialize() self:SetModel( "models/glide/weapons/homing_rocket.mdl" ) @@ -97,18 +18,23 @@ function ENT:Initialize() phys:SetDragCoefficient( 0 ) phys:EnableGravity( false ) phys:SetMass( 20 ) - phys:SetVelocityInstantaneous( self:GetForward() * 200 ) + phys:SetVelocityInstantaneous( self:GetForward() * 500 ) + + self:StartMotionController() end self.radius = 350 self.damage = 100 self.lifeTime = CurTime() + 6 - self.maxSpeed = 3000 - self.acceleration = 12000 - self.turnEfficiency = 7 + self.acceleration = 8000 + self.maxSpeed = 4000 -- This appears to be the default limit of the physics engine + self.turnRate = 50 -- degrees/s + self.missThreshold = 0.9 self.target = NULL - self.missThreshold = 0.9 + self.speed = 0 + self.aimDir = nil + self.applyThrust = true self.flareExplodeRadius = 200 * 200 @@ -117,7 +43,14 @@ end local IsValid = IsValid ---- Prepare the missile. +function ENT:OnRemove() + local phys = self:GetPhysicsObject() + + if IsValid( phys ) then + self:StopMotionController() + end +end + function ENT:SetupMissile( attacker, parent ) -- Set which player created this missile self.attacker = attacker @@ -126,6 +59,9 @@ function ENT:SetupMissile( attacker, parent ) self:SetOwner( parent ) end +--- Set the target this missile will track. +--- If the target is a player, seat, or a Glide vehicle, this will send +--- a network event to let the target know a missile is coming. function ENT:SetTarget( target ) if not target:IsPlayer() then -- Just use the target entity @@ -188,12 +124,34 @@ function ENT:Explode() self:Remove() end +function ENT:PhysicsCollide( data ) + -- Silently remove this missile when hitting the skybox + if data.TheirSurfaceProps == 76 then + self:Remove() + return + end + + self:Explode() +end + +function ENT:OnTakeDamage( dmginfo ) + -- Don't explode when other Glide missiles damaged this missile. + if dmginfo:IsExplosionDamage() then + local inflictor = dmginfo:GetInflictor() + + if IsValid( inflictor ) and inflictor:GetClass() == "glide_missile" then + return + end + end + + if not self.hasExploded then + self:Explode() + end +end + local FrameTime = FrameTime local Approach = math.Approach - local GetClosestFlare = Glide.GetClosestFlare -local ExpDecayAngle = Glide.ExpDecayAngle -local ZERO_ANGVEL = Vector() function ENT:Think() local t = CurTime() @@ -205,38 +163,29 @@ function ENT:Think() self:NextThink( t ) - local dt = FrameTime() local phys = self:GetPhysicsObject() if not self.applyThrust or not IsValid( phys ) then return true end - self:SetEffectiveness( Approach( self:GetEffectiveness(), 1, dt * 4 ) ) - if self:WaterLevel() > 0 then self.applyThrust = false phys:EnableGravity( true ) + return true end - local fw = self:GetForward() - local vel = phys:GetVelocity() - - -- Accelerate - local speed = vel:Length() - - if speed < self.maxSpeed then - speed = speed + self.acceleration * dt - end + local dt = FrameTime() - vel = fw * speed + self:SetEffectiveness( Approach( self:GetEffectiveness(), 1, dt * 4 ) ) -- Point towards the target local target = self.target local myPos = self:GetPos() + local fw = self:GetForward() -- Or towards a nearby flare - local flare, flareDistSqr = GetClosestFlare( myPos, self:GetForward(), 1500 ) + local flare, flareDistSqr = GetClosestFlare( myPos, fw, 1500 ) if IsValid( flare ) then target = flare @@ -254,43 +203,44 @@ function ENT:Think() local dir = targetPos - myPos dir:Normalize() - local decay = self:GetEffectiveness() * self.turnEfficiency - local myAng = self:GetAngles() - local targetAng = dir:Angle() - - myAng[1] = ExpDecayAngle( myAng[1], targetAng[1], decay, dt ) - myAng[2] = ExpDecayAngle( myAng[2], targetAng[2], decay, dt ) - myAng[3] = ExpDecayAngle( myAng[3], targetAng[3], decay, dt ) - - phys:SetAngles( myAng ) - + -- If the target is outside our FOV, stop tracking it if math.abs( dir:Dot( fw ) ) < self.missThreshold then - self.target = nil -- We've missed + self.target = nil + self.aimDir = nil + else + -- Let PhysicsSimulate handle this + self.aimDir = dir end else self:SetHasTarget( false ) end - phys:SetVelocityInstantaneous( vel ) - phys:SetAngleVelocityInstantaneous( ZERO_ANGVEL ) - return true end -function ENT:PhysicsCollide( data ) - if data.TheirSurfaceProps == 76 then - self:Remove() - return +local ApproachAngle = math.ApproachAngle +local ZERO_VEC = Vector() + +function ENT:PhysicsSimulate( phys, dt ) + if not self.applyThrust then return end + + -- Accelerate to reach maxSpeed + if self.speed < self.maxSpeed then + self.speed = self.speed + self.acceleration * dt end - self:Explode() -end -function ENT:OnTakeDamage( dmginfo ) - if dmginfo:IsExplosionDamage() then - local inflictor = dmginfo:GetInflictor() - if IsValid( inflictor ) and inflictor:GetClass() == "glide_missile" then - return - end + if self.aimDir then + local myAng = self:GetAngles() + local targetAng = self.aimDir:Angle() + local rate = self.turnRate * dt + + myAng[1] = ApproachAngle( myAng[1], targetAng[1], rate ) + myAng[2] = ApproachAngle( myAng[2], targetAng[2], rate ) + myAng[3] = ApproachAngle( myAng[3], targetAng[3], rate ) + + phys:SetAngles( myAng ) end - if not self.hasExploded then self:Explode() end + + phys:SetAngleVelocityInstantaneous( ZERO_VEC ) + phys:SetVelocityInstantaneous( self:GetForward() * self.speed ) end diff --git a/lua/entities/glide_missile/shared.lua b/lua/entities/glide_missile/shared.lua new file mode 100644 index 0000000..f8b28d0 --- /dev/null +++ b/lua/entities/glide_missile/shared.lua @@ -0,0 +1,18 @@ +AddCSLuaFile() + +ENT.Type = "anim" +ENT.Base = "base_anim" +ENT.PrintName = "Missile" + +ENT.Spawnable = false +ENT.AdminOnly = false +ENT.VJTag_ID_Danger = true + +ENT.PhysgunDisabled = true +ENT.DoNotDuplicate = true +ENT.DisableDuplicator = true + +function ENT:SetupDataTables() + self:NetworkVar( "Bool", "HasTarget" ) + self:NetworkVar( "Float", "Effectiveness" ) +end diff --git a/lua/entities/glide_missile_launcher.lua b/lua/entities/glide_missile_launcher.lua index c3822b6..80ff44e 100644 --- a/lua/entities/glide_missile_launcher.lua +++ b/lua/entities/glide_missile_launcher.lua @@ -111,7 +111,6 @@ function ENT:Think() missile.radius = self.explosionRadius missile.damage = self.explosionDamage missile.lifeTime = t + self.missileLifetime - missile.acceleration = 20000 end self:NextThink( t ) diff --git a/lua/weapons/glide_homing_launcher.lua b/lua/weapons/glide_homing_launcher.lua index ee21485..ec8ae23 100644 --- a/lua/weapons/glide_homing_launcher.lua +++ b/lua/weapons/glide_homing_launcher.lua @@ -194,7 +194,7 @@ function SWEP:PrimaryAttack() missile:Spawn() missile:SetupMissile( user, user ) missile.lifeTime = CurTime() + 10 - missile.turnEfficiency = 9 + missile.turnRate = 200 local target = self:GetLockTarget()