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

/*QUAKED mp_hp_spawn (0.8 0.8 0) (-16 -16 0) (16 16 72)
Players spawn near the Hardpoint positions.*/

/*QUAKED mp_hp_spawn_axis_start (1 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_hp_spawn_allies_start (0.6 0.6 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.*/

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, 0 );
		
		level.matchRules_damageMultiplier = 0;
		level.matchRules_vampirism = 0;	
	}

	
	level.teamBased 			= true;
	level.randomZoneSpawn		= getIntProperty( "scr_hp_radom_selection", 0 );
	level.hpStartTime 			= 0;
	level.zoneDuration 			= 60;
	level.initialZoneDelay 		= getIntProperty( "scr_hp_zone_delay", 0 );
	level.playerCaptureLPM 		= 6;
	level.scorePerPlayer 		= 0;
	level.onStartGameType		= ::onStartGameType;
	level.getSpawnPoint 		= ::getSpawnPoint;
	level.onPlayerKilled 		= ::onPlayerKilled;
	level.onPrecacheGameType 	= ::onPrecacheGameType;

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

	if ( getDvarInt( "g_hardcore" ) )
		game["dialog"]["gametype"] = "hc_" + game["dialog"]["gametype"];
	
	game["objective_active"]			= "mp_hp_new_hardpoint_active";
	game["objective_gained_sound"] 		= "mp_obj_notify_pos_sml";
	game["objective_lost_sound"]		= "mp_obj_notify_neg_sml";
	game["objective_contested_sound"]	= "mp_obj_notify_neg_sml";
	
	level.lastDialogTime = 0;
	level.zoneSpawnQueue = [];
}


initializeMatchRules()
{
	//	set common values
	setCommonRulesFromMatchRulesData();

	//	set everything else (private match options, default .cfg file values, and what normally is registered in the 'else' below)
	registerRoundLimitDvar( level.gameType, 1 );
	registerWinLimitDvar( level.gameType, 1 );
	registerHalfTimeDvar( level.gameType, 0 );
	
	SetDynamicDvar( "scr_hp_radom_selection", GetMatchRulesData("hpData", "randomZones") );
	SetDynamicDvar( "scr_hp_zone_delay", GetMatchRulesData("hpData", "initDelay") );
}


onPrecacheGameType()
{
	level.objectiveHintPrepareZone = &"MP_CONTROL_KOTH";
	level.objectiveHintCaptureZone = &"MP_CAPTURE_KOTH";
	level.objectiveHintDefendHQ = &"MP_DEFEND_KOTH";

	level.iconNeutral3D ="waypoint_captureneutral";
	level.iconNeutral2D ="waypoint_captureneutral";
	level.iconCapture3D = "waypoint_capture";
	level.iconCapture2D = "waypoint_capture";
	level.iconDefend3D = "waypoint_defend";
	level.iconDefend2D = "waypoint_defend";
	level.iconContested3D = "waypoint_contested";
	level.iconContested2D = "waypoint_contested";
	
	level.iconNeutralSpectator ="waypoint_esports_hardpoint_white";
	level.iconBlueSpectator ="waypoint_esports_hardpoint_blue";
	level.iconRedSpectator ="waypoint_esports_hardpoint_red";
	level.iconContestedSpectator ="waypoint_contested";

	level.zoneSpawningInStr = &"MP_KOTH_AVAILABLE_IN";
	level.zoneDestroyedInEnemyStr = &"MP_KOTH_MOVING_IN";	
}


updateObjectiveHintMessages( alliesMessage, axisMessage )
{
	game["strings"]["objective_hint_allies"] = alliesMessage;
	game["strings"]["objective_hint_axis"] = axisMessage;
}

updateObjectiveHintMessage( message )
{
	updateObjectiveHintMessages( message, message );
}

onStartGameType()
{
	if ( !isdefined( game["switchedsides"] ) )
		game["switchedsides"] = false;

	if ( game["switchedsides"] )
	{
		oldAttackers = game["attackers"];
		oldDefenders = game["defenders"];
		game["attackers"] = oldDefenders;
		game["defenders"] = oldAttackers;
	}
	
	setObjectiveText( "allies", &"OBJECTIVES_KOTH" );
	setObjectiveText( "axis", &"OBJECTIVES_KOTH" );
	
	if ( level.splitscreen )
	{
		setObjectiveScoreText( "allies", &"OBJECTIVES_KOTH" );
		setObjectiveScoreText( "axis", &"OBJECTIVES_KOTH" );
	}
	else
	{
		setObjectiveScoreText( "allies", &"OBJECTIVES_KOTH_SCORE" );
		setObjectiveScoreText( "axis", &"OBJECTIVES_KOTH_SCORE" );
	}
	
	if ( level.initialZoneDelay )
	{
		updateObjectiveHintMessage( level.objectiveHintPrepareZone );
	}
	else
	{
		updateObjectiveHintMessage( level.objectiveHintCaptureZone );
	}
	
	setClientNameMode("auto_change");

	initSpawns();
	
	allowed[0] = "hp";
	maps\mp\gametypes\_gameobjects::main(allowed);
	
	SetOmnvar( "ui_mlg_game_mode_status_1", 0 );
	SetOmnvar( "ui_mlg_game_mode_status_2", 0 );
	
	level thread onPlayerConnect();
	level thread SetupZones();
	level thread HardpointMainLoop();
	
//	/# thread debug_hp_spawns(); #/
}

/#
debug_hp_spawns()
{
	level endon( "game_ended" );
	
	while ( true )
	{
		foreach( player in level.players )
		{
			if( IsDefined( player.hp_anchor ) )
			{
				if( player.team == "allies" )
					thread drawSphere( player.origin, 1280, 1, (0,0,1) );
				else
					thread drawSphere( player.origin, 1280, 1, (1,1,0) );
			}
		}
		thread drawSphere( level.zone.origin, 832, 1, (1,0,0) );
		thread drawSphere( level.zone.origin, 2600, 1, (0,1,0) );
		wait( 1 );
	}
}
#/

onPlayerConnect()
{
	while( true )
	{
		level waittill( "connected", player );
		
		player.gameObjectHUDIndex 	= 0;
		player.objective			= 0;
		player.touchTriggers		= [];
	}
}

//===========================================
// 				initSpawns
//===========================================
initSpawns()
{
	// TODO: HQ spawnpoints
	level.spawnMins = ( 0, 0, 0 );
	level.spawnMaxs = ( 0, 0, 0 );
	
	maps\mp\gametypes\_spawnlogic::addStartSpawnPoints( "mp_hp_spawn_allies_start" );
	maps\mp\gametypes\_spawnlogic::addStartSpawnPoints( "mp_hp_spawn_axis_start" );
	maps\mp\gametypes\_spawnlogic::addSpawnPoints( "allies", "mp_hp_spawn" );
	maps\mp\gametypes\_spawnlogic::addSpawnPoints( "axis", "mp_hp_spawn" );
	
	level.mapCenter = maps\mp\gametypes\_spawnlogic::findBoxCenter( level.spawnMins, level.spawnMaxs );
	setMapCenter( level.mapCenter );
}

//===========================================
// 			getSpawnPoint
//===========================================
getSpawnPoint()
{
	spawnteam = self.pers["team"];
	if ( game["switchedsides"] )
		spawnteam = getOtherTeam( spawnteam );
	
	if ( level.useStartSpawns && level.inGracePeriod )
	{
		spawnPoints = maps\mp\gametypes\_spawnlogic::getSpawnpointArray( "mp_hp_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_Hardpoint( spawnPoints );
	}
	
	assert( isDefined(spawnPoint) );
	
	self maps\mp\gametypes\_spawnlogic::recon_set_spawnpoint( spawnpoint );
	
	return spawnPoint;
}

hpUpdateUseRate()
{
	level.zone.gameobject maps\mp\gametypes\_gameobjects::updateUseRate_internal();
	SetOmnvar( "ui_mlg_game_mode_status_1", level.zone.gameobject.numTouching["allies"] );
	SetOmnvar( "ui_mlg_game_mode_status_2", level.zone.gameobject.numTouching["axis"] );
}


spawn_next_zone()
{
	if( level.randomZoneSpawn )
	{
		level.zone = GetNextZoneFromQueue();
	}
	else
	{
		level.zone = GetNextZone();
	}
	
	setNeutralIcons();
	
	/#
	if ( isdefined( level.zone ) )
	{
		logString("zone spawned: ("+level.zone.trigOrigin[0]+","+level.zone.trigOrigin[1]+","+level.zone.trigOrigin[2]+")");
	}
	#/
}


hpCaptureLoop()
{
	level endon("game_ended");
	level endon("zone_moved");
	level.hpStartTime = gettime();
	
	while( 1 )
	{
		level.zone.gameobject maps\mp\gametypes\_gameobjects::allowUse( "any" );
		level.zone.gameobject maps\mp\gametypes\_gameobjects::setUseTime( 0 );
		level.zone.gameobject maps\mp\gametypes\_gameobjects::setUseText( &"MP_CAPTURING_OBJECTIVE" );

		level.zone.gameobject maps\mp\gametypes\_gameobjects::setVisibleTeam( "any" );
		level.zone.gameobject maps\mp\gametypes\_gameobjects::setModelVisibility( true );
		level.zone.gameobject maps\mp\gametypes\_gameobjects::mustMaintainClaim( false );
		level.zone.gameobject maps\mp\gametypes\_gameobjects::canContestClaim( true );
		
		level.zone.gameobject.onUse = ::onZoneCapture;

		msg = level waittill_any_return( "zone_captured", "zone_destroyed" );
	
		// this happens if it goes from contested to neutral
		if ( msg == "zone_destroyed" )
			continue;
			
		ownerTeam = level.zone.gameobject maps\mp\gametypes\_gameobjects::getOwnerTeam();

		if ( ownerTeam == "allies" )
			updateObjectiveHintMessages( level.objectiveHintDefendHQ, level.objectiveHintCaptureZone );
		else if ( ownerTeam == "axis" )
			updateObjectiveHintMessages( level.objectiveHintCaptureZone, level.objectiveHintDefendHQ );
		else
			updateObjectiveHintMessages( level.objectiveHintCaptureZone, level.objectiveHintCaptureZone );

		level.zone.gameobject maps\mp\gametypes\_gameobjects::allowUse( "none" );
		
		level.zone.gameobject.onUse = undefined;
		level.zone.gameobject.onUnoccupied = ::onZoneUnoccupied;
		level.zone.gameobject.onContested = ::onZoneContested;
		level.zone.gameobject.onUncontested = ::onZoneUncontested;
			
		level waittill( "zone_destroyed", destroy_team );
				
		thread forceSpawnTeam( ownerTeam );
		
		if ( isdefined( destroy_team ) )
		{
			level.zone.gameobject maps\mp\gametypes\_gameobjects::setOwnerTeam( destroy_team );
		}
		else
		{
			level.zone.gameobject maps\mp\gametypes\_gameobjects::setOwnerTeam( "none" );
		}
	}
}

updateGameObjectHUDIndex()
{
	while( true )
	{
		foreach( setPlayer in level.players )
		{
			checkPlayer = setPlayer;
			if( (setPlayer.team == "spectator") || (setPlayer.sessionstate == "spectator") )
			{
				// if we are a spectator, update our HUD index as though we were the player we are spectating.
				checkPlayer = setPlayer GetSpectatingPlayer();
			}
			
			if( !isReallyAlive( checkPlayer ) )
			{
				setPlayer setGameObjectHUDIndex( setPlayer, false );
				continue;
			}

			if( checkPlayer.touchTriggers.size == 0 )
			{
				setPlayer setGameObjectHUDIndex( setPlayer, false );
				continue;
			}
			
			foreach( gameObjectTrigger in checkPlayer.touchTriggers )
			{
				if( gameObjectTrigger == level.zone.gameobject.trigger )
				{
					setPlayer setGameObjectHUDIndex( setPlayer, true );
					break;
				}				
				setPlayer setGameObjectHUDIndex( setPlayer, false );
			}
		}
		
		wait(0.05);
	}
}

setGameObjectHUDIndex( player, isTouchingHardPoint )
{
	gameObjectHUDIndex = 0;
	
	if( isTouchingHardPoint )
	{
		gameObjectHUDIndex = 1;
		
		if( IsDefined(level.zone.gameobject.isContested) && level.zone.gameobject.isContested )
			gameObjectHUDIndex = 2;
	}

	if( gameObjectHUDIndex != player.gameObjectHUDIndex )
	{
		player SetClientOmnvar( "ui_hardpoint", gameObjectHUDIndex );
		player.gameObjectHUDIndex = gameObjectHUDIndex;
		player.objective = gameObjectHUDIndex;
	}
}

HardpointMainLoop()
{
	level endon("game_ended");
	
	level.zoneRevealTime = -100000;
		
	level.zone = GetFirstZone();
	
	gameFlagWait( "prematch_done" );
	
	level childthread updateGameObjectHUDIndex();
	
	while( 1 )
	{
		playSoundOnPlayers( "mp_suitcase_pickup" );
		flushGroupDialog( "gamemode_objective" );
		
		level.zone.gameobject maps\mp\gametypes\_gameobjects::setModelVisibility( true );
		level.zone.gameobject.onUpdateUseRate = ::hpUpdateUseRate;
		
		level.zoneRevealTime = gettime();
	
		if ( level.initialZoneDelay )
		{
			setWaitingIcons( level.zone );
			level.zone.gameobject maps\mp\gametypes\_gameobjects::setVisibleTeam( "any" );

			updateObjectiveHintMessage( level.objectiveHintPrepareZone );
			
			SetOmnvar( "ui_hardpoint_timer", getTime() + 1000 * level.initialZoneDelay );

			wait level.initialZoneDelay;			
		}
				
		waittillframeend;
		
		setNeutralIcons( level.zone );
		leaderDialog( "hp_online", undefined, "gamemode_objective" );
		updateObjectiveHintMessage( level.objectiveHintCaptureZone );
		level thread playSoundOnPlayers( game["objective_active"] );

		level.zone.gameobject maps\mp\gametypes\_gameobjects::enableObject();
		level.zone.gameobject.captureCount = 0;
		
		if ( level.zoneDuration )
		{
			thread MoveZoneAfterTime( level.zoneDuration );			
			SetOmnvar( "ui_hardpoint_timer", getTime() + 1000 * level.zoneDuration );
		}
		else
		{
			level.zoneDestroyedByTimer = false;
		}
	
		hpCaptureLoop();
		
		ownerTeam = level.zone.gameobject maps\mp\gametypes\_gameobjects::getOwnerTeam();
		
		level.zone.gameobject.lastCaptureTeam = undefined;
		level.zone.gameobject maps\mp\gametypes\_gameobjects::disableObject();
		level.zone.gameobject maps\mp\gametypes\_gameobjects::allowUse( "none" );
		level.zone.gameobject maps\mp\gametypes\_gameobjects::setOwnerTeam( "neutral" );
		level.zone.gameobject maps\mp\gametypes\_gameobjects::setModelVisibility( false );
		level.zone.gameobject maps\mp\gametypes\_gameobjects::mustMaintainClaim( false );
		level.zone.gameobject maps\mp\gametypes\_gameobjects::set2DIcon( "mlg", undefined );
		level.zone.gameobject maps\mp\gametypes\_gameobjects::set3DIcon( "mlg", undefined );
	
		level notify("zone_reset");
		
		if( IsDefined( level.hp_pause_for_dynamic_event ) && level.hp_pause_for_dynamic_event == true )
		{
			level waittill( "ready_for_next_hp_zone" );
		}
		
		spawn_next_zone();
		
		wait 0.5;
		
		thread forceSpawnTeam( ownerTeam );
		
		wait 0.5;
	}
}


forceSpawnTeam( team )
{
	players = level.players;
	for ( i = 0; i < players.size; i++ )
	{
		player = players[i];
		if ( !isdefined( player ) )
			continue;
		
		if ( player.pers["team"] == team )
		{
			player notify( "force_spawn" );
			wait .1;
		}
	}
}


onZoneCapture( player )
{
	capture_team = player.pers["team"];
	not_capture_team = getOtherTeam( capture_team );
	captureTime = getTime();
	
	player logString( "zone captured" );

	level.zone.gameobject.isContested = false;
	level.useStartSpawns = false;

	setTeamIcons( capture_team );

	if ( !isdefined(self.lastCaptureTeam) || (self.lastCaptureTeam != capture_team) )
	{
		// Copy touch list so there aren't any threading issues
		touchList = [];
		touchKeys = GetArrayKeys( self.touchList[capture_team] );
		for ( i = 0 ; i < touchKeys.size ; i++ )
			touchList[touchKeys[i]] = self.touchList[capture_team][touchKeys[i]];
		
		level thread give_capture_credit( touchList, captureTime, capture_team, self.lastCaptureTeam );
		level thread leaderDialog( "hp_secured", capture_team, "gamemode_objective" );
		level thread leaderDialog( "hp_lost", not_capture_team, "gamemode_objective" );
	}
	
	level thread playSoundOnPlayers( game["objective_gained_sound"], capture_team );
	level thread playSoundOnPlayers( game["objective_lost_sound"], not_capture_team );

	level.hpCapTeam = capture_team;

	self maps\mp\gametypes\_gameobjects::setOwnerTeam( capture_team );
	
	if ( IsDefined( self.lastCaptureTeam ) && ( self.lastCaptureTeam != capture_team ) )
	{
		for ( index = 0; index < level.players.size; index++ )
		{
			player = level.players[index];
			
			if ( player.pers["team"] == capture_team )
			{
				if ( player.lastKilltime + 500 > getTime() )
				{
					player maps\mp\gametypes\_missions::processChallenge( "ch_hp_killedLastContester" );
				}
			}
		}
	}
	
	level thread awardCapturePoints( capture_team, self.lastCaptureTeam );
	self.captureCount++;
	self.lastCaptureTeam = capture_team;
	
	self maps\mp\gametypes\_gameobjects::mustMaintainClaim( true );
	
	level notify( "zone_captured" );
	level notify( "zone_captured" + capture_team );
}

give_capture_credit( touchList, captureTime, capture_team, lastCaptureTeam )
{
	players = getArrayKeys( touchList );
	for ( i = 0; i < players.size; i++ )
	{
		player = touchList[players[i]].player;
		player updateCapsPerMinute( lastCaptureTeam );

		if ( !isScoreBoosting( player ) )
		{
			player maps\mp\gametypes\_missions::processChallenge( "ch_hp_capturedObjective" );
			
			if( level.hpStartTime + 500 > captureTime ) 
				player maps\mp\gametypes\_missions::processChallenge( "ch_hp_immediateCapture" );
			
			player thread maps\mp\_events::secureHardPointEvent();		
		}
		else
		{
			/#
				player IPrintlnBold( "GAMETYPE DEBUG: NOT GIVING YOU CAPTURE CREDIT AS BOOSTING PREVENTION" );
			#/
		}
		
		wait(0.05); // This can occasionally spike when players is large, so spread it out over multiple frames
	}
}

onZoneUnoccupied()
{
	level notify( "zone_destroyed" );
	level.hpCapTeam = "neutral";
	level.zone.gameobject.isContested = false;
	
	if( (self.numTouching["axis"] == 0) && (self.numTouching["allies"] == 0))
	{
		level.zone.gameobject.wasLeftUnoccupied = true;
		setNeutralIcons();
	}
}

onZoneContested()
{	
	zoneOwningTeam = level.zone.gameobject maps\mp\gametypes\_gameobjects::getOwnerTeam();
	level.zone.gameobject.wasContested = true;
	level.zone.gameobject.isContested = true;

	setContestedIcons();

	level thread playSoundOnPlayers( game["objective_contested_sound"] );
	leaderDialog( "hp_contested", undefined, "gamemode_objective" );
}

onZoneUncontested( lastClaimTeam )
{	
	assert( lastClaimTeam ==  level.zone.gameobject maps\mp\gametypes\_gameobjects::getOwnerTeam() );
	
	level.zone.gameobject.isContested = false;
	setTeamIcons( lastClaimTeam );

	level.zone.gameobject maps\mp\gametypes\_gameobjects::SetClaimTeam( lastClaimTeam );
}

setNeutralIcons( zone )
{
	if( !IsDefined(zone) )
		zone = level.zone;
	
	zone.gameObject maps\mp\gametypes\_gameobjects::set2DIcon( "friendly", level.iconNeutral2D );
	zone.gameObject maps\mp\gametypes\_gameobjects::set3DIcon( "friendly", level.iconNeutral3D );
	zone.gameObject maps\mp\gametypes\_gameobjects::set2DIcon( "enemy", level.iconNeutral2D );
	zone.gameObject maps\mp\gametypes\_gameobjects::set3DIcon( "enemy", level.iconNeutral3D );
	zone.gameObject maps\mp\gametypes\_gameobjects::set2DIcon( "mlg", level.iconNeutralSpectator );
	zone.gameObject maps\mp\gametypes\_gameobjects::set3DIcon( "mlg", level.iconNeutralSpectator );
}

setWaitingIcons( zone )
{
	if( !IsDefined(zone) )
		zone = level.zone;
	
	icon = "waypoint_waitfor_flag_neutral";
	zone.gameObject maps\mp\gametypes\_gameobjects::set2DIcon( "friendly", icon );
	zone.gameObject maps\mp\gametypes\_gameobjects::set3DIcon( "friendly", icon );
	zone.gameObject maps\mp\gametypes\_gameobjects::set2DIcon( "enemy", icon);
	zone.gameObject maps\mp\gametypes\_gameobjects::set3DIcon( "enemy", icon );
	zone.gameObject maps\mp\gametypes\_gameobjects::set2DIcon( "mlg", icon );
	zone.gameObject maps\mp\gametypes\_gameobjects::set3DIcon( "mlg", icon );
}

setContestedIcons()
{
	level.zone.gameObject maps\mp\gametypes\_gameobjects::set2DIcon( "friendly", level.iconContested2D );
	level.zone.gameObject maps\mp\gametypes\_gameobjects::set3DIcon( "friendly", level.iconContested3D );
	level.zone.gameObject maps\mp\gametypes\_gameobjects::set2DIcon( "enemy", level.iconContested2D );
	level.zone.gameObject maps\mp\gametypes\_gameobjects::set3DIcon( "enemy", level.iconContested3D );
	level.zone.gameObject maps\mp\gametypes\_gameobjects::set2DIcon( "mlg", level.iconContestedSpectator );
	level.zone.gameObject maps\mp\gametypes\_gameobjects::set3DIcon( "mlg", level.iconContestedSpectator );
}

setTeamIcons( ownerTeam )
{
	level.zone.gameObject maps\mp\gametypes\_gameobjects::set2DIcon( "friendly", level.iconDefend2D );
	level.zone.gameObject maps\mp\gametypes\_gameobjects::set3DIcon( "friendly", level.iconDefend3D );
	level.zone.gameObject maps\mp\gametypes\_gameobjects::set2DIcon( "enemy", level.iconCapture2D );
	level.zone.gameObject maps\mp\gametypes\_gameobjects::set3DIcon( "enemy", level.iconCapture3D );
	if ( ownerTeam == "allies" )
	{
		level.zone.gameObject maps\mp\gametypes\_gameobjects::set2DIcon( "mlg", level.iconBlueSpectator );
		level.zone.gameObject maps\mp\gametypes\_gameobjects::set3DIcon( "mlg", level.iconBlueSpectator );
	}
	else
	{
		level.zone.gameObject maps\mp\gametypes\_gameobjects::set2DIcon( "mlg", level.iconRedSpectator );
		level.zone.gameObject maps\mp\gametypes\_gameobjects::set3DIcon( "mlg", level.iconRedSpectator );
	}
}

MoveZoneAfterTime( time )
{
	level endon( "game_ended" );
	level endon( "zone_reset" );
	
	level.zoneMoveTime = gettime() + time * 1000;
	level.zoneDestroyedByTimer = false;
	
	wait time;

	if ( !isdefined( level.zone.gameobject.wasContested ) || level.zone.gameobject.wasContested == false )
	{
		if ( !isdefined( level.zone.gameobject.wasLeftUnoccupied ) || level.zone.gameobject.wasLeftUnoccupied == false )
		{
			zoneOwningTeam = level.zone.gameobject maps\mp\gametypes\_gameobjects::getOwnerTeam();
			foreach ( localPlayer in level.players )
			{
				if ( localPlayer.pers["team"] == zoneOwningTeam )
					localPlayer maps\mp\gametypes\_missions::processChallenge( "ch_hp_controlZoneEntirely" );
			}
		}
	}

	level.zoneDestroyedByTimer = true;

	level notify( "zone_moved" );
}


awardCapturePoints( team, lastCaptureTeam )
{
	level endon( "game_ended" );
	level endon( "zone_destroyed" );
	level endon( "zone_reset" );
	level endon( "zone_moved" );
	
	level notify("awardCapturePointsRunning");
	level endon("awardCapturePointsRunning");
	
	seconds = 1;
	score = 1;

	while ( !level.gameEnded )
	{
		wait seconds;
		maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone();
		
		if ( !level.zone.gameobject.isContested )
		{
			if ( level.scorePerPlayer )
			{
				score = level.zone.gameobject.numTouching[team];
			}
			
			maps\mp\gametypes\_gamescore::giveTeamScoreForObjective( team, score );
		}
	}
}


CompareZoneIndexes( zone_a, zone_b )
{
	script_index_a = zone_a.script_index;
	script_index_b = zone_b.script_index;
	
	if( !isdefined(script_index_a) && !isdefined(script_index_b) )
	{
		return false;
	}

	if( !isdefined(script_index_a) && isdefined(script_index_b) )
	{
/#
		println( "Hard Point: Missing script_index on zone at " + zone_a.origin );
#/
		return true;
	}
	
	if( isdefined(script_index_a) && !isdefined(script_index_b) )
	{
/#
		println( "Hard Point: Missing script_index on zone at " + zone_b.origin );
#/
		return false;
	}
	
	if( script_index_a > script_index_b )
	{
		return true;
	}
	
	return false;
}


getZoneArray( targetName )
{
  zones = getentarray( targetName, "targetname" );

	if( !isDefined( zones ) )
	{
		return undefined;
	}
	
	swapped = true;
	n = zones.size;
	
	// sort zone arrary by script_index number
	while ( swapped )
	{
		swapped = false;
		for( i = 0 ; i < n-1 ; i++ )
		{
			if( CompareZoneIndexes(zones[i], zones[i+1]) )
			{
				temp = zones[i];
				zones[i] = zones[i+1];
				zones[i+1] = temp;
				swapped = true;
			}
		}
		n--;
	}
	return zones;
}


SetupZones()
{
	zones = getZoneArray( "hp_zone_center" );
	trigs = getentarray("hp_zone_trigger", "targetname");
	
	AssertEx( IsDefined(zones) && (zones.size > 0), "Map needs hp_zone_center entities added" );
	AssertEx( IsDefined(trigs) && (trigs.size > 0), "Map needs hp_zone_trigger entities added" );
	
	level.zones = matchZonesToTriggers( zones, trigs );
	
	zonesAugmented = getZoneArray( "hp_zone_center_augmented" );
	trigsAugmented = getentarray("hp_zone_trigger_augmented", "targetname");
	
	if( IsDefined(zonesAugmented) && IsDefined(trigsAugmented) )
	{
		zonesAugmented = matchZonesToTriggers( zonesAugmented, trigsAugmented );
		
		if( isAugmentedGameMode() )
		{
			foreach( replacmentZone in zonesAugmented )
			{
				for( orginalZoneIndex = 0; orginalZoneIndex < level.zones.size; orginalZoneIndex++ )
				{
					if( level.zones[orginalZoneIndex].script_index == replacmentZone.script_index )
					{
						level.zones[orginalZoneIndex] = replacmentZone;
					}
				}
			}
		}
	}
	
	level.all_hp_zones = level.zones;
	
	return true;
}

matchZonesToTriggers( zones, trigs )
{
	for ( i = 0; i < zones.size; i++ )
	{
		errored = false;
		
		zone = zones[i];
		zone.trig = undefined;
		
		for ( j = 0; j < trigs.size; j++ )
		{
			if ( zone istouching( trigs[j] ) )
			{
				if ( isdefined( zone.trig ) )
				{
					AssertMsg( "Zone at " + zone.origin + " is touching more than one \"zonetrigger\" trigger" );
					errored = true;
					break;
				}
				zone.trig = trigs[j];
				break;
			}
		}
		
		if ( !isdefined( zone.trig ) )
		{
			if ( !errored )
			{
				AssertMsg( "Zone at " + zone.origin + " is not inside any \"zonetrigger\" trigger" );
				continue;
			}
		}
		
		assert( !errored );
		
		zone.trigorigin = zone.trig.origin;
		
		visuals = [];
		visuals[0] = zone;
		
		// the effects that define the trigger boarder
		if ( isdefined( zone.target ) )
		{
			otherVisuals = getEntArray( zone.target, "targetname" );
			for ( j = 0; j < otherVisuals.size; j++ )
			{
				visuals[visuals.size] = otherVisuals[j];
			}
		}
		
		zone.gameObject = maps\mp\gametypes\_gameobjects::createUseObject( "neutral", zone.trig, visuals, (0,0,0) );
		zone.gameObject maps\mp\gametypes\_gameobjects::disableObject();
		setNeutralIcons( zone );
		zone.gameobject maps\mp\gametypes\_gameobjects::set2DIcon( "mlg", undefined );
		zone.gameobject maps\mp\gametypes\_gameobjects::set3DIcon( "mlg", undefined );
		zone.gameObject maps\mp\gametypes\_gameobjects::setModelVisibility( false );
		zone.trig.useObj = zone.gameObject;
		zone.gameObject.objPoints["allies"] SetHideTrigger( zone.trig );
		zone.gameObject.objPoints["axis"] SetHideTrigger( zone.trig );
		zone.gameObject.objPoints["mlg"] SetHideTrigger( zone.trig );
		zone setUpNearbySpawns();
	}
	
	return zones;
}


setUpNearbySpawns()
{
	spawns = maps\mp\gametypes\_spawnlogic::getSpawnpointArray( "mp_hp_spawn" );
	
	for ( i = 0; i < spawns.size; i++ )
	{
		spawns[i].distsq = distanceSquared( spawns[i].origin, self.origin );
	}
	
	// sort by distsq
	for ( i = 1; i < spawns.size; i++ )
	{
		thespawn = spawns[i];
		for ( j = i - 1; j >= 0 && thespawn.distsq < spawns[j].distsq; j-- )
			spawns[j + 1] = spawns[j];
		spawns[j + 1] = thespawn;
	}
	
	first = [];
	second = [];
	third = [];
	outer = [];
	
	thirdSize = spawns.size / 3;
	for ( i = 0; i <= thirdSize; i++ )
	{
		first[ first.size ] = spawns[i];
	}
	for ( ; i < spawns.size; i++ )
	{
		outer[ outer.size ] = spawns[i];
		if ( i <= (thirdSize*2) )
			second[ second.size ] = spawns[i];
		else			
			third[ third.size ] = spawns[i];
	}
	
	self.gameObject.nearSpawns = first;
	self.gameObject.midSpawns = second;
	self.gameObject.farSpawns = third;
	self.gameObject.outerSpawns = outer;
}


GetFirstZone()
{
	zone = level.zones[ 0 ];

	level.prevZoneIndex = 0;

	return zone;
}


GetNextZone()
{
	nextZoneIndex = (level.prevZoneIndex + 1) % level.zones.size;
	zone = level.zones[ nextZoneIndex ];
	level.prevZoneIndex = nextZoneIndex;
	
	return zone;
}

ShuffleZones()
{
	level.zoneSpawnQueue = array_randomize( level.zones );
	
	// dont allow the current location to be picked next
	if( level.zone == level.zoneSpawnQueue[0] )
		level.zoneSpawnQueue = swap( level.zoneSpawnQueue, 0, RandomIntRange(1, level.zoneSpawnQueue.size) );
}


// shuffled picking
GetNextZoneFromQueue()
{
	if( level.zoneSpawnQueue.size == 0 )
		ShuffleZones();

	next_zone = level.zoneSpawnQueue[0];
	
	tmp = [];
	for( i = 1; i < level.zoneSpawnQueue.size; i++)
	{
		tmp[i-1] = level.zoneSpawnQueue[i];
	}
	
	level.zoneSpawnQueue = tmp;
	
	return next_zone;
}



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(level.zone) )
		return;
	
	ownerTeam = level.zone.gameObject.ownerTeam ;
		
	if( !IsDefined(ownerTeam) || (ownerTeam == "neutral") )
		return;
	
	if( IsDefined(sWeapon) && isKillstreakWeapon(sWeapon) )
		return;
	
	scoreEventProcessed = false;
	
	// attacker is touching the hard point
	if( !level.zone.gameobject.isContested && (attacker IsTouching(level.zone.trig)) )
	{
		scoreEventProcessed = true;
		attacker thread maps\mp\_events::killWhileCapture( self, killId );
	}
	
	// victim is touching the hard point
	if( self IsTouching(level.zone.trig) )
	{
		attacker.lastKilltime = getTime();
		
		if(scoreEventProcessed)
			return;
		
		if( self.team == ownerTeam )
		{
			attacker thread maps\mp\_events::assaultObjectiveEvent( self, killId );
		}
		else
		{
			attacker maps\mp\gametypes\_missions::processChallenge( "ch_hp_zoneDefense" );
			attacker thread maps\mp\_events::defendObjectiveEvent( self, killId );
			attacker setExtraScore1( attacker.pers["defends"] );
		}		
	}	
}

			
onEndGame( winningTeam )
{
	for ( i = 0; i < level.zones.size; i++ )
	{
		level.zones[i].gameobject maps\mp\gametypes\_gameobjects::allowUse( "none" );
	}
}

updateCapsPerMinute(lastOwnerTeam)
{
	if( !isDefined(self.capsPerMinute) )
	{
		self.numCaps = 0;
		self.capsPerMinute = 0;
	}
	
	// do not including neutral flags as part of the boosting prevention
	if ( !isdefined ( lastOwnerTeam ) || lastOwnerTeam == "neutral" )
		return;
		
	self.numCaps++;
	
	minutesPassed = getTimePassed() / ( 60 * 1000 );
	
	// players use the actual time played
	if ( IsPlayer(self) && IsDefined(self.timePlayed["total"]) )
		minutesPassed = self.timePlayed["total"] / 60;
		
	self.capsPerMinute = self.numCaps / minutesPassed;
	
	if ( self.capsPerMinute > self.numCaps )
		self.capsPerMinute = self.numCaps;
}

isScoreBoosting( player )
{
	if ( !level.rankedMatch )
		return false;
		
	if ( player.capsPerMinute > level.playerCaptureLPM )
		return true;

	return false;
}
