417 lines
17 KiB
Lua
417 lines
17 KiB
Lua
AddCSLuaFile()
|
|
|
|
ENT.Base = "base_gmodentity"
|
|
ENT.PrintName = "Particle Controller - Tracer"
|
|
ENT.Author = ""
|
|
|
|
ENT.Spawnable = false
|
|
ENT.AdminSpawnable = false
|
|
ENT.RenderGroup = RENDERGROUP_NONE
|
|
|
|
|
|
|
|
|
|
function ENT:SetupDataTables()
|
|
|
|
self:NetworkVar( "Entity", 0, "TargetEnt" );
|
|
|
|
self:NetworkVar( "String", 0, "EffectName" );
|
|
self:NetworkVar( "String", 1, "NumpadState" );
|
|
self:NetworkVar( "String", 2, "Impact_EffectName" );
|
|
|
|
self:NetworkVar( "Float", 0, "RepeatRate" );
|
|
self:NetworkVar( "Float", 1, "TracerSpread" );
|
|
self:NetworkVar( "Float", 2, "EffectLifetime" );
|
|
|
|
self:NetworkVar( "Int", 0, "AttachNum" );
|
|
self:NetworkVar( "Int", 1, "NumpadKey" );
|
|
self:NetworkVar( "Int", 2, "TracerCount" );
|
|
|
|
self:NetworkVar( "Bool", 0, "Active" );
|
|
self:NetworkVar( "Bool", 1, "Toggle" );
|
|
self:NetworkVar( "Bool", 2, "LeaveBulletHoles" );
|
|
|
|
//we have 3 pieces of information we need for util.effect, and they're not going to be changed after the ent's been spawned, so there's no reason we can't save space by storing them all in this vector
|
|
//self:NetworkVar( "Vector", 0, "UtilEffectInfo" ); //x = scale, y = magnitude, x = radius
|
|
self:NetworkVar( "Vector", 0, "Impact_UtilEffectInfo" ); //x = scale, y = magnitude, x = radius
|
|
self:NetworkVar( "Vector", 1, "Impact_ColorInfo" );
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function ENT:Initialize()
|
|
|
|
local target = self:GetTargetEnt()
|
|
|
|
|
|
if SERVER then
|
|
|
|
//Use ourselves as the target for control point 1 - because of how we have tracer effects set up, we're going to do this for both util and .pcf effects
|
|
local attachment1 = target:GetAttachment( self:GetAttachNum() )
|
|
if attachment1 != nil then
|
|
self:SetPos( attachment1.Pos )
|
|
self:SetAngles( attachment1.Ang )
|
|
self:Fire("setparentattachment", target:GetAttachments()[self:GetAttachNum()].name, 0.01)
|
|
self:SetTargetEnt(self)
|
|
end
|
|
|
|
else
|
|
|
|
//Set things up for a particlesystem effect
|
|
|
|
if !string.StartWith( self:GetEffectName(), "!UTILEFFECT!" ) then
|
|
|
|
//Make a table of control point information, so we only have to do this once instead of every time AttachParticle runs.
|
|
|
|
local clrtb = nil
|
|
if self:GetColor().r == 0 and self:GetColor().g == 0 and self:GetColor().b == 0 then
|
|
//MsgN("color = false")
|
|
else
|
|
//MsgN("color = true")
|
|
if self:GetColor().a == 1 then
|
|
clrtb = { position = Vector( self:GetColor().r / 255, self:GetColor().g / 255, self:GetColor().b / 255 ) }
|
|
else
|
|
clrtb = { position = Vector( self:GetColor().r, self:GetColor().g, self:GetColor().b ) }
|
|
end
|
|
end
|
|
self.cpointtable = {}
|
|
self.cpointtable[1] = { entity = target, attachtype = PATTACH_ABSORIGIN_FOLLOW }
|
|
|
|
//this entry is a placeholder - we want the second controlpoint to be at the endpos of a trace we haven't performed yet
|
|
self.cpointtable[2] = true
|
|
|
|
for i = 3, 64 do
|
|
if clrtb then
|
|
self.cpointtable[i] = clrtb
|
|
else
|
|
self.cpointtable[i] = self.cpointtable[1]
|
|
end
|
|
end
|
|
|
|
PrecacheParticleSystem(self:GetEffectName())
|
|
|
|
end
|
|
|
|
end
|
|
self:SetModel("models/hunter/plates/plate.mdl") //it's a tiny block so that model-covering fx show up at a single point instead of all over an invisible error model
|
|
self:SetNoDraw(true)
|
|
|
|
|
|
if self:GetRepeatRate() > 0 then
|
|
//we're going to be repeating the effect, so let's set that up and let think do the rest
|
|
self.NextRepeat = 0
|
|
else
|
|
//we won't be repeating the effect, so just attach it right now
|
|
if self:GetActive() then self:AttachParticle() end
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function ENT:Think()
|
|
|
|
if SERVER then return end
|
|
|
|
if self:GetNumpadState() == "off" then
|
|
//MsgN("turn off")
|
|
self:SetNumpadState("")
|
|
end
|
|
|
|
if self:GetNumpadState() == "on" then
|
|
//MsgN("turn on")
|
|
self:SetNumpadState("")
|
|
if self:GetRepeatRate() > 0 then
|
|
//we're going to be repeating the effect, so let's set that up and let think do the rest
|
|
self.NextRepeat = 0
|
|
else
|
|
//we won't be repeating the effect, so just attach it right now
|
|
if self:GetActive() then self:AttachParticle() end
|
|
end
|
|
end
|
|
|
|
|
|
if self:GetActive() == true and self:GetRepeatRate() > 0 then
|
|
if !( self.NextRepeat > CurTime() ) then
|
|
self:AttachParticle()
|
|
self.NextRepeat = CurTime() + self:GetRepeatRate()
|
|
end
|
|
end
|
|
|
|
self:NextThink(CurTime())
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function ENT:AttachParticle()
|
|
|
|
if SERVER then return end
|
|
|
|
local target = self:GetTargetEnt()
|
|
//local attachnum = self:GetAttachNum() //this isn't used anywhere
|
|
local effectname = self:GetEffectName()
|
|
|
|
local tracerspread = math.Clamp( self:GetTracerSpread(), 0, 4)
|
|
local tracercount = self:GetTracerCount()
|
|
local leavebulletholes = self:GetLeaveBulletHoles()
|
|
local effectlifetime = math.Clamp( self:GetEffectLifetime(), 0.5, 5)
|
|
|
|
local impact_effectname = self:GetImpact_EffectName()
|
|
|
|
if effectname == nil or !target:IsValid() then return end
|
|
|
|
|
|
for i = 1, tracercount do
|
|
|
|
//Do a trace to find the endpoint of the bullet
|
|
local tracedata = {}
|
|
if self:GetParent() != NULL then tracedata.filter = {self:GetParent(), self:GetParent():GetParent()} end //this returns a null entity if the player is outside of the visleaf, be careful
|
|
local tracepos = target:GetPos()
|
|
local traceang = target:GetAngles()
|
|
|
|
//spread
|
|
local randang = AngleRand()
|
|
traceang:RotateAroundAxis( traceang:Forward(), randang.r )
|
|
traceang:RotateAroundAxis( traceang:Right(), randang.p * (tracerspread / 2) )
|
|
traceang:RotateAroundAxis( traceang:Up(), randang.y * (tracerspread / 4) )
|
|
|
|
tracedata.start = tracepos
|
|
tracedata.endpos = tracepos+(traceang:Forward()*20000)
|
|
local trace = util.TraceLine(tracedata)
|
|
|
|
|
|
//we're going to tie the tracer effect and impact effect to a clientside ent that expires quickly, so people can't create a million permanent medigun beams or anything like that
|
|
local tracerent = octolib.createDummy("models/hunter/plates/plate.mdl")
|
|
tracerent.RenderGroup = RENDERGROUP_OTHER
|
|
tracerent:SetNoDraw(true)
|
|
tracerent:SetPos(trace.HitPos)
|
|
tracerent:SetAngles(trace.HitNormal:Angle())
|
|
timer.Simple(effectlifetime, function() if IsValid(tracerent) then tracerent:Remove() end; end)
|
|
tracerent:Spawn()
|
|
tracerent:Activate()
|
|
|
|
if self.cpointtable then self.cpointtable[2] = { entity = tracerent, attachtype = PATTACH_ABSORIGIN_FOLLOW } end
|
|
|
|
|
|
//Create a bullet hole effect, if enabled
|
|
if leavebulletholes == true then
|
|
local impacteffectdata = EffectData()
|
|
impacteffectdata:SetStart( tracepos )
|
|
impacteffectdata:SetOrigin( trace.HitPos )
|
|
impacteffectdata:SetNormal( trace.HitNormal )
|
|
impacteffectdata:SetEntity( trace.Entity )
|
|
impacteffectdata:SetSurfaceProp( trace.SurfaceProps )
|
|
impacteffectdata:SetHitBox( trace.HitBox )
|
|
util.Effect( "Impact", impacteffectdata )
|
|
end
|
|
|
|
|
|
//Create the tracer effect
|
|
if string.StartWith( effectname, "!UTILEFFECT!" ) then
|
|
|
|
//Create a util effect
|
|
|
|
//Unfortunately, we have to do all of this every single time the effect repeats, because if we do it in Initialize instead, a whole bunch of stuff doesn't work properly
|
|
|
|
//local effectscale = self:GetUtilEffectInfo().x
|
|
//local effectmagnitude = self:GetUtilEffectInfo().y
|
|
//local effectradius = self:GetUtilEffectInfo().z
|
|
|
|
local luaeffectdata = EffectData()
|
|
luaeffectdata:SetEntity( tracerent )
|
|
|
|
luaeffectdata:SetScale(5000) //for tracer effects, scale is the speed of the bullet, so we need to keep this high
|
|
//luaeffectdata:SetMagnitude( 20 )
|
|
//luaeffectdata:SetRadius( 10 )
|
|
|
|
//flags can be set by typing !FLAG#! at the end of the effect name
|
|
luaeffectdata:SetFlags( 0 )
|
|
if string.EndsWith( effectname, "!" ) then
|
|
if string.find( effectname, "!FLAG1!" ) then luaeffectdata:SetFlags( 1 ) effectname = string.Replace( effectname, "!FLAG1!", "" ) end
|
|
if string.find( effectname, "!FLAG2!" ) then luaeffectdata:SetFlags( 2 ) effectname = string.Replace( effectname, "!FLAG2!", "" ) end
|
|
if string.find( effectname, "!FLAG3!" ) then luaeffectdata:SetFlags( 3 ) effectname = string.Replace( effectname, "!FLAG3!", "" ) end
|
|
if string.find( effectname, "!FLAG4!" ) then luaeffectdata:SetFlags( 4 ) effectname = string.Replace( effectname, "!FLAG4!", "" ) end
|
|
if string.find( effectname, "!FLAG5!" ) then luaeffectdata:SetFlags( 5 ) effectname = string.Replace( effectname, "!FLAG5!", "" ) end
|
|
if string.find( effectname, "!FLAG6!" ) then luaeffectdata:SetFlags( 6 ) effectname = string.Replace( effectname, "!FLAG6!", "" ) end
|
|
if string.find( effectname, "!FLAG7!" ) then luaeffectdata:SetFlags( 7 ) effectname = string.Replace( effectname, "!FLAG7!", "" ) end
|
|
if string.find( effectname, "!FLAG8!" ) then luaeffectdata:SetFlags( 8 ) effectname = string.Replace( effectname, "!FLAG8!", "" ) end
|
|
if string.find( effectname, "!FLAG9!" ) then luaeffectdata:SetFlags( 9 ) effectname = string.Replace( effectname, "!FLAG9!", "" ) end
|
|
end
|
|
|
|
//colors can also be set the same way - i don't think there are any tracers that use these
|
|
luaeffectdata:SetColor(0)
|
|
if string.EndsWith( effectname, "!" ) then
|
|
if string.find( effectname, "!COLOR1!" ) then luaeffectdata:SetColor( 1 ) effectname = string.Replace( effectname, "!COLOR1!", "" ) end
|
|
if string.find( effectname, "!COLOR2!" ) then luaeffectdata:SetColor( 2 ) effectname = string.Replace( effectname, "!COLOR2!", "" ) end
|
|
if string.find( effectname, "!COLOR3!" ) then luaeffectdata:SetColor( 3 ) effectname = string.Replace( effectname, "!COLOR3!", "" ) end
|
|
if string.find( effectname, "!COLOR4!" ) then luaeffectdata:SetColor( 4 ) effectname = string.Replace( effectname, "!COLOR4!", "" ) end
|
|
if string.find( effectname, "!COLOR5!" ) then luaeffectdata:SetColor( 5 ) effectname = string.Replace( effectname, "!COLOR5!", "" ) end
|
|
if string.find( effectname, "!COLOR6!" ) then luaeffectdata:SetColor( 6 ) effectname = string.Replace( effectname, "!COLOR6!", "" ) end
|
|
if string.find( effectname, "!COLOR7!" ) then luaeffectdata:SetColor( 7 ) effectname = string.Replace( effectname, "!COLOR7!", "" ) end
|
|
if string.find( effectname, "!COLOR8!" ) then luaeffectdata:SetColor( 8 ) effectname = string.Replace( effectname, "!COLOR8!", "" ) end
|
|
if string.find( effectname, "!COLOR9!" ) then luaeffectdata:SetColor( 9 ) effectname = string.Replace( effectname, "!COLOR9!", "" ) end
|
|
end
|
|
|
|
//dumb situational crap
|
|
//if string.find( string.lower(effectname), "shakeropes" ) then luaeffectdata:SetMagnitude( effectmagnitude * 20 ) end
|
|
//if string.find( string.lower(effectname), "thumperdust" ) then luaeffectdata:SetScale( effectscale * 50 ) end
|
|
//if string.find( string.lower(effectname), "bloodspray" ) then luaeffectdata:SetScale( effectscale * 4 ) end
|
|
|
|
luaeffectdata:SetOrigin( trace.HitPos )
|
|
luaeffectdata:SetStart( tracepos )
|
|
//luaeffectdata:SetAngles( traceang )
|
|
luaeffectdata:SetNormal( trace.HitNormal:Angle():Forward() )
|
|
|
|
util.Effect( string.Replace( effectname, "!UTILEFFECT!", "" ), luaeffectdata )
|
|
|
|
else
|
|
|
|
//Create a particlesystem effect
|
|
|
|
tracerent:CreateParticleEffect(effectname,self.cpointtable)
|
|
|
|
end
|
|
|
|
|
|
//Create the impact effect, if enabled
|
|
if impact_effectname != "" then
|
|
if string.StartWith( impact_effectname, "!UTILEFFECT!" ) then
|
|
|
|
//Create a util effect
|
|
|
|
//Unfortunately, we have to do all of this every single time the effect repeats, because if we do it in Initialize instead, a whole bunch of stuff doesn't work properly
|
|
|
|
local effectscale = self:GetImpact_UtilEffectInfo().x
|
|
local effectmagnitude = self:GetImpact_UtilEffectInfo().y
|
|
local effectradius = self:GetImpact_UtilEffectInfo().z
|
|
|
|
local impacteffectdata = EffectData()
|
|
impacteffectdata:SetEntity( tracerent )
|
|
if ( string.find(impact_effectname, "Tracer", 0, true) != nil ) then impacteffectdata:SetScale(5000) else impacteffectdata:SetScale( effectscale ) end //for tracer effects, scale is the speed of the bullet, so we need to keep this high
|
|
impacteffectdata:SetMagnitude( effectmagnitude )
|
|
impacteffectdata:SetRadius(effectradius )
|
|
|
|
//flags can be set by typing !FLAG#! at the end of the effect name
|
|
impacteffectdata:SetFlags( 0 )
|
|
if string.EndsWith( impact_effectname, "!" ) then
|
|
if string.find( impact_effectname, "!FLAG1!" ) then impacteffectdata:SetFlags( 1 ) impact_effectname = string.Replace( impact_effectname, "!FLAG1!", "" ) end
|
|
if string.find( impact_effectname, "!FLAG2!" ) then impacteffectdata:SetFlags( 2 ) impact_effectname = string.Replace( impact_effectname, "!FLAG2!", "" ) end
|
|
if string.find( impact_effectname, "!FLAG3!" ) then impacteffectdata:SetFlags( 3 ) impact_effectname = string.Replace( impact_effectname, "!FLAG3!", "" ) end
|
|
if string.find( impact_effectname, "!FLAG4!" ) then impacteffectdata:SetFlags( 4 ) impact_effectname = string.Replace( impact_effectname, "!FLAG4!", "" ) end
|
|
if string.find( impact_effectname, "!FLAG5!" ) then impacteffectdata:SetFlags( 5 ) impact_effectname = string.Replace( impact_effectname, "!FLAG5!", "" ) end
|
|
if string.find( impact_effectname, "!FLAG6!" ) then impacteffectdata:SetFlags( 6 ) impact_effectname = string.Replace( impact_effectname, "!FLAG6!", "" ) end
|
|
if string.find( impact_effectname, "!FLAG7!" ) then impacteffectdata:SetFlags( 7 ) impact_effectname = string.Replace( impact_effectname, "!FLAG7!", "" ) end
|
|
if string.find( impact_effectname, "!FLAG8!" ) then impacteffectdata:SetFlags( 8 ) impact_effectname = string.Replace( impact_effectname, "!FLAG8!", "" ) end
|
|
if string.find( impact_effectname, "!FLAG9!" ) then impacteffectdata:SetFlags( 9 ) impact_effectname = string.Replace( impact_effectname, "!FLAG9!", "" ) end
|
|
end
|
|
|
|
//colors can also be set the same way
|
|
impacteffectdata:SetColor(0)
|
|
if string.EndsWith( impact_effectname, "!" ) then
|
|
if string.find( impact_effectname, "!COLOR1!" ) then impacteffectdata:SetColor( 1 ) impact_effectname = string.Replace( impact_effectname, "!COLOR1!", "" ) end
|
|
if string.find( impact_effectname, "!COLOR2!" ) then impacteffectdata:SetColor( 2 ) impact_effectname = string.Replace( impact_effectname, "!COLOR2!", "" ) end
|
|
if string.find( impact_effectname, "!COLOR3!" ) then impacteffectdata:SetColor( 3 ) impact_effectname = string.Replace( impact_effectname, "!COLOR3!", "" ) end
|
|
if string.find( impact_effectname, "!COLOR4!" ) then impacteffectdata:SetColor( 4 ) impact_effectname = string.Replace( impact_effectname, "!COLOR4!", "" ) end
|
|
if string.find( impact_effectname, "!COLOR5!" ) then impacteffectdata:SetColor( 5 ) impact_effectname = string.Replace( impact_effectname, "!COLOR5!", "" ) end
|
|
if string.find( impact_effectname, "!COLOR6!" ) then impacteffectdata:SetColor( 6 ) impact_effectname = string.Replace( impact_effectname, "!COLOR6!", "" ) end
|
|
if string.find( impact_effectname, "!COLOR7!" ) then impacteffectdata:SetColor( 7 ) impact_effectname = string.Replace( impact_effectname, "!COLOR7!", "" ) end
|
|
if string.find( impact_effectname, "!COLOR8!" ) then impacteffectdata:SetColor( 8 ) impact_effectname = string.Replace( impact_effectname, "!COLOR8!", "" ) end
|
|
if string.find( impact_effectname, "!COLOR9!" ) then impacteffectdata:SetColor( 9 ) impact_effectname = string.Replace( impact_effectname, "!COLOR9!", "" ) end
|
|
end
|
|
|
|
//dumb situational crap
|
|
if string.find( string.lower(impact_effectname), "shakeropes" ) then impacteffectdata:SetMagnitude( effectmagnitude * 20 ) end
|
|
if string.find( string.lower(impact_effectname), "thumperdust" ) then impacteffectdata:SetScale( effectscale * 50 ) end
|
|
if string.find( string.lower(impact_effectname), "bloodspray" ) then impacteffectdata:SetScale( effectscale * 4 ) end
|
|
|
|
impacteffectdata:SetStart( tracerent:GetPos() )
|
|
impacteffectdata:SetOrigin( tracerent:GetPos() )
|
|
impacteffectdata:SetAngles( tracerent:GetAngles() )
|
|
impacteffectdata:SetNormal( trace.HitNormal )
|
|
|
|
util.Effect( string.Replace( impact_effectname, "!UTILEFFECT!", "" ), impacteffectdata )
|
|
|
|
else
|
|
|
|
//Create a particlesystem effect
|
|
|
|
local clrtb = nil
|
|
if self:GetImpact_ColorInfo() == Vector(0,0,0) then
|
|
//MsgN("color = false")
|
|
else
|
|
//MsgN("color = true")
|
|
clrtb = { position = self:GetImpact_ColorInfo() }
|
|
end
|
|
local impactcpointtable = {}
|
|
impactcpointtable[1] = { entity = tracerent, attachtype = PATTACH_ABSORIGIN_FOLLOW }
|
|
|
|
for i = 2, 64 do
|
|
if clrtb then
|
|
impactcpointtable[i] = clrtb
|
|
else
|
|
impactcpointtable[i] = impactcpointtable[1]
|
|
end
|
|
end
|
|
|
|
tracerent:CreateParticleEffect(impact_effectname,impactcpointtable)
|
|
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
//numpad functions
|
|
if SERVER then
|
|
|
|
local function NumpadPress( pl, ent )
|
|
|
|
if ( !ent || ent == NULL ) then return end
|
|
|
|
if ( ent:GetToggle() ) then
|
|
if ent:GetActive() == false then
|
|
ent:SetActive(true)
|
|
ent:SetNumpadState("on")
|
|
else
|
|
ent:SetActive(false)
|
|
ent:SetNumpadState("off")
|
|
end
|
|
else
|
|
ent:SetActive( true )
|
|
ent:SetNumpadState("on")
|
|
end
|
|
|
|
end
|
|
|
|
local function NumpadRelease( pl, ent )
|
|
|
|
if ( !ent || ent == NULL ) then return end
|
|
|
|
if ( ent:GetToggle() ) then return end
|
|
|
|
ent:SetActive(false)
|
|
ent:SetNumpadState("off")
|
|
|
|
end
|
|
|
|
numpad.Register( "Particle_Press", NumpadPress )
|
|
numpad.Register( "Particle_Release", NumpadRelease )
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
//don't duplicate this
|
|
duplicator.RegisterEntityClass( "particlecontroller_tracer", function( ply, data )
|
|
end, "Data" )
|