#using scripts\shared\ai_shared;
#using scripts\shared\clientfield_shared;
#using scripts\shared\fx_shared;
#using scripts\shared\math_shared;
#using scripts\shared\scene_shared;
#using scripts\shared\spawner_shared;
#using scripts\shared\util_shared;
#using scripts\shared\gameobjects_shared;
#using scripts\shared\laststand_shared;
#using scripts\shared\system_shared;

#using scripts\shared\ai\systems\animation_state_machine_notetracks;
#using scripts\shared\ai\systems\animation_state_machine_utility;
#using scripts\shared\ai\archetype_utility;
#using scripts\shared\ai\archetype_locomotion_utility;
#using scripts\shared\ai\systems\behavior_tree_utility;
#using scripts\shared\ai\systems\blackboard;
#using scripts\shared\ai\systems\ai_blackboard;
#using scripts\shared\ai\systems\debug;
#using scripts\shared\ai\archetype_mocomps_utility;
#using scripts\shared\ai\systems\ai_interface;
#using scripts\shared\ai\archetype_warlord_interface;

                                                                                                            	  	  	              	   	                                                                                                                                       	                                                                                                                                                            	                                                                                                                                                                
    	   	                                                                                                                                         	                                	                                                                                                                                                                                                                                                                                                                                                                    	        	     	              	    	   	               	                                              	                                                           	                               	                     	                                                                                                           	                                                                 	                                                              	                                                                                                                                             	                            	                                     	                                       	                                                                                                              	   	                  	       	                                                    	                   	                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            	                                                                                                 	                                    	                                                                                        	           	                        	                                            	                                             	                                                   	                                                             	                                                         	                                                                    	                                                                                                                                                                                                                                	                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  	    	         	                                                              	                                                          	                                   	                                   	                                                    	                                       	                                           	     	                                                                                                                                                                                                                                                      	                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         	                                                               	        	                                                                                                                              	                                                                                                                     	                               	                                                      	                          
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         	   	                                                                                                                                                                                                                                  	                                                                                                                                                                                                                                                                                                                                                                                                                                                   
   	     	                                                                                                                                                                                                                                                                                                                                                                                                                                                             	                                                                                                                	         	    	                                                                                                    
        	                                                                                                                                                	                                                                                                                      	                                                                                     	                                                                                     	                                                            	                                                                                   	                                                                                     	                                                                                               	                                                                                                 	                                                                   	                                                                   	                                                                                                                 	                                                                                                                              	                                                                                                                       	                                                   	                                                                           	                                                                                 	                                       	                             	                                             	                                  	                                                                                 	                                                                                                                                                	      
                                                                                                                                               	
           	                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              	  	                                 	                  	    	                                                      	   	        	                                                                         	                      	             	                                                                                                                                       	  	                                                                                                          	     	                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            	                                                                                                      	                                                                                                     	                                                                                                     	                                                                                                	     	                                                                                                                                                                                                                                                                                                                                                                                                                                	     	    	                                                                                                                                    	                      	              	  	             	  
                                                                  	                             	  	                                      
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  	   	   	   	   	   	   	    	    	                                                                                                                                                                                                           	      	                        	   	   	   	   	   	  /#         #/	  	                                                                                                                                                                                                                           	                                                                                                                                                                                                                                                                                                                                                                                                                     	                              	    	   	     	     	                                                                                                                                                                                                      

#precache( "fx", "vehicle/fx_quadtank_airburst" );
#precache( "fx", "vehicle/fx_quadtank_airburst_ground" );

///***********************************WARLORD BEHAVIOR SUMMERY*******************************************//
//
//Fire Behavior
//-Threat (2 or more attackers)
//-Angry (2 or more attackers)
//-Exposed pain cooldown and pain don't interrupt shooting

//Move Behavior
//-Move from dangerous place (2 or more attackers)
//-Hunt Enemy
//-Prefered Locations
//-Juke
//*********************************************************************************************************//

function autoexec __init__sytem__() {     system::register("warlord",&__init__,undefined,undefined);    }
	
function __init__()
{		
	// INIT BLACKBOARD	
	spawner::add_archetype_spawn_function( "warlord", &WarlordBehavior::ArchetypeWarlordBlackboardInit );
	
	// INIT WARLORD ON SPAWN
	spawner::add_archetype_spawn_function( "warlord", &WarlordServerUtils::warlordSpawnSetup );
	
	// CLIENTFIELDS
	if( ai::shouldRegisterClientFieldForArchetype( "warlord" ) )
	{
		clientfield::register("actor", "warlord_damage_state", 1, 2, "int");
		clientfield::register("actor", "warlord_thruster_direction", 1, 3, "int");
		clientfield::register("actor", "warlord_type", 1, 2, "int");
		clientfield::register("actor", "warlord_lights_state", 1, 1, "int" );
	}		
	
	//REGISTER INTERFACE ATTRIBUTES
	WarlordInterface::RegisterWarlordInterfaceAttributes();
}

#namespace WarlordBehavior;

function autoexec RegisterBehaviorScriptFunctions()
{	
	// ------- WARLORD CONDITIONS -----------//
	BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("warlordCanJukeCondition",&canJukeCondition);;
	BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("warlordCanTacticalJukeCondition",&canTacticalJukeCondition);;
	BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("warlordShouldBeAngryCondition",&shouldBeAngryCondition);;
	BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("warlordShouldNormalMelee",&warlordShouldNormalMelee);;
	BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("warlordCanTakePainCondition",&canTakePainCondition);;
	
	// ------- WARLORD FUNCTIONS -----------//
	BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("warlordExposedPainActionStart",&exposedPainActionStart);;
	
	// ------- WARLORD ACTIONS -----------//
	BehaviorTreeNetworkUtility::RegisterBehaviorTreeAction("warlordDeathAction",&deathAction,undefined,undefined);;
	BehaviorTreeNetworkUtility::RegisterBehaviorTreeAction("warlordJukeAction",&jukeAction,undefined,&jukeActionTerminate);;

	// ------- WARLORD SERVICES -----------//
	BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("chooseBetterPositionService",&chooseBetterPositionService);;
	BehaviorTreeNetworkUtility::RegisterBehaviorTreeScriptAPI("WarlordAngryAttack",&WarlordAngryAttack);;
}


function private ArchetypeWarlordBlackboardInit()
{
	// CREATE BLACKBOARD
	Blackboard::CreateBlackBoardForEntity( self );
	
	// CREATE INTERFACE
	ai::CreateInterfaceForEntity( self );

	
	// USE UTILITY BLACKBOARD
	self AiUtility::RegisterUtilityBlackboardAttributes();
	
	// CREATE WARLORD BLACKBOARD - NOT NEEDED
	
	
	// REGISTER ANIMSCRIPTED CALLBACK
	self.___ArchetypeOnAnimscriptedCallback = &ArchetypeWarlordOnAnimscriptedCallback;
	
	// ENABLE DEBUGGING IN ODYSSEY
	/#self FinalizeTrackedBlackboardAttributes();#/;
}

function private ArchetypeWarlordOnAnimscriptedCallback( entity )
{
	// UNREGISTER THE BLACKBOARD
	entity.__blackboard = undefined;
	
	// REREGISTER BLACKBOARD
	entity ArchetypeWarlordBlackboardInit();
}

function private shouldHuntEnemyPlayer( entity )
{
	if(isdefined(entity.enemy) && isdefined(entity.HuntEnemyTime) && (GetTime() < entity.HuntEnemyTime))
	{
		return true;
	}
	
	return false;
}

function private _warlordHuntEnemy( entity )
{	
	/#
	WarlordDebugHelpers::TryState( entity, 3, true);
	#/
	
	if ( Distance2DSquared( entity.origin, self LastKnownPos(self.enemy) ) <= ( (250) * (250) ) )
	{
		return false;
	}
	
	if ( isdefined( entity.huntUpdateNextTime ) && GetTime() < entity.huntUpdateNextTime )
	{
		return false;
	}
	
	if(entity.isAngryAttack)
	{
		/#
		WarlordDebugHelpers::PrintState(3, (1, 0, 1), "Suspended by Angery Attack");
		#/
			
		return false;
	}
	
	positionOnNavMesh = GetClosestPointOnNavMesh( self LastKnownPos(self.enemy), 200 );
	
	if ( !isdefined( positionOnNavMesh ) )
	{
		positionOnNavMesh = self LastKnownPos(self.enemy);
	}
	
	queryResult = PositionQuery_Source_Navigation(
		positionOnNavMesh,
		150,
		250,
		0.5 * 90,
		36,
		entity,
		36 );
	
	PositionQuery_Filter_InClaimedLocation( queryResult, entity );
	PositionQuery_Filter_DistanceToGoal(queryResult, entity);
	
	if ( queryResult.data.size > 0 )
	{
		closestPoint = undefined;
		closestDistance = undefined;
	
		foreach ( point in queryResult.data )
		{
			if ( !point.inclaimedlocation && point.distToGoal == 0)
			{
				newClosestDistance = Distance2DSquared( entity.origin, point.origin );
			
				if ( !isdefined( closestPoint ) ||
					newClosestDistance < closestDistance )
				{
					closestPoint = point.origin;
					closestDistance = newClosestDistance;
				}
			}
		}
		
		if ( isdefined( closestPoint ) )
		{
			/#
			WarlordDebugHelpers::SetCurrentState(entity, 3, true);
			#/
			
			entity UsePosition( closestPoint );
			entity.huntUpdateNextTime = GetTime() + RandomIntRange( 500, 1500 );
			return true;
		}
	}
	
	/#
	WarlordDebugHelpers::SetStateFailed(entity, 3);
	#/
		
	entity.HuntEnemyTime = undefined;	
	return false;
}


function chooseBetterPositionService( entity )
{
	if ( entity ASMIsTransitionRunning() ||
		( entity GetBehaviortreeStatus() != 5 ) ||
		entity ASMIsSubStatePending() ||
		entity AsmIsTransDecRunning() )
	{
		return false;
	}

	
	shouldRepath = false;
	isTrackingEnemyLastPos = false;
	searchOrigin = undefined;
	bApproachingGoal = entity IsApproachingGoal();
	
	if( !bApproachingGoal)  //1- Go to goal if we are ouside it or changed
	{
		WarlordServerUtils::ClearPreferedPoint( entity );
		
		/#
		WarlordDebugHelpers::SetCurrentState( entity, 6);
		#/
		
		//if we are following an entity or goalradius is too small, just go to it directly
		if( isdefined(entity.goalent) || (entity.goalradius < (36 * 2)) )
		{
			goalPosOnMesh = GetClosestPointOnNavMesh( self.GoalPos, 200 );
			
			if(!isdefined(goalPosOnMesh))
			{
				goalPosOnMesh = self.GoalPos;
			}
			
			entity UsePosition( goalPosOnMesh );
			return true;
		}
	}
	
	if(bApproachingGoal && shouldHuntEnemyPlayer(entity)) 					//2- Should hunt enemy
	{
		return _warlordHuntEnemy(entity);
	}
	else if(bApproachingGoal && warlordserverutils::UpdatePreferedPoint(entity)) //3- Go to prefered point location
	{
		return true;
	}
	else if( isdefined( entity.lastenemysightpos ) && !warlordserverutils::HaveTooLowToAttackEnemy(entity))							 //4- Should investigate enemy last position if any
	{
		searchOrigin = entity.lastenemysightpos;
	}
	else
	{
		/#
		entity WarlordDebugHelpers::PrintState(undefined, (1, 0, 0), "searchOrigin = self.GoalPos");
		#/
		
		searchOrigin = entity.GoalPos;
	}


	// Clamp enemy's position to the navmesh.
	if ( isdefined( searchOrigin ) )
	{
		searchOrigin = GetClosestPointOnNavMesh( searchOrigin, 200 );
	}

	if ( !isdefined( searchOrigin ) )
	{
		return false;
	}
	
	if (!bApproachingGoal || !isdefined( entity.nextFindBetterPositionTime ) ||	GetTime() > entity.nextFindBetterPositionTime) //ShouldRepath if we are outside the goal or we are bored
	{
		shouldRepath = true;
	}
	
	if ( isdefined( entity.enemy ) && !entity SeeRecently( entity.enemy, 2 ) && isdefined( entity.lastenemysightpos )) //ShouldRepath if we lost the enemy
	{
		/#
		entity WarlordDebugHelpers::PrintState(undefined, (1, 1, 1), "TrackingEnemyLastPos");
		#/
			
	 	isTrackingEnemyLastPos = true;
	 	
		//if the warlord is moving near the lastKnowEnemyPos give it a chance to reach there
		if(isdefined(entity.pathGoalPos))
		{
			distanceToGoalSqr = DistanceSquared(searchOrigin, entity.pathGoalPos);
			
			if( distanceToGoalSqr < ( (200) * (200) ) )
			{
				shouldRepath = false;
			}
		}
		else
		{
			shouldRepath = true;
		}
	}
	
	if(!shouldRepath)
	{
		if( isdefined(entity.hasCrouchingEnemy) )
		{
			entity.hasCrouchingEnemy = undefined;
			shouldRepath = true;
		}
	}
	
	if (shouldRepath)
	{
/////////// Find points around the enemy.
		queryResult = PositionQuery_Source_Navigation( searchOrigin, 0, entity.engagemaxdist, 0.5 * 90, 36 * 2, entity, 36 * 2 );
			
		PositionQuery_Filter_InClaimedLocation( queryResult, entity );
		PositionQuery_Filter_DistanceToGoal(queryResult, entity);
	
	//ToDo - mali 3/12/2015: limit the number of Sight checks		
		
		//if there is an enemy try to pick the points that sees it, early out after 20 successful checks
		if(isdefined( entity.enemy ) && isTrackingEnemyLastPos && ( isdefined( entity.WarlordAggressiveMode ) && entity.WarlordAggressiveMode ))
		{
			PositionQuery_Filter_Sight(queryResult, self LastKnownPos(self.enemy), self GetEye() - self.origin, self, 20);
		}
	
		// Throw away points close to the current position or outside the goalradius.
		preferedPoints = [];
		randomPoints = [];
		bPointsAvailable = 0;
		bPointsInGoal = 0;
		bPointsVisible = 0;
		
		closePointDistance = 36;
		
		foreach ( point in queryResult.data )
		{
			if (point.inClaimedLocation)
				continue;
			
			bPointsAvailable++;
			
			if (point.distToGoal > 0) //outside goal radius
				continue;
			
			bPointsInGoal++;
			
			if (isdefined(point.visibility) && !point.visibility)
				continue;
			
			bPointsVisible++;
		
			if(point.distToOrigin2D < closePointDistance)
				continue;
			
			randomPoints[randomPoints.size] = point.origin;
		}
		
		//Get Valid Preferred Points as long as we are not forced to track the player
		if( !(isdefined( entity.enemy ) && isTrackingEnemyLastPos && ( isdefined( entity.WarlordAggressiveMode ) && entity.WarlordAggressiveMode )) )
		{
			preferedPoints = WarlordServerUtils::GetPreferedValidPoints(entity);
		}
		
/////////no perfect points found...fallback to less than perfect ones
		if ( randomPoints.size == 0 && preferedPoints.size == 0 )
		{
			if(bPointsAvailable == 0) //all available points are claimed by other actors!
			{
				return false;
			}
			else if(bPointsInGoal == 0) //if no points from the current searchOrigin, find one near the goal
			{
				// Find points on the edge of the goal radius
				searchOriginOnGoalRadius = entity.GoalPos + VectorNormalize(searchOrigin - entity.GoalPos) * entity.GoalRadius;
				
				queryResult = PositionQuery_Source_Navigation( searchOriginOnGoalRadius, 0,	entity.engagemaxdist, 0.5 * 90,	36 * 2, entity, 36 * 3 );
									
				PositionQuery_Filter_InClaimedLocation( queryResult, entity );
				PositionQuery_Filter_DistanceToGoal(queryResult, entity);
				
				//ToDo - mali 3/12/2015: limit the number of Sight checks
				/*
				if(isdefined( entity.enemy ) && isTrackingEnemyLastPos && entity.WarlordAggressiveMode === true)
				{
					PositionQuery_Filter_Sight(queryResult, self LastKnownPos(self.enemy), self GetEye() - self.origin, self, 20);
				}
				*/
				
				//try to find prefect nodes in the new search
				bPointsAvailable = 0;
				bPointsInGoal = 0;
				bPointsVisible = 0;
									
				foreach ( point in queryResult.data )
				{		
					if (point.inClaimedLocation)
						continue;
					
					bPointsAvailable++;
					
					if (point.distToGoal > 0) //outside goal radius
						continue;
					
					bPointsInGoal++;
					
					if (isdefined(point.visibility) && !point.visibility)
						continue;
					
					bPointsVisible++;
				
					if(point.distToOrigin2D < closePointDistance)
						continue;
					
					randomPoints[randomPoints.size] = point.origin;
				}
				
				//ok let's settle for less perfect nodes if still nothing found
				if ( randomPoints.size == 0 )
				{
					foreach ( point in queryResult.data )
					{
						if (point.inClaimedLocation)
							continue;
						
						if (point.distToGoal > 0) //still outside goal radius
							continue;
						
						if (bPointsVisible > 0 && isdefined(point.visibility) && !point.visibility)
							continue;
						
						randomPoints[randomPoints.size] = point.origin;
					}
				}
			}
			else //There are points inside the goal but not visible
			{
				foreach ( point in queryResult.data )
				{
					if (point.inClaimedLocation)
						continue;
					
					if (point.distToGoal > 0) //outside goal radius
						continue;
					
					if (bPointsVisible > 0 && isdefined(point.visibility) && !point.visibility)
						continue;
					
					randomPoints[randomPoints.size] = point.origin;
				}
			}
			
			if ( randomPoints.size == 0 )
			{
				if(!bApproachingGoal)
				{
					if ( !isdefined( randomPoints ) ) randomPoints = []; else if ( !IsArray( randomPoints ) ) randomPoints = array( randomPoints ); randomPoints[randomPoints.size]=entity.GoalPos;;
				}
				else
				{
					/#
					WarlordDebugHelpers::SetStateFailed(entity, 5);
					#/
					
					return false;
				}
			}
		}
		
		
/////////////Calculate final weights and goal

		goalWeight = -10000;
		
		engageMinFalloffDistSqrd = entity.engageMinFalloffDist * entity.engageMinFalloffDist;
		engageMinDistSqrd = entity.engageMinDist * entity.engageMinDist;
		engageMaxDistSqrd = entity.engageMaxDist * entity.engageMaxDist;
		engageMaxFalloffDistSqrd = entity.engageMaxFalloffDist * entity.engageMaxFalloffDist;
		
		if( isdefined( entity.enemy ) && IsSentient( entity.enemy ) )
		{
			enemyForward = VectorNormalize( AnglesToForward( entity.enemy.angles ) );
		}
		
		
		for ( index = 0; index < randomPoints.size; index++ )
		{
			// Rate the available positions.
			distanceSqrdToEnemy = Distance2DSquared( randomPoints[index], searchOrigin );
			
			//Warlord prefers far locations unless it is tracking the player or he is ina Aggressive mode
			bWeightSign = 1;
			if(isdefined(isTrackingEnemyLastPos) || ( isdefined( entity.WarlordAggressiveMode ) && entity.WarlordAggressiveMode ))
			{
				bWeightSign = -1;
			}
			
			pointWeight = 0;
			
			if ( distanceSqrdToEnemy < engageMinFalloffDistSqrd )
			{
				pointWeight = -1.0 * bWeightSign;
			}
			else if ( distanceSqrdToEnemy < engageMinDistSqrd )
			{
				pointWeight = -0.5 * bWeightSign;
			}
			else if ( distanceSqrdToEnemy > engageMaxFalloffDistSqrd )
			{
				pointWeight = 1.0 * bWeightSign;
			}
			else if ( distanceSqrdToEnemy > engageMaxDistSqrd )
			{
				pointWeight = 1.0 * bWeightSign;
			}
			
			// Lower the pointWeights of points directly behind the enemy.
			if(isdefined(enemyForward))
			{
				// TODO(David Young 3-25-14): Simplify the calculations after finding a good heuristic.
				// TODO(David Young 4-16-14): Clamping is unnecessary but there is a weird SRE that occurred because of the dot product returning a value lower than -1.
				angleFromForward = acos( math::clamp( VectorDot( VectorNormalize( pointWeight - entity.enemy.origin ), enemyForward ), -1, 1 ) );
				
				if ( angleFromForward > 80.0 )
				{
					pointWeight += -0.5;
				}
			}
			
			//Slightly random the points
			pointWeight += RandomFloatRange(-0.25, 0.25);
			
			// Set the best goal position.
			if ( goalWeight < pointWeight )
			{
				goalWeight = pointWeight;
				goalPosition = randomPoints[index];
			}
			
			
			// Draw debug info
			/#
			if ( GetDvarInt("ai_debugPositionService") > 0 && isdefined( GetEntByNum( GetDvarInt("ai_debugPositionService") ) ) && entity == GetEntByNum( GetDvarInt("ai_debugPositionService") ) )
			{
				as_debug::debugDrawWeightedPoint( entity, randomPoints[index], pointWeight, -1.25, 1.75 );
			}
			#/
			// End of debug
		}
		
		//preferedPoints have higher weight
		normalPointsMaxGoalWeight = goalWeight;
		foreach(point in preferedPoints)
		{
			if(point === entity.previous_prefered_point)
				continue;
			
			pointWeight = RandomFloatRange(normalPointsMaxGoalWeight - 0.25, normalPointsMaxGoalWeight + 0.5);
			
			// Set the best goal position.
			if ( goalWeight < pointWeight )
			{
				goalWeight = pointWeight;
				goalPosition = point.origin;
				preferedPoint = point;
			}
			
			// Draw debug info
			/#
			if ( GetDvarInt("ai_debugPositionService") > 0 && isdefined( GetEntByNum( GetDvarInt("ai_debugPositionService") ) ) && entity == GetEntByNum( GetDvarInt("ai_debugPositionService") ) )
			{
				as_debug::debugDrawWeightedPoint( entity, point.origin, pointWeight, -1.25, 1.75 );
			}
			#/
			// End of debug
		}
		
		// End of calculations
		
		if ( isdefined( goalPosition ) )
		{
			if ( entity FindPath( entity.origin, goalPosition, true, false ) )
			{
				entity UsePosition( goalPosition );
				entity.nextFindBetterPositionTime = GetTime() + entity.coversearchinterval;
				
				if(isdefined(preferedPoint))
				{
					/#
					WarlordDebugHelpers::SetCurrentState(entity, 4);
					#/
						
					WarlordServerUtils::SetPreferedPoint( entity, preferedPoint );
				}
				
				/#
				if(!isdefined(preferedPoint))
				{
					WarlordDebugHelpers::SetCurrentState(entity, 5);
				}
				#/
				
				return true;
			}
			
		}
		// End of setting the best goal position.
	}
	
	return false;
}

// ------- BEHAVIOR CONDITIONS -----------//
function canJukeCondition( behaviorTreeEntity )
{
	if ( isdefined( behaviorTreeEntity.nextJukeTime ) && GetTime() < behaviorTreeEntity.nextJukeTime )
	{
		return false;
	}
	
	return WarlordServerUtils::warlordCanJuke( behaviorTreeEntity );
}

function canTacticalJukeCondition( behaviorTreeEntity )
{
	if ( isdefined( behaviorTreeEntity.nextJukeTime ) && GetTime() < behaviorTreeEntity.nextJukeTime )
	{
		return false;
	}
	
	return WarlordServerUtils::warlordCanTacticalJuke( behaviorTreeEntity );
}

function warlordShouldNormalMelee( behaviorTreeEntity)
{
	if ( isdefined( behaviorTreeEntity.enemy ) && !( ( isdefined( behaviorTreeEntity.enemy.allowDeath ) && behaviorTreeEntity.enemy.allowDeath ) ) )
		return false;
	
	if ( AiUtility::hasEnemy( behaviorTreeEntity ) && !IsAlive( behaviorTreeEntity.enemy ) )
		return false;
	
	if( !IsSentient( behaviorTreeEntity.enemy ) )
		return false;
	
	if( IsVehicle( behaviorTreeEntity.enemy ) && !( isdefined( behaviorTreeEntity.enemy.good_melee_target ) && behaviorTreeEntity.enemy.good_melee_target ) )
		return false;
	
	if( !AiUtility::shouldMutexMelee( behaviorTreeEntity ) )
		return false;
	
	if( behaviorTreeEntity ai::has_behavior_attribute( "can_melee" ) && !behaviorTreeEntity ai::get_behavior_attribute( "can_melee" ) )
		return false;
		
	if(	behaviorTreeEntity.enemy ai::has_behavior_attribute( "can_be_meleed" ) && !behaviorTreeEntity.enemy ai::get_behavior_attribute( "can_be_meleed" ) )
		return false;
	
	if( !IsPlayer(behaviorTreeEntity.enemy) && !( isdefined( behaviorTreeEntity.enemy.magic_bullet_shield ) && behaviorTreeEntity.enemy.magic_bullet_shield ) ) //we don't want the warlord to keep punching an enemy who can't die
		return false;
	
	if(	AiUtility::hasCloseEnemyToMeleeWithRange( behaviorTreeEntity, ( (100) * (100) )) )
	{
		if( WarlordServerUtils::IsEnemyTooLowToAttack( behaviorTreeEntity.enemy) )
		{
			WarlordServerUtils::SetEnemyTooLowToAttack(behaviorTreeEntity);
			
			return false;
		}
		
		return true;
	}

	return false;
}

function canTakePainCondition( behaviorTreeEntity )
{
	return (GetTime() >= behaviorTreeEntity.nextExposedPain);
}


// ------- COMBAT LOCOMOTION ACTIONS -----------//

function jukeAction( behaviorTreeEntity, asmStateName )
{
	if(warlordserverutils::HaveTooLowToAttackEnemy(behaviorTreeEntity))
	{
		nextJukeTime = 3000 / 3;
	}
	else
	{
		nextJukeTime = 3000;
	}
			
	behaviorTreeEntity.nextJukeTime = GetTime() + nextJukeTime;
	
	AnimationStateNetworkUtility::RequestState( behaviorTreeEntity, asmStateName );
	
	jukeDirection = Blackboard::GetBlackBoardAttribute( behaviorTreeEntity, "_juke_direction");
	
	switch ( jukeDirection )
	{
	case "left":
		clientfield::set( "warlord_thruster_direction", 4 );
		break;
	case "right":
		clientfield::set( "warlord_thruster_direction", 3 );
		break;
	}
	
	behaviorTreeEntity ClearPath();

	//Notify other actors that the warlord just did a juke
	jukeInfo = SpawnStruct();
	jukeInfo.origin = behaviorTreeEntity.origin;
	jukeInfo.entity = behaviorTreeEntity;

	Blackboard::AddBlackboardEvent( "actor_juke", jukeInfo, 2000 );

	jukeInfo.entity playsound("fly_jetpack_juke_warlord");
	
	
	return 5;
}

function jukeActionTerminate( behaviorTreeEntity, asmStateName )
{
	Blackboard::SetBlackBoardAttribute( behaviorTreeEntity, "_juke_direction", undefined );
	
	clientfield::set( "warlord_thruster_direction", 0 );
	
	//use the new Juke landing position
	positionOnNavMesh = GetClosestPointOnNavMesh( behaviorTreeEntity.origin, 200 );
	
	if ( !isdefined( positionOnNavMesh ) )
	{
		positionOnNavMesh = behaviorTreeEntity.origin;
	}
	
	behaviorTreeEntity UsePosition( positionOnNavMesh );
	
	return 4;
}

// ------- RECHARGE ACTIONS -----------//	
function deathAction( behaviorTreeEntity, asmStateName )
{
	clientfield::set( "warlord_damage_state", 3 );

	AnimationStateNetworkUtility::RequestState( behaviorTreeEntity, asmStateName );
	
	return 5;
}

function exposedPainActionStart( behaviorTreeEntity )
{
	behaviorTreeEntity.nextExposedPain = GetTime() + RandomIntRange(500, 2500);
	
	AiUtility::keepClaimNode( behaviorTreeEntity );
}

// ------- Angry -----------//	
function shouldBeAngryCondition( behaviorTreeEntity )
{
	if( isdefined(behaviorTreeEntity.nextAngryTime) && (GetTime() < behaviorTreeEntity.nextAngryTime) )
		return false;
	
	if( !isDefined( behaviorTreeEntity.knownAttackers) || behaviorTreeEntity.knownAttackers.size == 0)
	{
		return false;
	}
	else if(behaviorTreeEntity.knownAttackers.size == 1 && isdefined(behaviorTreeEntity.enemy) && behaviorTreeEntity.knownAttackers[0].attacker == behaviorTreeEntity.enemy) //if attacker is enemy and we only have one attacker no need to do something extra
	{
		return false;
	}
	
	
	if(isdefined(behaviorTreeEntity.AccumilatedDamage) && behaviorTreeEntity.AccumilatedDamage > WarlordServerUtils::GetScaledForPlayers(200, 1.5, 2, 2.5) )
		return true;
	
	return behaviorTreeEntity.isAngryAttack;
}


function WarlordAngryAttack( entity )
{	
	/#
	WarlordDebugHelpers::PrintState(1, (0, 1, 0), " STARTED");
	#/
		
	entity.isAngryAttack = true;
	entity.forceFire = true;
	entity.AccumilatedDamage = 0;
	entity.nextAngryTime = GetTime() + 13000;
	
	WarlordServerUtils::UpdateAttackersList( entity );
	
	attackersArray = [];
	
	//Sort known Attackers based on threat
	for(i = 0;i < entity.knownAttackers.size; i++)
	{
		for( j = i + 1; j < entity.knownAttackers.size; j++)
		{
			if(entity.knownAttackers[i].threat < entity.knownAttackers[j].threat)
			{
				tmp = entity.knownAttackers[j].threat;
				entity.knownAttackers[j].threat = entity.knownAttackers[i].threat;
				entity.knownAttackers[i].threat = tmp;
			}
		}
	}
	
	foreach(data in entity.knownAttackers)
	{
		if ( !isdefined( attackersArray ) ) attackersArray = []; else if ( !IsArray( attackersArray ) ) attackersArray = array( attackersArray ); attackersArray[attackersArray.size]=data.attacker;;
	}
	
	thread WarlordAngryAttack_ShootThemAll(entity, attackersArray);
	
	return true;
}

function WarlordAngryAttack_ShootThemAll( entity, attackersArray )
{
	entity endon("disconnect");
	entity endon("death");
	
	entity notify("angry_attack");
	
	shootTime = GetDVarFloat("warlordangryattack", 3);
	
	foreach(attacker in attackersArray)
	{
		if(isdefined(attacker))
		{
			entity ai::shoot_at_target("normal", attacker, undefined, shootTime, undefined, true);
		}
	}
	
	/#
	WarlordDebugHelpers::PrintState(1, (0, 0, 1), " ENDED");
	#/
		
	entity.forceFire = false;
	entity.isAngryAttack = false;;
}

// end of #namespace WarlordBehavior

#namespace WarlordServerUtils;

function GetAlivePlayersCount(entity)
{
	if(entity.team == "allies")
	{
		return level.aliveCount["axis"];
	}
	else
	{
		return level.aliveCount["allies"];
	}	
}

function SetWarlordAggressiveMode( entity, b_aggressive_mode )
{
	entity.WarlordAggressiveMode = b_aggressive_mode;
	
	if(( isdefined( b_aggressive_mode ) && b_aggressive_mode ))
	{
		//Increase the threat bias towards the players relative to the allies
		foreach(player in level.players)
		{
			 entity SetPersonalThreatBias(player, 1000);
		}
	}
	else
	{
		//Reset the threat bias in non Aggressive mode
		foreach(player in level.players)
		{
			 entity SetPersonalThreatBias(player, 0, 1);
		}
	}
}

function AddPreferedPoint(entity, position, min_duration, max_duration, name)
{
	positionOnNavMesh = GetClosestPointOnNavMesh( position, 200, 25 );
	
	if(!isdefined(positionOnNavMesh))
	{
		/#	println( "^3Warning: Passing Warlord prefered point that is not on the NavMesh or is in invalid location: " + position ); 	#/
		return;
	}
	else
	{
		position = positionOnNavMesh;
	}
	
	if(!(entity IsPosAtGoal(position)))
	{
		/#	println( "^3Warning: Passing Warlord prefered point that it not inside the Goal: " + position ); 	#/
	}

	point = SpawnStruct();
	point.origin = position;
	point.min_duration = min_duration;
	point.max_duration = max_duration;
	point.name = name;

	if ( !isdefined( entity.prefered_points ) ) entity.prefered_points = []; else if ( !IsArray( entity.prefered_points ) ) entity.prefered_points = array( entity.prefered_points ); entity.prefered_points[entity.prefered_points.size]=point;;
}

function DeletePreferedPoint( entity, name )
{
	if(isdefined(entity.prefered_points))
	{
		points_to_remove = [];
		
		foreach(point in entity.prefered_points)
		{
			if( point.name === name )
			{
				if ( !isdefined( points_to_remove ) ) points_to_remove = []; else if ( !IsArray( points_to_remove ) ) points_to_remove = array( points_to_remove ); points_to_remove[points_to_remove.size]=point;;
			}
		}
		
		if(points_to_remove.size > 0)
		{
			foreach(point in points_to_remove)
			{
				ArrayRemoveValue( entity.prefered_points, point );
			}
			
			return true;
		}
	}
	
	return false;
}

function ClearAllPreferedPoints(entity)
{
	ClearPreferedPoint(entity);
	
	entity.prefered_points = [];
}

function ClearPreferedPointsOutsideGoal(entity)
{
	points_to_remove = [];
	
	foreach(point in entity.prefered_points)
	{
		if(!(entity IsPosAtGoal(point.origin)))
		{
			if ( !isdefined( points_to_remove ) ) points_to_remove = []; else if ( !IsArray( points_to_remove ) ) points_to_remove = array( points_to_remove ); points_to_remove[points_to_remove.size]=point;;
		}
	}
	
	foreach(point in points_to_remove)
	{
		ArrayRemoveValue( entity.prefered_points, point );
	}
}

function private SetPreferedPoint( entity, point)
{
	entity.previous_prefered_point = entity.current_prefered_point;
	entity.current_prefered_point = point;
}

function private ClearPreferedPoint( entity)
{
	/#
	WarlordDebugHelpers::SetCurrentState(entity, undefined);
	#/
	
	entity.current_prefered_point_start_time = undefined;
	entity.current_prefered_point_expiration = undefined;
	entity.current_prefered_point = undefined;
}

function private AtPreferedPoint(entity)
{
	if(isdefined(entity.current_prefered_point) && ( (DistanceSquared(entity.current_prefered_point.origin, entity.origin) < ( (36) * (36) )) && (abs(self.current_prefered_point.origin[2] - entity.origin[2]) < (90 / 2)) ) )
	{
		return true;
	}
	
	return false;
}

function private ReachingPreferedPoint(entity)
{
	if(!isdefined(entity.current_prefered_point))
		return false;
	
	if(AtPreferedPoint(entity))
	{
		return true;
	}
	
	if(isdefined(entity.pathGoalPos) && entity.pathGoalPos == entity.current_prefered_point.origin)
	{
		return true;
	}
	
	return false;
}

function private UpdatePreferedPoint(entity)
{
	if(isdefined(entity.current_prefered_point))
	{
		if(AtPreferedPoint(entity))
		{
			if(isdefined(entity.current_prefered_point_expiration))
			{
				if(GetTime() > entity.current_prefered_point_expiration)
				{
					ClearPreferedPoint(entity);
					return false;
				}
				
				return true;
			}
			else
			{
				if(isdefined(entity.current_prefered_point.min_duration))
				{
					entity.current_prefered_point_start_time = GetTime();
						
					if(!isdefined(entity.current_prefered_point.max_duration) || entity.current_prefered_point.max_duration == entity.current_prefered_point.min_duration)
					{
						entity.current_prefered_point_expiration = GetTime() + entity.current_prefered_point.min_duration;
					}
					else
					{
						duration = RandomIntRange(entity.current_prefered_point.min_duration, entity.current_prefered_point.max_duration);
						entity.current_prefered_point_expiration = GetTime() + duration;
					}
					
					return true;
				}
				else
				{
					ClearPreferedPoint(entity);
					return false;
				}
			}
			
			return true;
		}
		else if(!ReachingPreferedPoint(entity))
		{
			entity UsePosition( entity.current_prefered_point.origin );
		}
		
		return true;
	}
	
	return false;
}


function private GetPreferedValidPoints( entity )
{
	validPoints = [];
	
	if(isdefined(entity.prefered_points))
	{
		foreach(point in entity.prefered_points)
		{
			if(!(entity IsPosAtGoal(point.origin)))
			{
				distance = Distance2DSquared(entity.origin, point.origin);
				distance = sqrt(distance);
				continue;
			}
			
			if((entity IsPosInClaimedLocation(point.origin)))
				continue;
			
			if(isdefined( entity.enemy ) && ( isdefined( entity.WarlordAggressiveMode ) && entity.WarlordAggressiveMode ))
			{
				if( !BulletTracePassed( entity GetEye(), entity.enemy.origin + (0, 0, 50), false, entity, entity.enemy ) )
				   continue;
			}
 
			if ( !isdefined( validPoints ) ) validPoints = []; else if ( !IsArray( validPoints ) ) validPoints = array( validPoints ); validPoints[validPoints.size]=point;;
		}
	}
	
	return validPoints;
}

function GetScaledForPlayers(val, scale2, scale3, scale4)
{
	if(!isdefined( level.players))
	{
		return val;
	}

	if( level.players.size == 2 )
	{
		return val * scale2;
	}
	else if( level.players.size == 3 )
	{
		return val * scale3;
	}
	else if( level.players.size == 4 )
	{
		return val * scale4;
	}
	else
	{
		return val;
	}
}

function warlordCanJuke( entity )
{

	if (!isdefined( entity.enemy ) )
	{
		return false;
	}
	
	distanceSqr = DistanceSquared(entity.enemy.origin, entity.origin);
	
	if(distanceSqr < ( (300) * (300) ))
	{
		jukeDistance = 145 / 2;
	}
	else
	{
		jukeDistance = 145;
	}
	
	jukeDirection = AiUtility::calculateJukeDirection( entity, 18, jukeDistance );

	if(jukeDirection != "forward")
	{
		Blackboard::SetBlackBoardAttribute( entity, "_juke_direction", jukeDirection );
		
		if(jukeDistance == 145)
		{
			Blackboard::SetBlackBoardAttribute( entity, "_juke_distance", "long" );
		}
		else
		{
			Blackboard::SetBlackBoardAttribute( entity, "_juke_distance", "short" );
		}
		
		return true;
	}
	
	return false;
}

function warlordCanTacticalJuke( entity )
{
	if ( entity HasPath () )
	{
		locomotionDirection = AiUtility::BB_GetLocomotionFaceEnemyQuadrant();

		if( locomotionDirection == "locomotion_face_enemy_front" || locomotionDirection == "locomotion_face_enemy_back")
		{
			jukeDirection = AiUtility::calculateJukeDirection( entity, 50, 145 ) ;
			
			if(jukeDirection != "forward" )
			{
				Blackboard::SetBlackBoardAttribute( entity, "_juke_direction", jukeDirection );
				Blackboard::SetBlackBoardAttribute( entity, "_juke_distance", "long" );
				
				return true;
			}
		}
	}
	
	return false;
}

function IsEnemyTooLowToAttack( enemy )
{
	if(IsPlayer(enemy))
	{
		if(( isdefined( enemy.laststand ) && enemy.laststand ))
		{
			return true;
		}
		   
		playerStance = enemy GetStance();
		
		if(isdefined(playerStance) && (playerStance == "prone" || playerStance == "crouch"))
		{
			return true;
		}
	}
	
	return false;
}

function HaveTooLowToAttackEnemy( entity )
{
	if(!isdefined(entity.lastTimeToHaveCrouchingEnemy))
	{
		return false;
	}
	
	if( (GetTime() - entity.lastTimeToHaveCrouchingEnemy) <= 4000)
	{
		return true;
	}
	
	entity.lastTimeToHaveCrouchingEnemy = undefined;
	return false;
}

function SetEnemyTooLowToAttack( entity )
{
	if(HaveTooLowToAttackEnemy(entity))
	{
		return;
	}
	
	entity.lastTimeToHaveCrouchingEnemy = GetTime();
	entity.hasCrouchingEnemy = true;
}

function ComputeAttackerThreat( entity, attackerInfo)
{
	if(attackerInfo.damage < 250)
	   return 0;

   threat = 1;
   isAttackerPlayer = IsPlayer(attackerInfo.attacker);

	if(isAttackerPlayer)
	{
		threat *= 10;
	}
	
	distanceFromAttackerSqr = Distance2DSquared( entity.origin, attackerInfo.attacker.origin );
	normalizedDistanceFromAttacker = 0; 
	
	if(isAttackerPlayer)
	{
		if(distanceFromAttackerSqr <= ( (100) * (100) ))
		{
			threat *= 1000;
		}
		else
		{
			normalizedDistanceFromAttacker = distanceFromAttackerSqr / ( (entity.engageMaxFalloffDist) * (entity.engageMaxFalloffDist) );
			
			if(normalizedDistanceFromAttacker > 1)
				normalizedDistanceFromAttacker = 1;
			
			normalizedDistanceFromAttacker = 1 - normalizedDistanceFromAttacker;
		}
	}
	
	normalizedDamageFromAttacker = attackerInfo.damage / 1000;
	
	if(normalizedDamageFromAttacker > 1)
		normalizedDamageFromAttacker = 1;
	
	threat *= (normalizedDistanceFromAttacker * 0.65 + normalizedDamageFromAttacker * ( 1 - 0.65)) * 100;
	
	return threat;
}

function ShouldSwitchToNewThreat( entity, attacker, threat)
{	
	if(entity.enemy === attacker)
		return false;
	
	if(!isdefined(entity.currentDangerousAttacker))
		return true;
	
	if(entity.currentDangerousAttacker.Health <= 0)
		return true;
	
	if(entity.currentDangerousAttacker == attacker)
		return false;

	if(GetTime() - entity.lastDangerousAttackerTime < 1)
		return false;
	
	return true;
}

function UpdateAttackersList( entity, newAttacker, damage)
{
	if(!isdefined(entity.knownAttackers))
	{
		entity.knownAttackers = [];
	}
	
	maxThreat = 0;
	threatCount = 0;
	
	for(i = 0; i < entity.knownAttackers.size; i++)
	{
		attacker = entity.knownAttackers[i].attacker;
		
		if(!isdefined(attacker) || !IsEntity(attacker) || attacker.health <= 0 || (GetTime() - entity.knownAttackers[i].lastAttackTime) > 5000)
		{
			ArrayRemoveIndex(entity.knownAttackers, i);
			
			i--;
			continue;
		}
		
		//Check to see if this is a high immediate threat 
		entity.knownAttackers[i].threat = ComputeAttackerThreat(entity, entity.knownAttackers[i]);
		
		if(entity.knownAttackers[i].threat > maxThreat)
		{
			maxThreat = entity.knownAttackers[i].threat;
			dangerousAttackerInfo = entity.knownAttackers[i];
		}
	}
	
	if(isdefined(newAttacker))
	{
		for(i = 0; i < entity.knownAttackers.size; i++)
		{
			if(entity.knownAttackers[i].attacker == newAttacker)
			{
				attackData = entity.knownAttackers[i];
				attackData.lastAttackTime = GetTime();
				attackData.damage += damage;
				
				break;
			}
		}
		
		if(!isdefined(attackData))
		{
			attackData = SpawnStruct();
			attackData.attacker = newAttacker;
			attackData.lastAttackTime = GetTime();
			attackData.damage = damage;
			attackData.threat = 0;
			
			if ( !isdefined( entity.knownAttackers ) ) entity.knownAttackers = []; else if ( !IsArray( entity.knownAttackers ) ) entity.knownAttackers = array( entity.knownAttackers ); entity.knownAttackers[entity.knownAttackers.size]=attackData;;
		}
		
		//Check to see if this new attacker is a high immediate threat 
		attackData.threat = ComputeAttackerThreat(entity, attackData);
		
		if(attackData.threat > maxThreat)
		{
			maxThreat = attackData.threat;
			dangerousAttackerInfo = attackData;
		}
	}
	
	if(isdefined(dangerousAttackerInfo) && maxThreat > 0)
	{
		if(ShouldSwitchToNewThreat( entity, dangerousAttackerInfo.attacker, maxThreat))
		{
			thread WarlordDangerousEnemyAttack( entity, dangerousAttackerInfo.attacker, maxThreat);
		}
	}
	
	//if we are not moving then move if we are getting multiple damage
	CheckifWeShouldMove(entity);
}

function CheckifWeShouldMove( entity )
{
	if(!isdefined(entity.knownAttackers) || entity.knownAttackers.size <= 1)
		return;
	
	isStandStill = false;
	if(AtPreferedPoint(entity))
	{
		//at least allow a second for the warlord to stand on the prefered point before moving him
		if(!isdefined(entity.current_prefered_point_start_time) || GetTime() - entity.current_prefered_point_start_time < 1)
		{
			return;
		}

		isStandStill = true;
	}
	
	if(!isStandStill)
	{
		if(isdefined(entity.pathGoalPos))
		{
			if( (Distance2DSquared(entity.pathGoalPos, entity.origin) < ( (36) * (36) )) && (abs(entity.pathGoalPos[2] - entity.origin[2]) < (90 / 2)) )
			{
				isStandStill = true;
			}
		}
	}
	
	if(isStandStill)
	{
		if(HaveTooLowToAttackEnemy(entity))
		{
			dangerousAttackersCount = 1;
		}
		else
		{
			dangerousAttackersCount = 0;
			
			foreach(attackerInfo in entity.knownAttackers)
			{
				if(attackerInfo.damage > 200 )
				{
					dangerousAttackersCount++;
				}
			}
		}
		
		//if we are taking fire from many attackers start moving
		if(dangerousAttackersCount > 1)
		{	
			ClearPreferedPoint( entity );
			entity.nextFindBetterPositionTime = 0;
		}
	}
}

function WarlordDangerousEnemyAttack( entity, attacker, threat)
{
	entity endon("disconnect");
	entity endon("death");
	attacker endon("death");
	
	entity endon("angry_attack");
	
	entity notify("dangerous_attack");
	entity endon("dangerous_attack");
	
	entity.lastDangerousAttackerTime = GetTime();
	entity.currentDangerousAttacker = attacker;
	entity.currentMaxThreat = threat;
	
	/#
	WarlordDebugHelpers::PrintState(0, (0, 1, 0), " STARTED");
	#/
	
	shootTime = GetDVarFloat("warlordangryattack", 3);
	entity ai::shoot_at_target("normal", attacker, undefined, shootTime, undefined, true);
	
	entity.currentDangerousAttacker = undefined;
	
	/#
	WarlordDebugHelpers::PrintState(0, (0, 0, 1), " ENDED");
	#/
}

function warlordDamageOverride( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, weapon, vPoint, vDir, sHitLoc, vDamageOrigin, timeOffset, boneIndex, modelIndex, surfaceType, surfaceNormal )
{
	entity = self;

	//Non-player enemies should be very ineffective on the warlord
	if(!IsPlayer(eAttacker))
	{
		iDamage = int(iDamage * 0.05);
	}
	
	if ( isdefined(sMeansOfDeath) && ( sMeansOfDeath == "MOD_PROJECTILE" || sMeansOfDeath == "MOD_PROJECTILE_SPLASH" || sMeansOfDeath == "MOD_EXPLOSIVE" || sMeansOfDeath == "MOD_GRENADE" ) )
	{
		iDamage = int(iDamage * 0.25);
	}
	
	//maintain attackers list
	UpdateAttackersList( entity, eAttacker, iDamage);
	
	if ( entity.health <= entity.damageHeavyStateHealth )
	{
		clientfield::set( "warlord_damage_state", 2 );
	}
	else if ( entity.health <= entity.damageStateHealth )
	{
		clientfield::set( "warlord_damage_state", 1 );
	}
	else
	{
		clientfield::set( "warlord_damage_state", 0 );
	}
	
	if(!isdefined(entity.LastDamageTime))
	{
		entity.LastDamageTime = 0;
	}
	
	if((GetTime() - entity.LastDamageTime) > 1500)
	{
		entity.AccumilatedDamage = iDamage;
	}
	else
	{
		entity.AccumilatedDamage += iDamage;
	}

	WARLORD_HUNT_MAX_ACCUMILATED_DAMAGE_VAR = GetDVarInt("warlordhuntdamage", 350);

	if(entity.AccumilatedDamage > WarlordServerUtils::GetScaledForPlayers(WARLORD_HUNT_MAX_ACCUMILATED_DAMAGE_VAR, 1.5, 2, 2.5) )
	{		
		self.HuntEnemyTime = GetTime() + 15000;
	}
	
	entity.LastDamageTime = GetTime();
	
	return iDamage;
}

function warlordSpawnSetup()
{
	entity = self;
	
	entity.WarlordAggressiveMode = false;
	entity.isAngryAttack = false;
	entity.nextExposedPain = 0;
	entity.lastDangerousAttackerTime = 0;
	entity.currentMaxThreat = 0;
	entity.ignorerunAndgundist = true;
	
	entity.combatmode = "no_cover";
	
	AiUtility::AddAIOverrideDamageCallback( entity, &WarlordServerUtils::warlordDamageOverride );
	
	entity.health = int(GetScaledForPlayers(entity.health, 2, 2.5, 3));
	
	entity.fullHealth = entity.health;
	entity.damageStateHealth = Int( entity.fullHealth * 0.5 );
	entity.damageHeavyStateHealth = Int( entity.fullHealth * 0.25 );
	
	entity warlord_projectile_watcher();
	
	clientfield::set( "warlord_damage_state", 0 );
	clientfield::set( "warlord_lights_state", 1 );
	
	switch (entity.classname)
	{
	case "actor_spawner_bo3_warlord_enemy_hvt":
		clientfield::set( "warlord_type", 2 );
		break;
	default:
		clientfield::set( "warlord_type", 1 );
		break;
	}
}

function warlord_projectile_watcher()
{
	if( !isdefined( self.missile_repulsor ) ) 
	{
		self.missile_repulsor = missile_createrepulsorent( self, 40000, 256, true );
	}
	self thread repulsor_fx();
}


function remove_repulsor()
{
	self endon( "death" );
	
	if( isdefined( self.missile_repulsor ) ) 
	{
		missile_deleteattractor( self.missile_repulsor );
		self.missile_repulsor = undefined;
	}

	wait 0.5;
	
	if(isdefined(self))
	{
		self warlord_projectile_watcher();
	}
}

function repulsor_fx()
{
	self endon( "death" );
	self endon( "killing_repulsor" );
	
	while( 1 )
	{	
		self util::waittill_any( "projectile_applyattractor", "play_meleefx" );

		PlayFxOnTag( "vehicle/fx_quadtank_airburst", self, "tag_origin" );
		PlayFxOnTag( "vehicle/fx_quadtank_airburst_ground", self, "tag_origin" );

		self PlaySound( "wpn_trophy_alert" );
		
		self thread remove_repulsor();
		self notify( "killing_repulsor" );

	}
}

function trigger_player_shock_fx()
{
	if ( !isdefined( self._player_shock_fx_quadtank_melee ) )
	{
		self._player_shock_fx_quadtank_melee = 0;
	}

	self._player_shock_fx_quadtank_melee = !self._player_shock_fx_quadtank_melee;
	self clientfield::set_to_player( "player_shock_fx", self._player_shock_fx_quadtank_melee );
}

// end #namespace WarlordServerUtils;

#namespace WarlordDebugHelpers;

function PrintState(state, color, string)
{
	
/#
	if(GetDvarInt("ai_debugWarlord") > 0)
	{
		if(!isdefined(string))
		{
			string = "";
		}
		
		
		if(!isdefined(state))
		{
			if(!isdefined(self) || !isdefined(self.lastMessage) || self.lastMessage != string)
			{
				self.lastMessage = string;
				PrintTopRightln(string + GetTime(), color, -1 );
			}
			return;
		}
		
		if(state == 0)
		{
			PrintTopRightln("WARLORD_STATE_THREAT " + string + GetTime(), color, -1 );
		}
		else if(state == 1)
		{
			PrintTopRightln("WARLORD_STATE_ANGRY " + string + GetTime(), color, -1 );	
		}
		else if(state == 2)
		{
			PrintTopRightln("WARLORD_STATE_MOVE_FROM_DANGER " + string + GetTime(), color, -1 );	
		}
		else if(state == 3)
		{
			PrintTopRightln("WARLORD_STATE_HUNT " + string + GetTime(), color, -1 );	
		}
		else if(state == 4)
		{
			PrintTopRightln("WARLORD_STATE_PREFERED_POINT " + string + GetTime(), color, -1 );	
		}
		else if(state == 5)
		{
			PrintTopRightln("WARLORD_STATE_NORMAL_POINT " + string + GetTime(), color, -1 );	
		}
		else if(state == 6)
		{
			PrintTopRightln("WARLORD_STATE_GOAL " + string + GetTime(), color, -1 );	
		}
	}
#/	

}

function TryState(entity, state, bCheckNew)
{
	/#
	if(GetDvarInt("ai_debugWarlord") > 0)
	{
		if( !(isdefined(bCheckNew) && IsNewState( entity, state)) )
		{
			color = (1, 1, 1);
		
			entity PrintState( state, color );
		}
	}
	#/
}

function SetCurrentState( entity, state, bCanUpdate = false )
{
	/#
	if(GetDvarInt("ai_debugWarlord") > 0)
	{
		if(!isdefined(bCanUpdate) || IsNewState( entity, state))
		{
			color = (0, 1, 0);
		}
		else
		{
			color = (0, 1, 1);
		}
		
		if(!isdefined(state))
		{
			color = (0, 0, 1);
			entity PrintState( entity.currentState, color, " ENDED" );
		}
		
		entity PrintState( state, color );
	}
	#/
		
	entity.currentState = state;	
}

function SetStateFailed( entity, state )
{
	/#
	if(GetDvarInt("ai_debugWarlord") > 0)
	{
		color = (1, 1, 0);
		
		entity PrintState( state, color );
	}
	#/
}

function IsNewState( entity, state )
{
	bNewState = false;
	
	if(!isdefined(entity.currentState))
	{
		bNewState = true;
	}
	else if(!isdefined(state))
	{
		return false;
	}
	else if(entity.currentState != state)
	{
		bNewState = true;
	}
	
	return bNewState;
}

// end #namespace WarlordDebugHelpers;