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

/*
	CTF
*/

/*QUAKED mp_ctf_spawn (0.0 1.0 0.5) (-16 -16 0) (16 16 72)
Allied players spawn away from enemies and near their team at one of these positions at the start of a round.*/

/*QUAKED mp_ctf_spawn_axis_start (1.0 0.0 0.5) (-16 -16 0) (16 16 72)
Axis players spawn away from enemies and near their team at one of these positions at the start of a round.*/

/*QUAKED mp_ctf_spawn_allies_start (0.0 1.0 0.5) (-16 -16 0) (16 16 72)
Allied players spawn away from enemies and near their team at one of these positions at the start of a round.*/

CONST_FLAG_PICK_UP_TIME_FRIENDLY = 0.0;
CONST_FLAG_PICK_UP_TIME_ENEMY = 0.0;
main()
{
	maps\mp\gametypes\_globallogic::init();
	maps\mp\gametypes\_callbacksetup::SetupCallbacks();
	maps\mp\gametypes\_globallogic::SetupCallbacks();

	if ( isUsingMatchRulesData() )
	{
		level.initializeMatchRules = ::initializeMatchRules;
		[[level.initializeMatchRules]]();
		level thread reInitializeMatchRulesOnMigration();
	}
	else
	{
		registerTimeLimitDvar( level.gameType, 5 );
		registerScoreLimitDvar( level.gameType, 3 );
		registerRoundLimitDvar( level.gameType, 1 );
		registerWinLimitDvar( level.gameType, 1 );
		registerNumLivesDvar( level.gameType, 0 );
		registerHalfTimeDvar( level.gameType, 0 );
		
		level.matchRules_damageMultiplier = 0;
		level.matchRules_vampirism = 0;	
	}

	setOverTimeLimitDvar(5);
	
	if( IsDefined( game["round_time_to_beat"] ) )
	{
		setOverTimeLimitDvar( game["round_time_to_beat"] );
		game["round_time_to_beat"] = undefined;
	}
	
	setSpecialLoadouts();
	
	level.assists_disabled			= true;
	level.teamBased 				= true;
	level.objectiveBased 			= true;
	level.overtimeScoreWinOverride 	= true;
	level.onlyRoundOverride 		= true;
	level.pingState					= getIntProperty( "scr_ping_carrier", 1 );
	level.winByCaptures 			= getIntProperty( "scr_win_by_captures", false );
	
	level.onStartGameType 			= ::onStartGameType;
	level.getSpawnPoint 			= ::getSpawnPoint;
	level.onPlayerKilled 			= ::onPlayerKilled;
	level.onTimeLimit 				= ::onTimeLimit;
	level.onHalfTime 				= ::onHalfTime;
	
	if( !IsDefined( game["shut_out"] ) )
	{
		game["shut_out"]["axis"] 	= true;
		game["shut_out"]["allies"] 	= true;
	}
	
	if( level.winByCaptures )
		SetDynamicDvar( "scr_ctf_scorelimit", 0 );
	
	if ( level.matchRules_damageMultiplier || level.matchRules_vampirism )
		level.modifyPlayerDamage = maps\mp\gametypes\_damage::gamemodeModifyPlayerDamage;

	level.flagReturnTime = getIntProperty( "scr_ctf_returntime", 30 );

	game["dialog"]["gametype"] = "ctf_intro";

	if ( getDvarInt( "g_hardcore" ) )
		game["dialog"]["gametype"] = "hc_" + game["dialog"]["gametype"];

	game["dialog"]["offense_obj"] = "dom_start";
	game["dialog"]["defense_obj"] = "dom_start";
	
	self thread onPlayerConnect();
}


initializeMatchRules()
{
	//	set common values
	setCommonRulesFromMatchRulesData();
	
	//	set everything else (private match options, default .cfg file values, and what normally is registered in the 'else' below)
	SetDynamicDvar( "scr_ctf_roundlimit", 1 );
	registerRoundLimitDvar( "ctf", 1 );					
	SetDynamicDvar( "scr_ctf_winlimit", 1 );
	registerWinLimitDvar( "ctf", 1 );			
	SetDynamicDvar( "scr_ctf_halftime", 1 );
	registerHalfTimeDvar( "ctf", 1 );
	SetDynamicDvar( "scr_ctf_returntime", 	GetMatchRulesData( "ctfData", "returnTime" ) );
	SetDynamicDvar( "scr_ping_carrier", 	GetMatchRulesData( "ctfData", "enemyCarrierIcon" ) );
	SetDynamicDvar( "scr_win_by_captures", 	GetMatchRulesData( "ctfData", "winByCaptures" ) );
	SetDynamicDvar( "scr_ctf_flag_pick_up_time_friendly", GetMatchRulesData( "ctfData", "pickupTime" ) );
	SetDynamicDvar( "scr_ctf_flag_pick_up_time_enemy", GetMatchRulesData( "ctfData", "pickupTime" ) );
}


onStartGameType()
{
	if ( !isdefined( game["switchedsides"] ) )
		game["switchedsides"] = false;
	
	if ( isOvertimeText(game["status"]) )
		game["switchedsides"] = !game["switchedsides"];
	
	if (game["status"] == "halftime")
		SetOmnvar( "ui_current_round", 2 );
	else if (game["status"] == "overtime")
		SetOmnvar( "ui_current_round", 3 );
	else if (game["status"] == "overtime_halftime")
		SetOmnvar( "ui_current_round", 4 );

	if ( !isdefined( game["original_defenders"] ) )
		game["original_defenders"] = game["defenders"];

	if ( game["switchedsides"] )
	{
		oldAttackers = game["attackers"];
		oldDefenders = game["defenders"];
		game["attackers"] = oldDefenders;
		game["defenders"] = oldAttackers;
	}
	
	//	set scores to zero at beginning of every round, round wins is what counts to game win
	if( !level.winByCaptures )
	{
		game["teamScores"][game["attackers"]] = 0;		
		setTeamScore( game["attackers"], 0 );	
		game["teamScores"][game["defenders"]] = 0;		
		setTeamScore( game["defenders"], 0 );	
	}

	setClientNameMode("auto_change");
	
	level.flagStowedFXid["sentinel"]["friendly"] 		= LoadFx( "vfx/unique/vfx_flag_project_stowed_sentinel_friendly" );
	level.flagStowedFXid["sentinel"]["enemy"]			= LoadFx( "vfx/unique/vfx_flag_project_stowed_sentinel_enemy" );
	level.flagStowedFXid["atlas"]["friendly"] 			= LoadFx( "vfx/unique/vfx_flag_project_stowed_atlas_friendly" );
	level.flagStowedFXid["atlas"]["enemy"]				= LoadFx( "vfx/unique/vfx_flag_project_stowed_atlas_enemy" );
	
	level.flagGroundFXid["sentinel"]["friendly"] 		= LoadFx( "vfx/unique/vfx_flag_project_sentinel_friendly" );
	level.flagGroundFXid["sentinel"]["enemy"]			= LoadFx( "vfx/unique/vfx_flag_project_sentinel_enemy" );
	level.flagGroundFXid["atlas"]["friendly"] 			= LoadFx( "vfx/unique/vfx_flag_project_atlas_friendly" );
	level.flagGroundFXid["atlas"]["enemy"]				= LoadFx( "vfx/unique/vfx_flag_project_atlas_enemy" );
	
	level.flagBaseFXid["sentinel"]["friendly"] 			= LoadFx( "vfx/unique/vfx_marker_ctf" );
	level.flagBaseFXid["sentinel"]["enemy"] 			= LoadFx( "vfx/unique/vfx_marker_ctf_red" );
	level.flagBaseFXid["atlas"]["friendly"] 			= LoadFx( "vfx/unique/vfx_marker_ctf" );
	level.flagBaseFXid["atlas"]["enemy"] 				= LoadFx( "vfx/unique/vfx_marker_ctf_red" );
	
	if ( level.splitscreen )
	{
		setObjectiveScoreText( game["attackers"], &"OBJECTIVES_ONE_FLAG_ATTACKER" );
		setObjectiveScoreText( game["defenders"], &"OBJECTIVES_ONE_FLAG_DEFENDER" );
	}
	else
	{
		setObjectiveScoreText( game["attackers"], &"OBJECTIVES_ONE_FLAG_ATTACKER_SCORE" );
		setObjectiveScoreText( game["defenders"], &"OBJECTIVES_ONE_FLAG_DEFENDER_SCORE" );
	}
	
	setObjectiveText( game["attackers"], &"OBJECTIVES_CTF" );
	setObjectiveText( game["defenders"], &"OBJECTIVES_CTF" );
	setObjectiveHintText( game["attackers"], &"OBJECTIVES_ONE_FLAG_ATTACKER_HINT" );
	setObjectiveHintText( game["defenders"], &"OBJECTIVES_ONE_FLAG_DEFENDER_HINT" );

	initSpawn();
	
	allowed[0] = "ctf";
	maps\mp\gametypes\_gameobjects::main(allowed);
		
	level thread ctf();
	level thread updateScoreBoardCTF();
}


initSpawn()
{
	level.spawnMins = ( 0, 0, 0 );
	level.spawnMaxs = ( 0, 0, 0 );
	maps\mp\gametypes\_spawnlogic::addStartSpawnPoints( "mp_ctf_spawn_allies_start" );
	maps\mp\gametypes\_spawnlogic::addStartSpawnPoints( "mp_ctf_spawn_axis_start" );
	maps\mp\gametypes\_spawnlogic::addSpawnPoints( "allies", "mp_ctf_spawn" );
	maps\mp\gametypes\_spawnlogic::addSpawnPoints( "axis", "mp_ctf_spawn" );
	
	level.mapCenter = maps\mp\gametypes\_spawnlogic::findBoxCenter( level.spawnMins, level.spawnMaxs );
	setMapCenter( level.mapCenter );
}


getSpawnPoint()
{
	spawnTeam = self.team;
	
	if ( level.useStartSpawns && level.inGracePeriod )
	{
		if( !game["switchedsides"] )
			spawnTeam = getOtherTeam(spawnTeam);
		
		spawnPoints = maps\mp\gametypes\_spawnlogic::getSpawnpointArray( "mp_ctf_spawn_" + spawnteam + "_start" );	
		spawnPoint = maps\mp\gametypes\_spawnlogic::getSpawnpoint_Random( spawnPoints );
	}
	else
	{
		spawnPoints = maps\mp\gametypes\_spawnlogic::getTeamSpawnPoints( spawnteam );
		spawnPoint = maps\mp\gametypes\_spawnscoring::getSpawnpoint_CTF( spawnPoints, spawnteam );
	}
	
	self maps\mp\gametypes\_spawnlogic::recon_set_spawnpoint( spawnpoint );
	
	return spawnPoint;
}

updateScoreBoardCTF()
{
	while( true )
	{
		level waittill( "connected", player );
		player thread updateCaptues();
		player thread updateReturns();
		player thread updateDefends();
	}
}

updateCaptues()
{
	waittillframeend;
	self setExtraScore0( self.pers[ "captures" ] );
}

updateReturns()
{
	waittillframeend;
	self.assists = self.pers[ "returns" ];
}

updateDefends()
{
	waittillframeend;
	self setExtraScore1( self.pers[ "defends" ] );
}

ctf()
{
	level.flagModel["allies"] 	= maps\mp\gametypes\_teams::getTeamFlagModel( "allies" );
	level.carryFlag["allies"] 	= maps\mp\gametypes\_teams::getTeamFlagCarryModel( "allies" );

	level.flagModel["axis"] 	= maps\mp\gametypes\_teams::getTeamFlagModel( "axis" );
	level.carryFlag["axis"] 	= maps\mp\gametypes\_teams::getTeamFlagCarryModel( "axis" );

	level.iconEscort3D 			= "waypoint_escort";
	level.iconEscort2D 			= "waypoint_escort";
	level.iconKill3D 			= "waypoint_kill";
	level.iconKill2D 			= "waypoint_kill";
	level.iconCaptureFlag3D 	= "waypoint_capture_flag";
	level.iconCaptureFlag2D 	= "waypoint_capture_flag";
	level.iconDefendFlag3D 		= "waypoint_defend_flag";
	level.iconDefendFlag2D 		= "waypoint_defend_flag";
	level.iconReturnFlag3D 		= "waypoint_return_flag";
	level.iconReturnFlag2D 		= "waypoint_return_flag";
	level.iconWaitForFlag3D 	= "waypoint_waitfor_flag";
	level.iconWaitForFlag2D 	= "waypoint_waitfor_flag";
	
	//esports
	level.iconAwayBlue			= "waypoint_esports_ctf_taken_blue";
	level.iconAwayRed			= "waypoint_esports_ctf_taken_red";
	level.iconDroppedBlue		= "waypoint_esports_ctf_dropped_blue";
	level.iconDroppedRed		= "waypoint_esports_ctf_dropped_red";
	level.iconAtBaseBlue		= "waypoint_esports_ctf_flag_blue";
	level.iconAtBaseRed			= "waypoint_esports_ctf_flag_red";
	level.iconMissingBlue		= "waypoint_esports_ctf_flag_missing_blue";
	level.iconMissingRed		= "waypoint_esports_ctf_flag_missing_red";
	
	level.teamFlags[game["defenders"]] = createTeamFlag( game["defenders"], "allies" );
	level.teamFlags[game["attackers"]] = createTeamFlag( game["attackers"], level.otherTeam["allies"] );
	
	level.capZones[game["defenders"]] = createCapZone( game["defenders"], "allies" );
	level.capZones[game["attackers"]] = createCapZone( game["attackers"], level.otherTeam["allies"] );
	
	assignTeamSpawns();
	
	OnResetFlagHUDStatus( "allies" );
	OnResetFlagHUDStatus( "axis" );
}

OnResetFlagHUDStatus( flag_team )
{
	if ( flag_team == "allies" )
	{
		level.alliesFlagStatus 				= 0; // At home
		level.alliesFlagCarrierClientNum 	= -1;
	}
	else
	{
		level.axisFlagStatus 				= 0; // At home
		level.axisFlagCarrierClientNum 		= -1;
	}
	
	level notify( "update_flag_status" );
}

OnPickupFlagHUDStatus( player )
{
	team = player.pers["team"];

	// Players pick up the opposite team's flag
	if ( team == "allies" )
	{
		player.objective = 1;
		level.axisFlagStatus 				= 2; // Carried
		level.axisFlagCarrierClientNum 		= player GetEntityNumber();
	}
	else
	{
		player.objective = 2;
		level.alliesFlagStatus 				= 2; // Carried
		level.alliesFlagCarrierClientNum 	= player GetEntityNumber();
	}	

	level notify( "update_flag_status" );
}

OnDropFlagHUDStatus( flag_team )
{
	if ( flag_team == "allies" )
	{
		level.alliesFlagStatus 				= 1; // Away
		level.alliesFlagCarrierClientNum 	= -1;
	}
	else
	{
		level.axisFlagStatus 				= 1; // Away
		level.axisFlagCarrierClientNum 		= -1;
	}
	
	level notify( "update_flag_status" );
}

playerUpdateFlagStatus() // self == player
{
	if ( !IsDefined( self.team ) || !IsDefined( level.alliesFlagStatus ) )
		return;
	
	friendlyStatus 				= undefined;
	friendlyCarrierClientNum 	= undefined;
	enemyStatus 				= undefined;
	enemyCarrierClientNum 		= undefined;
	
	if ( self.team == "allies" || self.team == "spectator" )
	{
		friendlyStatus 				= level.alliesFlagStatus;
		friendlyCarrierClientNum	= level.alliesFlagCarrierClientNum;
		enemyStatus 				= level.axisFlagStatus;
		enemyCarrierClientNum 		= level.axisFlagCarrierClientNum;
	}
	else if ( self.team == "axis" )
	{
		friendlyStatus 				= level.axisFlagStatus;
		friendlyCarrierClientNum	= level.axisFlagCarrierClientNum;
		enemyStatus 				= level.alliesFlagStatus;
		enemyCarrierClientNum 		= level.alliesFlagCarrierClientNum;
	}
	else
	{
		return;
	}
	
	self SetClientOmnvar( "ui_ctf_friendly_status", 			friendlyStatus );
	self SetClientOmnvar( "ui_ctf_friendly_carrier_clientnum", 	friendlyCarrierClientNum );
	self SetClientOmnvar( "ui_ctf_enemy_status", 				enemyStatus );
	self SetClientOmnvar( "ui_ctf_enemy_carrier_clientnum", 	enemyCarrierClientNum );
	self SetClientOmnvar( "ui_ctf_status_changed", 				1 );	// Triggers the LUA update
}

playerUpdateFlagStatusOnJoinTeam()
{
	level endon( "game_ended" );
	self endon( "disconnect" );
	
	while( 1 )
	{
		self waittill( "joined_team" );
		self playerUpdateFlagStatus();
	}
}

playerWatchFlagStatus() // self == player
{
	level endon( "game_ended" );
	self endon( "disconnect" );
	
	self playerUpdateFlagStatus();
	
	self thread playerUpdateFlagStatusOnJoinTeam();
	
	while( true )
	{
		level waittill( "update_flag_status" );
		playerUpdateFlagStatus();
	}
}

hideHudElementOnGameEnd( hudElement )
{
	level waittill( "game_ended" );
	
	if ( isDefined( hudElement ) )
		hudElement.alpha = 0;
}

createTeamFlag( team, entityTeam )
{
	trigger = getEnt( "ctf_zone_" + entityTeam, "targetname" );
	if ( !isDefined( trigger ) ) 
	{
		error( "No ctf_zone_" + entityTeam + " trigger found in map." );
		return;
	}
	
	triggerAugmented = getEnt( "ctf_zone_augmented_" + entityTeam, "targetname" );
	
	if( IsDefined(triggerAugmented) )
	{
		if( isAugmentedGameMode() )
		{
			trigger Delete();
			trigger = triggerAugmented;
		}
		else
		{
			triggerAugmented Delete();
		}
	}
	
	visuals[0] = getEnt( "ctf_flag_" + entityTeam, "targetname" );
	if ( !isDefined( visuals[0] ) ) 
	{
		error( "No ctf_flag_" + entityTeam + " script_model found in map." );
		return;
	}
	
	visualsAugmented[0] = getEnt( "ctf_flag_augmented_" + entityTeam, "targetname" );
	
	if( IsDefined(visualsAugmented[0]) )
	{
		if( isAugmentedGameMode() )
		{
			visuals[0] delete();
			visuals[0] = visualsAugmented[0];
		}
		else
		{
			visualsAugmented[0] Delete();
		}
	}
	
	cloneTrigger = spawn( "trigger_radius", trigger.origin, 0, 96, trigger.height );
	trigger = cloneTrigger;
	
	visuals[0] setModel( level.flagModel[team] );
	visuals[0].oldContents = visuals[0] SetContents(0); //Prevent the fx trace from hitting the model in createCapZone()
	
	traceStart = visuals[0].origin + (0,0,32);
	traceEnd = visuals[0].origin + (0,0,-32);
	trace = bulletTrace( traceStart, traceEnd, false, undefined);
	visuals[0].origin = trace["position"];
	
	teamFlag = maps\mp\gametypes\_gameobjects::createCarryObject( team, trigger, visuals, (0,0,85) );
	
	pickUpTimeFriendly = GetDvarFloat( "scr_ctf_flag_pick_up_time_friendly", CONST_FLAG_PICK_UP_TIME_FRIENDLY);
	if( pickUpTimeFriendly > 0 )
		teamFlag maps\mp\gametypes\_gameobjects::setTeamUseTime( "friendly", pickUpTimeFriendly );
	
	pickUpTimeEnemy = GetDvarFloat( "scr_ctf_flag_pick_up_time_enemy", CONST_FLAG_PICK_UP_TIME_ENEMY);
	if( pickUpTimeEnemy > 0 )
		teamFlag maps\mp\gametypes\_gameobjects::setTeamUseTime( "enemy", pickUpTimeEnemy );

	teamFlag maps\mp\gametypes\_gameobjects::setTeamUseText( "enemy", &"MP_GRABBING_FLAG" );
	teamFlag maps\mp\gametypes\_gameobjects::setTeamUseText( "friendly", &"MP_RETURNING_FLAG" );
	teamFlag maps\mp\gametypes\_gameobjects::allowCarry( "enemy" );
	
	teamFlag maps\mp\gametypes\_gameobjects::setVisibleTeam( "none" );	
	teamFlag maps\mp\gametypes\_gameobjects::set2DIcon( "enemy", level.iconEscort2D );
	teamFlag maps\mp\gametypes\_gameobjects::set3DIcon( "enemy", level.iconEscort3D );
	
	if( level.pingState	!= 2 )
	{
		teamFlag maps\mp\gametypes\_gameobjects::set2DIcon( "friendly", level.iconKill2D );
		teamFlag maps\mp\gametypes\_gameobjects::set3DIcon( "friendly", level.iconKill3D );
	}
		
	if ( isAugmentedGameMode() )
		teamFlag.objPingDelay = 2.5; //Normally defaults to 5.0
	
	teamFlag.objIDPingFriendly = true;
	teamFlag.allowWeapons = true;
	teamFlag.requiresLOS = true;
	teamFlag.onPickup = ::onPickup;
	teamFlag.onPickupFailed = ::onPickup;
	teamFlag.onDrop = ::onDrop;
	teamFlag.onReset = ::onReset;
	teamFlag.goliathThink = ::goliathAttachFlag;
	teamFlag.canUseObject = ::canUse;
	teamFlag.goliathThink = ::goliathDropFlag;
	
	teamFlag.oldRadius = trigger.radius;
	teamFlag.origin = trigger.origin;
	teamFlag.furthestSpawnDist = 0;
	
	// always show enemy flag carrier
	if( level.pingState == 0 )
		teamFlag.objIDPingFriendly = false;

	if ( team == "allies" )
	{
		teamFlag maps\mp\gametypes\_gameobjects::set2DIcon( "mlg", level.iconAtBaseBlue );
		teamFlag maps\mp\gametypes\_gameobjects::set3DIcon( "mlg", level.iconAtBaseBlue );
		SetOmnvar( "ui_mlg_game_mode_status_1", 0 );
	}
	else
	{
		teamFlag maps\mp\gametypes\_gameobjects::set2DIcon( "mlg", level.iconAtBaseRed );
		teamFlag maps\mp\gametypes\_gameobjects::set3DIcon( "mlg", level.iconAtBaseRed );
		SetOmnvar( "ui_mlg_game_mode_status_2", 0 );
	}
	
	teamFlag thread flagEffects();
	
	return teamFlag;
}

createCapZone( team, entityTeam )
{
	trigger = getEnt( "ctf_zone_" + entityTeam, "targetname" );
	triggerAugmented = getEnt( "ctf_zone_augmented_" + entityTeam, "targetname" );
	
	if( IsDefined(triggerAugmented) && isAugmentedGameMode() )
		trigger = triggerAugmented;
	
	visuals = [];
	capZone = maps\mp\gametypes\_gameobjects::createUseObject( team, trigger, visuals, (0,0,85) );
	capZone maps\mp\gametypes\_gameobjects::allowUse( "friendly" );

	capZone maps\mp\gametypes\_gameobjects::setVisibleTeam( "any" );
	capZone maps\mp\gametypes\_gameobjects::set2DIcon( "friendly", level.iconDefendFlag2D );
	capZone maps\mp\gametypes\_gameobjects::set3DIcon( "friendly", level.iconDefendFlag3D );
	capZone maps\mp\gametypes\_gameobjects::set2DIcon( "enemy", level.iconCaptureFlag2D );
	capZone maps\mp\gametypes\_gameobjects::set3DIcon( "enemy", level.iconCaptureFlag3D );

	capZone maps\mp\gametypes\_gameobjects::setUseTime( 0 );
	capZone maps\mp\gametypes\_gameobjects::setKeyObject( level.teamFlags[ getOtherTeam( team ) ] );
	
	capZone.onUse = ::onUse;
	capZone.onCantUse = ::onCantUse;
		
	traceStart = trigger.origin + (0,0,32);
	traceEnd = trigger.origin + (0,0,-32);
	trace = bulletTrace( traceStart, traceEnd, false, undefined );
	
	capZone.baseEffectPos = trace["position"];
	capZone.baseEffectForward = trace["normal"];
	capZone thread captureZoneEffects();
	
	return capZone;
}


onBeginUse( player )
{
	team = player.pers["team"];

	if ( team == self maps\mp\gametypes\_gameobjects::getOwnerTeam() )
		self.trigger.radius = 1024;
	else
		self.trigger.radius = self.oldRadius;
}


onEndUse( player, team, success )
{
	self.trigger.radius = self.oldRadius;
}


onPickup( player )
{
	self notify ( "picked_up" );

	team = player.pers["team"];

	if ( team == "allies" )
		otherTeam = "axis";
	else
		otherTeam = "allies";
		
	flagEffectsStop();
	
	if ( team == self maps\mp\gametypes\_gameobjects::getOwnerTeam() )
	{
		self thread returnFlag();

		leaderDialog( "flag_returned", team, "status" );
		playSoundOnPlayers( "mp_obj_notify_pos_med", team );
			
		leaderDialog( "enemy_flag_returned", otherteam, "status" );
		playSoundOnPlayers( "mp_obj_notify_neg_med", otherteam );
			
		player thread maps\mp\_events::flagReturnEvent();
				
		OnResetFlagHUDStatus( team );
	}
	else
	{
		if ( isDefined( level.ctf_loadouts ) && isDefined( level.ctf_loadouts[team] ) )
			player thread applyFlagCarrierClass(self);
		else	
			player attachFlag(self);		

		OnPickupFlagHUDStatus( player );
		
		self maps\mp\gametypes\_gameobjects::setVisibleTeam( "any" );
		self maps\mp\gametypes\_gameobjects::set2DIcon( "enemy", level.iconEscort2D );
		self maps\mp\gametypes\_gameobjects::set3DIcon( "enemy", level.iconEscort3D );
		
		if( level.pingState	!= 2 )
		{
			self maps\mp\gametypes\_gameobjects::set2DIcon( "friendly", level.iconKill2D );
			self maps\mp\gametypes\_gameobjects::set3DIcon( "friendly", level.iconKill3D );
		}

		level.capZones[otherTeam] maps\mp\gametypes\_gameobjects::allowUse( "none" );
		level.capZones[otherTeam] maps\mp\gametypes\_gameobjects::setVisibleTeam( "friendly" );
		level.capZones[otherTeam] maps\mp\gametypes\_gameobjects::set2DIcon( "friendly", level.iconWaitForFlag2D );
		level.capZones[otherTeam] maps\mp\gametypes\_gameobjects::set3DIcon( "friendly", level.iconWaitForFlag3D );
		
		exludeList = [player];

		leaderDialog( "enemy_flag_taken", team, "status" );
		playSoundOnPlayers( "mp_obj_notify_pos_sml", team , exludeList );
		player playLocalSound( "mp_ctf_flag_pickup" );
		
		leaderDialog( "flag_taken", otherteam, "status" );
		playSoundOnPlayers( "mp_obj_notify_neg_sml", otherTeam );
		
		if ( self maps\mp\gametypes\_gameobjects::getOwnerTeam() == "allies" )//Lui wants the team that owns the flag not the player team
		{
			level.capZones[otherTeam] maps\mp\gametypes\_gameobjects::set2DIcon( "mlg", level.iconMissingBlue );
			level.capZones[otherTeam] maps\mp\gametypes\_gameobjects::set3DIcon( "mlg", level.iconMissingBlue );
			self maps\mp\gametypes\_gameobjects::set2DIcon( "mlg", level.iconAwayBlue );
			self maps\mp\gametypes\_gameobjects::set3DIcon( "mlg", level.iconAwayBlue );
			SetOmnvar( "ui_mlg_game_mode_status_1", player GetEntityNumber() );
		}
		else
		{
			level.capZones[otherTeam] maps\mp\gametypes\_gameobjects::set2DIcon( "mlg", level.iconMissingRed );
			level.capZones[otherTeam] maps\mp\gametypes\_gameobjects::set3DIcon( "mlg", level.iconMissingRed );
			self maps\mp\gametypes\_gameobjects::set2DIcon( "mlg", level.iconAwayRed );
			self maps\mp\gametypes\_gameobjects::set3DIcon( "mlg", level.iconAwayRed );
			SetOmnvar( "ui_mlg_game_mode_status_2", player GetEntityNumber() );
		}
			
		player thread maps\mp\_events::flagPickUpEvent();
		
		self thread flagAwayVO( player, team, otherteam );
	}
}

flagAwayVO( player, team, otherTeam )
{
	level endon( "game_ended" );
	self endon( "reset" );
	
	while( true )
	{
		wait( 15 );
		
		// the team flag is at home base
		if( level.teamFlags[team] maps\mp\gametypes\_gameobjects::isHome() )
		{
			// tell the flag carrier to take the enemy flag back to home base
			player leaderDialogOnPlayer( "enemy_flag_bringhome", "status" );
		}
		
		// tell the enemies to kill the flag carrier
		leaderDialog( "flag_getback", otherTeam, "status" );
	}
}

returnFlag()
{
	self maps\mp\gametypes\_gameobjects::returnHome();
}


onDrop( player )
{
	team = self maps\mp\gametypes\_gameobjects::getOwnerTeam();
	otherTeam = level.otherTeam[team];

	self maps\mp\gametypes\_gameobjects::allowCarry( "any" );
	self maps\mp\gametypes\_gameobjects::setVisibleTeam( "any" );
	self maps\mp\gametypes\_gameobjects::set2DIcon( "friendly", level.iconReturnFlag2D );
	self maps\mp\gametypes\_gameobjects::set3DIcon( "friendly", level.iconReturnFlag3D );
	self maps\mp\gametypes\_gameobjects::set2DIcon( "enemy", level.iconCaptureFlag2D );
	self maps\mp\gametypes\_gameobjects::set3DIcon( "enemy", level.iconCaptureFlag3D );
	
	OnDropFlagHUDStatus( team );
	
	if ( team == "allies" )
	{
		self maps\mp\gametypes\_gameobjects::set2DIcon( "mlg", level.iconDroppedBlue );
		self maps\mp\gametypes\_gameobjects::set3DIcon( "mlg", level.iconDroppedBlue );
		SetOmnvar( "ui_mlg_game_mode_status_1", 0 - level.flagReturnTime );
	}
	else
	{
		self maps\mp\gametypes\_gameobjects::set2DIcon( "mlg", level.iconDroppedRed );
		self maps\mp\gametypes\_gameobjects::set3DIcon( "mlg", level.iconDroppedRed );
		SetOmnvar( "ui_mlg_game_mode_status_2", 0 - level.flagReturnTime );
	}

	if( isDefined(player) )
	{
		player.objective = 0;
		
		if( isDefined(player.carryFlag) ) 
			player detachFlag();	
	}

	leaderDialog( "flag_dropped", team, "status" );
	playSoundOnPlayers( "mp_obj_notify_neg_sml", team );

	leaderDialog( "enemy_flag_dropped", otherTeam, "status" );
	playSoundOnPlayers( "mp_obj_notify_pos_sml", otherTeam );
	
	self thread flagEffects();
	
	self thread returnAfterTime();
}

returnAfterTime()
{
	self endon ( "picked_up" );
	self endon ( "reset" );
	
	wait ( level.flagReturnTime );
	
	team = self maps\mp\gametypes\_gameobjects::getOwnerTeam();
	otherTeam = level.otherTeam[team];

	leaderDialog( "flag_returned", team, "status" );
	playSoundOnPlayers( "mp_obj_notify_pos_med", team );
			
	leaderDialog( "enemy_flag_returned", otherteam, "status" );
	playSoundOnPlayers( "mp_obj_notify_neg_med", otherteam );
	
	self thread returnFlag();
}


onReset()
{
	team = self maps\mp\gametypes\_gameobjects::getOwnerTeam();
	otherTeam = level.otherTeam[team];

	self maps\mp\gametypes\_gameobjects::allowCarry( "enemy" );
	self maps\mp\gametypes\_gameobjects::setVisibleTeam( "none" );
	self maps\mp\gametypes\_gameobjects::set2DIcon( "enemy", level.iconEscort2D );
	self maps\mp\gametypes\_gameobjects::set3DIcon( "enemy", level.iconEscort3D );
	
	if( level.pingState	!= 2 )
	{
		self maps\mp\gametypes\_gameobjects::set2DIcon( "friendly", level.iconKill2D );
		self maps\mp\gametypes\_gameobjects::set3DIcon( "friendly", level.iconKill3D );
	}
		
	self thread flagEffects();

	OnResetFlagHUDStatus( team );
	
	level.capZones[team] maps\mp\gametypes\_gameobjects::allowUse( "friendly" );
	level.capZones[team] maps\mp\gametypes\_gameobjects::setVisibleTeam( "any" );
	level.capZones[team] maps\mp\gametypes\_gameobjects::set2DIcon( "friendly", level.iconDefendFlag2D );
	level.capZones[team] maps\mp\gametypes\_gameobjects::set3DIcon( "friendly", level.iconDefendFlag3D );
	level.capZones[team] maps\mp\gametypes\_gameobjects::set2DIcon( "enemy", level.iconCaptureFlag2D );
	level.capZones[team] maps\mp\gametypes\_gameobjects::set3DIcon( "enemy", level.iconCaptureFlag3D );
	
	if ( team == "allies" )
	{
		level.capZones[team] maps\mp\gametypes\_gameobjects::set2DIcon( "mlg", undefined );
		level.capZones[team] maps\mp\gametypes\_gameobjects::set3DIcon( "mlg", undefined );
		self maps\mp\gametypes\_gameobjects::set2DIcon( "mlg", level.iconAtBaseBlue );
		self maps\mp\gametypes\_gameobjects::set3DIcon( "mlg", level.iconAtBaseBlue );
		SetOmnvar( "ui_mlg_game_mode_status_1", 0 );
	}
	else
	{
		level.capZones[team] maps\mp\gametypes\_gameobjects::set2DIcon( "mlg", undefined );
		level.capZones[team] maps\mp\gametypes\_gameobjects::set3DIcon( "mlg", undefined );
		self maps\mp\gametypes\_gameobjects::set2DIcon( "mlg", level.iconAtBaseRed );
		self maps\mp\gametypes\_gameobjects::set3DIcon( "mlg", level.iconAtBaseRed );
		SetOmnvar( "ui_mlg_game_mode_status_2", 0 );
	}
	
}


onUse( player )
{
	team = player.pers["team"];
	if ( team == "allies" )
		otherTeam = "axis";
	else
		otherTeam = "allies";

	leaderDialog( "enemy_flag_captured", team, "status" );
	playSoundOnPlayers( "mp_obj_notify_pos_lrg", team );
		
	leaderDialog( "flag_captured", otherteam, "status" );
	playSoundOnPlayers( "mp_obj_notify_neg_lrg", otherteam );	

	player thread maps\mp\_events::flagCaptureEvent();
	
	maps\mp\gametypes\_gamescore::giveTeamScoreForObjective( team, 1 );	
	
	game["shut_out"][otherTeam] = false;

	if( IsDefined(player) )
	{
		player.objective = 0;
		
		if( isDefined(player.carryFlag) )
			player detachFlag();
	}
	
	if ( isDefined( level.ctf_loadouts ) && isDefined( level.ctf_loadouts[team] ) )
		player thread removeFlagCarrierClass();

	level.teamFlags[otherTeam] returnFlag();
	
	if ( self maps\mp\gametypes\_gameobjects::getOwnerTeam() == "allies" )
	{
		SetOmnvar( "ui_mlg_game_mode_status_1", 0 );
	}
	else
	{
		SetOmnvar( "ui_mlg_game_mode_status_2", 0 );
	}
	
	level thread checkRoundWin( team );
}


checkRoundWin( team )
{
	winEvent = "roundsWon";
		
	if( level.winByCaptures )
		winEvent = "teamScores";
	
	if( inOvertime() ) // end of overtime
	{
		updateRoundsWon( team );
		
		if( game["status"] == "overtime" ) // end of overtime first half
		{
			game["round_time_to_beat"] = getMinutesPassed();
			level thread maps\mp\gametypes\_gamelogic::endGame( "overtime_halftime", game[ "end_reason" ][ "score_limit_reached" ] );
		}
		else if( game["status"] == "overtime_halftime" ) // end of overtime second half
		{
			// extra point for time
			if( game["roundsWon"]["axis"] == game["roundsWon"]["allies"] )
				updateRoundsWon( team ); 
			
			// extra point for time
			if( game["teamScores"]["axis"] == game["teamScores"]["allies"] )
				game["teamScores"][team]++;
			
			level thread maps\mp\gametypes\_gamelogic::endGame( team, game[ "end_reason" ][ "score_limit_reached" ] );
		}
	}
	else if( game["teamScores"][team] == getWatchedDvar( "scorelimit" ) ) // reached score limit
	{
		updateRoundsWon( team );
		
		if( game["status"] == "normal" ) // end of first half
		{
			game["roundMillisecondsAlreadyPassed"] = getTimePassed();			
			level thread maps\mp\gametypes\_gamelogic::endGame( "halftime", game[ "end_reason" ][ "score_limit_reached" ] );
		}
		else if( game["status"] == "halftime" ) // end of second half
		{
			winner = team;
			
			if( game[winEvent]["axis"] == game[winEvent]["allies"] )
			{
				winner = "overtime";
				level.finalKillCam_winner = "none";	
			}				
							
			level thread maps\mp\gametypes\_gamelogic::endGame( winner, game[ "end_reason" ][ "switching_sides" ] );
		}
	}
}

updateRoundsWon( team )
{
	level.finalKillCam_winner = team;
	
	game["roundsWon"][team]++;
}


onHalfTime( reason )
{
	onTimeLimit();
}

onTimeLimit()
{
	level.finalKillCam_winner = "none";
	winEvent = "roundsWon";
		
	if( level.winByCaptures )
		winEvent = "teamScores";
	
	if( inOvertime() ) // end of overtime
	{
		if( game["status"] == "overtime" ) // end of overtime first half
		{
			level thread maps\mp\gametypes\_gamelogic::endGame( "overtime_halftime",  game[ "end_reason" ][ "time_limit_reached" ] );
		}
		else if( game["status"] == "overtime_halftime" ) // end of overtime second half
		{
			winner = "tie";
		
			if( game[winEvent]["axis"] > game[winEvent]["allies"] )
				winner = "axis";

			if( game[winEvent]["allies"] > game[winEvent]["axis"] )	
				winner = "allies";
	
			level thread maps\mp\gametypes\_gamelogic::endGame( winner, game[ "end_reason" ][ "time_limit_reached" ] );
		}
	}	
	else if ( game["status"] == "halftime" ) // end of second half
	{
		winner = "tie";
		
		if( game["teamScores"]["axis"] > game["teamScores"]["allies"] )
			winner = "axis";

		if( game["teamScores"]["allies"] > game["teamScores"]["axis"] )	
			winner = "allies";
		
		if( winner == "axis" || winner == "allies" )
			updateRoundsWon( winner );
		
		if( game[winEvent]["axis"] == game[winEvent]["allies"] )
			winner = "overtime";
		
		level thread maps\mp\gametypes\_gamelogic::endGame( winner, game[ "end_reason" ][ "time_limit_reached" ] );					
	}
	else // end of first half
	{
		if( game["teamScores"]["axis"] > game["teamScores"]["allies"] )
			updateRoundsWon( "axis" );
		
		if( game["teamScores"]["allies"] > game["teamScores"]["axis"] )			
			updateRoundsWon( "allies" );
		
		level thread maps\mp\gametypes\_gamelogic::endGame( "halftime", game[ "end_reason" ][ "time_limit_reached" ] );				
	}		
}


applyFlagCarrierClass( flagObj )
{
	self endon( "death" );
	self endon( "disconnect" );
	level endon( "game_ended" );
	
	//	remove placement item if carrying
	if ( IsDefined( self.isCarrying ) && self.isCarrying == true )
	{
		self notify( "force_cancel_placement" );
		wait( 0.05 );
	}		
	
	//	remove jugg
	if ( self isJuggernaut() )
	{
		self notify( "lost_juggernaut" );
		wait( 0.05 );
	}
	
	//	set the gamemodeloadout for giveLoadout() to use
	self.pers["gamemodeLoadout"] = level.ctf_loadouts[self.team];	
	
	//	spawnPlayer() calls giveLoadout() passing the player's class
	//	save their chosen class and override their current and last class 
	//	- both so killstreaks don't get reset
	//	- this is automatically set back to chosen class within giveLoadout()
	self.gamemode_chosenClass = self.class;
	self.gamemode_carrierClass = true;
	self.pers["class"] = "gamemode";
	self.pers["lastClass"] = "gamemode";
	self.class = "gamemode";
	self.lastClass = "gamemode";
	
	//	faux spawn
	self notify( "faux_spawn" );
	self maps\mp\gametypes\_class::giveAndApplyLoadout( self.team, "gamemode" );
	if ( self.loadoutKeepCurrentKillstreaks )
		self maps\mp\killstreaks\_killstreaks::updateKillstreaks( true );
	self refillBattery();
		
	//	attach flag after faux spawn (model may change for sniper or juggernaut loadout)
	self thread waitAttachFlag( flagObj );
}

refillBattery()
{
	offhandWeapons = self getWeaponsListOffhands();
	
	foreach( offhand in offhandWeapons )
		self BatteryFullRecharge( offhand );
}

waitAttachFlag( flagObj )
{
	level endon( "game_ende" );
	self endon( "disconnect" );
	self endon( "death" );
	
	self waittill( "spawned_player" );
	self attachFlag( flagObj );
}


removeFlagCarrierClass()
{
	self endon( "death" );
	self endon( "disconnect" );
	level endon( "game_ended" );
	
	//	remove placement item if carrying
	if ( IsDefined( self.isCarrying ) && self.isCarrying == true )
	{
		self notify( "force_cancel_placement" );
		wait( 0.05 );
	}			
	
	//	remove jugg
	if ( self isJuggernaut() )
	{
		self notify( "lost_juggernaut" );
		wait( 0.05 );
	}
	
	//	clear game mode loadout
	self.pers["gamemodeLoadout"] = undefined;	
	
	//	faux spawn
	self notify( "faux_spawn" );
	self maps\mp\gametypes\_class::giveAndApplyLoadout( self.team, self.class );
	if ( self.loadoutKeepCurrentKillstreaks )
		self maps\mp\killstreaks\_killstreaks::updateKillstreaks( true );
	self refillBattery();
}


onCantUse( player )
{

}


onPlayerKilled( eInflictor, attacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, psOffsetTime, deathAnimDuration, killId )
{
	if ( isDefined( attacker ) && isPlayer( attacker ) && attacker.pers["team"] != self.pers["team"] )
	{
		if( isDefined( attacker.carryFlag ) )
			attacker thread maps\mp\_events::killWithFlagEvent();

		if( isDefined( self.carryFlag ) )
		{
			attacker thread maps\mp\_events::killFlagCarrierEvent( killId );
			self detachFlag();
			return;
		}
		
		if( IsDefined(sWeapon) && isKillstreakWeapon(sWeapon) )
			return;
		
		minDistanceSQ = 96 * 96;
		
		foreach( zone in level.capZones )
		{
			attackerDistanceSq = Distance2DSquared( attacker.origin, zone.curorigin );
			victimDistanceSq = Distance2DSquared( self.origin, zone.curorigin );
			
			// check for defense medals
			if( zone.ownerTeam == attacker.team )
			{
				// attacker gets a defense medal for comabt near team objective
				if( (attackerDistanceSq < minDistanceSQ) || (victimDistanceSq < minDistanceSQ) )
				{
					attacker thread maps\mp\_events::defendObjectiveEvent( self, killId );
					attacker setExtraScore1( attacker.pers[ "defends" ] );
				}
			}
		
			// check assault medals
			if( zone.ownerTeam == self.team )
			{
				// attacker gets an asssault medal for combat near enemy objective
				if( (attackerDistanceSq < minDistanceSQ) || (victimDistanceSq < minDistanceSQ) )
					attacker thread maps\mp\_events::assaultObjectiveEvent( self, killId );
			}
		}
	}
	else if ( isDefined( self.carryFlag ) )
		self detachFlag();
}


attachFlag(flag)
{
	otherTeam = level.otherTeam[self.pers["team"]];
	
	self attach( level.carryFlag[otherTeam], "J_SpineUpper", true );
	self.carryFlag = level.carryFlag[otherTeam];
	
	flag thread flagEffects();
}


goliathAttachFlag()		// self = player
{
	level endon( "game_ended" );
	self endon( "death" );
	self endon( "disconnect" );
	self endon( "joined_team" );
	self endon( "faux_spawn" );
	self endon( "lost_ctf_flag" );
	
	self waittill( "goliath_equipped" );
	
	// Wait a second just in case the original flag hasn't been detached from the player yet.
	wait( 1.0 );
	
	if ( IsDefined( self.carryFlag ) )
	{
		self attach( self.carryFlag, "J_SpineUpper", true );
	}
	
	if ( IsDefined( self.carryObject ) )
	{
		self.carryObject thread flagEffects();
	}
}


canUse( player )
{
	if ( !isDefined( player ) )
		return false;
	
	if ( player isJuggernaut() )
		return false;
	
	return true;
}


goliathDropFlag()		// self = player
{
	level endon( "game_ended" );
	self endon( "death" );
	self endon( "disconnect" );
	self endon( "joined_team" );
	self endon( "faux_spawn" );
	
	self waittill( "goliath_equipped" );
	
	// Don't have to handle detaching the CTF flag. DetachAll() is called in _juggernaut.gsc when the player transforms.
	if ( IsDefined( self.carryObject ) )
	{
		self.carryFlag = undefined;
		self.carryObject thread maps\mp\gametypes\_gameobjects::setDropped();
	}
}


detachFlag()
{
	self notify( "lost_ctf_flag" );
	tagModel = "J_SpineUpper";
	self detach( self.carryFlag, tagModel );
	self.carryFlag = undefined;
}


setSpecialLoadouts()
{
	if ( isUsingMatchRulesData() && GetMatchRulesData( "defaultClasses", "axis", 5, "class", "inUse" ) )
	{
		level.ctf_loadouts["axis"] = getMatchRulesSpecialClass( "axis", 5 );	
	}
	if ( isUsingMatchRulesData() && GetMatchRulesData( "defaultClasses", "allies", 5, "class", "inUse" ) )
	{
		level.ctf_loadouts["allies"] = getMatchRulesSpecialClass( "allies", 5 );	
	}	
}


//============================================
// 		 		assginTeamSpawns
//============================================
assignTeamSpawns()
{
	spawnPoints = maps\mp\gametypes\_spawnlogic::getSpawnpointArray( "mp_ctf_spawn" );
	
	level.teamSpawnPoints["axis"] 	= [];
	level.teamSpawnPoints["allies"] = [];
	
	foreach( spawnPoint in spawnPoints )
	{	
		spawnPoint.teamBase = getNearestFlagTeam( spawnPoint );
		
		if( spawnPoint.teamBase == "axis" )
		{
			level.teamSpawnPoints["axis"][level.teamSpawnPoints["axis"].size] = spawnPoint;
		}
		else
		{
			level.teamSpawnPoints["allies"][level.teamSpawnPoints["allies"].size] = spawnPoint;
		}
	}	
	
	/#
	level thread ctfDebug();
	#/
}

reassign_ctf_team_spawns()
{
	spawnPoints = maps\mp\gametypes\_spawnlogic::getSpawnpointArray( "mp_ctf_spawn" );
	
	level.teamSpawnPoints["axis"] 	= [];
	level.teamSpawnPoints["allies"] = [];
	
	foreach( spawnPoint in spawnPoints )
	{	
		spawnPoint.teamBase = get_nearest_capZone_team( spawnPoint );
		
		if( spawnPoint.teamBase == "axis" )
		{
			level.teamSpawnPoints["axis"][level.teamSpawnPoints["axis"].size] = spawnPoint;
		}
		else
		{
			level.teamSpawnPoints["allies"][level.teamSpawnPoints["allies"].size] = spawnPoint;
		}
	}	
}

get_nearest_capZone_team( spawnPoint )
{
	isPathDataAvailable = maps\mp\gametypes\_spawnlogic::isPathDataAvailable();
	nearestZone			= undefined;
	nearestDist			= undefined;

	foreach( capZone in level.capZones )
	{
		dist = undefined;
		
		// find the actual pathing distance between the flag and spawn point
		if( isPathDataAvailable )
		{
			dist = GetPathDist( spawnPoint.origin, capZone.curorigin, 999999 );
		}
		
		// fail safe for bad pathing data		
		if( !IsDefined(dist) || (dist == -1) )
		{
			dist = distancesquared( capZone.curorigin, spawnPoint.origin );
		}
		
		// record the nearest flag
		if( !isdefined( nearestZone ) || dist < nearestDist )
{
			nearestZone 	= capZone;
			nearestDist 	= dist;
		}
	}
	
	return nearestZone.ownerTeam;
}


//===========================================
// 			getNearestFlagTeam
//===========================================
getNearestFlagTeam( spawnPoint )
{
	isPathDataAvailable = maps\mp\gametypes\_spawnlogic::isPathDataAvailable();
	nearestFlag			= undefined;
	nearestDist			= undefined;

	foreach( teamFlag in level.teamFlags )
	{
		if ( isDefined( spawnPoint.script_noteworthy ) )
		{
			if ( game["switchedsides"] )
			{
				if ( spawnPoint.script_noteworthy == "axis_override" && teamFlag.ownerTeam != "allies" )
					continue;
				if ( spawnPoint.script_noteworthy == "allies_override" && teamFlag.ownerTeam != "axis" )
					continue;
			}
			else
			{
				if ( spawnPoint.script_noteworthy == "axis_override" && teamFlag.ownerTeam != "axis" )
					continue;
				if ( spawnPoint.script_noteworthy == "allies_override" && teamFlag.ownerTeam != "allies" )
					continue;
			}
		}

		dist = undefined;
		
		// find the actual pathing distance between the flag and spawn point
		if( isPathDataAvailable )
		{
			dist = GetPathDist( spawnPoint.origin, teamFlag.origin, 999999 );
		}
		
		// fail safe for bad pathing data		
		if( !IsDefined(dist) || (dist == -1) )
		{
			dist = distancesquared( teamFlag.origin, spawnPoint.origin );
		}
		
		// record the nearest flag
		if( !isdefined( nearestFlag ) || dist < nearestDist )
		{
			nearestFlag 	= teamFlag;
			nearestDist 	= dist;
		}
	}
	
	spawnPoint.nearestFlag = nearestFlag;
	spawnPoint.nearestFlagDist = nearestDist;
	if ( spawnPoint.nearestFlag.furthestSpawnDist < nearestDist )
		spawnPoint.nearestFlag.furthestSpawnDist = nearestDist;

	return nearestFlag.ownerTeam;
}

flagEffectsStop()
{
	friendlyEnemyEffectsStop();
}

flagEffects()
{
	waittillframeend;
	if(IsDefined(self.carrier))
	{
		self friendlyEnemyLinkedEffects( level.flagStowedFXid, self.carrier, "tag_fx_flag" );
	}
	else
	{
		baseModel = self.visuals[0];
		self friendlyEnemyEffects( level.flagGroundFXid, baseModel.origin, AnglesToUp(baseModel.angles) );
	}
}

captureZoneEffects()	
{
	waittillframeend;
	self friendlyEnemyEffects( level.flagBaseFXid, self.baseEffectPos, self.baseEffectForward  );
}

friendlyEnemyEffectsStop()
{
	if(IsDefined(self.friendlyFX))
		self.friendlyFX Delete();
	if(IsDefined(self.enemyFX))
		self.enemyFX Delete();
}

friendlyEnemyLinkedEffects( fxIds, ent, tag )
{
	team = self.ownerTeam;
	friendlyFxId = fxIds[game[team]]["friendly"];
	enemyFxId = fxIds[game[team]]["enemy"];
	
	friendlyEnemyEffectsStop();
	
	self.friendlyFX = spawnLinkedFXShowToTeam( friendlyFxId, team, ent, tag );
	self.enemyFX = spawnLinkedFXShowToTeam( enemyFxId, getOtherTeam(team), ent, tag  );
}

friendlyEnemyEffects( fxIds, origin, fwd )
{
	team = self.ownerTeam;
	friendlyFxId = fxIds[game[team]]["friendly"];
	enemyFxId = fxIds[game[team]]["enemy"];

	friendlyEnemyEffectsStop();
		
	self.friendlyFX = spawnFXShowToTeam( friendlyFxId, team, origin, fwd );
	self.enemyFX = spawnFXShowToTeam( enemyFxId, getOtherTeam(team), origin, fwd );
}

spawnLinkedFXShowToTeam( fxId, team, ent, tag )
{
	fx = SpawnLinkedFX( fxId, ent, tag );
	fx fxShowToTeam( team );
	
	return fx;
}

captureZone_reset_base_effects()
{
	foreach( flag in level.teamFlags )
	{
		if( flag.visuals.size )
		{
			traceStart 				= flag.visuals[0].origin + (0,0,32);
			traceEnd 				= flag.visuals[0].origin + (0,0,-32);
			trace 					= bulletTrace( traceStart, traceEnd, false, undefined );
			upangles 				= vectorToAngles( trace["normal"] );
			
			flag.baseeffectforward	= anglesToForward( upangles );
			flag.baseeffectpos 		= trace["position"];
			flag thread flagEffects();
		
			zone = level.capZones[flag.ownerteam];
			
			zone.baseeffectforward	= flag.baseeffectforward;
			zone.baseeffectpos 		= flag.baseeffectpos;
			zone thread captureZoneEffects();
		}
	}
}

onPlayerConnect()
{
	while ( true )
	{
		level waittill( "connected", player );
		
		player thread playerWatchFlagStatus();
	}
}

/#
//============================================
// 		 		ctfDebug
//============================================
ctfDebug()
{
	setDevDvarIfUninitialized( "scr_ctfdebug", "0" );
	
	spawnPoints 		= maps\mp\gametypes\_spawnlogic::getSpawnpointArray( "mp_ctf_spawn" );
	isPathDataAvailable = maps\mp\gametypes\_spawnlogic::isPathDataAvailable();
	heightOffsetLines 	= (0,0,12);
	heightOffsetNames 	= (0,0,64);
	
	while( true )
	{
		if( getdvar("scr_ctfdebug") != "1" ) 
		{
			wait( 1 );
			continue;
		}
		
		SetDevDvar( "scr_showspawns", "1" );
		
		while( true )
		{
			if( getdvar("scr_ctfdebug") != "1" )
			{
				SetDevDvar( "scr_showspawns", "0" );
				break;
			}
			
			color 			= [];
			color["axis"] 	= ( 250/255, 247/255, 97/255 );
			color["allies"] = ( 250/255, 97/255, 240/255 );
			
			// draw the path from each spawn point to the nearest portal
			foreach( spawnPoint in spawnPoints )
			{
				if( isPathDataAvailable )
				{
					if( !IsDefined(spawnPoint.nodeArray) )
					{
						spawnPoint.nodeArray = GetNodesOnPath( spawnPoint.origin, level.teamFlags[spawnPoint.teamBase].origin );
					}
					
					if( !IsDefined(spawnPoint.nodeArray) || (spawnPoint.nodeArray.size == 0) )
					{
						continue;
					}
					
					line( spawnPoint.origin + heightOffsetLines, spawnPoint.nodeArray[0].origin + heightOffsetLines, color[spawnPoint.teamBase] );
					
					for( i = 0; i <  spawnPoint.nodeArray.size - 1; i++ )
					{
						line( spawnPoint.nodeArray[i].origin + heightOffsetLines, spawnPoint.nodeArray[i+1].origin + heightOffsetLines, color[spawnPoint.teamBase] );
					}
				}
				else
				{
					line( level.teamFlags[spawnPoint.teamBase].origin + heightOffsetLines, spawnPoint.origin + heightOffsetLines, color[spawnPoint.teamBase] );
				}
			}
			
			foreach( teamFlag in level.teamFlags )
			{
				if ( teamFlag.ownerTeam == "allies" )
				{
					flagName = "allies flag";
					
					if( !game["switchedsides"] )
						flagName = "axis flag";
			
					print3d( teamFlag.origin + heightOffsetNames, flagName );
				}
				
				if ( teamFlag.ownerTeam == "axis" )
				{
					flagName = "axis flag";
					
					if( !game["switchedsides"] )
						flagName = "allies flag";
							
					print3d( teamFlag.origin + heightOffsetNames, flagName );
				}
			}
			
			waitframe();
		}
	}
}
#/
