#include maps\mp\_utility;
#include common_scripts\utility;
#include maps\mp\gametypes\_hud_util;

// threatDetection style options are:
// glow 			-- glowy vfx blobs
// model 			-- script model of the threat that is posed
// vfx_model		-- a vfx posed model 
// attached_glow 	-- glowy vfx blobs that are attached to the players
// stencil_outline 	-- red dot/line things in screen space stenciled to the player's shape
	
init()
{
	level._threatdetection = SpawnStruct();
	level._threatdetection.default_style = "stencil_outline";
	
	SetDevDvarIfUninitialized( "threat_detection_mark_time", 3 );  
	SetDevDvarIfUninitialized( "threat_detection_highlight_style", level._threatdetection.default_style );  
	
	level._threatdetection.activeStyle = GetDvar( "threat_detection_highlight_style", level._threatdetection.default_style );
	level thread onPlayerConnect();
	level thread watchAgentSpawn();
}

changeThreatStyle( threatStyle )
{
	if( threatStyle == level._threatdetection.activeStyle )
		return;
	
	//turn everything off
	foreach( p in level.players )
	{
		//model and vfx_model
		if( IsDefined( p._threatdetection.mark_enemy_model ) )
			p._threatdetection.mark_enemy_model Delete();
		
		if( IsDefined( p._threatdetection.mark_friendly_model ) )
			p._threatdetection.mark_friendly_model Delete();
		
		//vfx
		if( IsDefined( p.mark_fx ) && IsDefined( p.mark_fx.fx_ent ))
		{
			foreach( i, fx in p.mark_fx.fx_ent )
			{
				if( IsDefined( fx.enemyMarker ) )
					fx.enemyMarker Delete();
				
				if( IsDefined( fx.friendlyMarker ) )
					fx.friendlyMarker Delete();
				
				if( IsDefined( fx.enemyLosMarker ) )
					fx.enemyLosMarker Delete();
				
				if( IsDefined( fx.friendlyLosMarker ) )
					fx.friendlyLosMarker Delete();			
			}
		}
		
	}
	
	//turn on the new one
	foreach( p in level.players )
		p threat_init( threatStyle ); 
	
	level._threatdetection.activeStyle = threatStyle;
}

GetThreatStyle()
{
	threatStyle = GetDvar( "threat_detection_highlight_style", level._threatdetection.default_style );
	if( threatStyle != level._threatdetection.activeStyle )
		changeThreatStyle( threatStyle );
	return threatStyle;
}


onPlayerConnect()
{
	for(;;)
	{
		level waittill("connected", player);
		player thread onPlayerSpawned();
	}
}

watchAgentSpawn()
{
	while (true)
	{	// note that this could miss an agent if multiple agents are spawned the same frame.
		level waittill( "spawned_agent", agent );
		agent._threatdetection = SpawnStruct();
		agent._threatdetection.showlist = [];
		agent thread onAgentSpawned();
	}
}

onAgentSpawned()
{
	self endon("death");
	self childthread monitorThreatHighlight();
	self childthread cleanUpOnDeath();
}

//called from player connect
onPlayerSpawned()
{
	self endon("disconnect");
	
	self._threatdetection = SpawnStruct();
	self._threatdetection.showlist = [];	
	self waittill( "spawned_player" );

	childthread monitorThreatHighlight();
	childthread cleanUpOnDeath();
	
	//if( self != level.players[0] )
	//{
		//childthread debugHelper();
	//}
	
	// Monitors if/when threat overlay should be displayed.
	childthread monitorThreatHighlightNotification();
	
	//for respawns
	while( 1 )
	{
		self waittill( "spawned_player" );
		
		threatStyle = GetThreatStyle();
		if( threatStyle == "attached_glow" )
		    visitFXEnt( ::visitorRelink, ::getHostileMarker, undefined );
	}
}


// Monitors if/when threat overlay should be displayed.
monitorThreatHighlightNotification()
{
	// Create a new hud element on client for the overlay.  Set initially to have an alpha of 0.0 so it's not visible.
	notification_overlay_element = NewClientHudElem( self );
	notification_overlay_element.x = 0;
	notification_overlay_element.y = 0;
	notification_overlay_element.alignX = "left";
	notification_overlay_element.alignY = "top";
	notification_overlay_element.horzAlign = "fullscreen";
	notification_overlay_element.vertAlign = "fullscreen";
	notification_overlay_element SetShader( "paint_overlay", 640, 480 );
	notification_overlay_element.alpha = 0.0;
	notification_overlay_element.color = ( 0, 0, 0 );
	notification_overlay_element.sort = -3;
	
	// Make sure nothing is getting set unexpectedly elsewhere.
	Assert( !IsDefined(notification_overlay_element.hidden));
	
	notification_overlay_element.hidden = true;
	
	FadeInTime = 0.5;
	FadeOutTime = .3;
	
	while(1)
	{

		AssertEx(IsDefined(self._threatdetection), "_threatdetection isn't defined");
		
		// Check if the showlist isn't empty (value updated in monitorThreatHighlight())
		if(self._threatdetection.showlist.size != 0) 
		{
			// If hud element (overlay) is hidden, branch out a thread to fade the overlay in and out.
			if( notification_overlay_element.hidden )
			{
				notification_overlay_element.hidden = false;
				notification_overlay_element childthread threatNotificationOverlayFlash( FadeInTime, FadeOutTime );
			}
		}
		else // If showlist is empty...
		{
			// ...and the hud element is visible, tell the looping thread to stop fading the overlay in and out.
			if(!notification_overlay_element.hidden)
			{
				notification_overlay_element.hidden = true;
				notification_overlay_element notify( "stop_overlay_flash" );
			
				// If the overlay wasn't completely faded out, force it to fade out quickly.
				if(notification_overlay_element.alpha > 0.0)
				{
					notification_overlay_element FadeOverTime( FadeOutTime );
					notification_overlay_element.color = ( 0, 0, 0 );
					notification_overlay_element.alpha = 0.0;
					wait( FadeOutTime );
				}
				
			}
		}
		
		wait(0.05);
	}
}

// Called on the overlay hud element in monitorThreatHighlightNotification() to cause it to fade in and out until notified to stop.
threatNotificationOverlayFlash( FadeInTime, FadeOutTime )
{
	self endon( "stop_overlay_flash" );
	
	self FadeOverTime( FadeInTime );
	self.color = ( 1, 1, 1 );
	self.alpha = 1.0;
		
	/*while(1)
	{
		self FadeOverTime( FadeInTime );
		self.alpha = 1.0;
		wait( FadeInTime );
		self FadeOverTime( FadeOutTime );
		self.alpha = 0.0;
		wait( FadeOutTime );
	}*/
}

debugHelper()
{
	while( 1 )
	{
		dist = Distance( self.origin, level.players[0].origin );
		Print3d( self.origin + (0,0,64), dist );
		thread draw_line_for_time( level.players[0].origin, self.origin, 1,1,1, 0.3 );
		if( isdefined( self._threatdetection.mark_enemy_model ) )
			thread draw_line_for_time( level.players[0].origin, self._threatdetection.mark_enemy_model.origin, 1,1,1, 0.3 );	
		wait 0.3;
	}
}


cleanUpOnDeath()
{
	self endon("disconnect");
	
	while( 1 )
	{
		self waittill("death");
		self removeThreatEvents();
	}
}

removeThreatEvents()
{
	foreach(player in level.players)
	{
		foreach( obj in player._threatdetection.showlist )
		{
			if(obj.player == self)
			{
				obj.endTime = 0;
			}
		}
	}
	
	threatStyle = GetThreatStyle();
	if( threatStyle == "attached_glow" )
	{
		visitFXEnt( ::visitorHideAll, ::getHostileMarker, undefined );
	}
}

detection_highlight_hud_effect_on( player, duration, bLessIntense )
{
	Assert( IsDefined( player ) );
	
	radar_highlight = NewClientHudElem( player );

	if( isDefined ( bLessIntense ) && bLessIntense )
		radar_highlight.color = (0.1, 0.0015, 0.0015);
	else
		radar_highlight.color = (1.0, 0.015, 0.015);
	
	radar_highlight.alpha = 1.0;
	
	radar_highlight SetRadarHighlight( duration );
	
	return radar_highlight;
}

detection_highlight_hud_effect_off( radar_highlight )
{
	if(isDefined(radar_highlight))
	{
		radar_highlight Destroy();
	}	
}

detection_highlight_hud_effect( player, duration, bLessIntense, playerEndonString )
{
	radar_highlight = detection_highlight_hud_effect_on( player, duration, bLessIntense );
	
	if ( IsDefined( playerEndonString ) )
		player waittill_notify_or_timeout( playerEndonString, duration );
	else
		wait duration;
	
	detection_highlight_hud_effect_off( radar_highlight );
}

detection_grenade_hud_effect( player, position, duration, radius )
{
	Assert( IsDefined( player ) );
	
	radar_ping = NewHudElem();

	radar_ping.x = position[0];
	radar_ping.y = position[1];
	radar_ping.z = position[2];
	
	SetDevDvarIfUninitialized("scr_paintexplosionalpha",1.0);
	SetDevDvarIfUninitialized("scr_paintexplosionred",1.0);
	SetDevDvarIfUninitialized("scr_paintexplosiongreen",0.015);
	SetDevDvarIfUninitialized("scr_paintexplosionblue",0.015);
	SetDevDvarIfUninitialized("scr_paintexplosionwidth",500);
			
	radar_ping.color = (GetDvarFloat("scr_paintexplosionred"), GetDvarFloat("scr_paintexplosiongreen"), GetDvarFloat("scr_paintexplosionblue"));
	radar_ping.alpha = GetDvarFloat("scr_paintexplosionalpha"); // mp uses 5 bits to store alpha
	
	width = GetDvarInt("paintexplosionwidth");
	// doubling radius and duration so we have no chance of it cycling
	radar_ping SetRadarPing(int(radius + width / 2), int(width), duration + .05);
	// /# IPrintLn("PING"); #/
	wait duration;
	// /# IPrintLn("PONG"); #/
	if(isDefined(radar_ping))
	{
		radar_ping Destroy();
	}
}


/*
=============
///ScriptDocBegin
"Name: exo_ping_hud_effect( <position> , <duration> , <radius> , <player>, <bLessIntense> )"
"Summary: creates an effect for the Exo Ping tactical equipment."
"Module: Entity"
"CallOn: a player"
"MandatoryArg: <position>: the position of the effect"
"MandatoryArg: <duration>: the duraction of the effect"
"MandatoryArg: <radius>: the radius of the effect"
"OptionalArg: <player>: the client to show this effect to. If undefined, the effect shows to all clients."
"OptionalArg: <bLessIntense>: less intense version of the effect. If false or undefined, uses the default color."
"Example: thread maps\mp\_threatdetection::exo_ping_hud_effect( self.origin + ( 0, 0, 40 ), EXO_PING_DURATION, EXO_PING_RANGE, self, true );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
exo_ping_hud_effect( position, duration, radius, player, bLessIntense )
{
	radar_ping = undefined;
	if ( IsDefined( player ) )
		radar_ping = NewClientHudElem( player );
	else
		radar_ping = NewHudElem();
	
	radar_ping.x = position[0];
	radar_ping.y = position[1];
	radar_ping.z = position[2];
	
	SetDevDvarIfUninitialized( "scr_exopingwidth", 100 );
	
	if( isDefined( bLessIntense ) && bLessIntense )
		radar_ping.color = ( 0.05, 0.05, 0.05 );
	else
		radar_ping.color = ( 0.8, 0.8, 0.8 );
	
	radar_ping.alpha = 0.05; // mp uses 5 bits to store alpha
	
	width = GetDvarInt( "scr_exopingwidth", 100 );
	// doubling radius and duration so we have no chance of it cycling
	radar_ping SetRadarPing( int( radius ), int( width ), duration + .05 );
	wait duration;
	if(isDefined(radar_ping))
	{
		radar_ping Destroy();
	}
}


addThreatEvent( players_arr, event_duration_sec, event_type, updateHostile, updateFriendly, bPlaySound )
{
	if( !IsAlive( self ) )
		return;
	
	currTime	= GetTime();
	endTime		= currTime + ( event_duration_sec * 1000 );
	losEndTime	= endTime - ( 9 * ( event_duration_sec * 1000 ) / 10 );
	
	threatStyle = GetThreatStyle();
	
	
	if( endTime - losEndTime < 250 )
		losEndTime = 250 + currTime;
	
	if( threatStyle == "model" )
	{
		losEndTime = currTime;
	}
	else if( threatStyle == "vfx_model" )
	{
		losEndTime = currTime;
	}
	else if( threatStyle == "attached_glow" )
	{
		losEndTime = endTime; //attached glow stays glowy
	}
	else if( threatStyle == "stencil_outline" )
	{
		losEndTime = endTime;
	}
	
	foreach( p in players_arr )
	{
		//if( p == self )
		//	continue;
		
		found = false;
		foreach( obj in self._threatdetection.showlist )
		{
			if( obj.player == p )
			{
				//only update the time if it would last longer
				if( endTime > obj.endTime )
				{
					obj.endTime		= endTime;
					obj.losEndTime	= losEndTime;
					obj.eventType 	= event_type;
				}
				found = true;
				break;
			}
		}
		
		if( !found )
		{
			index = self._threatdetection.showlist.size;
			
			self._threatdetection.showlist[ index ]				= SpawnStruct();
			self._threatdetection.showlist[ index ].player		= p;
			self._threatdetection.showlist[ index ].endTime		= endTime;
			self._threatdetection.showlist[ index ].losEndTime	= losEndTime;
			self._threatdetection.showlist[ index ].eventType	= event_type;
			
			if (IsPlayer(self))
			{
				if( !isDefined( bPlaySound ) || bPlaySound )
					self PlayLocalSound( "flag_spawned" );  // Play the flag_spawned oneshot any time someone is added to the showlist.
			}
		}
	}
				
	if (IsPlayer(self))
	{
		if( updateFriendly ) 
			visitFXEnt( ::visitorUpdateMarkerPos, ::getFriendlyMarker, undefined );
		
		if( updateHostile )
			visitFXEnt( ::visitorUpdateMarkerPos, ::getHostileMarker, undefined );
	}
}

stopThreatEventType( eventType )
{
	foreach( obj in self._threatdetection.showlist )
	{
		if ( obj.eventType == eventType )
		{
			obj.endTime = 0;
		}
	}
	
	threatStyle = GetThreatStyle();
	if( threatStyle == "attached_glow" )
	{
		visitFXEnt( ::visitorHideAll, ::getHostileMarker, undefined );
	}
}

visitFXEnt( visitorFn, accessorFn, show_to )
{
	threatStyle = GetThreatStyle();
	if( threatStyle == "glow" )
	{
		assert( IsPlayer( self ) );
		foreach( i, fx in self.mark_fx.fx_ent )
		{
			assert( IsDefined( [[accessorFn]]( fx ) ) );
			[[visitorFn]]( [[accessorFn]]( fx ), show_to, level._threatdetection.fx_data[i][0] );
		}	
	}
	else if( threatStyle == "model" )
	{
		assert( IsPlayer( self ) );
		assert( IsDefined( [[accessorFn]]( self._threatdetection ) ) );
		[[visitorFn]]( [[accessorFn]]( self._threatdetection ), show_to, "tag_origin" );
	}
	else if( threatStyle == "vfx_model" )
	{
		assert( IsPlayer( self ) );
		assert( IsDefined( [[accessorFn]]( self._threatdetection ) ) );
		[[visitorFn]]( [[accessorFn]]( self._threatdetection ), show_to, "tag_origin" );
	}
	else if( threatStyle == "attached_glow" )
	{
		assert( IsPlayer( self ) );
		foreach( i, fx in self.mark_fx.fx_ent )
		{
			assert( IsDefined( [[accessorFn]]( fx ) ) );
			[[visitorFn]]( [[accessorFn]]( fx ), show_to, level._threatdetection.fx_data[i][0] );
		}
	}
	else if( threatStyle == "stencil_outline" )
	{
		[[visitorFn]]( self, show_to, "tag_origin" );
	}
	else
	{
		AssertEx( 0, "unknown threat style " + threatStyle + ".  i dont know how to set it up" );
	}
}



visitorRelink( member, player, bone )
{
	member Unlink();
	member.origin = self GetTagOrigin( bone );
	member.angles = self GetTagAngles( bone );
	member LinkTo( self, bone );
	wait .05;
	playFXOnTag( member.fx, member, "tag_origin" );
}


visitorHideAll( member, player, bone )
{
	threatStyle = GetThreatStyle();
	if( threatStyle == "attached_glow" )
	{
		StopFXOnTag( member.fx, member, "tag_origin" );
	}
}

visitorUpdateMarkerPos( member, player, bone )
{	
	threatStyle = GetThreatStyle();
	member.origin = self GetTagOrigin( bone );
	member.angles = self GetTagAngles( bone );		
	if( threatStyle == "glow" )
	{	
		triggerFx( member );
	}
	else if( threatStyle == "model" )
	{
		//update model pose now
		desired_pose = "mp_hud_" + self GetStance() + "_char";
		assert( isdefined( member ) );
		is_hostile = ( member != self._threatDetection.mark_friendly_model );
		
		if( is_hostile )
		{
			desired_pose += "_hostile";
		}
		
		current_pose = member.model;
				
		if( desired_pose != current_pose )
			member SetModel( desired_pose );
	}
	else if( threatStyle == "vfx_model" )
	{
		switch( self GetStance() )
		{
			case "prone":
				desired_pose = "threat_detect_model_prone";
				break;
			case "crouch":
				desired_pose = "threat_detect_model_crouch";
				break;
			case "stand":					
				//fallthrough
			default:
				desired_pose = "threat_detect_model_stand";//self.stance;
				break;
		}
		
		assert( isdefined( member ) );
		is_hostile = ( member != self._threatDetection.mark_friendly_model );
		current_pose = self._threatdetection.friendly_pose;
		
		if( is_hostile )
		{
			desired_pose += "_hostile";
			current_pose = self._threatdetection.hostile_pose;
		}
		
		assert( IsDefined( current_pose ) );
		if( current_pose != desired_pose )
		{
			forward = AnglesToForward( self.angles );
			up = AnglesToUp( self.angles );
			if( is_hostile ) 
			{
				self._threatdetection.mark_enemy_model delete();
				self._threatdetection.mark_enemy_model = SpawnFx(  GetFX( desired_pose ), self.origin, forward, up );
				self._threatdetection.mark_enemy_model hide();
				self._threatdetection.hostile_pose = desired_pose; 
			}
			else		
			{
				self._threatdetection.mark_friendly_model delete();
				self._threatdetection.mark_friendly_model = SpawnFx(  GetFX( desired_pose ), self.origin, forward, up );
				self._threatdetection.mark_friendly_model hide();
				self._threatdetection.friendly_pose = desired_pose; 			
			}		
		}
		
		if( is_hostile ) 
			TriggerFX( self._threatdetection.mark_enemy_model );
		else
			TriggerFX( self._threatdetection.mark_friendly_model );
	}
	else if( threatStyle == "attached_glow" )
	{
		//do nothing on purpose
	}
	else if( threatStyle == "stencil_outline" )
	{
		//do nothing on purpose
	}
	else
	{
		AssertEx( 0, "unknown threat style " + threatStyle + ".  i dont know how to set it up" );
	}
}


getHostileMarker( obj )
{
	threatStyle = GetThreatStyle();
	if( threatStyle == "glow" )	
		return obj.enemyMarker;
	else if( threatStyle == "model" )
		return obj.mark_enemy_model;
	else if( threatStyle == "vfx_model" )
		return obj.mark_enemy_model;
	else if( threatStyle == "attached_glow" )
		return obj;
	else if( threatStyle == "stencil_outline" )
		return obj;
	else
	{
		AssertEx( 0, "unknown threat style " + threatStyle + ".  i dont know how to set it up" );
	}	
}

getFriendlyMarker( obj )
{
	threatStyle = GetThreatStyle();
	if( threatStyle == "glow" )
		return obj.friendlyMarker;
	else if( threatStyle == "model" )
		return obj.mark_friendly_model;
	else if( threatStyle == "vfx_model" )
		return obj.mark_friendly_model;	
	else
	{
		AssertEx( 0, "unknown threat style " + threatStyle + ".  i dont know how to set it up" );
	}			
}

getFriendlyLOSMarker( obj )
{
	threatStyle = GetThreatStyle();
	if( threatStyle == "glow" )
		return obj.friendlyLOSMarker;
	else if( threatStyle == "model" )
		return obj.mark_friendly_model;
	else if( threatStyle == "vfx_model" )
		return obj.mark_friendly_model;		
	else
	{
		AssertEx( 0, "unknown threat style " + threatStyle + ".  i dont know how to set it up" );
	}	
}

getHostileLOSMarker( obj )
{
	threatStyle = GetThreatStyle();
	if( threatStyle == "glow" )	
		return obj.enemyLOSMarker;
	else if( threatStyle == "model" )
		return obj.mark_enemy_model;		
	else if( threatStyle == "vfx_model" )
		return obj.mark_enemy_model;
	else if( threatStyle == "attached_glow" )
		return obj;	
	else if( threatStyle == "stencil_outline" )
		return obj;	
	else
	{
		AssertEx( 0, "unknown threat style " + threatStyle + ".  i dont know how to set it up" );
	}
}

//left for an exmple
visitHideAllMarkers( memberArr, player, bone )
{
	assert( !isdefined( player ) );
	foreach( m in memberArr )
	{
		m Hide();
	}
}
	
accessAllMarkers( obj )
{
	assert( isdefined( obj) );
	return [obj.friendlyMarker, obj.enemyMarker, obj.friendlyLOSMarker, obj.enemyLOSMarker];
}

getNormalDirectionVec( raw_vec )
{
	return VectorNormalize( flat_origin( raw_vec ) );
}

monitorThreatHighlight()
{
	self threat_init( GetThreatStyle() );
	
	height_offset = ( 0, 0, 32 );
	all_hidden = false;
	while( 1 )
	{
		wait .05;		
		currTime = GetTime();
		
		anyoneNeedsDraw = false;
		foreach( obj in self._threatdetection.showlist )
		{	
			if( obj.endTime >= currTime )
			{	
				if( !IsDefined( obj.player ) )
				{
					self._threatdetection.showlist = array_remove( self._threatdetection.showlist, obj );
					continue;
				}
				
				obj.los = false;
				
				//see if we're even in the view cone for the guy on the list
				forward = getNormalDirectionVec( anglesToForward( obj.player.angles ) );
				toTarget = getNormalDirectionVec( self.origin - obj.player.origin );
				cos = VectorDot( toTarget, forward );
				//we will cast against +/- 90
				if( cos < 0 )
					continue;
				
				if( check_los( obj ) )
				{
					//use LOS vfx
					obj.los = true;
					
					//if we're in LOS we have a different end time
					if( obj.losEndTime <= currTime )
					{
						self._threatdetection.showlist = array_remove( self._threatdetection.showlist, obj );
						continue;
					}
				}
				anyoneNeedsDraw = true;
			}
			else
			{
				self._threatdetection.showlist = array_remove( self._threatdetection.showlist, obj );
			}			
		}
		
		threatStyle = GetThreatStyle();
		if( !all_hidden )
		{
			all_hidden = true;
			if( threatStyle == "glow" )
			{
				foreach( i, fx in self.mark_fx.fx_ent )
				{
					fx.enemyMarker hide();
					fx.friendlyMarker hide();
					fx.enemyLosMarker hide();
					fx.friendlyLosMarker hide();
				}
			}
			else if( threatStyle == "model" )
			{
				self._threatdetection.mark_friendly_model hide();
				self._threatdetection.mark_enemy_model hide();
			}
			else if( threatStyle == "vfx_model" )
			{
				self._threatdetection.mark_friendly_model hide();
				self._threatdetection.mark_enemy_model hide();
			}
			else if( threatStyle == "attached_glow" )
			{
				foreach( i, obj in self.mark_fx.fx_ent )
				{
					StopFXOnTag( obj.fx, obj, "tag_origin" );
					obj hide();
				}				
			}
			else if( threatStyle == "stencil_outline" )
			{
				self clearThreatDetected();
			}
			else
			{
				AssertEx( 0, "unknown threat style " + threatStyle + ".  i dont know how to set it up" );
			}			
			
		}	
		if( !anyoneNeedsDraw )
			continue;
		
		//walk the list of players and show to who we want
		foreach( p in self._threatdetection.showlist )
		{
			assert( isdefined( p ) );
			if( p.los )
			{
				assert( p.losEndTime >= currTime );
				showThreat( p.player, ::getFriendlyLOSMarker, ::getHostileLOSMarker, ::visitorUpdateLOSMarker );
				prepare_show_threat( all_hidden, threatStyle, p.player );//playfxontag needs to be after the show of the ent
				all_hidden = false;
			}
			else
			{
				assert( p.endTime >= currTime );
				//if we cant see our target make sure we cant see the marker object
				result = BulletTrace( p.player.origin + height_offset, self.origin + height_offset, true, p.player );
				
				if( result["fraction"] < 1 && !isplayer(result["entity"]) ) // we can't see the blob
				{
					showThreat( p.player, ::getFriendlyMarker, ::getHostileMarker, ::visitorShowToPlayer );
					prepare_show_threat( all_hidden, threatStyle, p.player );//playfxontag needs to be after the show of the ent
					all_hidden = false;
				}
			}
		}
				
	}
}

prepare_show_threat( already_drawn, threatStyle, player )
{
	if( already_drawn ) //meaning we have not shown to anyone yet this frame
	{
		if( threatStyle == "attached_glow" )
		{
			assert( IsPlayer( self ) );
			showThreat( player, ::getFriendlyLOSMarker, ::getHostileLOSMarker, ::visitorRetriggerFX );
		}
	}				
}

visitorRetriggerFX( member, player, bone )
{
	/#
	threatStyle = GetThreatStyle();
	assert(threatStyle == "attached_glow" );
	#/
	//StopFXOnTag( member.fx, member, "tag_origin" );//stop is happening in the hide of monitorThreatHighlight
	PlayFXOnTag( member.fx, member, "tag_origin" );
}

check_los( obj )
{
	//temp to solve problem if player is behind partial cover.
	if( BulletTracePassed( obj.player GetEye(), self GetEye(), false, obj.player ) )
		return true;
	
	return false;
}

threat_init( threatStyle )
{
	mark_fx_struct = SpawnStruct();
	mark_fx_struct.fx_ent = [];

	if( threatStyle == "glow" )
	{
		foreach ( i,fx in level._threatdetection.fx_data )
		{
			mark_fx_ent = SpawnStruct();
			mark_fx_ent.origin = self GetTagOrigin( fx[0] );
			mark_fx_ent.angles = self GetTagAngles( fx[0] );
			
			mark_fx_ent.enemyMarker = SpawnFx( fx[1], mark_fx_ent.origin );
			triggerFx( mark_fx_ent.enemyMarker );
			mark_fx_ent.enemyMarker hide();
			
			mark_fx_ent.enemyLosMarker = SpawnFx( fx[3], mark_fx_ent.origin );
			triggerFx( mark_fx_ent.enemyLosMarker );
			mark_fx_ent.enemyLosMarker hide();
			
			mark_fx_ent.friendlyMarker = SpawnFx( fx[2], mark_fx_ent.origin );
			triggerFx( mark_fx_ent.friendlyMarker );
			mark_fx_ent.friendlyMarker hide();
			
			mark_fx_ent.friendlyLosMarker = SpawnFx( fx[4], mark_fx_ent.origin );
			triggerFx( mark_fx_ent.friendlyLosMarker );
			mark_fx_ent.friendlyLosMarker hide();
	
			mark_fx_struct.fx_ent[ i ] = mark_fx_ent;
		}
		
		self.mark_fx = mark_fx_struct;
	}
	else if( threatStyle == "model" )
	{
		model = spawn( "script_model", self.origin );
		model.origin = self.origin;
		model.angles = self.angles;
		//print( "we would like to use " + self.model );
		model setmodel( level._threatdetection.friendlyModel );
		model SetContents( 0 );
		//model codescripts\character::attachHead( "alias_africa_militia_heads_mp", xmodelalias\alias_africa_militia_heads_mp::main() );
		self._threatDetection.mark_friendly_model = model;
		
		model = spawn( "script_model", self.origin );
		model.origin = self.origin;
		model.angles = self.angles;
		//print( "we would like to use " + self.model );
		model setmodel( level._threatdetection.hostileModel );
		model SetContents( 0 );
		//model codescripts\character::attachHead( "alias_africa_militia_heads_mp", xmodelalias\alias_africa_militia_heads_mp::main() );
		self._threatDetection.mark_enemy_model = model;		
	}
	else if( threatStyle == "vfx_model")
	{	
		self._threatDetection.mark_friendly_model = SpawnStruct();
		self._threatDetection.mark_friendly_model = SpawnFx(  GetFX( "threat_detect_model_stand" ), self.origin, AnglesToForward( self.angles ), AnglesToUp( self.angles ) );
		self._threatDetection.friendly_pose = "threat_detect_model_stand";
		
		self._threatDetection.mark_enemy_model = SpawnStruct();
		self._threatDetection.mark_enemy_model = SpawnFx( GetFX( "threat_detect_model_stand_hostile" ), self.origin, AnglesToForward( self.angles ), AnglesToUp( self.angles ) );
		self._threatDetection.hostile_pose = "threat_detect_model_stand_hostile";
	}
	else if( threatStyle == "attached_glow" )
	{
	
		foreach ( i,fx in level._threatdetection.fx_data )
		{
			tag_org = spawn_tag_origin();
			tag_org show();  //spawn_tag_origin calls hide and if we dont show it, it will not be sent to any clients
			tag_org.origin = self GetTagOrigin( fx[0] );
			tag_org.angles = self GetTagAngles( fx[0] );
			tag_org LinkTo( self, fx[0] );
			tag_org.fx = fx[1];
			//wait .05;
			//PlayFXOnTag( tag_org.fx, tag_org, "tag_origin" );
			mark_fx_struct.fx_ent[ i ] = tag_org;
		}
		
		self.mark_fx = mark_fx_struct;
	}
	else if( threatStyle == "stencil_outline" )
	{
		// does nothing on purpose
	}
	else
	{
		AssertEx( 0, "unknown threat style " + threatStyle + ".  i dont know how to set it up" );
	}
}


visitorUpdateLOSMarker( member, player, bone )
{
	visitorUpdateMarkerPos( member, player, bone );
	visitorShowToPlayer( member, player, bone );
}

visitorShowToPlayer( member, player, bone )
{
	assert( IsDefined( player ) && isplayer( player ) );
	assert( IsDefined( member ) );
	
	threatStyle = GetThreatStyle();
	
	if (threatStyle == "stencil_outline")
	{
		member threatDetectedToPlayer( player );
	}
	else
	{
		member showToPlayer( player );	
	}
}

showThreat( player, friendlyFn, hostileFn, visitorFn )
{
	if ( player == self )
		return;
	
	memberFunc = hostileFn;
	
	if( ( level.teamBased || level.multiteambased ) && player.team == self.team )
	{
		memberFunc = friendlyFn;
	}
	
	//for FFA
	else if( player == self )
	{
		memberFunc = friendlyFn;
	}
	
	visitFXEnt( visitorFn, memberFunc, player );
}