--[[ | This file was obtained through the combined efforts | of Madbluntz & Plymouth Antiquarian Society. | | Credits: lifestorm, Gregory Wayne Rossel JR., | Maloy, DrPepper10 @ RIP, Atle! | | Visit for more: https://plymouth.thetwilightzone.ru/ --]] TOOL.Category = "Particle Controller" TOOL.Name = "ParCtrl - Projectiles" TOOL.Command = nil TOOL.ConfigName = "" TOOL.HighlightedEnt = nil TOOL.ClientConVar[ "projfx_enabled" ] = "1" TOOL.ClientConVar[ "projfx_effectname" ] = "Rocket_Smoke" TOOL.ClientConVar[ "projfx_utileffect_scale" ] = "1" TOOL.ClientConVar[ "projfx_utileffect_magnitude" ] = "1" TOOL.ClientConVar[ "projfx_utileffect_radius" ] = "10" TOOL.ClientConVar[ "projfx_color_enabled" ] = "0" TOOL.ClientConVar[ "projfx_color_r" ] = "255" TOOL.ClientConVar[ "projfx_color_g" ] = "20" TOOL.ClientConVar[ "projfx_color_b" ] = "0" TOOL.ClientConVar[ "projfx_color_outofone" ] = "0" TOOL.ClientConVar[ "impactfx_enabled" ] = "1" TOOL.ClientConVar[ "impactfx_effectname" ] = "!UTILEFFECT!Explosion!FLAG4!" TOOL.ClientConVar[ "impactfx_utileffect_scale" ] = "1" TOOL.ClientConVar[ "impactfx_utileffect_magnitude" ] = "1" TOOL.ClientConVar[ "impactfx_utileffect_radius" ] = "10" TOOL.ClientConVar[ "impactfx_color_enabled" ] = "0" TOOL.ClientConVar[ "impactfx_color_r" ] = "255" TOOL.ClientConVar[ "impactfx_color_g" ] = "20" TOOL.ClientConVar[ "impactfx_color_b" ] = "0" TOOL.ClientConVar[ "impactfx_color_outofone" ] = "0" //TOOL.ClientConVar[ "attachnum" ] = "1" //we're using the standard tool's attachnum var instead so that the selected attachment stays consistent when swapping between tools TOOL.ClientConVar[ "repeatrate" ] = "0.80" TOOL.ClientConVar[ "projmodel" ] = "models/weapons/w_missile.mdl" TOOL.ClientConVar[ "projmodel_skin" ] = "0" TOOL.ClientConVar[ "projmodel_attachnum" ] = "1" TOOL.ClientConVar[ "projmodel_material" ] = "" TOOL.ClientConVar[ "projmodel_invis" ] = "0" TOOL.ClientConVar[ "impactfx_effectlifetime" ] = "1.0" TOOL.ClientConVar[ "projent_spread" ] = "0.04" TOOL.ClientConVar[ "projent_velocity" ] = "1000" TOOL.ClientConVar[ "projent_gravity" ] = "0" TOOL.ClientConVar[ "projent_angle" ] = "0" TOOL.ClientConVar[ "projent_spin" ] = "0" TOOL.ClientConVar[ "projent_demomanfix" ] = "0" TOOL.ClientConVar[ "projent_lifetime_prehit" ] = "10" TOOL.ClientConVar[ "projent_lifetime_posthit" ] = "0" TOOL.ClientConVar[ "projent_serverside" ] = "0" TOOL.ClientConVar[ "propmodel" ] = "models/weapons/w_smg1.mdl" TOOL.ClientConVar[ "propangle" ] = "2" TOOL.ClientConVar[ "propinvis" ] = "0" TOOL.ClientConVar[ "numpadkey" ] = "52" TOOL.ClientConVar[ "toggle" ] = "1" TOOL.ClientConVar[ "starton" ] = "1" TOOL.Information = { { name = "left0", stage = 0, icon = "gui/lmb.png" }, { name = "middle0", stage = 0, icon = "gui/mmb.png" }, { name = "right0", stage = 0, icon = "gui/rmb.png" }, { name = "reload0", stage = 0, icon = "gui/r.png" }, } if ( CLIENT ) then language.Add( "tool.particlecontrol_proj.name", "Adv. Particle Controller - Projectiles" ) language.Add( "tool.particlecontrol_proj.desc", "Attach projectile effects to things" ) language.Add( "tool.particlecontrol_proj.help", "Projectile effects launch props that have one particle attached to them, and another particle that plays once they expire, either on impact or on a timer." ) language.Add( "tool.particlecontrol_proj.left0", "Add a projectile effect to an object" ) language.Add( "tool.particlecontrol_proj.middle0", "Scroll through an object's attachments" ) language.Add( "tool.particlecontrol_proj.right0", "Attach a new prop with the projectile effect on it" ) language.Add( "tool.particlecontrol_proj.reload0", "Remove all projectile effects from an object" ) end util.PrecacheSound("weapons/pistol/pistol_empty.wav") function TOOL:LeftClick( trace ) local projinfo = nil if self:GetClientNumber( "projfx_enabled", 0 ) == 1 then projinfo = { effectname = self:GetClientInfo( "projfx_effectname", 0 ), utileffectinfo = Vector( self:GetClientNumber( "projfx_utileffect_scale", 0 ), self:GetClientNumber( "projfx_utileffect_magnitude", 0 ), self:GetClientNumber( "projfx_utileffect_radius", 0 ) ), } if self:GetClientNumber( "projfx_color_enabled", 0 ) == 1 then if self:GetClientNumber( "projfx_color_outofone", 0 ) == 1 then projinfo.colorinfo = Color( self:GetClientNumber( "projfx_color_r", 0 ), self:GetClientNumber( "projfx_color_g", 0 ), self:GetClientNumber( "projfx_color_b", 0 ), 1 ) //we're using the alpha value to store color_outofone else projinfo.colorinfo = Color( self:GetClientNumber( "projfx_color_r", 0 ), self:GetClientNumber( "projfx_color_g", 0 ), self:GetClientNumber( "projfx_color_b", 0 ), 0 ) end end end local impactinfo = nil if self:GetClientNumber( "impactfx_enabled", 0 ) == 1 then impactinfo = { effectname = self:GetClientInfo( "impactfx_effectname", 0 ), utileffectinfo = Vector( self:GetClientNumber( "impactfx_utileffect_scale", 0 ), self:GetClientNumber( "impactfx_utileffect_magnitude", 0 ), self:GetClientNumber( "impactfx_utileffect_radius", 0 ) ), } if self:GetClientNumber( "impactfx_color_enabled", 0 ) == 1 then if self:GetClientNumber( "impactfx_color_outofone", 0 ) == 1 then impactinfo.colorinfo = Color( self:GetClientNumber( "impactfx_color_r", 0 ), self:GetClientNumber( "impactfx_color_g", 0 ), self:GetClientNumber( "impactfx_color_b", 0 ), 1 ) //we're using the alpha value to store color_outofone else impactinfo.colorinfo = Color( self:GetClientNumber( "impactfx_color_r", 0 ), self:GetClientNumber( "impactfx_color_g", 0 ), self:GetClientNumber( "impactfx_color_b", 0 ), 0 ) end end end local attachnum = self:GetOwner():GetInfoNum( "particlecontrol_attachnum", 0 ) //use the standard tool's attachnum var local repeatrate = self:GetClientNumber( "repeatrate", 0 ) local projmodel = self:GetClientInfo( "projmodel", 0 ) local projmodel_skin = self:GetClientNumber( "projmodel_skin", 0 ) local projmodel_attachnum = self:GetClientNumber( "projmodel_attachnum", 0 ) local projmodel_material = self:GetClientInfo( "projmodel_material", 0 ) local projmodel_invis = self:GetClientNumber( "projmodel_invis", 0 ) local impactfx_effectlifetime = self:GetClientNumber( "impactfx_effectlifetime", 0 ) local projent_spread = self:GetClientNumber( "projent_spread", 0 ) local projent_velocity = self:GetClientNumber( "projent_velocity", 0 ) local projent_gravity = self:GetClientNumber( "projent_gravity", 0 ) local projent_angle = self:GetClientNumber( "projent_angle", 0 ) local projent_spin = self:GetClientNumber( "projent_spin", 0 ) local projent_demomanfix = self:GetClientNumber( "projent_demomanfix", 0 ) local projent_lifetime_prehit = self:GetClientNumber( "projent_lifetime_prehit", 0 ) local projent_lifetime_posthit = self:GetClientNumber( "projent_lifetime_posthit", 0 ) local projent_serverside = self:GetClientNumber( "projent_serverside", 0 ) local numpadkey = self:GetClientNumber( "numpadkey", 0 ) local toggle = self:GetClientNumber( "toggle", 0 ) local starton = self:GetClientNumber( "starton", 0 ) local ply = self:GetOwner() if ( trace.Entity:IsValid() ) then if CLIENT then return true end if trace.Entity:GetClass() == "prop_effect" and trace.Entity.AttachedEntity then trace.Entity = trace.Entity.AttachedEntity end AttachParticleControllerProj( ply, trace.Entity, { NewTable = { ProjFXInfo = projinfo, ImpactFXInfo = impactinfo, AttachNum = attachnum, RepeatRate = repeatrate, ProjModel = projmodel, ProjModel_Skin = projmodel_skin, ProjModel_AttachNum = projmodel_attachnum, ProjModel_Material = projmodel_material, ProjModel_Invis = projmodel_invis, ImpactFX_EffectLifetime = impactfx_effectlifetime, ProjEnt_Spread = projent_spread, ProjEnt_Velocity = projent_velocity, ProjEnt_Gravity = projent_gravity, ProjEnt_Angle = projent_angle, ProjEnt_Spin = projent_spin, ProjEnt_DemomanFix = projent_demomanfix, ProjEnt_Lifetime_PreHit = projent_lifetime_prehit, ProjEnt_Lifetime_PostHit = projent_lifetime_posthit, ProjEnt_Serverside = projent_serverside, NumpadKey = numpadkey, Toggle = toggle, StartOn = starton, } } ) return true end end function TOOL:RightClick( trace ) local projinfo = nil if self:GetClientNumber( "projfx_enabled", 0 ) == 1 then projinfo = { effectname = self:GetClientInfo( "projfx_effectname", 0 ), utileffectinfo = Vector( self:GetClientNumber( "projfx_utileffect_scale", 0 ), self:GetClientNumber( "projfx_utileffect_magnitude", 0 ), self:GetClientNumber( "projfx_utileffect_radius", 0 ) ), } if self:GetClientNumber( "projfx_color_enabled", 0 ) == 1 then if self:GetClientNumber( "projfx_color_outofone", 0 ) == 1 then projinfo.colorinfo = Color( self:GetClientNumber( "projfx_color_r", 0 ), self:GetClientNumber( "projfx_color_g", 0 ), self:GetClientNumber( "projfx_color_b", 0 ), 1 ) //we're using the alpha value to store color_outofone else projinfo.colorinfo = Color( self:GetClientNumber( "projfx_color_r", 0 ), self:GetClientNumber( "projfx_color_g", 0 ), self:GetClientNumber( "projfx_color_b", 0 ), 0 ) end end end local impactinfo = nil if self:GetClientNumber( "impactfx_enabled", 0 ) == 1 then impactinfo = { effectname = self:GetClientInfo( "impactfx_effectname", 0 ), utileffectinfo = Vector( self:GetClientNumber( "impactfx_utileffect_scale", 0 ), self:GetClientNumber( "impactfx_utileffect_magnitude", 0 ), self:GetClientNumber( "impactfx_utileffect_radius", 0 ) ), } if self:GetClientNumber( "impactfx_color_enabled", 0 ) == 1 then if self:GetClientNumber( "impactfx_color_outofone", 0 ) == 1 then impactinfo.colorinfo = Color( self:GetClientNumber( "impactfx_color_r", 0 ), self:GetClientNumber( "impactfx_color_g", 0 ), self:GetClientNumber( "impactfx_color_b", 0 ), 1 ) //we're using the alpha value to store color_outofone else impactinfo.colorinfo = Color( self:GetClientNumber( "impactfx_color_r", 0 ), self:GetClientNumber( "impactfx_color_g", 0 ), self:GetClientNumber( "impactfx_color_b", 0 ), 0 ) end end end local attachnum = self:GetOwner():GetInfoNum( "particlecontrol_attachnum", 0 ) //use the standard tool's attachnum var local repeatrate = self:GetClientNumber( "repeatrate", 0 ) local projmodel = self:GetClientInfo( "projmodel", 0 ) local projmodel_skin = self:GetClientNumber( "projmodel_skin", 0 ) local projmodel_attachnum = self:GetClientNumber( "projmodel_attachnum", 0 ) local projmodel_material = self:GetClientInfo( "projmodel_material", 0 ) local projmodel_invis = self:GetClientNumber( "projmodel_invis", 0 ) local impactfx_effectlifetime = self:GetClientNumber( "impactfx_effectlifetime", 0 ) local projent_spread = self:GetClientNumber( "projent_spread", 0 ) local projent_velocity = self:GetClientNumber( "projent_velocity", 0 ) local projent_gravity = self:GetClientNumber( "projent_gravity", 0 ) local projent_angle = self:GetClientNumber( "projent_angle", 0 ) local projent_spin = self:GetClientNumber( "projent_spin", 0 ) local projent_demomanfix = self:GetClientNumber( "projent_demomanfix", 0 ) local projent_lifetime_prehit = self:GetClientNumber( "projent_lifetime_prehit", 0 ) local projent_lifetime_posthit = self:GetClientNumber( "projent_lifetime_posthit", 0 ) local projent_serverside = self:GetClientNumber( "projent_serverside", 0 ) local numpadkey = self:GetClientNumber( "numpadkey", 0 ) local toggle = self:GetClientNumber( "toggle", 0 ) local starton = self:GetClientNumber( "starton", 0 ) local ply = self:GetOwner() local propmodel = self:GetClientInfo( "propmodel", 0 ) local propangle = self:GetClientNumber( "propangle", 0 ) //propangle 1: spawn upright //propangle 2: spawn at surface angle if !util.IsValidModel(propmodel) then return false end if !util.IsValidProp(propmodel) then return false end if CLIENT then return true end prop = ents.Create( "prop_physics" ) prop:SetModel( propmodel ) prop:SetPos( trace.HitPos - trace.HitNormal * prop:OBBMins().z ) if propangle == 1 then prop:SetAngles(Angle(0,trace.HitNormal:Angle().y,0)) else prop:SetAngles(trace.HitNormal:Angle()) end prop:SetCollisionGroup(COLLISION_GROUP_NONE) prop:Spawn() local shouldweweld = true //don't weld if... if ( !util.IsValidPhysicsObject(prop, 0) ) then shouldweweld = false end //the prop doesn't have a phys object if ( !trace.Entity:IsValid() ) then shouldweweld = false end //the thing we clicked on doesn't exist/is the world if ( trace.Entity && trace.Entity:IsPlayer() ) then shouldweweld = false end //the thing we clicked on is a player if ( !util.IsValidPhysicsObject( trace.Entity, trace.PhysicsBone ) ) then shouldweweld = false end //the thing we clicked on doesn't have a phys object if shouldweweld == true then local const = constraint.Weld( prop, trace.Entity, 0, trace.PhysicsBone, 0, true, true ) else if util.IsValidPhysicsObject(prop, 0) then prop:GetPhysicsObject():EnableMotion(false) end end if self:GetClientNumber( "propinvis", 0 ) == 1 then prop:SetRenderMode(1) //we need to change the render mode so the transparency actually shows up prop:SetColor( Color(255,255,255,0) ) duplicator.StoreEntityModifier( prop, "colour", { Color = Color(255,255,255,0), RenderMode = 1, RenderFX = 0 } ) end undo.Create( "prop" ) undo.AddEntity( prop ) undo.SetPlayer( ply ) undo.Finish( "Prop ("..tostring(propmodel)..")" ) if ( prop:IsValid() ) then AttachParticleControllerProj( ply, prop, { NewTable = { ProjFXInfo = projinfo, ImpactFXInfo = impactinfo, AttachNum = attachnum, RepeatRate = repeatrate, ProjModel = projmodel, ProjModel_Skin = projmodel_skin, ProjModel_AttachNum = projmodel_attachnum, ProjModel_Material = projmodel_material, ProjModel_Invis = projmodel_invis, ImpactFX_EffectLifetime = impactfx_effectlifetime, ProjEnt_Spread = projent_spread, ProjEnt_Velocity = projent_velocity, ProjEnt_Gravity = projent_gravity, ProjEnt_Angle = projent_angle, ProjEnt_Spin = projent_spin, ProjEnt_DemomanFix = projent_demomanfix, ProjEnt_Lifetime_PreHit = projent_lifetime_prehit, ProjEnt_Lifetime_PostHit = projent_lifetime_posthit, ProjEnt_Serverside = projent_serverside, NumpadKey = numpadkey, Toggle = toggle, StartOn = starton, } } ) return true end end function TOOL:Reload( trace ) if ( trace.Entity:IsValid() ) then local fx = false if trace.Entity:GetClass() == "prop_effect" and trace.Entity.AttachedEntity then trace.Entity = trace.Entity.AttachedEntity end for _, asdf in pairs( ents:GetAll() ) do if asdf:GetClass() == "particlecontroller_proj" and asdf:GetParent() == trace.Entity then if SERVER then asdf:Remove() end fx = true end end if SERVER then duplicator.ClearEntityModifier( trace.Entity, "DupeParticleControllerProj" ) end return fx end end if CLIENT then local colorborder = Color(0,0,0,255) local colorselect = Color(0,255,0,255) local colorunselect = Color(255,255,255,255) function TOOL:DrawHUD() local pl = LocalPlayer() local tr = pl:GetEyeTrace() local attachnum = self:GetOwner():GetInfoNum( "particlecontrol_attachnum", 0 ) //use the standard tool's attachnum var local function DrawHighlightAttachments(ent) //If there aren't any attachments, then draw the model origin as selected and stop here: if !ent:GetAttachments() or !ent:GetAttachments()[1] then local _pos,_ang = ent:GetPos(), ent:GetAngles() local _pos = _pos:ToScreen() local textpos = {x = _pos.x+5,y = _pos.y-5} draw.RoundedBox(0,_pos.x - 3,_pos.y - 3,6,6,colorborder) draw.RoundedBox(0,_pos.x - 1,_pos.y - 1,2,2,colorselect) draw.SimpleTextOutlined("0: (origin)","Default",textpos.x,textpos.y,colorselect,TEXT_ALIGN_LEFT,TEXT_ALIGN_BOTTOM,2,colorborder) return end //Draw the unselected model origin, if applicable: if ent:GetAttachments()[attachnum] then local _pos,_ang = ent:GetPos(), ent:GetAngles() local _pos = _pos:ToScreen() local textpos = {x = _pos.x+5,y = _pos.y-5} draw.RoundedBox(0,_pos.x - 2,_pos.y - 2,4,4,colorborder) draw.RoundedBox(0,_pos.x - 1,_pos.y - 1,2,2,colorunselect) draw.SimpleTextOutlined("0: (origin)","Default",textpos.x,textpos.y,colorunselect,TEXT_ALIGN_LEFT,TEXT_ALIGN_BOTTOM,1,colorborder) end //Draw the unselected attachment points: for _, table in pairs(ent:GetAttachments()) do local _pos,_ang = ent:GetAttachment(table.id).Pos,ent:GetAttachment(table.id).Ang local _pos = _pos:ToScreen() local textpos = {x = _pos.x+5,y = _pos.y-5} if table.id != attachnum then draw.RoundedBox(0,_pos.x - 2,_pos.y - 2,4,4,colorborder) draw.RoundedBox(0,_pos.x - 1,_pos.y - 1,2,2,colorunselect) draw.SimpleTextOutlined(table.id ..": ".. table.name,"Default",textpos.x,textpos.y,colorunselect,TEXT_ALIGN_LEFT,TEXT_ALIGN_BOTTOM,1,colorborder) end end //Draw the selected attachment point or model origin last, so it renders above all the others: if !ent:GetAttachments()[attachnum] then //Model origin local _pos,_ang = ent:GetPos(), ent:GetAngles() local _pos = _pos:ToScreen() local textpos = {x = _pos.x+5,y = _pos.y-5} draw.RoundedBox(0,_pos.x - 3,_pos.y - 3,6,6,colorborder) draw.RoundedBox(0,_pos.x - 1,_pos.y - 1,2,2,colorselect) draw.SimpleTextOutlined("0: (origin)","Default",textpos.x,textpos.y,colorselect,TEXT_ALIGN_LEFT,TEXT_ALIGN_BOTTOM,2,colorborder) else //Attachment local _pos,_ang = ent:GetAttachment(attachnum).Pos,ent:GetAttachment(attachnum).Ang local _pos = _pos:ToScreen() local textpos = {x = _pos.x+5,y = _pos.y-5} draw.RoundedBox(0,_pos.x - 3,_pos.y - 3,6,6,colorborder) draw.RoundedBox(0,_pos.x - 1,_pos.y - 1,2,2,colorselect) draw.SimpleTextOutlined(attachnum ..": ".. ent:GetAttachments()[attachnum].name,"Default",textpos.x,textpos.y,colorselect,TEXT_ALIGN_LEFT,TEXT_ALIGN_BOTTOM,2,colorborder) end end if IsValid(tr.Entity) and tr.Entity == self.HighlightedEnt then DrawHighlightAttachments(self.HighlightedEnt) return end if IsValid(tr.Entity) and tr.Entity != self.HighlightedEnt then //unhighlight the old ent if it exists if self.HighlightedEnt != nil then self.HighlightedEnt = nil end //highlight the new ent self.HighlightedEnt = tr.Entity end if !IsValid(tr.Entity) and self.HighlightedEnt != nil then self.HighlightedEnt = nil end end function TOOL:Holster() if self.HighlightedEnt != nil then self.HighlightedEnt = nil end end //All credit for the toolgun scroll wheel code goes to the Wiremod devs. You guys are the best. local function get_active_tool(ply, tool) -- find toolgun local activeWep = ply:GetActiveWeapon() if not IsValid(activeWep) or activeWep:GetClass() ~= "gmod_tool" or activeWep.Mode ~= tool then return end return activeWep:GetToolObject(tool) end local function hookfunc( ply, bind, pressed ) if not pressed then return end if bind == "invnext" then local self = get_active_tool(ply, "particlecontrol_proj") if not self then return end return self:ScrollDown(ply:GetEyeTraceNoCursor()) elseif bind == "invprev" then local self = get_active_tool(ply, "particlecontrol_proj") if not self then return end return self:ScrollUp(ply:GetEyeTraceNoCursor()) end end if game.SinglePlayer() then -- wtfgarry (have to have a delay in single player or the hook won't get added) timer.Simple(5,function() hook.Add( "PlayerBindPress", "particlecontrol_proj_playerbindpress", hookfunc ) end) else hook.Add( "PlayerBindPress", "particlecontrol_proj_playerbindpress", hookfunc ) end //End shamefully copied code here. function TOOL:Scroll(trace,dir) if !IsValid(self.HighlightedEnt) then return end local attachcount = 0 if self.HighlightedEnt:GetAttachments() then attachcount = table.Count(self.HighlightedEnt:GetAttachments()) end local oldattachnum = self:GetOwner():GetInfoNum( "particlecontrol_attachnum", 0 ) //use the standard tool's attachnum var if oldattachnum > attachcount then oldattachnum = 0 end local attachnum = oldattachnum + dir if attachnum < 0 then attachnum = attachcount end if attachnum > attachcount then attachnum = 0 end RunConsoleCommand("particlecontrol_attachnum", tostring(attachnum)) //use the standard tool's attachnum var self:GetOwner():EmitSound("weapons/pistol/pistol_empty.wav") return true end function TOOL:ScrollUp(trace) return self:Scroll(trace,-1) end function TOOL:ScrollDown(trace) return self:Scroll(trace,1) end end if SERVER then local function SpawnParticleControllerProj(ply, ent, DataTable) if DataTable == nil or DataTable == {} or DataTable.ProjModel == nil or ent == nil or !IsValid(ent) then return end local ParticleControlProj = ents.Create( "particlecontroller_proj" ) ParticleControlProj:SetPos(ent:GetPos()) ParticleControlProj:SetAngles(ent:GetAngles()) ParticleControlProj:SetParent(ent) ent:DeleteOnRemove(ParticleControlProj) ParticleControlProj:SetTargetEnt(ent) if DataTable.ProjFXInfo != nil then ParticleControlProj:SetProjFX_EffectName(DataTable.ProjFXInfo.effectname) ParticleControlProj:SetProjFX_UtilEffectInfo(DataTable.ProjFXInfo.utileffectinfo) if DataTable.ProjFXInfo.colorinfo != nil then local projfxcolor = Vector(DataTable.ProjFXInfo.colorinfo.r, DataTable.ProjFXInfo.colorinfo.g, DataTable.ProjFXInfo.colorinfo.b) if DataTable.ProjFXInfo.colorinfo.a then projfxcolor = projfxcolor / 255 end ParticleControlProj:SetProjFX_ColorInfo( projfxcolor ) else ParticleControlProj:SetProjFX_ColorInfo( Vector(0,0,0) ) end else ParticleControlProj:SetProjFX_EffectName("") end if DataTable.ImpactFXInfo != nil then ParticleControlProj:SetImpactFX_EffectName(DataTable.ImpactFXInfo.effectname) ParticleControlProj:SetImpactFX_UtilEffectInfo(DataTable.ImpactFXInfo.utileffectinfo) if DataTable.ImpactFXInfo.colorinfo != nil then local impactfxcolor = Vector(DataTable.ImpactFXInfo.colorinfo.r, DataTable.ImpactFXInfo.colorinfo.g, DataTable.ImpactFXInfo.colorinfo.b) if DataTable.ImpactFXInfo.colorinfo.a then impactfxcolor = impactfxcolor / 255 end ParticleControlProj:SetImpactFX_ColorInfo( impactfxcolor ) else ParticleControlProj:SetImpactFX_ColorInfo( Vector(0,0,0) ) end else ParticleControlProj:SetImpactFX_EffectName("") end ParticleControlProj:SetAttachNum(DataTable.AttachNum) ParticleControlProj:SetRepeatRate(DataTable.RepeatRate) ParticleControlProj:SetProjModel(DataTable.ProjModel) ParticleControlProj:SetSkin(DataTable.ProjModel_Skin) //also use the controller ent to store the skin ParticleControlProj:SetProjModel_AttachNum(DataTable.ProjModel_AttachNum) ParticleControlProj:SetMaterial(DataTable.ProjModel_Material) //and the material ParticleControlProj:SetProjModel_Invis( tobool(DataTable.ProjModel_Invis) ) ParticleControlProj:SetImpactFX_EffectLifetime(DataTable.ImpactFX_EffectLifetime) ParticleControlProj:SetProjEnt_Spread(DataTable.ProjEnt_Spread) ParticleControlProj:SetProjEnt_Velocity(DataTable.ProjEnt_Velocity) ParticleControlProj:SetProjEnt_Gravity( tobool(DataTable.ProjEnt_Gravity) ) ParticleControlProj:SetProjEnt_Angle(DataTable.ProjEnt_Angle) ParticleControlProj:SetProjEnt_Spin(DataTable.ProjEnt_Spin) ParticleControlProj:SetProjEnt_DemomanFix( tobool(DataTable.ProjEnt_DemomanFix) ) ParticleControlProj:SetProjEnt_Lifetime_PreHit(DataTable.ProjEnt_Lifetime_PreHit) ParticleControlProj:SetProjEnt_Lifetime_PostHit(DataTable.ProjEnt_Lifetime_PostHit) ParticleControlProj:SetProjEnt_Serverside( tobool(DataTable.ProjEnt_Serverside) ) ParticleControlProj:SetActive( tobool(DataTable.StartOn) ) ParticleControlProj:SetToggle( tobool(DataTable.Toggle) ) ParticleControlProj:SetNumpadKey(DataTable.NumpadKey) numpad.OnDown( ply, DataTable.NumpadKey, "Particle_Press", ParticleControlProj ) numpad.OnUp( ply, DataTable.NumpadKey, "Particle_Release", ParticleControlProj ) ParticleControlProj:SetNumpadState("") ParticleControlProj:Spawn() ParticleControlProj:Activate() end function AttachParticleControllerProj( ply, ent, Data ) if Data.NewTable then SpawnParticleControllerProj(ply, ent, Data.NewTable) local dupetable = {} if ent.EntityMods and ent.EntityMods.DupeParticleControllerProj then dupetable = ent.EntityMods.DupeParticleControllerProj end table.insert(dupetable, Data.NewTable) duplicator.StoreEntityModifier( ent, "DupeParticleControllerProj", dupetable ) return end end function DupeParticleControllerProj( ply, ent, Data ) //due to a problem with the easy bonemerge tool that causes entity modifiers to be applied TWICE, we need to remove the effects that were added the first time for _, asdf in pairs( ents:GetAll() ) do if asdf:GetClass() == "particlecontroller_proj" and asdf:GetParent() == ent then asdf:Remove() end end for _, DataTable in pairs (Data) do SpawnParticleControllerProj(ply, ent, DataTable) end end duplicator.RegisterEntityModifier( "DupeParticleControllerProj", DupeParticleControllerProj ) end //we're still testing out a lot of stuff with the cpanel, so let's add a way to refresh it by reselecting the tool --[[ TOOL.ClientConVar[ "refresh" ] = 1 function TOOL:Think() if SERVER then return end if self:GetClientNumber("refresh") == 1 then RunConsoleCommand("particlecontrol_proj_refresh", "0"); //refresh the cpanel local panel = controlpanel.Get( "particlecontrol_proj" ) if ( !panel ) then return end panel:ClearControls() self.BuildCPanel(panel) end end function TOOL:Deploy() RunConsoleCommand("particlecontrol_proj_refresh", "1"); end ]] local ConVarsDefault = TOOL:BuildConVarList() function TOOL.BuildCPanel(panel) panel:AddControl( "Header", { Description = "#tool.particlecontrol_proj.help" } ) //Presets panel:AddControl( "ComboBox", { MenuButton = 1, Folder = "particlecontrol_proj", Options = { //[ "#preset.default" ] = ConVarsDefault [ "Example: Generic Rockets" ] = ConVarsDefault, [ "Example: TF2 Rockets" ] = { particlecontrol_proj_impactfx_color_b = "0", particlecontrol_proj_impactfx_color_enabled = "0", particlecontrol_proj_impactfx_color_g = "20", particlecontrol_proj_impactfx_color_outofone = "0", particlecontrol_proj_impactfx_color_r = "255", particlecontrol_proj_impactfx_effectlifetime = "1.000000", particlecontrol_proj_impactfx_effectname = "ExplosionCore_Wall", particlecontrol_proj_impactfx_enabled = "1", particlecontrol_proj_impactfx_utileffect_magnitude = "1", particlecontrol_proj_impactfx_utileffect_radius = "10", particlecontrol_proj_impactfx_utileffect_scale = "1", particlecontrol_proj_numpadkey = "52", particlecontrol_proj_projent_angle = "0", particlecontrol_proj_projent_demomanfix = "0", particlecontrol_proj_projent_gravity = "0", particlecontrol_proj_projent_lifetime_posthit = "0.000000", particlecontrol_proj_projent_lifetime_prehit = "10.000000", particlecontrol_proj_projent_serverside = "0", particlecontrol_proj_projent_spin = "0", particlecontrol_proj_projent_spread = "0", particlecontrol_proj_projent_velocity = "1100.000000", particlecontrol_proj_projfx_color_b = "0", particlecontrol_proj_projfx_color_enabled = "0", particlecontrol_proj_projfx_color_g = "20", particlecontrol_proj_projfx_color_outofone = "0", particlecontrol_proj_projfx_color_r = "255", particlecontrol_proj_projfx_effectname = "rockettrail", particlecontrol_proj_projfx_enabled = "1", particlecontrol_proj_projfx_utileffect_magnitude = "1", particlecontrol_proj_projfx_utileffect_radius = "10", particlecontrol_proj_projfx_utileffect_scale = "1", particlecontrol_proj_projmodel = "models/weapons/w_models/w_rocket.mdl", particlecontrol_proj_projmodel_attachnum = "1", particlecontrol_proj_projmodel_invis = "0", particlecontrol_proj_projmodel_skin = "0", particlecontrol_proj_propangle = "2", particlecontrol_proj_propinvis = "0", particlecontrol_proj_propmodel = "models/weapons/w_smg1.mdl", particlecontrol_proj_repeatrate = "0.800000", particlecontrol_proj_starton = "1", particlecontrol_proj_toggle = "1" }, [ "Example: TF2 Grenades, red" ] = { particlecontrol_proj_impactfx_color_b = "0", particlecontrol_proj_impactfx_color_enabled = "0", particlecontrol_proj_impactfx_color_g = "20", particlecontrol_proj_impactfx_color_outofone = "0", particlecontrol_proj_impactfx_color_r = "255", particlecontrol_proj_impactfx_effectlifetime = "1.000000", particlecontrol_proj_impactfx_effectname = "ExplosionCore_MidAir", particlecontrol_proj_impactfx_enabled = "1", particlecontrol_proj_impactfx_utileffect_magnitude = "1", particlecontrol_proj_impactfx_utileffect_radius = "10", particlecontrol_proj_impactfx_utileffect_scale = "1", particlecontrol_proj_numpadkey = "52", particlecontrol_proj_projent_angle = "0", particlecontrol_proj_projent_demomanfix = "0", particlecontrol_proj_projent_gravity = "1", particlecontrol_proj_projent_lifetime_posthit = "2.300000", particlecontrol_proj_projent_lifetime_prehit = "2.300000", particlecontrol_proj_projent_serverside = "0", particlecontrol_proj_projent_spin = "4", particlecontrol_proj_projent_spread = "0", particlecontrol_proj_projent_velocity = "1217.000000", particlecontrol_proj_projfx_color_b = "0", particlecontrol_proj_projfx_color_enabled = "0", particlecontrol_proj_projfx_color_g = "20", particlecontrol_proj_projfx_color_outofone = "0", particlecontrol_proj_projfx_color_r = "255", particlecontrol_proj_projfx_effectname = "pipebombtrail_red", particlecontrol_proj_projfx_enabled = "1", particlecontrol_proj_projfx_utileffect_magnitude = "1", particlecontrol_proj_projfx_utileffect_radius = "10", particlecontrol_proj_projfx_utileffect_scale = "1", particlecontrol_proj_projmodel = "models/weapons/w_models/w_grenade_grenadelauncher.mdl", particlecontrol_proj_projmodel_attachnum = "0", particlecontrol_proj_projmodel_invis = "0", particlecontrol_proj_projmodel_skin = "0", particlecontrol_proj_propangle = "2", particlecontrol_proj_propinvis = "0", particlecontrol_proj_propmodel = "models/weapons/w_smg1.mdl", particlecontrol_proj_repeatrate = "0.600000", particlecontrol_proj_starton = "1", particlecontrol_proj_toggle = "1" }, [ "Example: TF2 Grenades, blue" ] = { particlecontrol_proj_impactfx_color_b = "0", particlecontrol_proj_impactfx_color_enabled = "0", particlecontrol_proj_impactfx_color_g = "20", particlecontrol_proj_impactfx_color_outofone = "0", particlecontrol_proj_impactfx_color_r = "255", particlecontrol_proj_impactfx_effectlifetime = "1.000000", particlecontrol_proj_impactfx_effectname = "ExplosionCore_MidAir", particlecontrol_proj_impactfx_enabled = "1", particlecontrol_proj_impactfx_utileffect_magnitude = "1", particlecontrol_proj_impactfx_utileffect_radius = "10", particlecontrol_proj_impactfx_utileffect_scale = "1", particlecontrol_proj_numpadkey = "52", particlecontrol_proj_projent_angle = "0", particlecontrol_proj_projent_demomanfix = "0", particlecontrol_proj_projent_gravity = "1", particlecontrol_proj_projent_lifetime_posthit = "2.300000", particlecontrol_proj_projent_lifetime_prehit = "2.300000", particlecontrol_proj_projent_serverside = "0", particlecontrol_proj_projent_spin = "4", particlecontrol_proj_projent_spread = "0", particlecontrol_proj_projent_velocity = "1217.000000", particlecontrol_proj_projfx_color_b = "0", particlecontrol_proj_projfx_color_enabled = "0", particlecontrol_proj_projfx_color_g = "20", particlecontrol_proj_projfx_color_outofone = "0", particlecontrol_proj_projfx_color_r = "255", particlecontrol_proj_projfx_effectname = "pipebombtrail_blue", particlecontrol_proj_projfx_enabled = "1", particlecontrol_proj_projfx_utileffect_magnitude = "1", particlecontrol_proj_projfx_utileffect_radius = "10", particlecontrol_proj_projfx_utileffect_scale = "1", particlecontrol_proj_projmodel = "models/weapons/w_models/w_grenade_grenadelauncher.mdl", particlecontrol_proj_projmodel_attachnum = "0", particlecontrol_proj_projmodel_invis = "0", particlecontrol_proj_projmodel_skin = "1", particlecontrol_proj_propangle = "2", particlecontrol_proj_propinvis = "0", particlecontrol_proj_propmodel = "models/weapons/w_smg1.mdl", particlecontrol_proj_repeatrate = "0.600000", particlecontrol_proj_starton = "1", particlecontrol_proj_toggle = "1" }, }, CVars = table.GetKeys( ConVarsDefault ) } ) panel:AddControl( "Checkbox", { Label = "Enable projectile effects?", Command = "particlecontrol_proj_projfx_enabled" } ) AddParticleBrowser(panel, { name = "Projectile Effect", commands = { effectname = "particlecontrol_proj_projfx_effectname", color = "particlecontrol_proj_projfx_color", utileffect = "particlecontrol_proj_projfx_utileffect", enabled = "particlecontrol_proj_projfx_enabled", }, }) panel:AddControl( "Checkbox", { Label = "Enable impact/expire effects?", Command = "particlecontrol_proj_impactfx_enabled" } ) AddParticleBrowser(panel, { name = "Impact/Expire Effect", commands = { effectname = "particlecontrol_proj_impactfx_effectname", color = "particlecontrol_proj_impactfx_color", utileffect = "particlecontrol_proj_impactfx_utileffect", enabled = "particlecontrol_proj_impactfx_enabled", }, }) //panel:AddControl( "Label", { Text = "" } ) panel:AddControl( "Label", { Text = "" } ) panel:AddControl("Slider", { Label = "Attachment", Type = "Integer", Min = "0", Max = "10", Command = "particlecontrol_attachnum", //use the standard tool's attachnum var }) panel:ControlHelp( "Attachment point on the model to fire projectiles from. Set to 0 to fire from the model origin." ) panel:AddControl("Slider", { Label = "Repeat Rate", Type = "Float", Min = "0", Max = "1", Command = "particlecontrol_proj_repeatrate" }) panel:ControlHelp( "How often the projectile fires. Set to 0 to not repeat." ) panel:AddControl( "Label", { Text = "" } ) panel:AddControl( "Label", { Text = "" } ) local projmodellist = { Label = "Projectile Model:", ConVar = "particlecontrol_proj_projmodel", Category = "Projectile Model", Height = 1, Models = {} } projmodellist.Models["models/hunter/plates/plate.mdl"] = {} projmodellist.Models["models/weapons/w_missile.mdl"] = {} projmodellist.Models["models/weapons/w_models/w_rocket.mdl"] = {} projmodellist.Models["models/weapons/w_models/w_grenade_grenadelauncher.mdl"] = {} panel:AddControl( "PropSelect", projmodellist ) local projmodelentry = vgui.Create( "DTextEntry", panel ) projmodelentry:SetConVar( "particlecontrol_proj_projmodel" ) panel:AddItem(projmodelentry) panel:AddControl("Slider", { Label = "Projectile Skin", Type = "Integer", Min = "0", Max = "10", Command = "particlecontrol_proj_projmodel_skin", }) panel:AddControl("Slider", { Label = "Projectile Attachment", Type = "Integer", Min = "0", Max = "10", Command = "particlecontrol_proj_projmodel_attachnum", }) panel:ControlHelp( "Attachment point on the projectile to attach the effect to. Set to 0 to attach to model origin." ) panel:AddControl("TextBox", { Label = "Projectile Material", Description = "", MaxLength = 255, Text = "", Command = "particlecontrol_proj_projmodel_material", }) panel:AddControl( "Checkbox", { Label = "Invisible projectiles (particles only)", Command = "particlecontrol_proj_projmodel_invis" } ) panel:AddControl( "Label", { Text = "" } ) panel:AddControl( "Label", { Text = "" } ) panel:AddControl("Slider", { Label = "Projectile Spread", Type = "Float", Min = "0", Max = "1", Command = "particlecontrol_proj_projent_spread" }) panel:ControlHelp( "Each unit is 90 degrees of spread - you can type in 2 for 180 degrees or even 4 for 360 degrees." ) panel:AddControl("Slider", { Label = "Projectile Velocity", Type = "Float", Min = "0", Max = "3000", Command = "particlecontrol_proj_projent_velocity" }) //panel:ControlHelp( "" ) panel:AddControl( "Checkbox", { Label = "Projectile Gravity", Command = "particlecontrol_proj_projent_gravity" } ) panel:AddControl( "ComboBox", { Label = "Projectile Angle", MenuButton = "0", Options = { ["Forward"] = { particlecontrol_proj_projent_angle = 0 }, ["Left"] = { particlecontrol_proj_projent_angle = 1 }, ["Right"] = { particlecontrol_proj_projent_angle = 2 }, ["Up"] = { particlecontrol_proj_projent_angle = 3 }, ["Down"] = { particlecontrol_proj_projent_angle = 4 }, ["Back"] = { particlecontrol_proj_projent_angle = 5 }, } }) panel:AddControl( "ComboBox", { Label = "Projectile Spin", MenuButton = "0", Options = { ["Don't spin"] = { particlecontrol_proj_projent_spin = 0 }, ["Spin pitch"] = { particlecontrol_proj_projent_spin = 1 }, ["Spin yaw"] = { particlecontrol_proj_projent_spin = 2 }, ["Spin roll"] = { particlecontrol_proj_projent_spin = 3 }, ["Spin random"] = { particlecontrol_proj_projent_spin = 4 }, } }) panel:AddControl( "Checkbox", { Label = "Demoman Weapon Fix", Command = "particlecontrol_proj_projent_demomanfix" } ) panel:ControlHelp( "Demoman weapon props have their muzzle attachment at an angle for some reason. Use this to fix it." ) panel:AddControl("Slider", { Label = "Lifetime", Type = "Float", Min = "0", Max = "10", Command = "particlecontrol_proj_projent_lifetime_prehit" }) panel:ControlHelp( "How long projectiles last after being launched." ) panel:AddControl("Slider", { Label = "Lifetime (after impact)", Type = "Float", Min = "0", Max = "10", Command = "particlecontrol_proj_projent_lifetime_posthit" }) panel:ControlHelp( "How long projectiles last after hitting something. Set to 0 to expire on impact." ) //TODO: how to explain that lifetime before hit and lifetime after hit are separate timers? panel:AddControl( "Checkbox", { Label = "Serverside projectiles?", Command = "particlecontrol_proj_projent_serverside" } ) panel:ControlHelp( "Use serverside props for projectiles. These will collide properly with everything instead of passing through, but they'll also put a lot more stress on the game (meaning more lag), and they'll show up in the wrong spot if bonemerged. Only turn this on if you need it." ) panel:AddControl("Slider", { Label = "Impact Effect Lifetime", Type = "Float", Min = "0.5", Max = "5", Command = "particlecontrol_proj_impactfx_effectlifetime" }) //panel:ControlHelp( "Number of seconds before impact effects are removed." ) panel:AddControl( "Label", { Text = "" } ) panel:AddControl( "Label", { Text = "" } ) local modellist = { Label = "Prop:", ConVar = "particlecontrol_proj_propmodel", Category = "Prop", Height = 1, Models = {} } modellist.Models["models/hunter/plates/plate025x025.mdl"] = {} modellist.Models["models/hunter/plates/plate.mdl"] = {} modellist.Models["models/weapons/w_smg1.mdl"] = {} modellist.Models["models/weapons/w_models/w_rocketlauncher.mdl"] = {} panel:AddControl( "PropSelect", modellist ) panel:AddControl( "ComboBox", { Label = "Prop Angle", MenuButton = "0", Options = { ["Spawn upright"] = { particlecontrol_proj_propangle = "1" }, ["Spawn at surface angle"] = { particlecontrol_proj_propangle = "2" } } }) panel:AddControl( "Checkbox", { Label = "Invisible prop (particles only)", Command = "particlecontrol_proj_propinvis" } ) panel:AddControl( "Label", { Text = "" } ) panel:AddControl( "Label", { Text = "" } ) panel:AddControl( "Numpad", { Label = "Effect Key", Command = "particlecontrol_proj_numpadkey", ButtonSize = 22 }) panel:AddControl( "Checkbox", { Label = "Toggle", Command = "particlecontrol_proj_toggle" } ) panel:AddControl( "Checkbox", { Label = "Start on?", Command = "particlecontrol_proj_starton" } ) end