SPAWN_COUNT = 1; CLIENT_MENU = 1; function UTIL_GenerateContraptionSpawner( player ) if( player ~= 1 and CLIENT_MENU == 0 ) then return; end _spawnmenu.RemoveCategory( player, "VMF Loader" ); local folders = _file.Find( "contraptions/*" ); for i = 1, table.getn( folders ) do if( folders[i] ~= ".." ) then if( folders[i] == "." ) then local files = _file.Find( "contraptions/" .. folders[i] .. "/*.vmf" ); for j = 1, table.getn( files ) do local sTitle = string.gsub( files[j], ".vmf", "" ); sTitle = string.gsub( sTitle, "_abs", "" ); sTitle = string.gsub( sTitle, "^%l", string.upper ); sTitle = string.gsub( sTitle, "_", " " ); -- Convert underscores to spaces for the menu. _spawnmenu.AddItem( player, "VMF Loader", "+" .. sTitle, "loadvmf contraptions/" .. files[j] ); end else if( _file.IsDir( "contraptions/" .. folders[i] ) == true ) then _spawnmenu.AddItem( player, "VMF Loader", "@" .. folders[i], "" ); local files = _file.Find( "contraptions/" .. folders[i] .. "/*.vmf" ); for j = 1, table.getn( files ) do local sTitle = string.gsub( files[j], ".vmf", "" ); sTitle = string.gsub( sTitle, "_abs", "" ); sTitle = string.gsub( sTitle, "^%l", string.upper ); sTitle = string.gsub( sTitle, "_", " " ); -- Convert underscores to spaces for the menu. _spawnmenu.AddItem( player, "VMF Loader", "+" .. sTitle, "loadvmf contraptions/" .. folders[i] .. "/" .. files[j] ); end end end end end end function event_PlayerInitSpawn( player ) UTIL_GenerateContraptionSpawner( player ); end HookEvent( "eventPlayerInitialSpawn", event_PlayerInitSpawn ); function cc_loadmap( user, arguments ) if( user ~= 1 and CLIENT_MENU == 0 ) then _PrintMessage( user, HUD_PRINTCONSOLE, "VMFLoader: Sorry client contraption spawning is disabled." ); return; end if( _file.Exists( arguments ) ) then -- Read file. local sBuffer = _file.Read( arguments ); -- Make sure this contains no brushes if( string.find( sBuffer, "\tsolid" ) ~= nil ) then _PrintMessage( user, HUD_PRINTCONSOLE, "VMFLoader: Sorry, that file cannot be loaded because it contains brushes.\n" ); return; end -- Increment spawn counter. SPAWN_COUNT = SPAWN_COUNT + 1; -- Get location where they want to spawn this contraption local vecpos = _PlayerGetShootPos( user ); local plyang = _PlayerGetShootAng( user ); _TraceLine( vecpos, plyang, 4096, user ); local spawnPos = _TraceEndPos( ); -- Absolute Positioning local bAbsolute = false; if( string.find( arguments, "_abs" ) ) then bAbsolute = true; end -- Chop off versioninfo, visgroups, viewsettings, world, camera, and cordon blocks. sBuffer = string.sub( sBuffer, string.find( sBuffer, "entity" ) - 1 ); if( string.find( sBuffer, "cameras" ) ~= nil ) then sBuffer = string.sub( sBuffer, 1, string.find( sBuffer, "cameras" ) - 1 ); end -- Strip out editor blocks we don't need them. local iFound = nil; for i = 1, string.len( sBuffer ) do iFound = string.find( sBuffer, "editor" ); if( iFound ~= nil ) then i = i + 6; sBuffer = string.sub( sBuffer, 1, string.find( sBuffer, "editor" ) - 1 ) .. string.sub( sBuffer, string.find( sBuffer, "}", iFound ) + 2 ); end end -- Convert connection blocks into an easy to parse format. sBuffer = string.gsub( sBuffer, "\tconnections", "" ); sBuffer = string.gsub( sBuffer, "\t{", "\t<" ); sBuffer = string.gsub( sBuffer, "\t}", "\t>" ); -- Start parsing the entites for sEntity in string.gfind( sBuffer, "%b{}" ) do sEntity = string.sub( sEntity, 2, string.len( sEntity ) - 2 ); -- Get rid of those pesky brackets sEntity = string.gsub( sEntity, "\t", "" ); -- Get rid of the pesky tabs. -- Extract the connections if they exist. local tConnections = {}; iFound = string.find( sEntity, "<" ); if( iFound ~= nil ) then local sConnections = string.sub( sEntity, string.find( sEntity, "<" ) + 1, string.find( sEntity, ">" ) - 1 ); for sLine in string.gfind( sConnections, "[^\r\n]+" ) do sLine = string.gsub( sLine, "\"", "" ); -- Strip quotes -- Replace _respawn with SPAWN_COUNT sLine = string.gsub( sLine, "_respawn", tostring( SPAWN_COUNT ) ); table.insert( tConnections, sLine ); end sEntity = string.sub( sEntity, 1, string.find( sEntity, "<" ) - 1 ) .. string.sub( sEntity, string.find( sEntity, ">" ) + 1 ); end -- Extract keyvalues. local sClassname; local tKeyValues = {}; for sLine in string.gfind( sEntity, "[^\r\n]+" ) do if( string.find( sLine, "\"id\"" ) == nil ) then sLine = string.gsub( sLine, "\"", "" ); -- Strip quotes if( string.find( sLine, "classname" ) ~= nil ) then sClassname = string.sub( sLine, string.find( sLine, " " ) + 1 ); else table.insert( tKeyValues, sLine ); end end end -- Create entity local entity = _EntCreate( sClassname ); -- Apply keyvalues local bDontApplyKey = false; for i = 1, table.getn( tKeyValues ) do local sKey = string.sub( tKeyValues[i], 1, string.find( tKeyValues[i], " " ) - 1 ); local sValue = string.sub( tKeyValues[i], string.find( tKeyValues[i], " " ) + 1 ); -- Replace _respawn with SPAWN_COUNT sValue = string.gsub( sValue, "_respawn", tostring( SPAWN_COUNT ) ); -- Do some interception if( sKey == "origin" and bAbsolute == false ) then local x = string.sub( sValue, 1, string.find( sValue, " " ) - 1 ); sValue = string.sub( sValue, string.find( sValue, " " ) + 1 ); local y = string.sub( sValue, 1, string.find( sValue, " " ) - 1 ); sValue = string.sub( sValue, string.find( sValue, " " ) + 1 ); local z = sValue; local vPos = vector3( tonumber( x ), tonumber( y ), tonumber( z ) ); vPos = vecAdd( vPos, spawnPos ); sValue = vecString( vPos ); elseif( sKey == "model" ) then _EntPrecacheModel( sValue ); -- Fix any errors due to the model not being precached. elseif( sKey == "material" ) then _EntSetMaterial( entity, sValue ); bDontApplyKey = true; elseif( sKey == "luascript" ) then _OpenScript( sValue ); -- Load a script for this object. bDontApplyKey = true; elseif( sKey == "parentname" ) then _EntFire( entity, "setparent", sValue, 0 ); -- TODO: Figure out why the parentname keyvalue won't work. bDontApplyKey = true; end if( bDontApplyKey == false ) then _EntSetKeyValue( entity, sKey, sValue ); end bDontApplyKey = false; end -- Spawn _EntSpawn( entity ); _EntActivate( entity ); -- Apply connections for i = 1, table.getn( tConnections ) do _EntFire( entity, "addoutput", tConnections[i], 0 ); end end -- Alert them it was loaded properly _PrintMessage( user, HUD_PRINTCONSOLE, "VMFLoader: " .. arguments .. " loaded successfully." ); else _PrintMessage( user, HUD_PRINTCONSOLE, "VMFLoader: Sorry, that file cannot be found.\n" ); end end CONCOMMAND( "loadvmf", cc_loadmap ); function cc_reloadmenu( user, arguments ) UTIL_GenerateContraptionSpawner( 0 ); end CONCOMMAND( "vmfloader_reload", cc_reloadmenu ); function cc_saveprops( user, arguments ) local eProps = _EntitiesFindByClass( "prop_physics" ); local sMap = "versioninfo\r\n{\r\n\t\"editorversion\" \"400\"\r\n\t\"editorbuild\" \"3057\"\r\n\t\"mapversion\" \"11\"\r\n\t\"formatversion\" \"100\"\r\n\t\"prefab\" \"0\"\r\n}\r\nvisgroups\r\n{\r\n\tvisgroup\r\n\t{\r\n\t\t\"name\" \"Auto\"\r\n\t\t\"visgroupid\" \"1\"\r\n\t\t\"color\" \"168 105 206\"\r\n\t\tvisgroup\r\n\t\t{\r\n\t\t\t\"name\" \"Entities\"\r\n\t\t\t\"visgroupid\" \"2\"\r\n\t\t\t\"color\" \"237 178 171\"\r\n\t\t}\r\n\t}\r\n}\r\nviewsettings\r\n{\r\n\t\"bSnapToGrid\" \"1\"\r\n\t\"bShowGrid\" \"1\"\r\n\t\"nGridSpacing\" \"32\"\r\n\t\"bShow3DGrid\" \"0\"\r\n}\r\nworld\r\n{\r\n\t\"id\" \"1\"\r\n\t\"mapversion\" \"11\"\r\n\t\"classname\" \"worldspawn\"\r\n\t\"skyname\" \"sky_wasteland02\"\r\n\t\"maxpropscreenwidth\" \"-1\"\r\n}\r\n"; for i = 1, table.getn( eProps ) do sMap = sMap .. "entity\r\n"; sMap = sMap .. "{\r\n"; sMap = sMap .. "\t\"id\" \"\"\r\n"; sMap = sMap .. "\t\"classname\" \"prop_physics\"\r\n"; sMap = sMap .. "\t\"origin\" \"" .. vecString( _EntGetPos( eProps[i] ) ) .. "\"\r\n"; sMap = sMap .. "\t\"angles\" \"" .. vecString( _EntGetAngAngle( eProps[i] ) ) .. "\"\r\n"; sMap = sMap .. "\t\"model\" \"" .. string.gsub( _EntGetModel( eProps[i] ), "\\", "/" ) .. "\"\r\n"; sMap = sMap .. "\t\"spawnflags\" \"264\"\r\n"; sMap = sMap .. "\teditor\r\n\t{\r\n\t\t\"color\" \"220 30 220\"\r\n\t\t\"visgroupid\" \"2\"\r\n\t\t\"visgroupshown\" \"1\"\r\n\t}\r\n"; sMap = sMap .. "}\r\n"; end sMap = sMap .. "cameras\r\n{\r\n\t\"activecamera\" \"-1\"\r\n}\r\ncordon\r\n{\r\n\t\"mins\" \"(99999 99999 99999)\"\r\n\t\"maxs\" \"(-99999 -99999 -99999)\"\r\n\t\"active\" \"0\"\r\n}\r\n"; _file.Write( "contraptions/" .. arguments .. "_abs.vmf", sMap ); -- Regenerate spawn menu. UTIL_GenerateContraptionSpawner( 0 ); end CONCOMMAND( "vmfloader_saveprops", cc_saveprops ); function cc_setclientmenu( player, arguments ) CLIENT_MENU = tonumber( arguments ); end CONCOMMAND( "vmfloader_clientmenu", cc_setclientmenu ); -- Generate contraption menu for the next spawn. UTIL_GenerateContraptionSpawner( );