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

/* -=-=-=-=-=-=-=-=-=-=

SP & CO-OP XP/Rank system

-=-=-=-=-=-=-=-=-=-=-=- */

init()
{
	maps\_hud::init();

	// &&1 was promoted to &&2 &&3!
	precacheString( &"RANK_PLAYER_WAS_PROMOTED_N" );
	// &&1 was promoted to &&2!
	precacheString( &"RANK_PLAYER_WAS_PROMOTED" );
	// You've been promoted!
	precacheString( &"RANK_PROMOTED" );
	// I
	precacheString( &"RANK_ROMANI" );
	// II
	precacheString( &"RANK_ROMANII" );
	// +
	precachestring( &"SCRIPT_PLUS" );
	precacheshader( "line_horizontal" );
	precacheshader( "line_vertical" );
	precacheshader( "gradient_fadein" );
	precachemenu( "coop_eog_summary" );
	precachemenu( "coop_eog_summary2" );
	precachemenu( "sp_eog_summary" );

	level._maxRank = int( tableLookup( "sp/rankTable.csv", 0, "maxrank", 1 ) );

/*	for ( rId = 0; rId <= level.maxRank; rId++ )
		precacheShader( tableLookup( "sp/rankIconTable.csv", 0, rId, 1 ) );*/

	rankId = 0;
	rankName = tableLookup( "sp/ranktable.csv", 0, rankId, 1 );
	assert( isDefined( rankName ) && rankName != "" );

	while ( isDefined( rankName ) && rankName != "" )
	{
		level._rankTable[ rankId ][ 1 ] = tableLookup( "sp/ranktable.csv", 0, rankId, 1 );
		level._rankTable[ rankId ][ 2 ] = tableLookup( "sp/ranktable.csv", 0, rankId, 2 );
		level._rankTable[ rankId ][ 3 ] = tableLookup( "sp/ranktable.csv", 0, rankId, 3 );
		level._rankTable[ rankId ][ 7 ] = tableLookup( "sp/ranktable.csv", 0, rankId, 7 );

		precacheString( tableLookupIString( "sp/ranktable.csv", 0, rankId, 10 ) );

		rankId++ ;
		rankName = tableLookup( "sp/ranktable.csv", 0, rankId, 1 );
	}

	thread xp_setup();
	foreach ( player in level._players )
		player thread xp_player_init();
}

xp_player_init()
{
	if ( !isDefined( self.summary ) )
	{
		self.summary[ "summary" ] = [];
		self.summary[ "summary" ][ "xp" ] = 0;
		self.summary[ "summary" ][ "score" ] = 0;

		self.summary[ "rankxp" ] = 0;
		self.summary[ "rank" ] = 0;
	}

	setdvar( "player_1_xp", "0" );
	setdvar( "player_2_xp", "0" );

	setdvar( "player_1_rank", "0" );
	setdvar( "player_2_rank", "0" );

	self.rankUpdateTotal = 0;
	self.hud_rankscroreupdate = newclientHudElem( self );
	self.hud_rankscroreupdate.horzAlign = "center";
	self.hud_rankscroreupdate.vertAlign = "middle";
	self.hud_rankscroreupdate.alignX = "center";
	self.hud_rankscroreupdate.alignY = "middle";
	self.hud_rankscroreupdate.x = 0;
	self.hud_rankscroreupdate.y = -60;
	self.hud_rankscroreupdate.font = "default";
	self.hud_rankscroreupdate.fontscale = 2;
	self.hud_rankscroreupdate.archived = false;
	self.hud_rankscroreupdate.color = ( 1, 1, 0.65 );
	self.hud_rankscroreupdate fontPulseInit();

	// XP BAR
	self.hud_xpbar = xp_bar_client_elem( self );
	self xpbar_update();
}

xp_bar_client_elem( client )
{
	hudelem = newClientHudElem( client );
	hudelem.x = ( hud_width_format() / 2 ) * ( -1 );
	hudelem.y = 0;
	hudelem.sort = 5;
	hudelem.horzAlign = "center_adjustable";
	hudelem.vertAlign = "bottom_adjustable";
	hudelem.alignX = "left";
	hudelem.alignY = "bottom";
	hudelem setshader( "gradient_fadein", get_xpbarwidth(), 4 );// gradient_fadein
	hudelem.color = ( 1, 0.8, 0.4 );
	hudelem.alpha = 0.65;
	hudelem.foreground = true;
	return hudelem;
}

hud_width_format()
{
	// screen formatting
	if ( getDvar( "hiDef" ) == "1" || getDvar( "wideScreen" ) == "1" )
	{
		if ( isSplitscreen() )
			return 966;// customized to match hud's xpbar background
		else
			return 720;
	}
	else
	{
		if ( isSplitscreen() )
			return 726;// customized to match hud's xpbar background
		else
			return 540;
	}
}

xpbar_update()
{
	if ( !get_xpbarwidth() )
		self.hud_xpbar.alpha = 0;
	else
		self.hud_xpbar.alpha = 0.65;

	self.hud_xpbar setshader( "gradient_fadein", get_xpbarwidth(), 4 );
}

get_xpbarwidth()
{
	if ( self == level._player )
		player_num = "1";
	else
		player_num = "2";

	rank_range = int( tableLookup( "sp/rankTable.csv", 0, getdvar( "player_" + player_num + "_rank" ), 3 ) );
	rank_xp = int( getdvar( "player_" + player_num + "_xp" ) ) - int( tableLookup( "sp/rankTable.csv", 0, getdvar( "player_" + player_num + "_rank" ), 2 ) );

	fullwidth = hud_width_format();
	newwidth = int( fullwidth * ( rank_xp / rank_range ) );

	return newwidth;
}

xp_setup()
{
	level._xpScale = 1;
	if ( level._console )
	{
		level._xpScale = 1;// getDvarInt( "scr_xpscale" );
	}

	registerScoreInfo( "kill", 10 );
	registerScoreInfo( "headshot", 10 );
	registerScoreInfo( "assist", 2 );
	registerScoreInfo( "suicide", 0 );
	registerScoreInfo( "teamkill", 0 );
}

giveXP_think()
{
	self waittill( "death", attacker, type, weapon );
	// split for recursive call
	self giveXP_helper( attacker );
}

giveXP_helper( attacker )
{
	// if AI removed by script/game, no xp to player
	if ( !isdefined( attacker ) )
		return;

	// if player is last to kill, give player kill points	
	if ( isPlayer( attacker ) )
	{
		attacker thread giveXp( "kill" );
		return;
	}

	// no xp if enemy was finished off by other enemies
	if ( isAI( attacker ) && attacker isBadGuy() )
		return;

	// if enemy shot by player was killed by destructibles
	if ( is_special_targetname_attacker( attacker ) )
	{
		if ( isdefined( attacker.attacker ) )
			self thread giveXP_helper( attacker.attacker );
		return;
	}

	// if enemy shot by player was killed by natural causes, no xp
	if ( !isPlayer( attacker ) && !isAI( attacker ) )
		return;

	// if enemy shot by player was killed by friendly, give assist
	if ( isdefined( self.attacker_list ) && self.attacker_list.size > 0 )
	{
		for ( i = 0; i < self.attacker_list.size; i++ )
		{
			// if attacker is player and not the last to kill, give player assist points
			if ( isPlayer( self.attacker_list[ i ] ) && self.attacker_list[ i ] != attacker )
				self.attacker_list[ i ] thread giveXp( "assist" );
		}
	}
}

is_special_targetname_attacker( attacker )
{
	assert( isdefined( attacker ) );

	if ( !isdefined( attacker.targetname ) )
		return false;

	 if ( attacker.targetname == "destructible" )
	 	return true;

	  if ( string_starts_with( attacker.targetname, "sentry_" ) )
	  	return true;

	  return false;
}

AI_xp_init()
{
	self thread giveXP_think();
	self.attacker_list = [];
	self.last_attacked = 0;
	self add_damage_function( ::xp_took_damage );
}

xp_took_damage( damage, attacker, direction_vec, point, type, modelName, tagName )
{
	if ( !isdefined( attacker ) )
		return;

	currentTime = gettime();
	timeElapsed = currentTime - self.last_attacked;
	if ( timeElapsed <= 10 * 1000 )
	{
		self.attacker_list[ self.attacker_list.size ] = attacker;
		self.last_attacked = gettime();
		return;
	}

	self.attacker_list = [];
	self.attacker_list[ 0 ] = attacker;
	self.last_attacked = gettime();
}

// used by _utility.gsc, edit with cre
updatePlayerScore( type, value )
{
	self notify( "update_xp" );
	self endon( "update_xp" );

	if ( getdvar( "xp_enable", "0" ) != "1" )
		return;

	//assertex ( isDefined( level.scoreInfo ), "Trying to give player XP when XP feature is not enabled, set dvar xp_enable to 1." );
	//assertex ( isDefined( type ), "First string parameter <type> is undefined or missing, you must label this XP reward." );

	if ( !isDefined( value ) )
	{
		if ( isDefined( level._scoreInfo[ type ] ) )
			value = getScoreInfoValue( type );
		else
			value = getScoreInfoValue( "kill" );
	}

	value = int( value * level._xpScale );

	if ( type == "kill" )
		self.hud_rankscroreupdate.color = ( 1, 1, 0.5 );

	if ( type == "assist" )
	{
		// assist points can never add up over kill points
		if ( value > getScoreInfoValue( "kill" ) )
			value = getScoreInfoValue( "kill" );

		self.hud_rankscroreupdate.color = ( 1, 1, 0.5 );
	}

	self.rankUpdateTotal += value;

	// +
	self.hud_rankscroreupdate.label = &"SCRIPT_PLUS";

	self.hud_rankscroreupdate setValue( self.rankUpdateTotal );
	self.hud_rankscroreupdate.alpha = 0.65;
	self.hud_rankscroreupdate thread fontPulse( self );

	wait 1;
	self.hud_rankscroreupdate fadeOverTime( 0.75 );
	self.hud_rankscroreupdate.alpha = 0;

	// set xp dvar for hud menu to print
	self.summary[ "summary" ][ "score" ] += self.rankUpdateTotal;
	self.summary[ "summary" ][ "xp" ] += self.rankUpdateTotal;
	self.summary[ "rankxp" ] += self.rankUpdateTotal;

	if ( self == level._player )
	{
		setdvar( "player_1_xp", self.summary[ "summary" ][ "xp" ] );
		setdvar( "player_1_rank", self.summary[ "rank" ] );
	}
	else
	{
		setdvar( "player_2_xp", self.summary[ "summary" ][ "xp" ] );
		setdvar( "player_2_rank", self.summary[ "rank" ] );
	}

	self xpbar_update();

	self.rankUpdateTotal = 0;

	if ( self updateRank() )
		self thread updateRankAnnounceHUD();
}

fontPulseInit()
{
	self.baseFontScale = self.fontScale;
	self.maxFontScale = self.fontScale * 2;
	//self.moveUpSpeed = 1.25;
	self.inFrames = 3;
	self.outFrames = 5;
}


fontPulse( player )
{
	self notify( "fontPulse" );
	self endon( "fontPulse" );

	scaleRange = self.maxFontScale - self.baseFontScale;
	//self thread fontMoveup( -60 );

	while ( self.fontScale < self.maxFontScale )
	{
		self.fontScale = min( self.maxFontScale, self.fontScale + ( scaleRange / self.inFrames ) );
		wait 0.05;
	}

	while ( self.fontScale > self.baseFontScale )
	{
		self.fontScale = max( self.baseFontScale, self.fontScale - ( scaleRange / self.outFrames ) );
		wait 0.05;
	}
}

/*
fontMoveup( start )
{
	self endon( "fontPulse" );
	self.y = start;

	while ( abs( start ) - abs( self.y ) < 60 )
	{
		self.y = self.y - self.moveUpSpeed;
		wait 0.05;
	}
}*/

updateRank()
{
	newRankId = self getRank();
	if ( newRankId == self.summary[ "rank" ] )
		return false;

	oldRank = self.summary[ "rank" ];
	rankId = self.summary[ "rank" ];

	self.summary[ "rank" ] = newRankId;

	if ( self == level._player )
		setdvar( "player_1_rank", self.summary[ "rank" ] );
	else
		setdvar( "player_2_rank", self.summary[ "rank" ] );

	self xpbar_update();

	while ( rankId <= newRankId )
	{
		self.setPromotion = true;
		rankId++ ;
	}
	return true;
}

updateRankAnnounceHUD()
{
	self endon( "disconnect" );

	self notify( "update_rank" );
	self endon( "update_rank" );

	self notify( "reset_outcome" );
	newRankName = self getRankInfoFull( self.summary[ "rank" ] );

	notifyData = spawnStruct();

	// You've been promoted!
	notifyData.titleText = &"RANK_PROMOTED";
	notifyData.iconName = self getRankInfoIcon( self.summary[ "rank" ] );
	notifyData.sound = "sp_level_up";
	notifyData.duration = 4.0;

	rank_char = level._rankTable[ self.summary[ "rank" ] ][ 1 ];
	subRank = int( rank_char[ rank_char.size - 1 ] );

	if ( subRank == 2 )
	{
		notifyData.textLabel = newRankName;
		// I
		notifyData.notifyText = &"RANK_ROMANI";
		notifyData.textIsString = true;
	}
	else if ( subRank == 3 )
	{
		notifyData.textLabel = newRankName;
		// II
		notifyData.notifyText = &"RANK_ROMANII";
		notifyData.textIsString = true;
	}
	else
	{
		notifyData.notifyText = newRankName;
	}

	self thread notifyMessage( notifyData );

	if ( subRank > 1 )
		return;

}


notifyMessage( notifyData )
{
	self endon( "death" );
	self endon( "disconnect" );

	if ( !self.doingNotify )
	{
		self thread showNotifyMessage( notifyData );
		return;
	}

	self.notifyQueue[ self.notifyQueue.size ] = notifyData;
}


showNotifyMessage( notifyData )
{
	self endon( "disconnect" );

	self.doingNotify = true;

	waitRequireVisibility( 0 );

	if ( isDefined( notifyData.duration ) )
		duration = notifyData.duration;
	else
		duration = 4.0;

	self thread resetOnCancel();

	if ( isDefined( notifyData.sound ) )
		self playLocalSound( notifyData.sound );

	if ( isDefined( notifyData.glowColor ) )
		glowColor = notifyData.glowColor;
	else
		glowColor = ( 0.3, 0.6, 0.3 );

	anchorElem = self.notifyTitle;

	if ( isDefined( notifyData.titleText ) )
	{

			if ( isDefined( notifyData.titleLabel ) )
				self.notifyTitle.label = notifyData.titleLabel;
			else
				// string not found for 
				self.notifyTitle.label = &"";

			if ( isDefined( notifyData.titleLabel ) && !isDefined( notifyData.titleIsString ) )
				self.notifyTitle setValue( notifyData.titleText );
			else
				self.notifyTitle setText( notifyData.titleText );
			self.notifyTitle setPulseFX( 100, int( duration * 1000 ), 1000 );
			self.notifyTitle.glowColor = glowColor;
			self.notifyTitle.alpha = 1;
	}

	if ( isDefined( notifyData.notifyText ) )
	{
			if ( isDefined( notifyData.textLabel ) )
				self.notifyText.label = notifyData.textLabel;
			else
				// string not found for 
				self.notifyText.label = &"";

			if ( isDefined( notifyData.textLabel ) && !isDefined( notifyData.textIsString ) )
				self.notifyText setValue( notifyData.notifyText );
			else
				self.notifyText setText( notifyData.notifyText );
			self.notifyText setPulseFX( 100, int( duration * 1000 ), 1000 );
			self.notifyText.glowColor = glowColor;
			self.notifyText.alpha = 1;
			anchorElem = self.notifyText;
	}

	if ( isDefined( notifyData.notifyText2 ) )
	{
			self.notifyText2 setParent( anchorElem );

			if ( isDefined( notifyData.text2Label ) )
				self.notifyText2.label = notifyData.text2Label;
			else
				// string not found for 
				self.notifyText2.label = &"";

			self.notifyText2 setText( notifyData.notifyText2 );
			self.notifyText2 setPulseFX( 100, int( duration * 1000 ), 1000 );
			self.notifyText2.glowColor = glowColor;
			self.notifyText2.alpha = 1;
			anchorElem = self.notifyText2;
	}

	if ( isDefined( notifyData.iconName ) )
	{
		self.notifyIcon setParent( anchorElem );
		self.notifyIcon setShader( notifyData.iconName, 60, 60 );
		self.notifyIcon.alpha = 0;
		self.notifyIcon fadeOverTime( 1.0 );
		self.notifyIcon.alpha = 1;

		waitRequireVisibility( duration );

		self.notifyIcon fadeOverTime( 0.75 );
		self.notifyIcon.alpha = 0;
	}
	else
	{
		waitRequireVisibility( duration );
	}

	self notify( "notifyMessageDone" );
	self.doingNotify = false;

	if ( self.notifyQueue.size > 0 )
	{
		nextNotifyData = self.notifyQueue[ 0 ];

		newQueue = [];
		for ( i = 1; i < self.notifyQueue.size; i++ )
			self.notifyQueue[ i - 1 ] = self.notifyQueue[ i ];
		self.notifyQueue[ i - 1 ] = undefined;

		self thread showNotifyMessage( nextNotifyData );
	}
}

resetOnCancel()
{
	self notify( "resetOnCancel" );
	self endon( "resetOnCancel" );
	self endon( "notifyMessageDone" );
	self endon( "disconnect" );

	level waittill( "cancel_notify" );

	self.notifyTitle.alpha = 0;
	self.notifyText.alpha = 0;
	self.notifyIcon.alpha = 0;
	self.doingNotify = false;
}

// waits for waitTime, plus any time required to let flashbangs go away.
waitRequireVisibility( waitTime )
{
	interval = .05;

	while ( !self canReadText() )
		wait interval;

	while ( waitTime > 0 )
	{
		wait interval;
		if ( self canReadText() )
			waitTime -= interval;
	}
}

canReadText()
{
	if ( self isFlashbanged() )
		return false;
	return true;
}

isFlashbanged()
{
	return isDefined( self.flashEndTime ) && gettime() < self.flashEndTime;
}

// ============== helpers ===============

registerScoreInfo( type, value )
{
	level._scoreInfo[ type ][ "value" ] = value;
}

getScoreInfoValue( type )
{
	return( level._scoreInfo[ type ][ "value" ] );
}

getRankInfoMinXP( rankId )
{
	return int( level._rankTable[ rankId ][ 2 ] );
}

getRankInfoXPAmt( rankId )
{
	return int( level._rankTable[ rankId ][ 3 ] );
}

getRankInfoMaxXp( rankId )
{
	return int( level._rankTable[ rankId ][ 7 ] );
}

getRankInfoFull( rankId )
{
	return tableLookupIString( "sp/ranktable.csv", 0, rankId, 10 );
}

getRankInfoIcon( rankId )
{
	return tableLookup( "sp/rankIconTable.csv", 0, rankId, 1 );
}

getRank()
{
	rankXp = self.summary[ "rankxp" ];
	rankId = self.summary[ "rank" ];

	if ( rankXp < ( getRankInfoMinXP( rankId ) + getRankInfoXPAmt( rankId ) ) )
		return rankId;
	else
		return self getRankForXp( rankXp );
}

getRankForXp( xpVal )
{
	rankId = 0;
	rankName = level._rankTable[ rankId ][ 1 ];
	assert( isDefined( rankName ) );

	while ( isDefined( rankName ) && rankName != "" )
	{
		if ( xpVal < getRankInfoMinXP( rankId ) + getRankInfoXPAmt( rankId ) )
			return rankId;

		rankId++ ;
		if ( isDefined( level._rankTable[ rankId ] ) )
			rankName = level._rankTable[ rankId ][ 1 ];
		else
			rankName = undefined;
	}

	rankId -- ;
	return rankId;
}

getRankXP()
{
	return self.summary[ "rankxp" ];
}