
include( 'prop_tools.lua' )

/*---------------------------------------------------------
   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( "Ragdoll ("..tostring(model)..")" )
	
	player:AddCleanup( "ragdolls", e )

end


local function SpawnProp( Player, data, vOffset )

	// Make sure this is allowed
	if ( !gamemode.Call( "PlayerSpawnProp", Player, data.Model ) ) then return end
	
	local Prop = ents.Create( "prop_physics" )
		Prop:SetModel( data.Model )
	Prop:Spawn()
	
	duplicator.DoGeneric( Prop, data, vOffset )
	
	Prop:Activate()
	
	timer.Simple( 0.01, CheckPropSolid, Prop, COLLISION_GROUP_NONE, COLLISION_GROUP_WORLD )

	// tell the gamemode we just spawned something
	gamemode.Call( "PlayerSpawnedProp", Player, data.Model, Prop )
	DoPropSpawnedEffect( Prop )
	
	return Prop
	
end

duplicator.RegisterEntity( "prop_physics", SpawnProp )

/*---------------------------------------------------------
   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 )

	timer.Simple( 0.01, CheckPropSolid, e, COLLISION_GROUP_NONE, COLLISION_GROUP_WORLD )
	DoPropSpawnedEffect( e )

	undo.Create("Prop")
		undo.SetPlayer(player)
		undo.AddEntity(e)
	undo.Finish( "Prop ("..tostring(model)..")" )
	
	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( "Effect ("..tostring(model)..")" )
	
	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 )

// 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, equipment, sf_extra )

	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

	// check for any special key values that we need to set on this npc.	
	local npc_kvs = npc_table.keyvalues
	local spawnflagsset = false

	if (npc_kvs && !sf_extra) then
	
		sf_extra = 0
	
		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 )
		npc.equipment = equipment
		
	end
	
	// Set spawn flags
	//if ( sf_extra ) then
	
		spawnflags = sf_extra or 0
		spawnflags = spawnflags + 256 // Long Visibility/Shoot 
		spawnflags = spawnflags + 512 // Fade Corpse
		spawnflags = spawnflags + 1024 // Think outside PVS
		
		npc:SetKeyValue("spawnflags", spawnflags)
		npc.sf_extra = sf_extra

	//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], player.NPC_Additional_Equipment )
	
	if (npc && npc:IsValid()) then

		gamemode.Call( "PlayerSpawnedNPC", player, npc )

		undo.Create("NPC")
			undo.SetPlayer(player)
			undo.AddEntity(npc)
		undo.Finish( "NPC ("..tostring(arguments[1])..")" )
		
		player:AddCleanup( "npcs", npc )
		
	end
end

concommand.Add( "gmod_spawnnpc", CCSpawnNPC )

function CCSetNPCWeapon( player, command, arguments )

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

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

end

concommand.Add( "gmod_setnpcweapon", CCSetNPCWeapon )

local function GenericNPCDuplicator( Player, data, vOffset )

	if ( !gamemode.Call( "PlayerSpawnNPC", Player, data.Class, data.equipment ) ) then return end

	PrintTable( data )
	
	local Entity = InternalSpawnNPC( Player, vOffset, Vector(0,0,1), data.Class, data.equipment, data.sf_extra )
	
	if ( Entity && Entity:IsValid()) then

		if ( data.Model ) then Entity:SetModel( data.Model ) end
		
		gamemode.Call( "PlayerSpawnedNPC", Player, Entity )
		Player:AddCleanup( "npcs", Entity )
		table.Add( Entity:GetTable(), data )
		
	end
	
	return Entity
	
end

duplicator.RegisterEntity( "npc_alyx", GenericNPCDuplicator )
duplicator.RegisterEntity( "npc_antlion", GenericNPCDuplicator )
duplicator.RegisterEntity( "npc_antlionguard", GenericNPCDuplicator )
duplicator.RegisterEntity( "npc_barnacle", GenericNPCDuplicator )
duplicator.RegisterEntity( "npc_barney", GenericNPCDuplicator )
duplicator.RegisterEntity( "npc_breen", GenericNPCDuplicator )
duplicator.RegisterEntity( "npc_combine_s", GenericNPCDuplicator )
duplicator.RegisterEntity( "npc_combine_p", GenericNPCDuplicator )
duplicator.RegisterEntity( "npc_combine_e", GenericNPCDuplicator )
duplicator.RegisterEntity( "npc_crow", GenericNPCDuplicator )
duplicator.RegisterEntity( "npc_cscanner", GenericNPCDuplicator )
duplicator.RegisterEntity( "npc_dog", GenericNPCDuplicator )
duplicator.RegisterEntity( "npc_eli", GenericNPCDuplicator )
duplicator.RegisterEntity( "npc_fastzombie", GenericNPCDuplicator )
duplicator.RegisterEntity( "npc_gman", GenericNPCDuplicator )
duplicator.RegisterEntity( "npc_headcrab", GenericNPCDuplicator )
duplicator.RegisterEntity( "npc_headcrab_black", GenericNPCDuplicator )
duplicator.RegisterEntity( "npc_headcrab_fast", GenericNPCDuplicator )
duplicator.RegisterEntity( "npc_kleiner", GenericNPCDuplicator )
duplicator.RegisterEntity( "npc_manhack", GenericNPCDuplicator )
duplicator.RegisterEntity( "npc_metropolice", GenericNPCDuplicator )
duplicator.RegisterEntity( "npc_monk", GenericNPCDuplicator )
duplicator.RegisterEntity( "npc_mossman", GenericNPCDuplicator )
duplicator.RegisterEntity( "npc_pigeon", GenericNPCDuplicator )
duplicator.RegisterEntity( "npc_rollermine", GenericNPCDuplicator )
duplicator.RegisterEntity( "npc_seagull", GenericNPCDuplicator )
duplicator.RegisterEntity( "npc_zombie", GenericNPCDuplicator )
duplicator.RegisterEntity( "npc_zombie_torso", GenericNPCDuplicator )
duplicator.RegisterEntity( "npc_citizen_rebel", GenericNPCDuplicator )
duplicator.RegisterEntity( "npc_citizen", GenericNPCDuplicator )
duplicator.RegisterEntity( "npc_citizen_dt", GenericNPCDuplicator )
duplicator.RegisterEntity( "npc_citizen_medic", GenericNPCDuplicator )

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

function CCSpawnSENT( player, command, arguments )

	if ( arguments[1] == nil ) then return end
	
	// Make sure this is a SWEP
	local sent = scripted_ents.GetStored( arguments[1] )
	if (sent == nil) then return end
	
	sent = sent.t
	
	// We need a spawn function. The SENT can then spawn itself properly
	if (!sent.SpawnFunction) then return end
	
	// You're not allowed to spawn this unless you're an admin!
	if ( !sent.Spawnable && !player:IsAdmin() ) 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 entity = sent:SpawnFunction( player, tr )
	
	if ( entity ) then
	
		gamemode.Call( "PlayerSpawnedSENT", player, entity )
		
		undo.Create("SENT")
			undo.SetPlayer(player)
			undo.AddEntity(entity)
		undo.Finish( "Scripted Entity ("..tostring(arguments[1])..")" )
		
		player:AddCleanup( "sents", entity )		
		entity:SetVar( "Player", player )
	
	end
	
	
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( "Vehicle ("..tostring(arguments[1])..")" )
		
		player:AddCleanup( "vehicles", npc )
		
		gamemode.Call( "PlayerSpawnedVehicle", player, vehicle )
		
	end
	
	
end

concommand.Add( "gm_spawnvehicle", CCSpawnVehicle )


/*---------------------------------------------------------
	// Give a swep.. duh.
---------------------------------------------------------*/
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
	
	// You're not allowed to spawn this!
	if ( !swep.Spawnable && !player:IsAdmin() ) then
		return
	end
	
	MsgAll( "Giving "..player:Nick().." a "..swep.Classname.."\n" )
	player:Give( swep.Classname )
	
end

concommand.Add( "gm_giveswep", CCSpawnSWEP )
