
/*---------------------------------------------------------
   Name: CCSpawn
   Desc: Console Command for a player to spawn different items
---------------------------------------------------------*/
function CCSpawn( player, command, arguments )

	if ( arguments[1] == nil ) then return end
	if ( !gamemode.Call( "PlayerSpawnObject", player ) ) then return end
	if ( !util.IsValidModel( arguments[1] ) ) then return end

	if ( util.IsValidProp( arguments[1] ) ) then 
	
		GMODSpawnProp( player, arguments[1] )
		return
	
	end
	
	if ( util.IsValidRagdoll( arguments[1] ) ) then 
	
		GMODSpawnRagdoll( player, arguments[1] )
		return
	
	end

	// Not a ragdoll or prop.. must be an 'effect' - spawn it as one
	GMODSpawnEffect( player, arguments[1] )
	
end


/*---------------------------------------------------------
   Name: GMODSpawnRagdoll - player spawns a ragdoll
---------------------------------------------------------*/
function GMODSpawnRagdoll( player, model )

	if ( !gamemode.Call( "PlayerSpawnRagdoll", player, model ) ) then return end
	local e = DoPlayerEntitySpawn( player, "prop_ragdoll", model )
	gamemode.Call( "PlayerSpawnedRagdoll", player, model, e )
	
	undo.Create("Ragdoll")
		undo.SetPlayer(player)
		undo.AddEntity(e)
	undo.Finish()
	
	player:AddCleanup( "ragdolls", e )

end


/*---------------------------------------------------------
   Name: CCSpawnProp - player spawns a prop
---------------------------------------------------------*/
function GMODSpawnProp( player, model )

	if ( !gamemode.Call( "PlayerSpawnProp", player, model ) ) then return end
	local e = DoPlayerEntitySpawn( player, "prop_physics", model )
	gamemode.Call( "PlayerSpawnedProp", player, model, e )

	undo.Create("Prop")
		undo.SetPlayer(player)
		undo.AddEntity(e)
	undo.Finish()
	
	player:AddCleanup( "props", e )

end


/*---------------------------------------------------------
   Name: GMODSpawnEffect
---------------------------------------------------------*/
function GMODSpawnEffect( player, model )

	if ( !gamemode.Call( "PlayerSpawnEffect", player, model ) ) then return end
	local e = DoPlayerEntitySpawn( player, "prop_effect", model )
	gamemode.Call( "PlayerSpawnedEffect", player, model, e )
	
	undo.Create("Effect")
		undo.SetPlayer(player)
		undo.AddEntity(e)
	undo.Finish()
	
	player:AddCleanup( "effects", e )

end

/*---------------------------------------------------------
   Name: DoPlayerEntitySpawn
   Desc: Utility function for player entity spawning functions
---------------------------------------------------------*/
function DoPlayerEntitySpawn( player, entity_name, model )

	local vStart = player:GetShootPos()
	local vForward = player:GetAimVector()

	local trace = {}
	trace.start = vStart
	trace.endpos = vStart + (vForward * 2048)
	trace.filter = player
	
	local tr = util.TraceLine( trace )

	// PrintTable( tr )

	// Prevent spawning too close
	//if ( !tr.Hit || tr.Fraction < 0.05 ) then 
	//	return 
	//end
	
	local ent = ents.Create( entity_name )
	if ( !ent:IsValid() ) then return end

	ang = player:EyeAngles()
	ang.yaw = ang.yaw + 180 // Rotate it 180 degrees in my favour
	ang.roll = 0
	ang.pitch = 0
	
	if (entity_name == "prop_ragdoll") then
		ang.pitch = -90
		tr.HitPos = tr.HitPos
	end
	
	ent:SetModel( model )
	ent:SetAngles( ang )
	ent:SetPos( tr.HitPos )
	ent:Spawn()
	ent:Activate()

	// Attempt to move the object so it sits flush
	// We could do a TraceEntity instead of doing all 
	// of this - but it feels off after the old way

	local vFlushPoint = tr.HitPos - ( tr.HitNormal * 512 )	// Find a point that is definitely out of the object in the direction of the floor
		vFlushPoint = ent:NearestPoint( vFlushPoint )			// Find the nearest point inside the object to that point
		vFlushPoint = ent:GetPos() - vFlushPoint				// Get the difference
		vFlushPoint = tr.HitPos + vFlushPoint					// Add it to our target pos
										
	if (entity_name != "prop_ragdoll") then
	
		// Set new position
		ent:SetPos( vFlushPoint )
	
	else
	
		// With ragdolls we need to move each physobject
		local VecOffset = vFlushPoint - ent:GetPos()
		for i=0, ent:GetPhysicsObjectCount()-1 do
			local phys = ent:GetPhysicsObjectNum( i )
			phys:SetPos( phys:GetPos() + VecOffset )
		end
		
	end

	return ent
	
end


concommand.Add( "gm_spawn", CCSpawn )


/*---------------------------------------------------------
   Name: CCSpawn
   Desc: Console Command for a player to spawn different items
---------------------------------------------------------*/
function CCSpawnSWEP( player, command, arguments )

	if ( arguments[1] == nil ) then return end

	// Make sure this is a SWEP
	local swep = weapons.GetStored( arguments[1] )
	if (swep == nil) then return end
	
	// Ask the gamemode if it's ok to spawn this
	if ( !gamemode.Call( "PlayerSpawnSWEP", player, arguments[1] ) ) then return end
	
	local vStart = player:GetShootPos()
	local vForward = player:GetAimVector()
	
	local trace = {}
	trace.start = vStart
	trace.endpos = vStart + (vForward * 2048)
	trace.filter = player
	
	local tr = util.TraceLine( trace )
	
	local swep = ents.Create( arguments[1] )
	if (!swep:IsValid()) then return end
	swep:SetPos( tr.HitPos + tr.HitNormal * 8 )
	swep:Spawn()
	swep:Activate()
	
end

concommand.Add( "gm_spawnswep", CCSpawnSWEP )

// Read the file here so we don't load it on every NPC spawn(!)
local NPCSpawnFile = file.Read( "../settings/npc_spawn/npcs.txt" )
local NPCKV = util.KeyValuesToTable( NPCSpawnFile )

local function InternalSpawnNPC( player, position, normal, type )

	if (NPCKV == nil) then return end

	// find and check the npc table
	local npc_table = NPCKV[ type ]
	if (npc_table == nil) then return nil end
	if (tonumber(npc_table.allow) == nil or tonumber(npc_table.allow) == 0) then return nil end
	
	position = position + normal * 2
	
	// This NPC can only be spawned on a certain surface
	if ( npc_table.surface ) then
	
		// Npc can only be spawned on a ceiling.. this could be a bit more leniant
		if ( npc_table.surface == "ceiling" ) then
			if ( normal != Vector( 0, 0, -1 ) ) then return nil end
		end
		
	end
	
	// check for overridden class type and create the npc
	local entType = npc_table.class or type
	local npc = ents.Create( entType )
	if (!npc:IsValid()) then return nil end

	local _equipment = player:GetTable().NPC_Additional_Equipment

	// check for any special key values that we need to set on this npc.	
	local npc_kvs = npc_table.keyvalues
	local spawnflagsset = false
	local sf_extra = 0
	if (npc_kvs) then
	
		for key,val in pairs(npc_kvs) do
			
			if (key == "_addspawnflag") then
				sf_extra = sf_extra + tonumber(val)
			else
			
				npc:SetKeyValue(key, val)
				
				if (key == "spawnflags") then
					spawnflagsset = true
				end
			
			end			
		end
		
	end
	
	// Set pos
	npc:SetPos(position)
	
	local Angles = player:GetAngles()
		Angles.pitch = 0
		Angles.roll = 0
		Angles.yaw = Angles.yaw + 180
		
	npc:SetAngles( Angles )
	
	// Give eqipment if set
	if (_equipment) then
	
		npc:SetKeyValue("additionalequipment", _equipment)
		
	end
	
	// Set spawn flags
	if (!spawnflagsset) then
	
		spawnflags = sf_extra
		spawnflags = spawnflags + 256 // Long Visibility/Shoot 
		spawnflags = spawnflags + 512 // Fade Corpse
		spawnflags = spawnflags + 1024 // Think outside PVS
		
		npc:SetKeyValue("spawnflags", spawnflags)
		
	end
	
	// spawn and activate!
	npc:Spawn()
	npc:Activate()
	
	// trigger any events that are needed!
	local npc_fire = npc_table.entfire
	if (npc_fire) then
	
		for key,val in pairs(npc_fire) do
			npc:Fire( tostring(val.input) or "", tostring(val.param) or "", tonumber(val.delay) or 0 )
		end
		
	end
	
	return npc
end

function CCSpawnNPC( player, command, arguments )

	if ( arguments[1] == nil ) then return end

	if ( !gamemode.Call( "PlayerSpawnNPC", player, arguments[1], player:GetTable().NPC_Additional_Equipment ) ) then return end
	
	local vStart = player:GetShootPos()
	local vForward = player:GetAimVector()
	
	local trace = {}
	trace.start = vStart
	trace.endpos = vStart + (vForward * 2048)
	trace.filter = player
	
	local tr = util.TraceLine( trace )
	
	local npc = InternalSpawnNPC( player, tr.HitPos, tr.HitNormal, arguments[1] )
	
	if (npc && npc:IsValid()) then
		undo.Create("NPC")
			undo.SetPlayer(player)
			undo.AddEntity(npc)
		undo.Finish()
		player:AddCleanup( "npcs", npc )
		player:AddCount( "npcs", npc )
	end
end

concommand.Add( "gmod_spawnnpc", CCSpawnNPC )

function CCSetNPCWeapon( player, command, arguments )

	if ( arguments[1] == nil ) then return end

	player:GetTable().NPC_Additional_Equipment = arguments[1]

end

concommand.Add( "gmod_setnpcweapon", CCSetNPCWeapon )


/*---------------------------------------------------------
   Name: CCSpawnSENT
   Desc: Console Command for a player to spawn different items
---------------------------------------------------------*/

function CCSpawnSENT( player, command, arguments )

	if ( arguments[1] == nil ) then return end
	
	// I don't think we're allowing this yet.
	if ( true ) then return end

	// Make sure this is a SWEP
	local sent = scripted_ents.GetStored( arguments[1] )
	if (sent == nil) then return end
	
	// Ask the gamemode if it's ok to spawn this
	if ( !gamemode.Call( "PlayerSpawnSENT", player, arguments[1] ) ) then return end
	
	local vStart = player:GetShootPos()
	local vForward = player:GetAimVector()
	
	local trace = {}
	trace.start = vStart
	trace.endpos = vStart + (vForward * 2048)
	trace.filter = player
	
	local tr = util.TraceLine( trace )
	
	local sent = ents.Create( arguments[1] )
	if (!sent:IsValid()) then return end
	sent:SetPos( tr.HitPos + tr.HitNormal * 8 )
	sent:Spawn()
	sent:Activate()
	
end

concommand.Add( "gm_spawnsent", CCSpawnSENT )



/*---------------------------------------------------------
   Name: CCSpawnVehicle
   Desc: Player attempts to spawn vehicle
---------------------------------------------------------*/
function CCSpawnVehicle( player, command, arguments )

	if ( arguments[1] == nil ) then return end
	if ( !gamemode.Call( "PlayerSpawnVehicle", player ) ) then return end
	
	local vStart = player:GetShootPos()
	local vForward = player:GetAimVector()
	
	local trace = {}
	trace.start = vStart
	trace.endpos = vStart + (vForward * 2048)
	trace.filter = player
	
	local tr = util.TraceLine( trace )
	
	local vehicle = vehicles.PlayerSpawn( player, arguments[1], tr.HitPos, tr.HitNormal )	
	
	if ( vehicle && vehicle:IsValid() ) then
	
		undo.Create("Vehicle")
			undo.SetPlayer(player)
			undo.AddEntity(vehicle)
		undo.Finish()
		
		player:AddCleanup( "vehicles", npc )
		
		gamemode.Call( "PlayerSpawnedVehicle", player, vehicle )
		
	end
	
	
end

concommand.Add( "gm_spawnvehicle", CCSpawnVehicle )