//-----------------------------------------------
// AI functions for Hunters
//---------------------------------------------------------------------------

// globals
$AIHuntersFlagSearchRadius     	= 300;
$AIHuntersCloseFlagDist        	= 40;
$AIHuntersAttackClientFlagCount	= 10;
$AIHuntersMinFlagsToCap				= 3;

//---------------------------------------------------------------------------

function HuntersGame::onAIRespawn(%game, %client)
{
   //add the default task
	if (! %client.defaultTasksAdded)
	{
		%client.defaultTasksAdded = true;
	   %client.addTask(AIEngageTask);
	   %client.addTask(AIPickupItemTask);                                             
	   %client.addTask(AIUseInventoryTask);
	   %client.addTask(AITauntCorpseTask);
		%client.addTask(AIEngageTurretTask);
		%client.addtask(AIDetectMineTask);
	   %client.addTask(AIPatrolTask);
      %client.huntersTask = %client.addTask(AIHuntersTask);
	}

   //set the inv flag
   %client.spawnUseInv = true;
}

//---------------------------------------------------------------------------
   
function HuntersGame::AIInit(%game)
{
   //call the default AIInit() function
   AIInit();
}

//---------------------------------------------------------------------------
//AIHuntersTask functions
//---------------------------------------------------------------------------
function AIHuntersTask::init(%task, %client)
{
}

//---------------------------------------------------------------------------

function AIHuntersTask::assume(%task, %client)
{
   %task.setWeightFreq(10);
   %task.setMonitorFreq(10);
   %task.pickupFlag = -1;
	%task.engageTarget = -1;
   %task.getHealth = -1;
   %task.capFlags = false;
}

function AIHuntersTask::retire(%task, %client)
{
   %task.pickupFlag = -1;
	%task.engageTarget = -1;
   %task.getHealth = -1;
   %task.capFlags = false;
}

//---------------------------------------------------------------------------

function AIHuntersTask::weight(%task, %client)
{
   // init flag search vars
   %player = %client.player;
	if (!isObject(%player))
		return;

   %clientPos = %player.getWorldBoxCenter();
   %task.pickupFlag = -1;
	%task.engageTarget = -1;
   %task.capFlags = false;

	//find the closest flag
   %flagVars = AIFindClosestFlag(%client, $AIHuntersFlagSearchRadius);
	%closestFlag = getWord(%flagVars, 0);
	%closestFlagDist = getWord(%flagVars, 1);

	//find the dist to the nexus
	%nexusPos = Game.Nexus.getId().getWorldBoxCenter();
	%nexusDist = %client.getPathDistance(%nexusPos);
	if (%nexusDist < 0)
		%nexusDist = 32767;

   //validate the health item
   if (isObject(%task.getHealth))
   {
		if (%task.getHealth.isHidden())
         %task.getHealth = -1;
      else if ((%task.getHealth.getDataBlock().getName() $= "DeployedStationInventory") || 
                        (%task.getHealth.getDataBlock().getName() $= "StationInventory"))
      {
         if (%task.getHealth.isDisabled() && !%task.getHealth.isPowered())
            %task.getHealth = -1;
      }
   }

	//find the dist to the closest health
	%healthDist = 32767;
	%damage = %client.player.getDamagePercent();
   if (%client.flagCount < 5)
      %damageTolerance = 0.7;
   else
      %damageTolerance = 0.25 + ((%client.getSkillLevel() * %client.getSkillLevel()) * 0.35);
	if (%damage > %damageTolerance)
	{
	   if (!isObject(%task.getHealth))
	   {
		   //search for a health kit
		   %closestHealth = AIFindSafeItem(%client, "Health");
		   if (isObject(%closestHealth))
		   {
			   %healthDist = %client.getPathDistance(%closestHealth.getWorldBoxCenter());
			   if (%healthDist < 0)
				   %healthDist = 32767;
            else
               %healthItem = %closestHealth;
		   }

		   //else search for an inventory station
		   else
		   {
			   %result = AIFindClosestInventory(%client, false);
			   %closestInv = getWord(%result, 0);
			   %closestDist = getWord(%result, 1);
			   if (isObject(%closestInv))
            {
				   %healthDist = %closestDist;
               %healthItem = %closestInv;
            }
		   }
	   }
      else
      {
         %healthDist = %client.getPathDistance(%task.getHealth.getWorldBoxCenter());
         if (%healthDist < 0)
         {
            %healthDist = 32767;
            %task.getHealth = -1;
         }
      }
   }
   else
      %task.getHealth = -1;

	//see if we need to cap - make sure we're actually able first 
	%mustCap = false;
	%shouldCap = false;
	%numToScore = %client.flagCount - 1;
	%hoardModeOn = Game.hoardModeActive();
	if ((!Game.greedMode || %numToScore >= Game.greedMinFlags) && !Game.hoardModeActive() && %numToScore >= $AIHuntersMinFlagsToCap)
	{
		//find out how many points would be scored
      %potentialScore = (%numToScore * (%numToScore + 1)) / 2;

		//find out how many flags we need to take the lead...
		%needFlagsForLead = 0;
		%highestScore = 0;
		%clientIsInLead = false;
		if (Game.teamMode)
		{
			%teamCount = Game.numTeams;
			for (%i = 1; %i <= %teamCount; %i++)
			{
				if ($teamScore[%i] > %highestScore)
					%highestScore = $teamScore[%i];
			}

			//see if we're in the lead...
			if (%highestScore == $teamScore[%client.team])
				%clientIsInLead = true;
			else
			{
				%tempScore = $teamScore[%client.team] + %potentialScore;
				%flagValue = %numToScore + 1;
				while (%tempScore < %highestScore)
				{
					%tempScore += %flagValue;
					%flagValue++;
					%needFlagsForLead++;
				}
			}
		}
		else
		{
			%clientCount = ClientGroup.getCount();
			for (%i = 0; %i < %clientCount; %i++)
			{
				%cl = ClientGroup.getObject(%i);
				if (%cl.score > %highestScore)
					%highestScore = %cl.score;
			}

			//see if we're in the lead
			if (%highestScore == %client.score)
				%clientIsInLead = true;
			else
			{
				%tempScore = %client.score + %potentialScore;
				%flagValue = %numToScore + 1;
				while (%tempScore < %highestScore)
				{
					%tempScore += %flagValue;
					%flagValue++;
					%needFlagsForLead++;
				}
			}
		}

		//the current target is more dangerous than the closest enemy
		%currentTarget = %client.getEngageTarget();
		if (AIClientIsAlive(%currentTarget))
		{
			%closestEnemy = %currentTarget;
		   %closestEnemydist = %client.getPathDistance(%currentTarget.player.position);
			if (%closestEnemyDist < 0)
				%closestEnemyDist = 32767;
		}
		else
		{
         %losTimeout = $AIClientMinLOSTime + ($AIClientLOSTimeout * %client.getSkillLevel());
			%result = AIFindClosestEnemy(%client, $AIHuntersCloseFlagDist, %losTimeout);
	      %closestEnemy = getWord(%result, 0);
		   %closestEnemydist = getWord(%result, 1);
		}

		//find out how much time is left...
	 	%curTimeLeftMS = ($Host::TimeLimit * 60 * 1000) + $missionStartTime - getSimTime();

		//If there's a tough or equal enemy nearby, or no flags, think about capping
		//ie.  never cap if there are flags nearby and no enemies...
      if ((AICheckEnemyDanger(%client, 35) >= 3 && %damage > %damageTolerance) || 
			(!isObject(%closestFlag) || %closestFlagDist > $AIHuntersCloseFlagDist))
		{
			//if we've got enough to take the lead, and there are no flags in the vicinity
			if ((!%clientIsInLead && %needFlagsForLead == 0) || %highestScore == 0)
				%mustCap = true;

			//else if we're about to get our butt kicked...
			else if (AIClientIsAlive(%closestEnemy) && AIEngageWhoWillWin(%closestEnemy, %client) == %closestEnemy)
			{
				%mustCap = true;
			}

			//else if there's no time left in the mission, cap whatever we've got now...
			else if (%curTimeLeftMS <= 30000)
				%mustCap = true;

			//else we don't need to cap - see if we should to play it smart
			else
			{
				//if we'd need more than just a couple to take the lead...
				%waitForFlagsTolerance = %client.getSkillLevel() * $AIHuntersMinFlagsToCap * 2;
				if (%needFlagsForLead == 0 || (%needFlagsForLead > %waitForFlagsTolerance))
				{
					%numEnemyFlags = 0;
					%clientCount = ClientGroup.getCount();
					for (%i = 0; %i < %clientCount; %i++)
					{
						%cl = ClientGroup.getObject(%i);
						if (%cl != %client && %cl.flagCount > %numEnemyFlags)
							%numEnemyFlags = %cl.flagCount;
					}

					//if we're in the lead, or no one has the flags we need, or it's team mode,
					//decide whether to cap based on skill level
					if (%needFlagsForLead == 0 || %numEnemyFlags < %needFlagsForLead || Game.teamMode)
					{
						if (%numToScore >= $AIHuntersMinFlagsToCap + (%client.getSkillLevel() * %client.getSkillLevel() * 15))
                  {
							%shouldCap = true;
                  }
					}
				}
			}
		}

		//now that we've checked all the possibilities, see if we should or must cap
		if (%mustCap || %shouldCap)
		{
			if (%mustCap)
				%task.setWeight($AIHuntersWeightMustCap);
			else
				%task.setWeight($AIHuntersWeightShouldCap);

			%task.capFlags = true;
			return;
		}
	}

   ////////////////////////////////////////////////////////////////////////////////////
	//  if we've made it this far, we either can't cap, or there's no need to cap...  //
   ////////////////////////////////////////////////////////////////////////////////////

   //see if we need health
   if (%damage > %damageTolerance && (isObject(%healthItem) || isObject(%task.getHealth)))
   {
      if (!isObject(%task.getHealth))
         %task.getHealth = %healthItem;

      %task.setWeight($AIHuntersWeightNeedHealth);
      return;
   }
	
	//find the closest player with the most flags (that we have los to)
   %losTimeout = $AIClientMinLOSTime + ($AIClientLOSTimeout * %client.getSkillLevel());
	%bestClientToEngage = findClientWithMostFlags(%client, %losTimeout);
	%bestClientDist = 32767;
	if (AIClientIsAlive(%bestClientToEngage))
	{
		%bestClientDist = %client.getPathDistance(%bestClientToEngage.player.position);
		if (%bestClientDist < 0)
			%bestClientDist = 32767;
	}

	//see if there's a flag
	if (isObject(%closestFlag))
	{
		//see if there's a client to shoot
		if (AIClientIsAlive(%bestClientToEngage))
		{
			//calc weight base on closesness to the nearest flag vs. and number of flags the client has...
			%engageDistFactor = 30 + %bestClientDist - (%bestClientToEngage.flagCount * 5);
			if (%closestFlagDist < %engageDistFactor)
			{
			   %task.pickupFlag = %closestFlag;
				%task.engageTarget = %bestClientToEngage;
				%task.setWeight($AIHuntersWeightPickupFlag);
			}

			//otherwise, ignore the flag, and go for the client
			else
			{
			   %task.pickupFlag = -1;
				%task.engageTarget = %bestClientToEngage;
				%task.setWeight($AIHuntersWeightMustEngage);
			}
		}

		//else no one to attack
		else
		{
		   %task.pickupFlag = %closestFlag;
			%task.engageTarget = -1;
			%task.setWeight($AIHuntersWeightPickupFlag);
		}
	}

	//else no flag, see if we have someone to attack
	else if (AIClientIsAlive(%bestClientToEngage))
	{
	   %task.pickupFlag = -1;
		%task.engageTarget = %bestClientToEngage;
		%task.setWeight($AIHuntersWeightShouldEngage);
	}

	//nothing hunter's related to do...
	else
	{
	   %task.pickupFlag = -1;
		%task.engageTarget = -1;
		%task.setWeight(0);
	}
}

//---------------------------------------------------------------------------

function AIHuntersTask::monitor(%task, %client)
{
   //see if we should cap
   if (%task.capFlags)
	{
		%nexusPos = Game.Nexus.getId().getWorldBoxCenter();
      %client.stepMove(%nexusPos, 0.25);
	}

	//see if we've got a flag to pick up and/or someone to engage
   else if (isObject(%task.pickupFlag))
   {   
      %client.stepMove(%task.pickupFlag.getWorldBoxCenter(), 0.25);

		if (AIClientIsAlive(%task.engageTarget))
			%client.setEngageTarget(%task.engageTarget);
   }

	//see if we've should go for health...
   else if (isObject(%task.getHealth))
   {
      %client.stepMove(%task.getHealth.getWorldBoxCenter(), 1);

		if (AIClientIsAlive(%task.engageTarget))
			%client.setEngageTarget(%task.engageTarget);
   }

	//else see if there's just someone to engage
	else if (AIClientIsAlive(%task.engageTarget))
		%client.stepEngage(%task.engageTarget);
	
	//if we're not engaging someone related to the hunters task, engage whoever the AIEngageTask wants...
	else
		%client.setEngageTarget(%client.shouldEngage);
}

//---------------------------------------------------------------------------
// AIHunters utility functions
//---------------------------------------------------------------------------

//this function checks to make sure a bot isn't in a mosh pit of enemies
//notice it cheats by not using LOS...  )
function AICheckEnemyDanger(%client, %radius)
{
   %numEnemies = 0;
   for (%i = 0; %i < ClientGroup.getCount(); %i++)
   {
      %cl = ClientGroup.getObject(%i);
      if (AIClientIsAlive(%cl) && %cl.team != %client.team)
      {
         %dist = %client.getPathDistance(%cl.player.position);
         if (%dist < %radius)
         {
            %winner = AIEngageWhoWillWin(%cl, %client);
            if (%winner == %cl)
               %numEnemies += 3;
            else if (%winner != %client)
               %numEnemies++;
         }
      }
   }

   return %numEnemies;
}

function AIFindClosestFlag(%client, %radius)
{
   %closestFlag = -1;
	%closestDist = %radius;
	%flagCount = $FlagGroup.getCount();
	for (%i = 0; %i < %flagCount; %i++)
	{
		%flag = $FlagGroup.getObject(%i);
		%flagPos = %flag.getWorldBoxCenter();
		%dist = %client.getPathDistance(%flagPos);

		if (%dist > 0 && %dist < %closestDist)
		{
			%closestDist = %dist;
			%closestFlag = %flag;
		}
	}
	return %closestFlag @ " " @ %closestDist;
}

//---------------------------------------------------------------------------

function findClientWithMostFlags(%srcClient, %losTimeout)
{
   %clientCount = 0;
   %closestClient = -1;
   %highestFlagFactor = -1;		//take both distance and flag count into consideration

   %count = ClientGroup.getCount();
   for(%i = 0; %i < %count; %i++)
   {
		%cl = ClientGroup.getObject(%i);

		//make sure we find someone who's alive, and on an opposing team
		if (AIClientIsAlive(%cl) && %cl.team != %srcClient.team)
		{
			%clIsCloaked = false;
			if (%cl.player.getInventory("CloakingPack") > 0 && %cl.player.getImageState($BackpackSlot) $= "activate")
				%clIsCloaked = true;

			//make sure the client can see the enemy
			%hasLOS = %srcClient.hasLOSToClient(%cl);
			%losTime = %srcClient.getClientLOSTime(%cl);
			if (%hasLOS || (%losTime < %losTimeout && AIClientIsAlive(%cl, %losTime + 1000)))
			{
	         %testPos = %cl.player.getWorldBoxCenter();
	         %distance = %srcClient.getPathDistance(%testPos);
				if (%distance < 0)
					%distance = 32767;

				//calculate the flag factor
				%flagFactor = (100 - %distance) + (%cl.flagCount * 5);

				//see if it's the most suitable client...
	         if (%flagFactor > %highestFlagFactor && (!%clIsCloaked || %distance < 8))
	         {
	            %closestClient = %cl;
	            %highestFlagFactor = %flagFactor;
	         }
			}
      }
   }
   
   return %closestClient;
}

//---------------------------------------------------------------------------

function aih()
{
	exec("scripts/aiHunters.cs");
}

function aiHlist()
{
	%count = ClientGroup.getCount();
	for (%i = 0; %i < %count; %i++)
	{
		%cl = ClientGroup.getObject(%i);
		if (%cl.isAIControlled())
			error(%cl SPC getTaggedString(%cl.name) SPC "score:" SPC %cl.score SPC "flags:" SPC %cl.flagCount - 1 SPC "capFlags:" SPC %cl.huntersTask.capFlags);
	}
}

