Files
wnsrc/lua/autorun/particlecontrol_autorun.lua

1208 lines
45 KiB
Lua
Raw Normal View History

2024-08-04 22:55:00 +03:00
--[[
| 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/
--]]
AddCSLuaFile()
ParticleListTable = {
Games = {},
Addons = {},
}
local doubleslash = "//"
local function AddToParticleListTable(filename,isaddon,icon)
//Converts a list string, with one item per line, into a table, taking comments and empty strings into account. Returns nil if the table is empty.
//The format of these tables is item1 = true, item2 = true, etc., not 1 = item1, 2 = item2, etc.; this is so we can use "if table["itemX"] then" to check if a value is inside of it.
//
//Instead of the arg being the string itself, the args are 1: the table containing the string, and 2: the key the string can be found at.
//This is so we can find continuations of the string, which'll be in the same table, but under separate keys.
//
local function ListStringToTable(listcontainer,listname)
//Find the list string we're looking for inside the container, and explode it into its own table.
local liststring = listcontainer[listname]
if type(liststring) != "string" then MsgN("PARTICLE CONTROL ERROR: List string \"" .. listname .. "\" inside of \"" .. filename .. "\" doesn't seem to actually be a string! Check the file and make sure you've formatted everything correctly!") return end
if #liststring == 4095 then MsgN("PARTICLE CONTROL ERROR: List string \"" .. listname .. "\" inside of \"" .. filename .. "\" is more than 4095 characters long - that's too long for the engine to read all of it! Split it up into multiple lists!") end
local listtab = string.Explode("\n", liststring)
//Also find any list strings that are continuations of our main list string, and explode them into the same table.
for listname2, liststring2 in pairs (listcontainer) do
if string.StartWith(listname2, listname .. "_cont") then
if type(liststring) != "string" then MsgN("PARTICLE CONTROL ERROR: List string \"" .. listname2 .. "\" inside of \"" .. filename .. "\" doesn't seem to actually be a string! Check the file and make sure you've formatted everything correctly!") return end
if #liststring2 == 4095 then MsgN("PARTICLE CONTROL ERROR: List string \"" .. listname2 .. "\" inside of \"" .. filename .. "\" is more than 4095 characters long - that's too long for the engine to read all of it! Split it up into multiple lists!") end
table.Add(listtab, string.Explode("\n", liststring2) )
end
end
//Finally, filter out any comments or blank spaces from the table.
local listtabfiltered = {}
for _, str in pairs (listtab) do
//if there's a doubleslash in the string then remove it and everything after it
local commentpos, _ = string.find(str, doubleslash)
local resultstr = str
if commentpos then
resultstr = string.sub( str, 1, commentpos - 1 )
end
//trim any spaces from the string, and don't add it to the filtered table if it's just an empty string at this point
resultstr = string.Trim(resultstr)
if resultstr != "" then listtabfiltered[resultstr] = true end
end
if table.Count(listtabfiltered) > 0 then
return listtabfiltered
end
end
local filestr = file.Read(filename,"LUA")
if !filestr then MsgN("PARTICLE CONTROL ERROR: Particle list file \"" .. filename .. "\" doesn't exist or isn't a readable file! Something went wrong!") return end
local keyvalues = util.KeyValuesToTable( filestr, false, true )
local resulttable = { Info = {}, Particles = {} }
if !keyvalues or (table.Count(keyvalues) == 0) then MsgN("PARTICLE CONTROL ERROR: Particle list file \"" .. filename .. "\" isn't a valid keyvalue table and couldn't be read! Check the file and make sure you've formatted everything correctly!") return end
if !keyvalues.Info then
//On Mac/Linux systems, the keyvalues-to-table function messes up and returns the keyname "Info" as something else, like "inf", so it's possible that the info subtable is
//still here, just under a different keyname. The info is the only thing in keyvalues that should be a table value, so we'll check for that:
local foundinfo = false
for k, v in pairs (keyvalues) do
if type(v) == "table" then
keyvalues.Info = v
MsgN("(" .. filename .. ": Found Info table under keyname " .. tostring(k) .. ")")
foundinfo = true
end
end
//If there's no table value in keyvalues at all, then this is a probably a custom file where someone messed up the formatting.
if !foundinfo then
MsgN("PARTICLE CONTROL ERROR: Particle list file \"" .. filename .. "\" doesn't have an \"Info\" table that we can find! Check the file and make sure you've formatted everything correctly!")
return
end
end
if !keyvalues.Info.CategoryName then MsgN("PARTICLE CONTROL ERROR: Particle list file \"" .. filename .. "\"'s Info table doesn't have a \"CategoryName\" string that we can find! Check the file and make sure you've formatted everything correctly!") return end
resulttable.Info.CategoryName = keyvalues.Info.CategoryName
resulttable.Info.Icon = icon
resulttable.Info.EffectOptions = {}
if keyvalues.Info["EffectOptions"] then
if keyvalues.Info.EffectOptions["Beams"] then
resulttable.Info.EffectOptions["Beams"] = ListStringToTable(keyvalues.Info.EffectOptions, "Beams")
end
if keyvalues.Info.EffectOptions["Color1"] then
resulttable.Info.EffectOptions["Color1"] = ListStringToTable(keyvalues.Info.EffectOptions, "Color1")
end
if keyvalues.Info.EffectOptions["Color255"] then
resulttable.Info.EffectOptions["Color255"] = ListStringToTable(keyvalues.Info.EffectOptions, "Color255")
end
if keyvalues.Info.EffectOptions["Tracers"] then
resulttable.Info.EffectOptions["Tracers"] = ListStringToTable(keyvalues.Info.EffectOptions, "Tracers")
end
end
if keyvalues.Info["UtilEffects"] then
resulttable.Info["UtilEffects"] = keyvalues.Info["UtilEffects"]
end
keyvalues.Info = nil
for k, _ in pairs (keyvalues) do
if !string.StartWith(k,doubleslash) and string.EndsWith(k, ".pcf") then //ignore .pcf lists that start with a doubleslash; also ignore lists that don't end with .pcf since these are probably continuations
local particlelist = ListStringToTable(keyvalues, k)
if particlelist then
game.AddParticles("particles/" .. k)
resulttable.Particles[k] = particlelist
end
end
end
local subtablename = string.StripExtension( string.GetFileFromFilename(filename) )
if isaddon then
MsgN( "PARTICLE CONTROL: Added list for addon: " .. resulttable.Info.CategoryName )
ParticleListTable.Addons[subtablename] = resulttable
else
MsgN( "PARTICLE CONTROL: Added list for game: " .. resulttable.Info.CategoryName )
ParticleListTable.Games[subtablename] = resulttable
end
end
//Source Engine / Garry's Mod
AddToParticleListTable("particlelists/gmod.lua", false, "games/16/garrysmod.png")
//Team Fortress 2
if IsMounted("tf") then
AddToParticleListTable("particlelists/tf2.lua", false, "games/16/tf.png") //TODO: currently causes a crash when loading updated pcfs, uncomment once fixed by devs
end
//Half-Life 2: Episode 1
if IsMounted("episodic") then
AddToParticleListTable("particlelists/ep1.lua", false, "games/16/episodic.png")
end
//Half-Life 2: Episode 2
if IsMounted("ep2") then
AddToParticleListTable("particlelists/ep2.lua", false, "games/16/ep2.png")
end
//Portal
if IsMounted("portal") then
AddToParticleListTable("particlelists/portal.lua", false, "games/16/portal.png")
end
//Counter-Strike: Source
if IsMounted("cstrike") then
AddToParticleListTable("particlelists/cstrike.lua", false, "games/16/cstrike.png")
end
//Find all list files in the particlelists/addons/ directory, and add them to their own section of the table
local alladdons, _ = file.Find( "lua/particlelists/addons/*.lua", "GAME" )
for _, filename in pairs (alladdons) do
if !string.StartWith( filename, "_" ) then //don't add files that start with an _, this is how we stop it from reading the example files
AddToParticleListTable("particlelists/addons/" .. filename, true, "icon16/bricks.png")
end
end
if CLIENT then
//Favorites - this table is global because it's used by every Particle Browser panel on the client
ParticleBrowserFavorites = {
Effects = {}, //effects in the favorites list
Children = {}, //all DTreeNode panels that are using this favorites list
}
local parctrlfavs_str = file.Read("particlecontrol_favorites.txt","DATA")
if parctrlfavs_str then ParticleBrowserFavorites.Effects = util.JSONToTable(parctrlfavs_str) end
function ParticleBrowserFavorites_UpdateEffect( effectdisplay, effectoptions )
if effectoptions then
//We're adding the effect
ParticleBrowserFavorites.Effects[effectdisplay] = effectoptions
else
//We're removing the effect
ParticleBrowserFavorites.Effects[effectdisplay] = nil
end
for _, panel in pairs (ParticleBrowserFavorites.Children) do
if panel then
ParticleBrowserFavorites_UpdateChildPanel( panel )
end
end
file.Write( "particlecontrol_favorites.txt", util.TableToJSON(ParticleBrowserFavorites.Effects, true) )
end
function ParticleBrowserFavorites_UpdateChildPanel( panel )
if !panel then return end
if panel.FavoriteNodes then
//Remove all of the nodes from the panel
for k, node in pairs (panel.FavoriteNodes) do
node:Remove()
panel.FavoriteNodes[k] = nil
end
else
panel.FavoriteNodes = {}
end
for effectdisplay, effectoptions in SortedPairs (ParticleBrowserFavorites.Effects) do
local effect = effectoptions["InternalName"]
panel.FavoriteNodes[effect] = panel:AddNode(effectdisplay)
panel.FavoriteNodes[effect].EffectOptions = {}
if effectoptions["Beam"] then
panel.FavoriteNodes[effect].EffectOptions["Beam"] = true
end
if effectoptions["Colorable"] then
panel.FavoriteNodes[effect].EffectOptions["Colorable"] = true
end
if effectoptions["ColorOutOfOne"] then
panel.FavoriteNodes[effect].EffectOptions["ColorOutOfOne"] = true
end
//Don't use the icons for features we're not using
local beamicon = ( (panel.CommandInfo.mode_beam) and (panel.FavoriteNodes[effect].EffectOptions["Beam"] == true) )
local coloricon = ( (panel.CommandInfo.color) and (panel.FavoriteNodes[effect].EffectOptions["Colorable"] == true) )
//Set the effect icon:
if beamicon and coloricon then
panel.FavoriteNodes[effect].Icon:SetImage("icon16/fire_line_rainbow.png")
elseif beamicon then
panel.FavoriteNodes[effect].Icon:SetImage("icon16/fire_line.png")
elseif coloricon then
panel.FavoriteNodes[effect].Icon:SetImage("icon16/fire_rainbow.png")
else
panel.FavoriteNodes[effect].Icon:SetImage("icon16/fire.png")
end
panel.FavoriteNodes[effect].DoClick = function()
RunConsoleCommand( panel.CommandInfo.effectname, effect )
if panel.CommandInfo.mode_beam then
if panel.FavoriteNodes[effect].EffectOptions["Beam"] then
RunConsoleCommand( panel.CommandInfo.mode_beam, "1" )
else
RunConsoleCommand( panel.CommandInfo.mode_beam, "0" )
end
end
if panel.CommandInfo.color then
if panel.FavoriteNodes[effect].EffectOptions["Colorable"] then
RunConsoleCommand( panel.CommandInfo.color .. "_enabled", "1" )
else
RunConsoleCommand( panel.CommandInfo.color .. "_enabled", "0" )
end
if panel.FavoriteNodes[effect].EffectOptions["ColorOutOfOne"] then
RunConsoleCommand( panel.CommandInfo.color .. "_outofone", "1" )
else
RunConsoleCommand( panel.CommandInfo.color .. "_outofone", "0" )
end
end
end
panel.FavoriteNodes[effect].DoRightClick = function()
//We only need the remove option here, since it's already in the favorites list
local menu = DermaMenu()
local option = menu:AddOption( "Un-favorite \'\'" .. effectdisplay .. "\'\'", function()
ParticleBrowserFavorites_UpdateEffect( effectdisplay ) //calling this function with no second arg tells it to remove this effect from the list
end )
option:SetImage("icon16/delete.png")
menu:Open()
end
end
end
function AddParticleBrowser(panel,data)
local self = {}
//Data table contents:
//{
//name = "Effect" (name to show at the top of the panel)
//commands = (table of concommands that the panel uses)
// {
// effectname = "particlecontrol_effectname"
// mode_beam = "particlecontrol_mode_beam" (optional)
// color = "particlecontrol_color" (optional, assumes there are cvars derived from this name (X_enabled, X_r, X_g, X_b, X_outofone) )
// utileffect = "particlecontrol_utileffect" (optional, assumes there are cvars derived from this name (X_scale, X_magnitude, X_radius) )
//
// enabled = "particlecontrol_enabled" (optional - toggles whether or not the panel is open using this cvar)
// }
//}
self.back = vgui.Create("DForm", panel)
self.back:SetLabel(data.name)
self.back.Paint = function()
derma.SkinHook( "Paint", "CollapsibleCategory", self.back, self.back:GetWide(), self.back:GetTall() )
surface.SetDrawColor( Color(0,0,0,70) )
surface.DrawRect( 0, 0, self.back:GetWide(), self.back:GetTall() )
end
self.back.Header:SetImage("icon16/fire.png")
self.back.Header.DoClick = function() end
panel:AddPanel(self.back)
self.tree = vgui.Create("DTree", self.back)
self.tree:SetHeight(400)
self.back:AddItem(self.tree)
self.tree:GetParent():DockPadding(5,5,5,0)
local function PopulateEffectList(parent, effectstab, effectoptions)
for k, v in SortedPairs (effectstab) do
local effect = k
local effectdisplay = k
//if v is another string instead of just a filler "true" value, then that means k is the display name and v is the internal name
if isstring(v) then
effect = v
end
parent[effect] = parent:AddNode(effectdisplay)
parent[effect].EffectOptions = {}
if effectoptions["Beams"] and effectoptions["Beams"][effect] then
parent[effect].EffectOptions["Beam"] = true
end
if effectoptions["Color1"] and effectoptions["Color1"][effect] then
parent[effect].EffectOptions["Colorable"] = true
parent[effect].EffectOptions["ColorOutOfOne"] = true
end
if effectoptions["Color255"] and effectoptions["Color255"][effect] then
parent[effect].EffectOptions["Colorable"] = true
parent[effect].EffectOptions["ColorOutOfOne"] = false
end
//Don't use the icons for features we're not using
local beamicon = ( (data.commands.mode_beam) and (parent[effect].EffectOptions["Beam"] == true) )
local coloricon = ( (data.commands.color) and (parent[effect].EffectOptions["Colorable"] == true) )
//Set the effect icon:
if beamicon and coloricon then
parent[effect].Icon:SetImage("icon16/fire_line_rainbow.png")
elseif beamicon then
parent[effect].Icon:SetImage("icon16/fire_line.png")
elseif coloricon then
parent[effect].Icon:SetImage("icon16/fire_rainbow.png")
else
parent[effect].Icon:SetImage("icon16/fire.png")
end
//Left Click: Select the effect by setting its concommands
parent[effect].DoClick = function()
RunConsoleCommand( data.commands.effectname, effect )
if data.commands.mode_beam then
if parent[effect].EffectOptions["Beam"] then
RunConsoleCommand( data.commands.mode_beam, "1" )
else
RunConsoleCommand( data.commands.mode_beam, "0" )
end
end
if data.commands.color then
if parent[effect].EffectOptions["Colorable"] then
RunConsoleCommand( data.commands.color .. "_enabled", "1" )
else
RunConsoleCommand( data.commands.color .. "_enabled", "0" )
end
if parent[effect].EffectOptions["ColorOutOfOne"] then
RunConsoleCommand( data.commands.color .. "_outofone", "1" )
else
RunConsoleCommand( data.commands.color .. "_outofone", "0" )
end
end
end
//Right Click: Display an option to add/remove the effect from favorites
parent[effect].DoRightClick = function()
local menu = DermaMenu()
if !ParticleBrowserFavorites.Effects[effectdisplay] then
local option = menu:AddOption( "Favorite \'\'" .. effectdisplay .. "\'\'", function()
ParticleBrowserFavorites_UpdateEffect( effectdisplay, {
["InternalName"] = effect,
["Beam"] = tobool(parent[effect].EffectOptions["Beam"]),
["Colorable"] = tobool(parent[effect].EffectOptions["Colorable"]),
["ColorOutOfOne"] = tobool(parent[effect].EffectOptions["ColorOutOfOne"]),
} )
end )
option:SetImage("icon16/add.png")
else
local option = menu:AddOption( "Un-favorite \'\'" .. effectdisplay .. "\'\'", function()
ParticleBrowserFavorites_UpdateEffect( effectdisplay ) //calling this function with no second arg tells it to remove this effect from the list
end )
option:SetImage("icon16/delete.png")
end
menu:Open()
end
end
end
//Games
self.tree.games = self.tree:AddNode("Games")
self.tree.games.Icon:SetImage("icon16/folder_database.png")
self.tree.games:SetExpanded(true)
//
for tabname, tab in SortedPairs (ParticleListTable.Games) do
self.tree.games[tabname] = self.tree.games:AddNode(tab.Info.CategoryName)
self.tree.games[tabname].Icon:SetImage(tab.Info.Icon)
//Create the utilfx list
if tab.Info.UtilEffects then
self.tree.games[tabname].UtilEffects = self.tree.games[tabname]:AddNode("Scripted Effects")
self.tree.games[tabname].UtilEffects.Icon:SetImage("icon16/page_gear.png")
self.tree.games[tabname].UtilEffects.IsPopulated = false
self.tree.games[tabname].UtilEffects.DoClick = function()
if self.tree.games[tabname].UtilEffects.IsPopulated == true then return end
PopulateEffectList(self.tree.games[tabname].UtilEffects, tab.Info.UtilEffects, tab.Info.EffectOptions)
self.tree.games[tabname].UtilEffects.IsPopulated = true
self.tree.games[tabname].UtilEffects:SetExpanded(true)
end
end
//Create the .pcf lists
for pcfname, particles in SortedPairs (tab.Particles) do
local pcftabname = string.StripExtension(pcfname) //just in case having a dot in the key name causes problems
self.tree.games[tabname][pcftabname] = self.tree.games[tabname]:AddNode(pcfname)
self.tree.games[tabname][pcftabname].Icon:SetImage("icon16/page.png")
self.tree.games[tabname][pcftabname].IsPopulated = false
self.tree.games[tabname][pcftabname].DoClick = function()
if self.tree.games[tabname][pcftabname].IsPopulated == true then return end
PopulateEffectList(self.tree.games[tabname][pcftabname], particles, tab.Info.EffectOptions)
self.tree.games[tabname][pcftabname].IsPopulated = true
self.tree.games[tabname][pcftabname]:SetExpanded(true)
end
end
end
//Addons
self.tree.addons = self.tree:AddNode("Addons")
self.tree.addons.Icon:SetImage("icon16/folder_database.png")
self.tree.addons:SetExpanded(true)
//
for tabname, tab in SortedPairs (ParticleListTable.Addons) do
self.tree.addons[tabname] = self.tree.addons:AddNode(tab.Info.CategoryName)
self.tree.addons[tabname].Icon:SetImage(tab.Info.Icon)
//Create the utilfx list
if tab.Info.UtilEffects then
self.tree.addons[tabname].UtilEffects = self.tree.addons[tabname]:AddNode("Scripted Effects")
self.tree.addons[tabname].UtilEffects.Icon:SetImage("icon16/page_gear.png")
self.tree.addons[tabname].UtilEffects.IsPopulated = false
self.tree.addons[tabname].UtilEffects.DoClick = function()
if self.tree.addons[tabname].UtilEffects.IsPopulated == true then return end
PopulateEffectList(self.tree.addons[tabname].UtilEffects, tab.Info.UtilEffects, tab.Info.EffectOptions)
self.tree.addons[tabname].UtilEffects.IsPopulated = true
self.tree.addons[tabname].UtilEffects:SetExpanded(true)
end
end
//Create the .pcf lists
for pcfname, particles in SortedPairs (tab.Particles) do
local pcftabname = string.StripExtension(pcfname) //just in case having a dot in the key name causes problems
self.tree.addons[tabname][pcftabname] = self.tree.addons[tabname]:AddNode(pcfname)
self.tree.addons[tabname][pcftabname].Icon:SetImage("icon16/page.png")
self.tree.addons[tabname][pcftabname].IsPopulated = false
self.tree.addons[tabname][pcftabname].DoClick = function()
if self.tree.addons[tabname][pcftabname].IsPopulated == true then return end
PopulateEffectList(self.tree.addons[tabname][pcftabname], particles, tab.Info.EffectOptions)
self.tree.addons[tabname][pcftabname].IsPopulated = true
self.tree.addons[tabname][pcftabname]:SetExpanded(true)
end
end
end
//Favorites
self.tree.favorites = self.tree:AddNode("Favorites")
self.tree.favorites.Icon:SetImage("icon16/star.png")
self.tree.favorites:SetExpanded(true)
//
table.insert( ParticleBrowserFavorites.Children, self.tree.favorites ) //Add it to the Favorites table's list of child panels and let it do the rest of the work
self.tree.favorites.CommandInfo = data.commands //Store the console command info in the panel so the UpdateChildPanel function can access it
ParticleBrowserFavorites_UpdateChildPanel( self.tree.favorites )
self.effectnameentry = vgui.Create( "DTextEntry", self.back )
self.effectnameentry:SetConVar( data.commands.effectname )
self.back:AddItem(self.effectnameentry)
self.effectnameentry:GetParent():DockPadding(5,5,5,10)
if data.commands.mode_beam then
self.beam = vgui.Create("DForm", self.back)
self.beam.Paint = function()
derma.SkinHook( "Paint", "CollapsibleCategory", self.beam, self.beam:GetWide(), self.beam:GetTall() )
surface.SetDrawColor( Color(0,0,0,110) )
surface.DrawRect( 0, 0, self.beam:GetWide(), self.beam:GetTall() )
end
self.beam:Dock(TOP)
self.back:AddItem(self.beam)
self.beam:GetParent():DockPadding(5,0,5,0)
//Modify the header and tweak things so it doesn't show up when the category is closed.
self.beam.Header:SetText( "Beam" )
self.beam.Header:SetImage("icon16/fire_line.png")
self.beam.Header.DoClick = function() end
self.beam.PerformLayout = function()
local us = self.beam //so when we copy this over to another dform we only have to change this one line
local Padding = us:GetPadding() or 0
if ( us.Contents ) then
if ( us:GetExpanded() ) then
us.Contents:InvalidateLayout( true )
us.Contents:SetVisible( true )
else
us.Contents:SetVisible( false )
end
end
if ( us:GetExpanded() ) then
us:SizeToChildren( false, true )
else
us:SetTall(0) //this is the only real change from the standard DForm:PerformLayout()
end
-- Make sure the color of header text is set
us.Header:ApplySchemeSettings()
us.animSlide:Run()
us:UpdateAltLines();
end
self.beam.text = vgui.Create("DLabel", self.beam)
self.beam.text:SetColor( Color(60,60,60,255) )
self.beam.text:SetText("This effect attaches to two points.")
self.beam.text:SetWrap( true )
self.beam.text:SetAutoStretchVertical( true )
self.beam.text:Dock(TOP)
self.beam.text:SizeToContents()
self.beam:AddItem(self.beam.text)
self.beam.text:GetParent():DockPadding( 15, 10, 15, 10 )
end
if data.commands.color then
self.color = vgui.Create("DForm", self.back)
self.color.Paint = function()
derma.SkinHook( "Paint", "CollapsibleCategory", self.color, self.color:GetWide(), self.color:GetTall() )
surface.SetDrawColor( Color(0,0,0,110) )
surface.DrawRect( 0, 0, self.color:GetWide(), self.color:GetTall() )
end
self.color:Dock(TOP)
self.back:AddItem(self.color)
self.color:GetParent():DockPadding(5,0,5,0)
//Modify the header and tweak things so it doesn't show up when the category is closed.
self.color.Header:SetText( "Colorable" )
self.color.Header:SetImage("icon16/fire_rainbow.png")
self.color.Header.DoClick = function() end
self.color.PerformLayout = function()
local us = self.color //so when we copy this over to another dform we only have to change this one line
local Padding = us:GetPadding() or 0
if ( us.Contents ) then
if ( us:GetExpanded() ) then
us.Contents:InvalidateLayout( true )
us.Contents:SetVisible( true )
else
us.Contents:SetVisible( false )
end
end
if ( us:GetExpanded() ) then
us:SizeToChildren( false, true )
else
us:SetTall(0) //this is the only real change from the standard DForm:PerformLayout()
end
-- Make sure the color of header text is set
us.Header:ApplySchemeSettings()
us.animSlide:Run()
us:UpdateAltLines();
end
self.color.text = vgui.Create("DLabel", self.color)
self.color.text:SetColor( Color(60,60,60,255) )
self.color.text:SetText("This effect is colorable.")
self.color.text:SetWrap( true )
self.color.text:SetAutoStretchVertical( true )
self.color.text:Dock(TOP)
self.color.text:SizeToContents()
self.color:AddItem(self.color.text)
self.color.text:GetParent():DockPadding( 15, 10, 15, 10 )
self.color.selection = vgui.Create( "CtrlColor", self.color )
self.color.selection:SetLabel( "" )
self.color.selection:SetConVarR( data.commands.color .. "_r" )
self.color.selection:SetConVarG( data.commands.color .. "_g" )
self.color.selection:SetConVarB( data.commands.color .. "_b" )
self.color.selection:SetConVarA( nil )
self.color.selection:Dock(TOP)
self.color:AddItem(self.color.selection)
self.color.selection:GetParent():DockPadding( 10, 0, 10, 10 )
end
if data.commands.utileffect then
self.utilfx = vgui.Create("DForm", self.back)
self.utilfx.Paint = function()
derma.SkinHook( "Paint", "CollapsibleCategory", self.utilfx, self.utilfx:GetWide(), self.utilfx:GetTall() )
surface.SetDrawColor( Color(0,0,0,110) )
surface.DrawRect( 0, 0, self.utilfx:GetWide(), self.utilfx:GetTall() )
end
self.utilfx:Dock(TOP)
self.back:AddItem(self.utilfx)
self.utilfx:GetParent():DockPadding(5,0,5,0)
//Modify the header and tweak things so it doesn't show up when the category is closed.
self.utilfx.Header:SetText( "Scripted Effect" )
self.utilfx.Header:SetImage("icon16/cog.png")
self.utilfx.Header.DoClick = function() end
self.utilfx.PerformLayout = function()
local us = self.utilfx //so when we copy this over to another dform we only have to change this one line
local Padding = us:GetPadding() or 0
if ( us.Contents ) then
if ( us:GetExpanded() ) then
us.Contents:InvalidateLayout( true )
us.Contents:SetVisible( true )
else
us.Contents:SetVisible( false )
end
end
if ( us:GetExpanded() ) then
us:SizeToChildren( false, true )
else
us:SetTall(0) //this is the only real change from the standard DForm:PerformLayout()
end
-- Make sure the color of header text is set
us.Header:ApplySchemeSettings()
us.animSlide:Run()
us:UpdateAltLines();
end
self.utilfx.text = vgui.Create("DLabel", self.utilfx)
self.utilfx.text:SetColor( Color(60,60,60,255) )
self.utilfx.text:SetText("These options can be used to modify certain scripted effects in different ways depending on the effect being used.")
self.utilfx.text:SetWrap( true )
self.utilfx.text:SetAutoStretchVertical( true )
self.utilfx.text:Dock(TOP)
self.utilfx.text:SizeToContents()
self.utilfx:AddItem(self.utilfx.text)
self.utilfx.text:GetParent():DockPadding( 15, 10, 15, 5 )
self.utilfx.slider1 = vgui.Create( "DNumSlider", self.utilfx )
self.utilfx.slider1:SetText( "Util.Effect Scale" )
self.utilfx.slider1:SetMinMax(0.2, 10)
self.utilfx.slider1:SetDecimals(2)
self.utilfx.slider1:SetConVar( data.commands.utileffect .. "_scale" )
self.utilfx.slider1:SetHeight(15)
self.utilfx.slider1:SizeToContents()
self.utilfx.slider1:SetDark(true)
self.utilfx:AddItem(self.utilfx.slider1)
self.utilfx.slider2 = vgui.Create( "DNumSlider", self.utilfx )
self.utilfx.slider2:SetText( "Util.Effect Magnitude" )
self.utilfx.slider2:SetMinMax(1, 10)
self.utilfx.slider2:SetDecimals(2)
self.utilfx.slider2:SetConVar( data.commands.utileffect .. "_magnitude" )
self.utilfx.slider2:SetHeight(15)
self.utilfx.slider2:SizeToContents()
self.utilfx.slider2:SetDark(true)
self.utilfx:AddItem(self.utilfx.slider2)
self.utilfx.slider3 = vgui.Create( "DNumSlider", self.utilfx )
self.utilfx.slider3:SetText( "Util.Effect Radius" )
self.utilfx.slider3:SetMinMax(10, 1000)
self.utilfx.slider3:SetDecimals(2)
self.utilfx.slider3:SetConVar( data.commands.utileffect .. "_radius" )
self.utilfx.slider3:SetHeight(15)
self.utilfx.slider3:SizeToContents()
self.utilfx.slider3:SetDark(true)
self.utilfx:AddItem(self.utilfx.slider3)
end
self.back.Think = function()
if self.beam then
if GetConVarNumber( data.commands.mode_beam ) > 0 then
//expand it
if self.beam:GetExpanded() == false then self.beam:Toggle() end
else
//contract it
if self.beam:GetExpanded() == true then self.beam:Toggle() end
self.beam:GetParent():SetTall(self.beam:GetTall())
end
end
if self.color then
if GetConVarNumber( data.commands.color .. "_enabled" ) > 0 then
//expand it
if self.color:GetExpanded() == false then self.color:Toggle() end
else
//contract it
if self.color:GetExpanded() == true then self.color:Toggle() end
self.color:GetParent():SetTall(self.color:GetTall())
end
end
if self.utilfx then
if string.StartWith( GetConVarString(data.commands.effectname), "!UTILEFFECT!" ) then
//expand it
if self.utilfx:GetExpanded() == false then self.utilfx:Toggle() end
else
//contract it
if self.utilfx:GetExpanded() == true then self.utilfx:Toggle() end
self.utilfx:GetParent():SetTall(self.utilfx:GetTall())
end
end
if data.commands.enabled then
if GetConVarNumber( data.commands.enabled ) > 0 then
//expand it
if self.back:GetExpanded() == false then self.back:Toggle() end
else
//contract it
if self.back:GetExpanded() == true then self.back:Toggle() end
end
end
end
end
//Favorites - this table is global too because this is all copied over from the standard Particle Browser, even though I don't suspect we'll ever have more than one of these
ParticleBrowserTracerFavorites = {
Effects = {}, //effects in the favorites list
Children = {}, //all DTreeNode panels that are using this favorites list
}
local parctrltracerfavs_str = file.Read("particlecontrol_favorites_tracer.txt","DATA")
if parctrltracerfavs_str then ParticleBrowserTracerFavorites.Effects = util.JSONToTable(parctrltracerfavs_str) end
function ParticleBrowserTracerFavorites_UpdateEffect( effectdisplay, effectoptions )
if effectoptions then
//We're adding the effect
ParticleBrowserTracerFavorites.Effects[effectdisplay] = effectoptions
else
//We're removing the effect
ParticleBrowserTracerFavorites.Effects[effectdisplay] = nil
end
for _, panel in pairs (ParticleBrowserTracerFavorites.Children) do
if panel then
ParticleBrowserTracerFavorites_UpdateChildPanel( panel )
end
end
file.Write( "particlecontrol_favorites_tracer.txt", util.TableToJSON(ParticleBrowserTracerFavorites.Effects, true) )
end
function ParticleBrowserTracerFavorites_UpdateChildPanel( panel )
if !panel then return end
if panel.FavoriteNodes then
//Remove all of the nodes from the panel
for k, node in pairs (panel.FavoriteNodes) do
node:Remove()
panel.FavoriteNodes[k] = nil
end
else
panel.FavoriteNodes = {}
end
for effectdisplay, effectoptions in SortedPairs (ParticleBrowserTracerFavorites.Effects) do
local effect = effectoptions["InternalName"]
panel.FavoriteNodes[effect] = panel:AddNode(effectdisplay)
panel.FavoriteNodes[effect].EffectOptions = {}
if effectoptions["Colorable"] then
panel.FavoriteNodes[effect].EffectOptions["Colorable"] = true
end
if effectoptions["ColorOutOfOne"] then
panel.FavoriteNodes[effect].EffectOptions["ColorOutOfOne"] = true
end
//Don't use the icons for features we're not using
local coloricon = ( (panel.CommandInfo.color) and (panel.FavoriteNodes[effect].EffectOptions["Colorable"] == true) )
//Set the effect icon:
if coloricon then
panel.FavoriteNodes[effect].Icon:SetImage("icon16/fire_line_rainbow.png")
else
panel.FavoriteNodes[effect].Icon:SetImage("icon16/fire_line.png")
end
panel.FavoriteNodes[effect].DoClick = function()
RunConsoleCommand( panel.CommandInfo.effectname, effect )
if panel.CommandInfo.color then
if panel.FavoriteNodes[effect].EffectOptions["Colorable"] then
RunConsoleCommand( panel.CommandInfo.color .. "_enabled", "1" )
else
RunConsoleCommand( panel.CommandInfo.color .. "_enabled", "0" )
end
if panel.FavoriteNodes[effect].EffectOptions["ColorOutOfOne"] then
RunConsoleCommand( panel.CommandInfo.color .. "_outofone", "1" )
else
RunConsoleCommand( panel.CommandInfo.color .. "_outofone", "0" )
end
end
end
panel.FavoriteNodes[effect].DoRightClick = function()
//We only need the remove option here, since it's already in the favorites list
local menu = DermaMenu()
local option = menu:AddOption( "Un-favorite \'\'" .. effectdisplay .. "\'\'", function()
ParticleBrowserTracerFavorites_UpdateEffect( effectdisplay ) //calling this function with no second arg tells it to remove this effect from the list
end )
option:SetImage("icon16/delete.png")
menu:Open()
end
end
end
function AddParticleBrowserTracer(panel,data)
local self = {}
//Data table contents:
//{
//name = "Effect" (name to show at the top of the panel)
//commands = (table of concommands that the panel uses)
// {
// effectname = "particlecontrol_effectname"
// color = "particlecontrol_color" (optional, assumes there are cvars derived from this name (X_enabled, X_r, X_g, X_b, X_outofone) )
// }
//}
self.back = vgui.Create("DForm", panel)
self.back:SetLabel(data.name)
self.back.Paint = function()
derma.SkinHook( "Paint", "CollapsibleCategory", self.back, self.back:GetWide(), self.back:GetTall() )
surface.SetDrawColor( Color(0,0,0,70) )
surface.DrawRect( 0, 0, self.back:GetWide(), self.back:GetTall() )
end
self.back.Header:SetImage("icon16/fire.png")
self.back.Header.DoClick = function() end
panel:AddPanel(self.back)
self.tree = vgui.Create("DTree", self.back)
self.tree:SetHeight(400)
self.back:AddItem(self.tree)
self.tree:GetParent():DockPadding(5,5,5,0)
local function PopulateEffectList(parent, effectstab, effectoptions)
for k, v in SortedPairs (effectstab) do
local effect = k
local effectdisplay = k
//if v is another string instead of just a filler "true" value, then that means k is the display name and v is the internal name
if isstring(v) then
effect = v
end
parent[effect] = parent:AddNode(effectdisplay)
parent[effect].EffectOptions = {}
if effectoptions["Color1"] and effectoptions["Color1"][effect] then
parent[effect].EffectOptions["Colorable"] = true
parent[effect].EffectOptions["ColorOutOfOne"] = true
end
if effectoptions["Color255"] and effectoptions["Color255"][effect] then
parent[effect].EffectOptions["Colorable"] = true
parent[effect].EffectOptions["ColorOutOfOne"] = false
end
//Don't use the icons for features we're not using
local coloricon = ( (data.commands.color) and (parent[effect].EffectOptions["Colorable"] == true) )
//Set the effect icon:
if coloricon then
parent[effect].Icon:SetImage("icon16/fire_line_rainbow.png")
else
parent[effect].Icon:SetImage("icon16/fire_line.png")
end
//Left Click: Select the effect by setting its concommands
parent[effect].DoClick = function()
RunConsoleCommand( data.commands.effectname, effect )
if data.commands.color then
if parent[effect].EffectOptions["Colorable"] then
RunConsoleCommand( data.commands.color .. "_enabled", "1" )
else
RunConsoleCommand( data.commands.color .. "_enabled", "0" )
end
if parent[effect].EffectOptions["ColorOutOfOne"] then
RunConsoleCommand( data.commands.color .. "_outofone", "1" )
else
RunConsoleCommand( data.commands.color .. "_outofone", "0" )
end
end
end
//Right Click: Display an option to add/remove the effect from favorites
parent[effect].DoRightClick = function()
local menu = DermaMenu()
if !ParticleBrowserTracerFavorites.Effects[effectdisplay] then
local option = menu:AddOption( "Favorite \'\'" .. effectdisplay .. "\'\'", function()
ParticleBrowserTracerFavorites_UpdateEffect( effectdisplay, {
["InternalName"] = effect,
["Colorable"] = tobool(parent[effect].EffectOptions["Colorable"]),
["ColorOutOfOne"] = tobool(parent[effect].EffectOptions["ColorOutOfOne"]),
} )
end )
option:SetImage("icon16/add.png")
else
local option = menu:AddOption( "Un-favorite \'\'" .. effectdisplay .. "\'\'", function()
ParticleBrowserTracerFavorites_UpdateEffect( effectdisplay ) //calling this function with no second arg tells it to remove this effect from the list
end )
option:SetImage("icon16/delete.png")
end
menu:Open()
end
end
end
//Games
self.tree.games = self.tree:AddNode("Games")
self.tree.games.Icon:SetImage("icon16/folder_database.png")
self.tree.games:SetExpanded(true)
//
for tabname, tab in SortedPairs (ParticleListTable.Games) do
if tab.Info.EffectOptions.Tracers then
self.tree.games[tabname] = self.tree.games:AddNode(tab.Info.CategoryName)
self.tree.games[tabname].Icon:SetImage(tab.Info.Icon)
//Create the utilfx list
if tab.Info.UtilEffects then
local tracerlist = {}
for k, v in pairs (tab.Info.UtilEffects) do
//in a utileffect table, the effect name is stored in the value
if tab.Info.EffectOptions.Tracers[v] then tracerlist[k] = v end
end
if table.Count(tracerlist) > 0 then
self.tree.games[tabname].UtilEffects = self.tree.games[tabname]:AddNode("Scripted Effects")
self.tree.games[tabname].UtilEffects.Icon:SetImage("icon16/page_gear.png")
self.tree.games[tabname].UtilEffects.IsPopulated = false
self.tree.games[tabname].UtilEffects.DoClick = function()
if self.tree.games[tabname].UtilEffects.IsPopulated == true then return end
PopulateEffectList(self.tree.games[tabname].UtilEffects, tracerlist, tab.Info.EffectOptions)
self.tree.games[tabname].UtilEffects.IsPopulated = true
self.tree.games[tabname].UtilEffects:SetExpanded(true)
end
end
end
//Create the .pcf lists
for pcfname, particles in SortedPairs (tab.Particles) do
local tracerlist = {}
for k, v in pairs (particles) do
//in a particle table, the effect name is stored in the key
if tab.Info.EffectOptions.Tracers[k] then tracerlist[k] = v end
end
if table.Count(tracerlist) > 0 then
local pcftabname = string.StripExtension(pcfname) //just in case having a dot in the key name causes problems
self.tree.games[tabname][pcftabname] = self.tree.games[tabname]:AddNode(pcfname)
self.tree.games[tabname][pcftabname].Icon:SetImage("icon16/page.png")
self.tree.games[tabname][pcftabname].IsPopulated = false
self.tree.games[tabname][pcftabname].DoClick = function()
if self.tree.games[tabname][pcftabname].IsPopulated == true then return end
PopulateEffectList(self.tree.games[tabname][pcftabname], tracerlist, tab.Info.EffectOptions)
self.tree.games[tabname][pcftabname].IsPopulated = true
self.tree.games[tabname][pcftabname]:SetExpanded(true)
end
end
end
end
end
//Addons
self.tree.addons = self.tree:AddNode("Addons")
self.tree.addons.Icon:SetImage("icon16/folder_database.png")
self.tree.addons:SetExpanded(true)
//
for tabname, tab in SortedPairs (ParticleListTable.Addons) do
if tab.Info.EffectOptions.Tracers then
self.tree.addons[tabname] = self.tree.addons:AddNode(tab.Info.CategoryName)
self.tree.addons[tabname].Icon:SetImage(tab.Info.Icon)
//Create the utilfx list
if tab.Info.UtilEffects then
local tracerlist = {}
for k, v in pairs (tab.Info.UtilEffects) do
//in a utileffect table, the effect name is stored in the value
if tab.Info.EffectOptions.Tracers[v] then tracerlist[k] = v end
end
if table.Count(tracerlist) > 0 then
self.tree.addons[tabname].UtilEffects = self.tree.addons[tabname]:AddNode("Scripted Effects")
self.tree.addons[tabname].UtilEffects.Icon:SetImage("icon16/page_gear.png")
self.tree.addons[tabname].UtilEffects.IsPopulated = false
self.tree.addons[tabname].UtilEffects.DoClick = function()
if self.tree.addons[tabname].UtilEffects.IsPopulated == true then return end
PopulateEffectList(self.tree.addons[tabname].UtilEffects, tracerlist, tab.Info.EffectOptions)
self.tree.addons[tabname].UtilEffects.IsPopulated = true
self.tree.addons[tabname].UtilEffects:SetExpanded(true)
end
end
end
//Create the .pcf lists
for pcfname, particles in SortedPairs (tab.Particles) do
local tracerlist = {}
for k, v in pairs (particles) do
//in a particle table, the effect name is stored in the key
if tab.Info.EffectOptions.Tracers[k] then tracerlist[k] = v end
end
if table.Count(tracerlist) > 0 then
local pcftabname = string.StripExtension(pcfname) //just in case having a dot in the key name causes problems
self.tree.addons[tabname][pcftabname] = self.tree.addons[tabname]:AddNode(pcfname)
self.tree.addons[tabname][pcftabname].Icon:SetImage("icon16/page.png")
self.tree.addons[tabname][pcftabname].IsPopulated = false
self.tree.addons[tabname][pcftabname].DoClick = function()
if self.tree.addons[tabname][pcftabname].IsPopulated == true then return end
PopulateEffectList(self.tree.addons[tabname][pcftabname], tracerlist, tab.Info.EffectOptions)
self.tree.addons[tabname][pcftabname].IsPopulated = true
self.tree.addons[tabname][pcftabname]:SetExpanded(true)
end
end
end
end
end
//Favorites
self.tree.favorites = self.tree:AddNode("Favorites (Tracers)")
self.tree.favorites.Icon:SetImage("icon16/star.png")
self.tree.favorites:SetExpanded(true)
//
table.insert( ParticleBrowserTracerFavorites.Children, self.tree.favorites ) //Add it to the Favorites table's list of child panels and let it do the rest of the work
self.tree.favorites.CommandInfo = data.commands //Store the console command info in the panel so the UpdateChildPanel function can access it
ParticleBrowserTracerFavorites_UpdateChildPanel( self.tree.favorites )
self.effectnameentry = vgui.Create( "DTextEntry", self.back )
self.effectnameentry:SetConVar( data.commands.effectname )
self.back:AddItem(self.effectnameentry)
self.effectnameentry:GetParent():DockPadding(5,5,5,10)
if data.commands.color then
self.color = vgui.Create("DForm", self.back)
self.color.Paint = function()
derma.SkinHook( "Paint", "CollapsibleCategory", self.color, self.color:GetWide(), self.color:GetTall() )
surface.SetDrawColor( Color(0,0,0,110) )
surface.DrawRect( 0, 0, self.color:GetWide(), self.color:GetTall() )
end
self.color:Dock(TOP)
self.back:AddItem(self.color)
self.color:GetParent():DockPadding(5,0,5,0)
//Modify the header and tweak things so it doesn't show up when the category is closed.
self.color.Header:SetText( "Colorable" )
self.color.Header:SetImage("icon16/fire_rainbow.png")
self.color.Header.DoClick = function() end
self.color.PerformLayout = function()
local us = self.color //so when we copy this over to another dform we only have to change this one line
local Padding = us:GetPadding() or 0
if ( us.Contents ) then
if ( us:GetExpanded() ) then
us.Contents:InvalidateLayout( true )
us.Contents:SetVisible( true )
else
us.Contents:SetVisible( false )
end
end
if ( us:GetExpanded() ) then
us:SizeToChildren( false, true )
else
us:SetTall(0) //this is the only real change from the standard DForm:PerformLayout()
end
-- Make sure the color of header text is set
us.Header:ApplySchemeSettings()
us.animSlide:Run()
us:UpdateAltLines();
end
self.color.text = vgui.Create("DLabel", self.color)
self.color.text:SetColor( Color(60,60,60,255) )
self.color.text:SetText("This effect is colorable.")
self.color.text:SetWrap( true )
self.color.text:SetAutoStretchVertical( true )
self.color.text:Dock(TOP)
self.color.text:SizeToContents()
self.color:AddItem(self.color.text)
self.color.text:GetParent():DockPadding( 15, 10, 15, 10 )
self.color.selection = vgui.Create( "CtrlColor", self.color )
self.color.selection:SetLabel( "" )
self.color.selection:SetConVarR( data.commands.color .. "_r" )
self.color.selection:SetConVarG( data.commands.color .. "_g" )
self.color.selection:SetConVarB( data.commands.color .. "_b" )
self.color.selection:SetConVarA( nil )
self.color.selection:Dock(TOP)
self.color:AddItem(self.color.selection)
self.color.selection:GetParent():DockPadding( 10, 0, 10, 10 )
end
self.back.Think = function()
if self.color then
if GetConVarNumber( data.commands.color .. "_enabled" ) > 0 then
//expand it
if self.color:GetExpanded() == false then self.color:Toggle() end
else
//contract it
if self.color:GetExpanded() == true then self.color:Toggle() end
self.color:GetParent():SetTall(self.color:GetTall())
end
end
end
end
end