#using scripts\codescripts\struct;

#using scripts\shared\gameobjects_shared;
#using scripts\shared\math_shared;
#using scripts\shared\popups_shared;
#using scripts\shared\rank_shared;
#using scripts\shared\sound_shared;
#using scripts\shared\scoreevents_shared;
#using scripts\shared\util_shared;

    	   	                                                                                                                                         	                                	                                                                                                                                                                                                                                                                                                                                                                    	        	     	              	    	   	               	                                              	                                                           	                               	                     	                                                                                                           	                                                                 	                                                              	                                                                                                                                             	                            	                                     	                                       	                                                                                                              	   	                  	       	                                                    	                   	                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            	                                                                                                 	                                    	                                                                                        	           	                        	                                            	                                             	                                                   	                                                             	                                                         	                                                                    	                                                                                                                                                                                                                                	                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  	    	         	                                                              	                                                          	                                   	                                   	                                                    	                                       	                                           	     	                                                                                                                                                                                                                                                      	                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         	                                                               	        	                                                                                                                              	                                                                                                                     	                               	                                                      	                          

#using scripts\mp\gametypes\_globallogic;
#using scripts\mp\gametypes\_globallogic_audio;
#using scripts\mp\gametypes\_globallogic_score;
#using scripts\mp\gametypes\_spawning;
#using scripts\mp\gametypes\_spawnlogic;

#using scripts\mp\_util;
#using scripts\mp\teams\_teams;

#using scripts\mp\killstreaks\_killstreaks;


/*
	Team Defender
	Objective: 	Score points for your team by eliminating players on the opposing team.
				Team with flag scores double kill points.
				First corpse spawns the flag.
	Map ends:	When one team reaches the score limit, or time limit is reached
	Respawning:	No wait / Near teammates

	Level requirementss
	------------------
		Spawnpoints:
			classname		mp_tdm_spawn
			All players spawn from these. The spawnpoint chosen is dependent on the current locations of 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.
*/


#precache( "string", "OBJECTIVES_TDEF" );
#precache( "string", "OBJECTIVES_TDEF_SCORE" );
#precache( "string", "OBJECTIVES_TDEF_ATTACKER_HINT" );
#precache( "string", "MP_NEUTRAL_FLAG_CAPTURED_BY" );
#precache( "string", "MP_NEUTRAL_FLAG_DROPPED_BY" );
#precache( "string", "MP_GRABBING_FLAG" );
#precache( "string", "OBJECTIVES_TDEF_ATTACKER_HINT" );
#precache( "string", "OBJECTIVES_TDEF_DEFENDER_HINT" );		
#precache( "string", "OBJECTIVES_TDEF" );	
#precache( "string", "OBJECTIVES_TDEF_SCORE" );	
#precache( "string", "OBJECTIVES_TDEF_HINT" );		
#precache( "string", "MP_FIRST_BLOOD" );		

function main()
{	
	globallogic::init();

	util::registerRoundSwitch( 0, 9 );
	util::registerTimeLimit( 0, 1440 );
	util::registerScoreLimit( 0, 10000 );
	util::registerRoundLimit( 0, 10 );
	util::registerNumLives( 0, 100 );
	
	level.matchRules_enemyFlagRadar = true;		
	level.matchRules_damageMultiplier = 0;
	level.matchRules_vampirism = 0;	
	
	setSpecialLoadouts();

	level.teamBased = true;
	level.initGametypeAwards =&initGametypeAwards;
	level.onPrecacheGameType =&onPrecacheGameType;
	level.onStartGameType =&onStartGameType;
	level.onPlayerKilled =&onPlayerKilled;
	level.onSpawnPlayer =&onSpawnPlayer;
	level.onRoundEndGame =&onRoundEndGame;
	level.onRoundSwitch =&onRoundSwitch;

	gameobjects::register_allowed_gameobject( level.gameType );
	gameobjects::register_allowed_gameobject( "tdm" );

	game["dialog"]["gametype"] = "team_def";
	if ( getDvarInt( "g_hardcore" ) )
		game["dialog"]["gametype"] = "hc_" + game["dialog"]["gametype"];
	
	game["dialog"]["got_flag"] = "ctf_wetake";
	game["dialog"]["enemy_got_flag"] = "ctf_theytake";
	game["dialog"]["dropped_flag"] = "ctf_wedrop";
	game["dialog"]["enemy_dropped_flag"] = "ctf_theydrop";

	game["strings"]["overtime_hint"] = &"MP_FIRST_BLOOD";
}


function onPrecacheGameType()
{

}


function onStartGameType()
{
	setClientNameMode("auto_change");

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

	if ( game["switchedsides"] )
	{
		oldAttackers = game["attackers"];
		oldDefenders = game["defenders"];
		game["attackers"] = oldDefenders;
		game["defenders"] = oldAttackers;
	}

	util::setObjectiveText( "allies", &"OBJECTIVES_TDEF" );
	util::setObjectiveText( "axis", &"OBJECTIVES_TDEF" );
	
	if ( level.splitscreen )
	{
		util::setObjectiveScoreText( "allies", &"OBJECTIVES_TDEF" );
		util::setObjectiveScoreText( "axis", &"OBJECTIVES_TDEF" );
	}
	else
	{
		util::setObjectiveScoreText( "allies", &"OBJECTIVES_TDEF_SCORE" );
		util::setObjectiveScoreText( "axis", &"OBJECTIVES_TDEF_SCORE" );
	}
	util::setObjectiveHintText( "allies", &"OBJECTIVES_TDEF_ATTACKER_HINT" );
	util::setObjectiveHintText( "axis", &"OBJECTIVES_TDEF_ATTACKER_HINT" );
				
	level.spawnMins = ( 0, 0, 0 );
	level.spawnMaxs = ( 0, 0, 0 );	
	spawnlogic::place_spawn_points( "mp_tdm_spawn_allies_start" );
	spawnlogic::place_spawn_points( "mp_tdm_spawn_axis_start" );
	spawnlogic::add_spawn_points( "allies", "mp_tdm_spawn" );
	spawnlogic::add_spawn_points( "axis", "mp_tdm_spawn" );
	spawning::updateAllSpawnPoints();
	
	level.mapCenter = math::find_box_center( level.spawnMins, level.spawnMaxs );
	setMapCenter( level.mapCenter );
	
	spawnpoint = spawnlogic::get_random_intermission_point();
	setDemoIntermissionPoint( spawnpoint.origin, spawnpoint.angles );
		
	if ( !util::isOneRound() )
	{
		level.displayRoundEndText = true;
		if( level.scoreRoundWinBased )
		{
			globallogic_score::resetTeamScores();
		}
	}

	tdef();	
}

function tdef()
{	
//	level.icon2D["allies"] = teams::get_flag_icon( "allies" );
//	level.icon2D["axis"] = teams::get_flag_icon( "axis" );
	
	level.carryFlag["allies"] = teams::get_flag_carry_model( "allies" );
	level.carryFlag["axis"] = teams::get_flag_carry_model( "axis" );
	level.carryFlag["neutral"] = teams::get_flag_model( "neutral" );

	level.gameFlag = undefined;	
}


function onPlayerKilled( eInflictor, attacker, iDamage, sMeansOfDeath, weapon, vDir, sHitLoc, psOffsetTime, deathAnimDuration )
{
	if ( !isPlayer( attacker ) || attacker.team == self.team )
		return;
		
	victim = self;
	
	score = rank::getScoreInfoValue( "kill" );
	assert( isdefined( score ) );
	
	//	we got the flag	- give bonus
	if ( isdefined( level.gameFlag ) && level.gameFlag gameobjects::get_owner_team() == attacker.team )
	{
		//	I'm the carrier
		if ( isdefined( attacker.carryFlag ) )
		{
			attacker AddPlayerStat( "KILLSASFLAGCARRIER", 1 );
		}
		//	someone else is
		else
		{
			//	give flag carrier a bonus for kills achieved by team
			//  scoreevents::processScoreEvent( "team_assist", level.gameFlag.carrier );
		}
		
		//scoreevents::processScoreEvent( "kill_bonus", attacker );	
		
		score *= 2;
	}
	//	no flag yet		- create it
	else if ( !isdefined( level.gameFlag ) && canCreateFlagAtVictimOrigin( victim ) )
	{
		level.gameFlag = createFlag( victim );
		
		score += rank::getScoreInfoValue( "MEDAL_FIRST_BLOOD" );								
	}
	//	killed carrier	- give bonus
	else if ( isdefined( victim.carryFlag ) )
	{
		killCarrierBonus = rank::getScoreInfoValue( "kill_carrier" );
		
		level thread popups::DisplayTeamMessageToAll( &"MP_KILLED_FLAG_CARRIER", attacker );
		scoreevents::processScoreEvent( "kill_flag_carrier", attacker );
		attacker RecordGameEvent("kill_carrier");	
		attacker AddPlayerStat( "FLAGCARRIERKILLS", 1 );
		attacker notify( "objective", "kill_carrier" );
		
		score += killCarrierBonus;			
	}

	if( !isdefined( killstreaks::get_killstreak_for_weapon( weapon ) ) || ( isdefined( level.killstreaksGiveGameScore ) && level.killstreaksGiveGameScore ) )				
		attacker globallogic_score::giveTeamScoreForObjective( attacker.team, score );
	
	otherTeam = util::getOtherTeam( attacker.team );
	if ( game["state"] == "postgame" && game["teamScores"][attacker.team] > game["teamScores"][otherTeam] )
		attacker.finalKill = true;
}


function onDrop( player )
{
	// get the time when they dropped it
	if( isdefined( player ) && isdefined( player.tdef_flagTime ) )
	{
		flagTime = int( GetTime() - player.tdef_flagTime );
		player AddPlayerStat( "HOLDINGTEAMDEFENDERFLAG", flagTime );
		
		if ( ( flagTime/100 ) / 60 < 1 )
			flagMinutes = 0;
		else
			flagMinutes = int( ( flagTime/100 ) / 60 );
		
		player AddPlayerStatWithGameType( "DESTRUCTIONS", flagMinutes );
		
		player.tdef_flagTime = undefined;
		player notify( "dropped_flag" );
	}

	team = self gameobjects::get_owner_team();
	otherTeam = util::getOtherTeam( team );

	self.currentCarrier = undefined;

	self gameobjects::set_owner_team( "neutral" );
	self gameobjects::allow_carry( "any" );
	self gameobjects::set_visible_team( "any" );
	self gameobjects::set_2d_icon( "friendly", level.iconCaptureFlag2D );
	self gameobjects::set_3d_icon( "friendly", level.iconCaptureFlag3D );
	self gameobjects::set_2d_icon( "enemy", level.iconCaptureFlag2D );
	self gameobjects::set_3d_icon( "enemy", level.iconCaptureFlag3D );

	if ( isdefined( player ) )
	{
 		if ( isdefined( player.carryFlag ) )
			player detachFlag();
		
		util::printAndSoundOnEveryone( team, undefined, &"MP_NEUTRAL_FLAG_DROPPED_BY", &"MP_NEUTRAL_FLAG_DROPPED_BY", "mp_war_objective_lost", "mp_war_objective_lost", player );	
	}
	else
	{
		sound::play_on_players( "mp_war_objective_lost", team );
		sound::play_on_players( "mp_war_objective_lost", otherTeam );
	}

	globallogic_audio::leader_dialog( "dropped_flag", team);
	globallogic_audio::leader_dialog( "enemy_dropped_flag", otherTeam );
}


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

	// get the time when they picked it up
	player.tdef_flagTime = GetTime();
	player thread watchForEndGame();

	score = rank::getScoreInfoValue( "capture" );
	assert( isdefined( score ) );	

	team = player.team;
	otherTeam = util::getOtherTeam( team );
			
	//	flag carrier class?  (do before attaching flag)
	if ( isdefined( level.tdef_loadouts ) && isdefined( level.tdef_loadouts[team] ) )
		player thread applyFlagCarrierClass(); // attaches flag
	else
		player attachFlag();
		
	self.currentCarrier = player;	
	player.carryIcon setShader( level.icon2D[team], player.carryIcon.width, player.carryIcon.height );	
	
	self gameobjects::set_owner_team( team );
	self gameobjects::set_visible_team( "any" );
	self gameobjects::set_2d_icon( "friendly", level.iconEscort2D );
	self gameobjects::set_3d_icon( "friendly", level.iconEscort2D );
	self gameobjects::set_2d_icon( "enemy", level.iconKill3D );
	self gameobjects::set_3d_icon( "enemy", level.iconKill3D );
	
	globallogic_audio::leader_dialog( "got_flag", team );
	globallogic_audio::leader_dialog( "enemy_got_flag", otherTeam );	

	level thread popups::DisplayTeamMessageToAll( &"MP_CAPTURED_THE_FLAG", player );
	scoreevents::processScoreEvent( "flag_capture", player );
	player RecordGameEvent("pickup");
	player AddPlayerStatWithGameType( "CAPTURES", 1 );
	player notify( "objective", "captured" );

	util::printAndSoundOnEveryone( team, undefined, &"MP_NEUTRAL_FLAG_CAPTURED_BY", &"MP_NEUTRAL_FLAG_CAPTURED_BY", "mp_obj_captured", "mp_enemy_obj_captured", player );
	
	//	give a capture bonus to the capturing team if the flag is changing hands
	if ( self.currentTeam == otherTeam )
		player globallogic_score::giveTeamScoreForObjective( team, score );
	self.currentTeam = team;
	
	//	activate portable radar on flag for the opposing team
	if ( level.matchRules_enemyFlagRadar )
		self thread flagAttachRadar( otherTeam );	
}


function applyFlagCarrierClass()
{
	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(.05);};
	}	
	
	//	set the gamemodeloadout for loadout::giveLoadout() to use
	self.pers["gamemodeLoadout"] = level.tdef_loadouts[self.team];	
	
	//	set faux TI to respawn in place
	spawnPoint = spawn( "script_model", self.origin );
	spawnPoint.angles = self.angles;
	spawnPoint.playerSpawnPos = self.origin;
	spawnPoint.notTI = true;		
	self.setSpawnPoint = spawnPoint;
			
	//	globallogic_spawn::spawnPlayer() calls loadout::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 loadout::giveLoadout()
	self.gamemode_chosenClass = self.curClass;				
	self.pers["class"] = "gamemode";
	self.pers["lastClass"] = "gamemode";
	self.curClass = "gamemode";
	self.lastClass = "gamemode";		
		
	//	attach flag after faux spawn (model may change for sniper or juggernaut loadout)
	self thread waitAttachFlag();
}


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


function watchForEndGame()
{
	self endon( "dropped_flag" );
	self endon( "disconnect" );

	level waittill( "game_ended" );

	if( isdefined( self ) )
	{
		if( isdefined( self.tdef_flagTime ) )
		{
			flagTime = int( GetTime() - self.tdef_flagTime );
			self AddPlayerStat( "HOLDINGTEAMDEFENDERFLAG", flagTime );
			
			if ( ( flagTime/100 ) / 60 < 1 )
				flagMinutes = 0;
			else
				flagMinutes = int( (flagTime/100)/60 );

			self AddPlayerStatWithGameType( "DESTRUCTIONS", flagMinutes );
		}
	}
}


function canCreateFlagAtVictimOrigin( victim )
{
	mineTriggers = getEntArray( "minefield", "targetname" );
	hurtTriggers = getEntArray( "trigger_hurt", "classname" );
	radTriggers = getEntArray( "radiation", "targetname" );
		
	for ( index = 0; index < radTriggers.size; index++ )
	{
		if ( victim isTouching( radTriggers[index] ) )
			return false;
	}

	for ( index = 0; index < mineTriggers.size; index++ )
	{
		if ( victim isTouching( mineTriggers[index] ) )
			return false;
	}

	for ( index = 0; index < hurtTriggers.size; index++ )
	{
		if ( victim isTouching( hurtTriggers[index] ) )
			return false;
	}	
	
	return true;
}


function createFlag( victim )
{	
	//	flag
	visuals[0] = spawn( "script_model", victim.origin );
	visuals[0] setModel( level.carryFlag["neutral"] );
	
	//	trigger	
	trigger = spawn( "trigger_radius", victim.origin, 0, 96, 72);	
	
	gameFlag = gameobjects::create_carry_object( "neutral", trigger, visuals, (0,0,85) );
	gameFlag gameobjects::allow_carry( "any" );
	
	gameFlag gameobjects::set_visible_team( "any" );
	gameFlag gameobjects::set_2d_icon( "enemy", level.iconCaptureFlag2D );
	gameFlag gameobjects::set_3d_icon( "enemy", level.iconCaptureFlag3D );
	gameFlag gameobjects::set_2d_icon( "friendly", level.iconCaptureFlag2D );
	gameFlag gameobjects::set_3d_icon( "friendly", level.iconCaptureFlag3D );

	gameFlag gameobjects::set_carry_icon( level.icon2D["axis"] ); //temp, manually changed after picked up
	
	gameFlag.allowWeapons = true;
	gameFlag.onPickup =&onPickup;
	gameFlag.onPickupFailed =&onPickup;
	gameFlag.onDrop =&onDrop;
	
	gameFlag.oldRadius = 96;	
	gameFlag.currentTeam = "none";
	gameFlag.requiresLOS = true;
	
	//	set it as flag trigger when on ground
	level.favorCloseSpawnEnt = gameFlag.trigger;
	level.favorCloseSpawnScalar = 3;	
	
	//	for this mode, the flag's home position is wherever its last safe position was, not where it was initially created
	gameFlag thread updateBasePosition();
	
	return gameFlag;
}


function updateBasePosition()
{
	level endon( "game_ended" );
	
	while( true )
	{
		if ( isdefined( self.safeOrigin ) )
		{
			self.baseOrigin = self.safeOrigin;
			self.trigger.baseOrigin = self.safeOrigin;
			self.visuals[0].baseOrigin = self.safeOrigin;
		}			
		{wait(.05);};
	}
}


function attachFlag()
{
	self attach( level.carryFlag[self.team], "J_spine4", true );
	self.carryFlag = level.carryFlag[self.team];
	
	//	set it as flag carrier when carried
	level.favorCloseSpawnEnt = self;	
}


function detachFlag()
{
	self detach( self.carryFlag, "J_spine4" );
	self.carryFlag = undefined;		
	
	//	set it as flag trigger when on ground
	level.favorCloseSpawnEnt = level.gameFlag.trigger;	
}


function flagAttachRadar( team )
{
	level endon("game_ended");
	self  endon( "dropped" );	
}


function getFlagRadarOwner( team )
{
	level endon("game_ended");
	self  endon( "dropped" );
	
	while ( true )
	{	
		foreach( player in level.players )
		{
			if ( isAlive( player ) && player.team == team )
				return player;
		}
		{wait(.05);};
	}
}


function flagRadarMover()
{
	level endon("game_ended");
	self  endon( "dropped" );
	self.portable_radar endon( "death" );
	
	for( ;; )
	{
		self.portable_radar MoveTo( self.currentCarrier.origin, .05 );
		{wait(.05);};
	}
}


function flagWatchRadarOwnerLost()
{
	level endon("game_ended");
	self  endon( "dropped" );
	
	radarTeam = self.portable_radar.team;
	
	self.portable_radar.owner util::waittill_any( "disconnect", "joined_team", "joined_spectators" );
	
	//	make a new one
	flagAttachRadar( radarTeam );
}

function onRoundEndGame( roundWinner )
{
	winner = globallogic::determineTeamWinnerByGameStat( "roundswon" );
	
	return winner;
}

function onSpawnPlayer(predictedSpawn)
{
	self.usingObj = undefined;
	
	if ( level.useStartSpawns && !level.inGracePeriod )
	{
		level.useStartSpawns = false;
	}
	
	spawning::onSpawnPlayer(predictedSpawn);
}

function onRoundSwitch()
{
	game["switchedsides"] = !game["switchedsides"];
}

function initGametypeAwards()
{
}


function setSpecialLoadouts()
{
}