#include common_scripts\utility;
#include maps\mp\_utility;
#include maps\mp\gametypes\_hud_util;
#include maps\mp\_audio;
/*
	Domination
	Objective: 	Score points by holding flag locations
	Map ends:	When the score limit is reached
	Respawning:	No wait / Near teammates

	Level requirements
	------------------
		Spawnpoints:
			classname		mp_dom_spawn
			All players spawn from these. The spawnpoint chosen is dependent on the current locations of owned flags, teammates and 
			enemies at the time of spawn. Players generally spawn behind their teammates relative to the direction of enemies.

		Spectator Spawnpoints:
			classname		mp_global_intermission
			Spectators spawn from these and intermission is viewed from these positions.
			Atleast one is required, any more and they are randomly chosen between.

		Flags:
			classname       trigger_radius
			targetname      flag_primary
			Flags that need to be captured to win. Primary flags take time to capture; secondary flags are instant.
			
			Set scr_domdebug to 1 to see spawnpoints are considered connected to each flag.
*/

/*QUAKED mp_dom_spawn (0.5 0.5 1.0) (-16 -16 0) (16 16 72)
Players spawn near their flags at one of these positions.*/

/*QUAKED mp_dom_spawn_axis_start (1.0 0.0 1.0) (-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_dom_spawn_allies_start (0.0 1.0 1.0) (-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.*/


//===========================================
// 				constants
//===========================================
CONST_DOM_TRAP_TIME = 80000;
CONST_USE_TIME 		= 10;

main()
{
	if(getdvar("mapname") == "mp_background")
		return;

	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, 30 );
		registerScoreLimitDvar( level.gameType, 300 );
		registerRoundLimitDvar( level.gameType, 1 );
		registerWinLimitDvar( level.gameType, 1 );
		registerNumLivesDvar( level.gameType, 0 );
		registerHalfTimeDvar( level.gameType, 1 );
		
		level.matchRules_damageMultiplier = 0;
		level.matchRules_vampirism = 0;	
	}

	level.teamBased = true;
	level.allowNeutral = false;
	level.onStartGameType = ::onStartGameType;
	level.getSpawnPoint = ::getSpawnPoint;
	level.onPlayerKilled = ::onPlayerKilled;
	level.onSpawnPlayer = ::onSpawnPlayer;
	level.domRoundStartTime = getTime();
	level.allowBoostingAboveTriggerRadius = true;
	level.domCaptureTime = getFloatProperty( "scr_dom_capture_time", CONST_USE_TIME );
	
	level.threeCapTime["axis"]["time"] 		= 0;
	level.threeCapTime["axis"]["awarded"] 	= false;
	level.threeCapTime["allies"]["time"] 	= 0;
	level.threeCapTime["allies"]["awarded"] = false;
	
	level.alliesCapturing = [];
	level.axisCapturing = [];
	
	level.dom = SpawnStruct();
	
	if ( level.matchRules_damageMultiplier || level.matchRules_vampirism )
		level.modifyPlayerDamage = maps\mp\gametypes\_damage::gamemodeModifyPlayerDamage;
	
	game["dialog"]["gametype"] = "dom_intro";

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

	game["dialog"]["offense_obj"] = "dom_start";
	game["dialog"]["defense_obj"] = "dom_start";
}


initializeMatchRules()
{
	assert( isUsingMatchRulesData() );

	//	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_dom_roundlimit", 1 );
	registerRoundLimitDvar( "dom", 1 );		
	SetDynamicDvar( "scr_dom_winlimit", 1 );
	registerWinLimitDvar( "dom", 1 );			
	SetDynamicDvar( "scr_dom_halftime", 1 );
	registerHalfTimeDvar( "dom", 1 );

	SetDynamicDvar( "scr_dom_capture_time", GetMatchRulesData("domData", "captureTime") );	
	SetDynamicDvar( "scr_dom_allowNeutral", GetMatchRulesData( "domData", "allowNeutral" ) );
	SetDynamicDvar( "scr_dom_halftimeswitchsides", GetMatchRulesData( "domData", "halfTimeSwitchSides" ) );
}


onStartGameType()
{	
	if( !isDefined( game["switchedsides"] ) )
	game["switchedsides"] = false;
	
	if( game["switchedsides"] )
	{
		oldAttackers = game["attackers"];
		oldDefenders = game["defenders"];
		game["attackers"] = oldDefenders;
		game["defenders"] = oldAttackers;
	}
	
	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 );
	
	setObjectiveText( "allies", &"OBJECTIVES_DOM" );
	setObjectiveText( "axis", &"OBJECTIVES_DOM" );

	if ( level.splitscreen )
	{
		setObjectiveScoreText( "allies", &"OBJECTIVES_DOM" );
		setObjectiveScoreText( "axis", &"OBJECTIVES_DOM" );
	}
	else
	{
		setObjectiveScoreText( "allies", &"OBJECTIVES_DOM_SCORE" );
		setObjectiveScoreText( "axis", &"OBJECTIVES_DOM_SCORE" );
	}
	setObjectiveHintText( "allies", &"OBJECTIVES_DOM_HINT" );
	setObjectiveHintText( "axis", &"OBJECTIVES_DOM_HINT" );

	setClientNameMode("auto_change");

	initSpawns();
	precacheFlags();
	
	allowed[0] = "dom";
	maps\mp\gametypes\_gameobjects::main( allowed );
	
	SetOmnvar( "ui_mlg_game_mode_status_1", 0 );
	SetOmnvar( "ui_mlg_game_mode_status_2", 0 );
	SetOmnvar( "ui_mlg_game_mode_status_3", 0 );
		
	level thread domFlags();
	level thread updateDomScores();
	level thread updateMLGObjectives();
	level thread updateScoreBoardDOM();
	
	level.halftime_switch_sides = dvarIntValue( "halftimeswitchsides", 1, 0, 1 );
	level.allowNeutral = dvarIntValue( "allowNeutral", 0, 0, 1 );
}

updateScoreBoardDOM()
{
	level endon( "game_ended" );
	
	while( true )
	{
		level waittill( "connected", player );
		player thread updateCaptues();
		player thread updateDefends();
	}
}

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

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

updateMLGObjectives()
{
	level endon( "game_ended" );
	
	while (1)
	{
		foreach( player in level.players )
		{
			player.objective = 0;
			foreach( domPoint in level.domFlags )
			{
				if ( player IsTouching( domPoint.levelFlag ) )
				{
					if( domPoint.numTouching["axis"] * domPoint.numTouching["allies"] > 0)
					{
						player.objective = 1;
					}
					else if ( domPoint.ownerTeam == "neutral" )
					{
						if ( player.team == "allies" )
							player.objective = 2;
						else
							player.objective = 3;
					}
					else if ( domPoint.ownerTeam == "allies" )
					{
						if ( player.team == "allies" )
							player.objective = 4;
						else
							player.objective = 5;
					}
					else if ( domPoint.ownerTeam == "axis" )
					{
						if ( player.team == "axis" )
							player.objective = 6;
						else
							player.objective = 7;
					}

					if ( player.objective > 0 )
					{
						if ( domPoint.label == "_b" )
							player.objective += 7;
						else if ( domPoint.label == "_c" )
							player.objective += 14;
					}
				}
			}
		}
		wait( 0.05 );
	}
}

//===========================================
// 				precacheFlags
//===========================================
precacheFlags()
{
	game["neutral"] = "neutral";
		
	level.flagBaseModel = "flag_holo_base_ground";
	
	level.flagFXid["sentinel"]["friendly"] 		= LoadFx( "vfx/unique/vfx_flag_project_sentinel_friendly" );
	level.flagFXid["sentinel"]["enemy"]			= LoadFx( "vfx/unique/vfx_flag_project_sentinel_enemy" );
	level.flagFXid["atlas"]["friendly"] 		= LoadFx( "vfx/unique/vfx_flag_project_atlas_friendly" );
	level.flagFXid["atlas"]["enemy"]			= LoadFx( "vfx/unique/vfx_flag_project_atlas_enemy" );
	level.flagFXid["neutral"]["friendly"]		= LoadFx( "vfx/unique/vfx_flag_project_neutral" );
	level.flagFXid["neutral"]["enemy"]			= LoadFx( "vfx/unique/vfx_flag_project_neutral" );
	
	level.boarderFXid["sentinel"]["friendly"] 	= LoadFx( "vfx/unique/vfx_marker_dom" );
	level.boarderFXid["sentinel"]["enemy"] 		= LoadFx( "vfx/unique/vfx_marker_dom_red" );
	level.boarderFXid["atlas"]["friendly"] 		= LoadFx( "vfx/unique/vfx_marker_dom" );
	level.boarderFXid["atlas"]["enemy"] 		= LoadFx( "vfx/unique/vfx_marker_dom_red" );
	level.boarderFXid["neutral"]["friendly"] 	= LoadFx( "vfx/unique/vfx_marker_dom_white" );
	level.boarderFXid["neutral"]["enemy"] 		= LoadFx( "vfx/unique/vfx_marker_dom_white" );
}


//===========================================
// 				initSpawns
//===========================================
initSpawns()
{
	level.spawnMins = ( 0, 0, 0 );
	level.spawnMaxs = ( 0, 0, 0 );
	
	maps\mp\gametypes\_spawnlogic::addStartSpawnPoints( "mp_dom_spawn_allies_start" );
	maps\mp\gametypes\_spawnlogic::addStartSpawnPoints( "mp_dom_spawn_axis_start" );
	maps\mp\gametypes\_spawnlogic::addSpawnPoints( "allies", "mp_dom_spawn" );
	maps\mp\gametypes\_spawnlogic::addSpawnPoints( "axis", "mp_dom_spawn" );
	
	level.mapCenter = maps\mp\gametypes\_spawnlogic::findBoxCenter( level.spawnMins, level.spawnMaxs );
	setMapCenter( level.mapCenter );
}


//===========================================
// 			getSpawnPoint
//===========================================
getSpawnPoint()
{
	spawnteam = self.pers["team"];
	
	if ( level.useStartSpawns && level.inGracePeriod )
	{
		if( game["switchedsides"] )
			spawnTeam = getOtherTeam(spawnTeam);
		
		spawnPoints = maps\mp\gametypes\_spawnlogic::getSpawnpointArray( "mp_dom_spawn_" + spawnteam + "_start" );
		spawnPoint = maps\mp\gametypes\_spawnlogic::getSpawnpoint_startSpawn( spawnPoints );
	}
	else
	{
		teamDomPoints 			= getTeamDomPoints( spawnteam );
		enemyTeam 				= getOtherTeam( spawnteam );
		enemyDomPoints 			= getTeamDomPoints( enemyTeam );
		perferdDomPointArray 	= getPreferedDomPoints( teamDomPoints, enemyDomPoints );
		
		spawnPoints = maps\mp\gametypes\_spawnlogic::getTeamSpawnPoints( spawnteam );
		spawnPoint = maps\mp\gametypes\_spawnscoring::getSpawnpoint_Domination( spawnPoints, perferdDomPointArray );
	}
	
	assert( isDefined(spawnpoint) );
	
	self maps\mp\gametypes\_spawnlogic::recon_set_spawnpoint( spawnpoint );
	
	return spawnpoint;
}


//===========================================
// 			getTeamDomPoints
//===========================================
getTeamDomPoints( team )
{
	teamDomPoints = [];
	
	foreach( domPoint in level.domFlags )
	{
		if ( domPoint.ownerTeam == team )
		{
			teamDomPoints[teamDomPoints.size] = domPoint;
		}
	}
	
	return teamDomPoints;
}


//===========================================
// 			getPreferedDomPoints
//===========================================
getPreferedDomPoints( teamDomPoints, enemyDomPoints )
{
	perferdDomPointArray 		= [];
	perferdDomPointArray[0] 	= false;	// dom point A
	perferdDomPointArray[1] 	= false;	// dom point B
	perferdDomPointArray[2] 	= false;	// dom point C
	
	// the player's team owns all the dom points
	self_pers_team = self.pers["team"];
	if( teamDomPoints.size == level.domFlags.size )
	{
		myTeam			= self_pers_team;
		bestFlagPoint 	= level.bestSpawnFlag[ self_pers_team ];
		
		// pefer a spawn point near the first dom point the player's team captured
		perferdDomPointArray[ bestFlagPoint.useObj.domPointNumber ] = true;
		return perferdDomPointArray;
	}		
	
	// check conditions for spawn locking
	if( (teamDomPoints.size == 1) && (enemyDomPoints.size == 2) )
	{
		enemyteam 		= getOtherTeam( self.team );
		scoreDifference	= maps\mp\gametypes\_gamescore::_getTeamScore( enemyteam ) - maps\mp\gametypes\_gamescore::_getTeamScore( self.team );
		
		// the player's team is losing
		if( scoreDifference > 25 )
		{
			teamTimeSinceCapture			 	= getTimeSinceDomPointCapture( teamDomPoints[0] );
			enemyTimeSinceCaptureFirstPoint 	= getTimeSinceDomPointCapture( enemyDomPoints[0] );
			enemyTimeSinceCaptureSecondPoint 	= getTimeSinceDomPointCapture( enemyDomPoints[1] );
		
			// the player's team is spawn trapepd
			if( (teamTimeSinceCapture > CONST_DOM_TRAP_TIME ) && (enemyTimeSinceCaptureFirstPoint > CONST_DOM_TRAP_TIME) && (enemyTimeSinceCaptureSecondPoint > CONST_DOM_TRAP_TIME) )
			{
				// stop perfering spawn points near the tapped location
				return perferdDomPointArray;
			}
		}
	}		
	
	if( teamDomPoints.size > 0 )
	{
		// prefer spawn locations near dom points the player's team owns
		foreach( domPoint in teamDomPoints )
		{
			perferdDomPointArray[domPoint.domPointNumber] = true;
		}
		
		return perferdDomPointArray;
	}

	// the player's team owns zero flags
	if( teamDomPoints.size == 0 )
	{
		// pefer a spawn point near the first dom point the player's team captured
		myTeam			= self_pers_team;
		bestFlagPoint 	= level.bestSpawnFlag[myTeam];
		
		// the enemy does not own all the flags
		if( (enemyDomPoints.size > 0) && (enemyDomPoints.size < level.domFlags.size) )
		{
			// perfer a spawn point near the uncapture dom locations
			bestFlagPoint = getUnownedFlagNearestStart( myTeam );
			level.bestSpawnFlag[myTeam] = bestFlagPoint;
		}
		
		perferdDomPointArray[ bestFlagPoint.useObj.domPointNumber ] = true;
		return perferdDomPointArray;
	}
	
	return perferdDomPointArray;
}

//===========================================
// 		getTimeSinceDomPointCapture
//===========================================
getTimeSinceDomPointCapture( domPoint )
{
	return ( getTime() - domPoint.captureTime );
}


domFlags()
{
	level.lastStatus["allies"]		= 0;
	level.lastStatus["axis"] 		= 0;
	
	primaryFlags 			= getEntArray( "flag_primary", "targetname" );
	primaryFlagsAugmented 	= getEntArray( "flag_primary_augmented", "targetname" );
	
	if( primaryFlags.size < 2 )
	{
		assertmsg( "^1Not enough domination flags found in level!" );
		return;
	}
	
	if( isAugmentedGameMode() )
	{
		for( aIndex = 0; aIndex < primaryFlagsAugmented.size; aIndex++ )
		{
			replacement = primaryFlagsAugmented[aIndex].script_label;
			
			for( pIndex = 0; pIndex < primaryFlags.size; pIndex++ )
			{
				if( primaryFlags[pIndex].script_label == replacement )
				{
					primaryFlags[pIndex] delete();
					break;
				}
			}
			
		}
	}
	else
	{
		for( index = 0; index < primaryFlagsAugmented.size; index++ )
			primaryFlagsAugmented[index] delete();
	}
	
	level.flags = [];
	level.flags = array_combine( primaryFlags, primaryFlagsAugmented );
	
	level.domFlags = [];
	for ( index = 0; index < level.flags.size; index++ )
	{
		trigger = level.flags[index];
		if ( isDefined( trigger.target ) )
		{
			visuals[0] = getEnt( trigger.target, "targetname" );
		}
		else
		{
			visuals[0] = spawn( "script_model", trigger.origin );
			visuals[0].angles = trigger.angles;
		}

		visuals[0] SetModel( level.flagBaseModel );

		domFlag = maps\mp\gametypes\_gameobjects::createUseObject( "neutral", trigger, visuals, (0,0,100) );
		domFlag maps\mp\gametypes\_gameobjects::allowUse( "enemy" );
		domFlag maps\mp\gametypes\_gameobjects::setUseTime( level.domCaptureTime );
		domFlag maps\mp\gametypes\_gameobjects::setUseText( &"MP_SECURING_POSITION" );
		label = domFlag maps\mp\gametypes\_gameobjects::getLabel();
		domFlag.label = label;
		domFlag maps\mp\gametypes\_gameobjects::set2DIcon( "friendly", "waypoint_defend" + label );
		domFlag maps\mp\gametypes\_gameobjects::set3DIcon( "friendly", "waypoint_defend" + label );
		domFlag maps\mp\gametypes\_gameobjects::set2DIcon( "enemy", "waypoint_captureneutral" + label );
		domFlag maps\mp\gametypes\_gameobjects::set3DIcon( "enemy", "waypoint_captureneutral" + label );
		domFlag maps\mp\gametypes\_gameobjects::set2DIcon( "mlg", "waypoint_esports_dom_white" + label );
		domFlag maps\mp\gametypes\_gameobjects::set3DIcon( "mlg", "waypoint_esports_dom_white" + label );
		domFlag maps\mp\gametypes\_gameobjects::setVisibleTeam( "any" );
		domFlag.onUse = ::onUse;
		domFlag.onBeginUse = ::onBeginUse;
		domFlag.onUseUpdate = ::onUseUpdate;
		domFlag.onEndUse = ::onEndUse;
		domFlag.noUseBar = true;
		domFlag.id = "domFlag";
		domflag.has_been_captured = false;
		domFlag.firstCapture = true;

		traceStart 	= visuals[0].origin + (0,0,32);
		traceEnd 	= visuals[0].origin + (0,0,-32);
		trace 		= bulletTrace( traceStart, traceEnd, false, visuals[0] );
	
		upangles = vectorToAngles( trace["normal"] );
		domFlag.baseeffectforward	= anglesToForward( upangles );
		domFlag.baseeffectright 	= anglesToRight( upangles );
		domFlag.baseeffectpos 		= trace["position"];
		
		visuals[0].origin = trace["position"];
		
		domFlag thread updateVisuals( true );
	
		level.flags[index].useObj 	= domFlag;
		domFlag.levelFlag 			= level.flags[index];
		
		level.domFlags[level.domFlags.size] = domFlag;
	}
	
	spawn_axis_start 			= maps\mp\gametypes\_spawnlogic::getSpawnpointArray( "mp_dom_spawn_axis_start" );
	spawn_allies_start 			= maps\mp\gametypes\_spawnlogic::getSpawnpointArray( "mp_dom_spawn_allies_start" );
	level.startPos["allies"] 	= spawn_allies_start[0].origin;
	level.startPos["axis"] 		= spawn_axis_start[0].origin;
	
	// level.bestSpawnFlag is used as a last resort when the enemy holds all flags.
	level.bestSpawnFlag = [];
	level.bestSpawnFlag[ "allies" ] = getUnownedFlagNearestStart( "allies", undefined );
	level.bestSpawnFlag[ "axis" ] = getUnownedFlagNearestStart( "axis", level.bestSpawnFlag[ "allies" ] );
	
	flagSetup();
	
	/#
	thread domDebug();
	#/
}


getUnownedFlagNearestStart( team, excludeFlag )
{
	best = undefined;
	bestdistsq = undefined;
	for ( i = 0; i < level.flags.size; i++ )
	{
		flag = level.flags[i];
		
		if ( flag getFlagTeam() != "neutral" )
			continue;
		
		distsq = distanceSquared( flag.origin, level.startPos[team] );
		if ( (!isDefined( excludeFlag ) || flag != excludeFlag) && (!isdefined( best ) || distsq < bestdistsq) )
		{
			bestdistsq = distsq;
			best = flag;
		}
	}
	return best;
}


onBeginUse( player )
{
	ownerTeam = self maps\mp\gametypes\_gameobjects::getOwnerTeam();
	self.didStatusNotify = false;
	
	self maps\mp\gametypes\_gameobjects::setUseTime( level.domCaptureTime );
	
	if ( ownerTeam == "neutral" )
	{
		statusDialog( "securing"+self.label, player.team );
		
		if ( self.firstCapture || self.curProgress == 0 )
			self maps\mp\gametypes\_gameobjects::setUseTime( level.domCaptureTime/2 );
		
		return;
	}
	
	if ( ownerTeam == "allies" )
	{
		level.alliesCapturing[level.alliesCapturing.size] = self.label;
		otherTeam = "axis";
	}
	else
	{
		level.axisCapturing[level.axisCapturing.size] = self.label;
		otherTeam = "allies";
	}
}


onUseUpdate( team, progress, change )
{
	ownerTeam = self maps\mp\gametypes\_gameobjects::getOwnerTeam();
	if ( progress > 0.05 && change && !self.didStatusNotify )
	{
		if ( ownerTeam == "neutral" )
		{
			statusDialog( "securing"+self.label, team );
			self.prevOwnerTeam = getOtherTeam( team );
			if (team == "allies")
			{
				self maps\mp\gametypes\_gameobjects::set2DIcon( "mlg", "waypoint_esports_dom_blue_taking" + self.label );
				self maps\mp\gametypes\_gameobjects::set3DIcon( "mlg", "waypoint_esports_dom_blue_taking" + self.label );
			}
			else
			{
				self maps\mp\gametypes\_gameobjects::set2DIcon( "mlg", "waypoint_esports_dom_red_taking" + self.label );
				self maps\mp\gametypes\_gameobjects::set3DIcon( "mlg", "waypoint_esports_dom_red_taking" + self.label );
			}
		}
		else
		{
			// force the losing dialog because it gets lost a lot in the other chatter
			statusDialog( "losing"+self.label, ownerTeam, true );
			statusDialog( "securing"+self.label, team );
			if (ownerTeam == "allies")
			{
				self maps\mp\gametypes\_gameobjects::set2DIcon( "mlg", "waypoint_esports_dom_blue_losing" + self.label );
				self maps\mp\gametypes\_gameobjects::set3DIcon( "mlg", "waypoint_esports_dom_blue_losing" + self.label );
			}
			else
			{
				self maps\mp\gametypes\_gameobjects::set2DIcon( "mlg", "waypoint_esports_dom_red_losing" + self.label );
				self maps\mp\gametypes\_gameobjects::set3DIcon( "mlg", "waypoint_esports_dom_red_losing" + self.label );
			}
		}
		
		self maps\mp\gametypes\_gameobjects::set2DIcon( "enemy", "waypoint_taking" + self.label );
		self maps\mp\gametypes\_gameobjects::set3DIcon( "enemy", "waypoint_taking" + self.label );
		self maps\mp\gametypes\_gameobjects::set2DIcon( "friendly", "waypoint_losing" + self.label );
		self maps\mp\gametypes\_gameobjects::set3DIcon( "friendly", "waypoint_losing" + self.label );

		self.didStatusNotify = true;
	}
	else if( level.allowNeutral && (progress > 0.49) && change && self.didStatusNotify && (ownerTeam != "neutral") )
	{
		self maps\mp\gametypes\_gameobjects::setOwnerTeam( "neutral" );
		self updateVisuals();
		statusDialog( "lost"+self.label, ownerTeam, true );
		level thread playSoundOnPlayers( "mp_obj_notify_pos_lrg", team );
		level thread playSoundOnPlayers( "mp_obj_notify_neg_lrg", ownerTeam );
		updateUIFlagOmnvars( self.label, "neutral" );	
		self thread giveFlagNeutralizeXP( self.touchList[team] );
	}
}


statusDialog( dialog, team, forceDialog )
{
	time = getTime();
	
	if ( getTime() < level.lastStatus[team] + 5000 && (!isDefined( forceDialog ) || !forceDialog) )
		return;
		
	thread delayedLeaderDialog( dialog, team );
	level.lastStatus[team] = getTime();	
}


onEndUse( team, player, success )
{
	if( IsPlayer( player ) )
	{
		player SetClientOmnvar( "ui_capture_icon", 0 );
	}

	ownerTeam = self maps\mp\gametypes\_gameobjects::getOwnerTeam();
	if ( ownerTeam != "neutral" )
	{
		self maps\mp\gametypes\_gameobjects::set2DIcon( "enemy", "waypoint_capture" + self.label );
		self maps\mp\gametypes\_gameobjects::set3DIcon( "enemy", "waypoint_capture" + self.label );
		self maps\mp\gametypes\_gameobjects::set2DIcon( "friendly", "waypoint_defend" + self.label );
		self maps\mp\gametypes\_gameobjects::set3DIcon( "friendly", "waypoint_defend" + self.label );
		if (ownerTeam == "allies")
		{
			self maps\mp\gametypes\_gameobjects::set2DIcon( "mlg", "waypoint_esports_dom_blue" + self.label );
			self maps\mp\gametypes\_gameobjects::set3DIcon( "mlg", "waypoint_esports_dom_blue" + self.label );
		}
		else
		{
			self maps\mp\gametypes\_gameobjects::set2DIcon( "mlg", "waypoint_esports_dom_red" + self.label );
			self maps\mp\gametypes\_gameobjects::set3DIcon( "mlg", "waypoint_esports_dom_red" + self.label );
		}
	}
	else
	{
		self maps\mp\gametypes\_gameobjects::set2DIcon( "enemy", "waypoint_captureneutral" + self.label );
		self maps\mp\gametypes\_gameobjects::set3DIcon( "enemy", "waypoint_captureneutral" + self.label );
		self maps\mp\gametypes\_gameobjects::set2DIcon( "friendly", "waypoint_captureneutral" + self.label );
		self maps\mp\gametypes\_gameobjects::set3DIcon( "friendly", "waypoint_captureneutral" + self.label );
		self maps\mp\gametypes\_gameobjects::set2DIcon( "mlg", "waypoint_esports_dom_white" + self.label );
		self maps\mp\gametypes\_gameobjects::set3DIcon( "mlg", "waypoint_esports_dom_white" + self.label );
	}

	if ( team == "allies" )
	{
		array_remove( level.alliesCapturing, self.label );
	}
	else
	{
		array_remove( level.alliesCapturing, self.label );
	}
}


updateVisuals( shouldWait )
{
	if( IsDefined(shouldWait) && shouldWait )
		waittillframeend;
	
	if( !IsRemovedEntity( self.visuals[0] ) )
	{
	baseModel = self.visuals[0];
	self friendlyEnemyEffects( baseModel.origin, AnglesToUp(baseModel.angles) );
}
}

friendlyEnemyEffects( origin, fwd )
{
	team = self.ownerTeam;
	
	friendlyFlagFxId = level.flagFXid[game[team]]["friendly"];
	friendlyBoarderFxId = level.boarderFXid[game[team]]["friendly"];
		
	enemyFlagFxId = level.flagFXid[game[team]]["enemy"];
	enemyBoarderFxId = level.boarderFXid[game[team]]["enemy"];

	friendlyEnemyEffectsStop();
		
	self.friendlyFlagFxId = spawnFXShowToTeam( friendlyFlagFxId, team, origin, fwd );
	self.friendlyBoarderFxId = spawnFXShowToTeam( friendlyBoarderFxId, team, origin, fwd );
	
	self.enemyFlagFxId = spawnFXShowToTeam( enemyFlagFxId, getOtherTeam(team), origin, fwd );
	self.enemyBoarderFxId = spawnFXShowToTeam( enemyBoarderFxId, getOtherTeam(team), origin, fwd );
}

friendlyEnemyEffectsStop()
{
	if(IsDefined(self.friendlyFlagFxId))
		self.friendlyFlagFxId Delete();
	
	if(IsDefined(self.friendlyBoarderFxId))
		self.friendlyBoarderFxId Delete();
	
	if(IsDefined(self.enemyFlagFxId))
		self.enemyFlagFxId Delete();
	
	if(IsDefined(self.enemyBoarderFxId))
		self.enemyBoarderFxId Delete();
}

updateUIFlagOmnvars( label, team )
{

	uiOmnvar = "ui_mlg_game_mode_status_1";
	if ( label == "_b" )
		uiOmnvar = "ui_mlg_game_mode_status_2";
	if ( label == "_c" )
		uiOmnvar = "ui_mlg_game_mode_status_3";
		
	if ( team == "allies" )
		SetOmnvar( uiOmnvar, -1 );
	else if ( team == "axis" )
		SetOmnvar( uiOmnvar, 1 );
	else
		SetOmnvar( uiOmnvar, 0 );

}

onUse( player )
{
	team = player.team;
	oldTeam = self maps\mp\gametypes\_gameobjects::getOwnerTeam();
	label = self maps\mp\gametypes\_gameobjects::getLabel();
	isOpeningMove = false;
	
	if( self.firstCapture && label == "_b" )
		isOpeningMove = true;
	
	self.captureTime = getTime();
	self.firstCapture = false;
	self.has_been_captured = true;
	
	if( IsDefined(self.ownedTheEntireGame) )
	{
		self.ownedTheEntireRound = false;
	}
	else
	{
		self.ownedTheEntireRound = true;
	}
	
	self maps\mp\gametypes\_gameobjects::setOwnerTeam( team );
	self maps\mp\gametypes\_gameobjects::set2DIcon( "enemy", "waypoint_capture" + label );
	self maps\mp\gametypes\_gameobjects::set3DIcon( "enemy", "waypoint_capture" + label );
	self maps\mp\gametypes\_gameobjects::set2DIcon( "friendly", "waypoint_defend" + self.label );
	self maps\mp\gametypes\_gameobjects::set3DIcon( "friendly", "waypoint_defend" + self.label );
	if (team == "allies")
	{
		self maps\mp\gametypes\_gameobjects::set2DIcon( "mlg", "waypoint_esports_dom_blue" + self.label );
		self maps\mp\gametypes\_gameobjects::set3DIcon( "mlg", "waypoint_esports_dom_blue" + self.label );
	}
	else
	{
		self maps\mp\gametypes\_gameobjects::set2DIcon( "mlg", "waypoint_esports_dom_red" + self.label );
		self maps\mp\gametypes\_gameobjects::set3DIcon( "mlg", "waypoint_esports_dom_red" + self.label );
	}
		
	self updateVisuals();
	
	level.useStartSpawns = false;
	
	assert( team != "neutral" );
	
	if ( oldTeam == "neutral" )
	{
		otherTeam = getOtherTeam( team );
		
		statusDialog( "secured"+self.label, team, true );
		statusDialog( "enemy_has"+self.label, otherTeam, true );
		
		level thread playSoundOnPlayers( "mp_obj_notify_pos_lrg", team );
	}
	else
	{
		if ( getTeamFlagCount( team ) == level.flags.size )
		{
			statusDialog( "secure_all", team );
			statusDialog( "lost_all", oldTeam );
		}
		else
		{	
			statusDialog( "secured"+self.label, team, true );
			statusDialog( "lost"+self.label, oldTeam, true );
		}
		
		level thread playSoundOnPlayers( "mp_obj_notify_pos_lrg", team );
		level thread playSoundOnPlayers( "mp_obj_notify_neg_lrg", oldTeam );
		
		level.bestSpawnFlag[ oldTeam ] = self.levelFlag;
	}
	
	updateUIFlagOmnvars( self.label, team );
	
	player thread snd_play_team_splash( "mp_obj_notify_pos_lrg", "mp_obj_notify_neg_lrg" );	
	self thread giveFlagCaptureXP( self.touchList[team], isOpeningMove );
}

giveFlagCaptureXP( touchList, isOpeningMove )
{
	level endon ( "game_ended" );
	
	player = self maps\mp\gametypes\_gameobjects::getEarliestClaimPlayer();
	
	if( IsDefined(player.owner) )
		player = player.owner;
	
	if( IsPlayer(player) )
	{
		level thread teamPlayerCardSplash( "callout_securedposition" + self.label, player );
	}
	
	players = getArrayKeys( touchList );
	for( index = 0; index < players.size; index++ )
	{
		player = touchList[players[index]].player;
		
		if( IsDefined(player.owner) )
			player = player.owner;
		
		if( !IsPlayer(player) )
			continue;
		
		player thread maps\mp\_events::domCaptureEvent( isOpeningMove );
		player thread updateCPM();
		
		wait(0.05);	// This can occasionally spike when players is large, so spread it out over multiple frames
	}
}

giveFlagNeutralizeXP( touchList )
{
	level endon ( "game_ended" );
	
	player = self maps\mp\gametypes\_gameobjects::getEarliestClaimPlayer();
	
	if( IsDefined(player.owner) )
		player = player.owner;
	
	if( IsPlayer(player) )
	{
		level thread teamPlayerCardSplash( "callout_neutralized_position" + self.label, player );
	}
	
	players = getArrayKeys( touchList );
	for( index = 0; index < players.size; index++ )
	{
		player = touchList[players[index]].player;
		
		if( IsDefined(player.owner) )
			player = player.owner;
		
		if( !IsPlayer(player) )
			continue;
		
		player thread maps\mp\_events::domNeutralizeEvent();
		
		wait(0.05);	// This can occasionally spike when players is large, so spread it out over multiple frames
	}
}

delayedLeaderDialog( sound, team )
{
	level endon ( "game_ended" );
	wait .1;
	WaitTillSlowProcessAllowed();
	
	leaderDialog( sound, team );
}
delayedLeaderDialogBothTeams( sound1, team1, sound2, team2 )
{
	level endon ( "game_ended" );
	wait .1;
	WaitTillSlowProcessAllowed();
	
	leaderDialogBothTeams( sound1, team1, sound2, team2 );
}


updateDomScores()
{
	level endon ( "game_ended" );
	
	while ( !level.gameEnded )
	{
		domFlags = getOwnedDomFlags();
		
		if ( domFlags.size )
		{
			lastCapTime = undefined;
			threeCapTeam = undefined;
			
			foreach( domFlag in domFlags )
			{
				team = domFlag maps\mp\gametypes\_gameobjects::getOwnerTeam();
				assert( team == "allies" || team == "axis" );
				maps\mp\gametypes\_gamescore::giveTeamScoreForObjective( team, 1 );	
				
				// track the last capture time
				if( !IsDefined(lastCapTime) || (lastCapTime < domFlag.captureTime) )
					lastCapTime = domFlag.captureTime;
				
				if( !IsDefined(threeCapTeam) )
					threeCapTeam = team;
				
				// track a team three capping
				if( threeCapTeam != team )
					threeCapTeam = "none";
			}
			
			// challenge for three capping
			if( (domFlags.size == 3) && (threeCapTeam != "none") && (level.players.size > 5) )
			{
				level.threeCapTime[threeCapTeam]["time"] = GetTime() - lastCapTime;
				level.threeCapTime[getOtherTeam(threeCapTeam)]["time"] 	= 0;
				
				if( (level.threeCapTime[threeCapTeam]["time"] > 60000) && (level.threeCapTime["axis"]["awarded"] == false) )
				{
					level.threeCapTime["axis"]["awarded"] = true;
					
					foreach( player in level.players )
					{
						if( player.team != threeCapTeam )
							continue;
						
						player maps\mp\gametypes\_missions::processChallenge( "ch_dom_tripcap" );
					}
				}
			}
			else
			{
				level.threeCapTime["axis"]["time"] 		= 0;
				level.threeCapTime["allies"]["time"] 	= 0;
			}
		}
		
		players_are_capturing = false;
		A_captured = false;
		C_captured = false;
				
		foreach ( flag in level.domFlags )
		{
			if( flag.label == "_a" )
			{
				if( flag.has_been_captured == true )
					A_captured = true;
			}
			else if( flag.label == "_c" )
			{
				if( flag.has_been_captured == true )
					C_captured = true;
			}
		}
		
		if( A_captured == true || C_captured == true )
			players_are_capturing = true;
		
		// end the game if people aren't playing
		if ( matchMakingGame() && !level.allowNeutral && !players_are_capturing && (getDomRoundTimePassed() > 120000) && !getdvarint( "force_ranking" ) )
		{
			level.finalKillCam_winner = "none";
			thread maps\mp\gametypes\_gamelogic::endGame( "none", game[ "end_reason" ][ "time_limit_reached" ] );
			return;			
		}
		
		wait ( 5.0 );
		maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone();
	}
}

getDomRoundTimePassed()
{
	return( gettime() - level.domRoundStartTime );
}

onPlayerKilled( eInflictor, attacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, psOffsetTime, deathAnimDuration, killId )
{
	if( !isPlayer(attacker) )
		return;
	
	if( maps\mp\gametypes\_damage::isFriendlyFire( self, attacker ) )
		return;
	
	if( attacker == self )
		return;
	
	if( IsDefined(sWeapon) && isKillstreakWeapon(sWeapon) )
		return;

	awardedAssault = false;
	victim = self;
	
	// award kill while capture medal
	foreach( trigger in attacker.touchTriggers )
	{
		if( trigger != level.flags[0] &&
			trigger != level.flags[1] &&
			trigger != level.flags[2] )
			continue;
		
		ownerTeam = trigger.useObj.ownerTeam;
		
		// attacker is touching a flag owned by another team
		if( attacker.team != ownerTeam )
		{
			attacker thread maps\mp\_events::killWhileCapture( victim, killId );
			awardedAssault = true;
		}
	}

	minDistanceSQ = 300 * 300;

	// award defense and assault medals
	foreach( trigger in level.flags )
	{
		ownerTeam 			= trigger.useObj.ownerTeam;
		victimDistanceSq 	= DistanceSquared( trigger.origin, victim.origin );
		attackerDistanceSq 	= DistanceSquared( trigger.origin, attacker.origin );
		
		// check for defense medals
		if( ownerTeam == attacker.team )
		{
			if( (victimDistanceSq < minDistanceSQ) || (attackerDistanceSq < minDistanceSQ) )
			{
				attacker thread maps\mp\_events::defendObjectiveEvent( victim, killId );
				attacker setExtraScore1( attacker.pers[ "defends" ] );
			}
		}
		
		// quit if player has earned the kill while capture medal
		if( awardedAssault )
			break;
		
		// check for assault medals
		if( ownerTeam == victim.team )
		{
			// attacker gets an asssault medal for combat near enemy objective
			if( (attackerDistanceSq < minDistanceSQ) || (victimDistanceSq < minDistanceSQ) )
				attacker thread maps\mp\_events::assaultObjectiveEvent( self, killId );
		}

	}
}

getOwnedDomFlags()
{
	domFlags = [];
	foreach ( domFlag in level.domFlags )
	{
		if ( domFlag maps\mp\gametypes\_gameobjects::getOwnerTeam() != "neutral" && isDefined( domFlag.captureTime ) )
			domFlags[domFlags.size] = domFlag;
	}
	
	return domFlags;
}


getTeamFlagCount( team )
{
	score = 0;
	for (i = 0; i < level.flags.size; i++) 
	{
		if ( level.domFlags[i] maps\mp\gametypes\_gameobjects::getOwnerTeam() == team )
			score++;
	}	
	return score;
}

getFlagTeam()
{
	return self.useObj maps\mp\gametypes\_gameobjects::getOwnerTeam();
}


//===========================================
// 				flagSetup
//===========================================
flagSetup()
{
	/#
	setDevDvarIfUninitialized( "scr_domdebug", "0" );
	#/
			
	// give each dom point a unique number
	foreach( domPoint in level.domFlags )
	{
		switch( domPoint.label )
		{
			case "_a":
				domPoint.domPointNumber = 0;
				break;
			case "_b":
				domPoint.domPointNumber = 1;
				break;
			case "_c":
				domPoint.domPointNumber = 2;
				break;
		}	
	}
	
	spawnPoints = maps\mp\gametypes\_spawnlogic::getSpawnpointArray( "mp_dom_spawn" );
	
	// assign each spawn point to the nearest a dom point
	foreach( spawnPoint in spawnPoints )
	{
		spawnPoint.domPointA 	= false;
		spawnPoint.domPointB 	= false;
		spawnPoint.domPointC 	= false;
	
		spawnPoint.nearFlagPoint = getNearestFlagPoint( spawnPoint );
		
		switch( spawnPoint.nearFlagPoint.useObj.domPointNumber )
		{
			case 0:
				spawnPoint.domPointA = true;
				break;
			case 1:
				spawnPoint.domPointB = true;
				break;
			case 2:
				spawnPoint.domPointC = true;
				break;
		}	
	}
}


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

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


/#
//===========================================
// 				domDebug
//===========================================
domDebug()
{
	spawnPoints 		= maps\mp\gametypes\_spawnlogic::getTeamSpawnPoints( "axis" );
	isPathDataAvailable = maps\mp\gametypes\_spawnlogic::isPathDataAvailable();
	heightOffsetLines 	= (0,0,12);
	heightOffsetNames 	= (0,0,64);
	
	while( true )
	{
		if( getdvar("scr_domdebug") != "1" ) 
		{
			wait( 1 );
			continue;
		}
		
		SetDevDvar( "scr_showspawns", "1" );
		
		while( true )
		{
			if( getdvar("scr_domdebug") != "1" )
			{
				SetDevDvar( "scr_showspawns", "0" );
				break;
			}
			
			// draw the path from each spawn point to the nearest dom flag
			foreach( spawnPoint in spawnPoints )
			{
				if( isPathDataAvailable )
				{
					if( !IsDefined(spawnPoint.nodeArray) )
					{
						spawnPoint.nodeArray = GetNodesOnPath( spawnPoint.origin, spawnPoint.nearFlagPoint.origin );
					}
					
					if( !IsDefined(spawnPoint.nodeArray) || (spawnPoint.nodeArray.size == 0) )
					{
						continue;
					}
					
					line( spawnPoint.origin + heightOffsetLines, spawnPoint.nodeArray[0].origin + heightOffsetLines, (0.2, 0.2, 0.6) );
					
					for( i = 0; i <  spawnPoint.nodeArray.size - 1; i++ )
					{
						line( spawnPoint.nodeArray[i].origin + heightOffsetLines, spawnPoint.nodeArray[i+1].origin + heightOffsetLines, (0.2, 0.2, 0.6) );
					}
				}
				else
				{
					line( spawnPoint.nearFlagPoint.origin + heightOffsetLines, spawnPoint.origin + heightOffsetLines, (0.2, 0.2, 0.6) );
				}
			}
			
			// show the best flag locations
			foreach( flagLocation in level.flags )
			{
				if ( flagLocation == level.bestSpawnFlag["allies"] )
					print3d( flagLocation.origin + heightOffsetNames, "allies best spawn flag" );
				
				if ( flagLocation == level.bestSpawnFlag["axis"] )
					print3d( flagLocation.origin + heightOffsetNames, "axis best spawn flag" );
			}
			
			wait( 0.05 );
		}
	}
}
#/


onSpawnPlayer()
{
}

updateCPM()
{
	if ( !isDefined( self.CPM ) )
	{
		self.numCaps = 0;
		self.CPM = 0;
	}
	
	self.numCaps++;
	
	if ( getMinutesPassed() < 1 )
		return;
		
	self.CPM = self.numCaps / getMinutesPassed();
}

getCapXPScale()
{
	if ( self.CPM < 4 )
		return 1;
	else
		return 0.25;
}
