//------------------------------
//AI Objective Q functions...

$ObjectiveClientsSet = 0;
function AIObjectiveFindClients(%objective)
{
   //create and clear the set
   if (! $ObjectiveClientsSet)
   {
      $ObjectiveClientsSet = new SimSet();
      MissionCleanup.add($ObjectiveClientSet);
   }
   $ObjectiveClientsSet.clear();
   
   %clientCount = 0;
   %count = ClientGroup.getCount();
   for(%i = 0; %i < %count; %i++)
   {
		%cl = ClientGroup.getObject(%i);
      if (%cl.objective == %objective)
         $ObjectiveClientsSet.add(%cl);
   }
   return $ObjectiveClientsSet.getCount();
}

function AIObjectiveGetClosestClient(%location, %team)
{
   if (%location $= "")
      return -1;
      
   if (%team $= "")
      %team = 0;
      
   %closestClient = -1;
   %closestDistance = 32767;
   %count = ClientGroup.getCount();
   for(%i = 0; %i < %count; %i++)
   {
		%cl = ClientGroup.getObject(%i);
      if (%cl.isAIControlled() && (%cl.team == %team || %team == 0) && AIClientIsAlive(%cl))
      {
         %testPos = %cl.player.getWorldBoxCenter();
         %distance = VectorDist(%location, %testPos);
         if (%distance < %closestDistance)
         {
            %closestDistance = %distance;
            %closestClient = %cl;
         }
      }
	}
   
   return %closestClient;
}

function AICountObjectives(%team)
{
	%objCount = 0;
	%count = $ObjectiveQ[%team].getCount();
	for (%i = 0; %i < %count; %i++)
	{
		%grp = $ObjectiveQ[%team].getObject(%i);
		if (%grp.getClassName() !$= "AIObjective")
			%objCount += %grp.getCount();
		else
			%objCount++;
	}
	error("DEBUG" SPC %team SPC "has" SPC %objCount SPC "objectives.");
}

function AIAddTableObjective(%objective, %weight, %level, %bump, %position)
{
	$objTable[%position, objective] = %objective;
	$objTable[%position, weight] = %weight;
	$objTable[%position, level] = %level;
	$objTable[%position, bump] = %bump;
	$objTableCount = %position + 1;
}

function AIChooseObjective(%client, %useThisObjectiveQ)
{
	//pick which objectiveQ to use, or use the default
	if (%useThisObjectiveQ <= 0 && %client.team < 0)
		return;

	if (%useThisObjectiveQ <= 0)
		%useThisObjectiveQ = $ObjectiveQ[%client.team];

	if (!isObject(%useThisObjectiveQ) || %useThisObjectiveQ.getCount() <= 0)
		return;

	//since most objectives check for inventory, find the closest inventory stations first
	%inventoryStr = AIFindClosestInventories(%client);

   //find the most appropriate objective
   if (!%client.objective)
	{
		//note, the table is never empty, during the course of this function
		AIAddTableObjective(0, 0, 0, 0, 0);
	}
   else
	{
		//should re-evaluate the current objective weight - but never decrease the weight!!!
		%testWeight = %client.objective.weight(%client, %client.objectiveLevel, 0, %inventoryStr);
		if (%testWeight <= 0 || %testWeight > %client.objectiveWeight)
			%client.objectiveWeight = %testWeight;

		if (%client.objectiveWeight > 0)
			AIAddTableObjective(%client.objective, %client.objectiveWeight, %client.objectiveLevel, 0, 0);
		else
			AIAddTableObjective(0, 0, 0, 0, 0);
	}

   %objCount = %useThisObjectiveQ.getCount();
 	for (%i = 0; %i < %objCount; %i++)
 	{
	   %objective = %useThisObjectiveQ.getObject(%i);

		//don't re-evaluate the client's own
		if (%objective == %client.objective)
			continue;

		//try this objective at each of the 4 weight levels to see if it is weighted higher
		for (%level = 1; %level <= 4; %level++)
		{
			%minWeight = 0;
			%bumpWeight = 0;
			%bumpClient = "";

			//we can bump clients off the objective for the first three levels
			if (%level <= 3)
			{
				//if the objective is part of a group, check the whole group
				if (%objective.group > 0)
				{
					%bumpClient = %objective.group.clientLevel[%level];
					%bumpWeight = %bumpClient.objectiveWeight;
				}
				else
				{
					%bumpClient = %objective.clientLevel[%level];
					%bumpWeight = %bumpClient.objectiveWeight;
				}
			}

			//find the minimum weight the objective must have to be considered
			%minWeight = (%bumpWeight > $objTable[0, weight] ? %bumpWeight : $objTable[0, weight]);

			//evaluate the weight
	      %weight = %objective.weight(%client, %level, %minWeight, %inventoryStr);

			//make sure we got a valid weight
			if (%weight <= 0)
				break;

			//if it's the highest so far, it now replaces anything else in the table
			if (%weight > $objTable[0, weight])
			{
				//never bump someone unless you out- weight them
				if (%weight > %bumpWeight)
				{
					AIAddTableObjective(%objective, %weight, %level, %bumpClient, 0);

					//no need to keep checking the other levels
					break;
				}
			}

			//else if it's equal to the highest objective we've seen so far, and higher from our current objective
			else if (%weight == $objTable[0, weight] && %weight > %client.objectiveWeight)
			{
				//never bump someone unless you outweigh them
				if (%weight > %bumpWeight)
				{
					//if this wouldn't require us to bump someone, or the table is empty
					if (%bumpWeight <= 0 || $objTable[0, weight] <= 0)
					{
						//if the table currently contains objectives which would require bumping someone, clear it
						if ($objTable[0, bump] > 0)
							%position = 0;
						else
							%position = $objTableCount;

						//add it to the table
						AIAddTableObjective(%objective, %weight, %level, %bumpClient, %position);

						//no need to keep checking the other levels
						break;
					}

					//otherwise, the table is not empty, and this would require us to bump someone
					//only add it if everything else in the table would also require us to bump someone
					else if ($objTable[0, bump] > 0)
					{
						AIAddTableObjective(%objective, %weight, %level, %bumpClient, $objTableCount);

						//no need to keep checking the other levels
						break;
					}
				}
			}

			//else it must have been less than our highest objective so far- again, no need to keep checking other levels...
			else
				break;
		}
	}

	//if we have a table of possible objectives which are higher than our current- choose one at random
	if ($objTableCount > 0 && $objTable[0, objective] != %client.objective)
	{
		//choose the new one
		%index = mFloor(getRandom() * ($objTableCount - 0.01));

		//clear the old objective
      if (%client.objective)
      {
         if (%client.objectiveLevel <= 3)
			{
				if (%client.objective.group > 0)
				{
					if (%client.objective.group.clientLevel[%client.objectiveLevel] == %client)
						%client.objective.group.clientLevel[%client.objectiveLevel] = "";
				}
				else
				{
					if (%client.objective.clientLevel[%client.objectiveLevel] == %client)
		            %client.objective.clientLevel[%client.objectiveLevel] = "";
				}
			}
         %client.objective.unassignClient(%client);
      }
		
		//assign the new
      %chooseObjective = $objTable[%index, objective];
      %client.objective = %chooseObjective;
      %client.objectiveWeight = $objTable[%index, weight];
      %client.objectiveLevel = $objTable[%index, level];
		if (%client.objectiveLevel <= 3)
		{
			if (%chooseObjective.group > 0)
		      %chooseObjective.group.clientLevel[%client.objectiveLevel] = %client;
			else
		      %chooseObjective.clientLevel[%client.objectiveLevel] = %client;
		}
      %chooseObjective.assignClient(%client);

      //see if this objective needs to be acknowledged
      if (%chooseObjective.shouldAcknowledge && %chooseObjective.issuedByHuman && isObject(%chooseObjective.issuedByClientId))
      {
         //cancel any pending acknowledgements - a bot probably just got bumped off this objective
         cancel(%chooseObjective.ackSchedule);
         %chooseObjective.ackSchedule = schedule(5500, %chooseObjective, "AIAcknowledgeObjective", %client, %chooseObjective);
      }

      //if we had to bump someone off this objective, 
		%bumpClient = $objTable[%index, bump];
      if (%bumpClient > 0)
      {
         //unassign the bumped client and choose a new objective
			AIUnassignClient(%bumpClient);
         Game.AIChooseGameObjective(%bumpClient);
      }
	}

   //debuging - refresh aidebugq() if required
   //if ($AIDebugTeam >= 0)
   //   aiDebugQ($AIDebugTeam);
}

function AIAcknowledgeObjective(%client, %objective)
{
   %objective.shouldAcknowledge = false;
   //make sure the client is still assigned to this objective
   if (%client.objective == %objective)
	   serverCmdAcceptTask(%client, %objective.issuedByClientId, -1, %objective.ackDescription);
}

function AIForceObjective(%client, %newObjective, %useWeight)
{
   //if we found a new objective, release the old, and assign the new
   if (%newObjective && %newObjective != %client.objective)
   {
      //see if someone was already assigned to this objective
		if (%newObjective.group > 0)
	      %prevClient = newObjective.group.clientLevel[1];
		else
	      %prevClient = newObjective.clientLevel[1];
      if (%prevClient > 0)
         AIUnassignClient(%prevClient);

		//see if we should override the weight
		if (%useWeight < %newObjective.weightLevel1)
			%useWeight = %newObjective.weightLevel1;
      
      //release the client, and force the assignment
      AIUnassignClient(%client);
      %client.objective = %newObjective;
      %client.objectiveWeight = %useWeight;
      %client.objectiveLevel = 1;
		if (%newObjective.group > 0)
	      %newObjective.group.clientLevel[1] = %client;
		else
	      %newObjective.clientLevel[1] = %client;
		%newObjective.forceClientId = %client;
      %newObjective.assignClient(%client);

      //don't acknowledge anything that's been forced...
      %newObjective.shouldAcknowledge = false;
      
      //now reassign the prev client
      if (%prevClient)
         Game.AIChooseGameObjective(%prevClient);
   }
   
   //debuging - refresh aidebugq() if required
   //if ($AIDebugTeam >= 0)
   //   aiDebugQ($AIDebugTeam);
}

function AIUnassignClient(%client)
{
	//first, dissolve any link with a human
	aiReleaseHumanControl(%client.controlByHuman, %client);

   if (%client.objective)
   {
      if (%client.objectiveLevel <= 3)
		{
			if (%client.objective.group > 0)
			{
				//make sure the clientLevel was actually this client
				if (%client.objective.group.clientLevel[%client.objectiveLevel] == %client)
					%client.objective.group.clientLevel[%client.objectiveLevel] = "";
			}
			else
			{
				if (%client.objective.clientLevel[%client.objectiveLevel] == %client)
		         %client.objective.clientLevel[%client.objectiveLevel] = "";
			}
		}
      %client.objective.unassignClient(%client);
      %client.objective = "";
		%client.objectiveWeight = 0;
   }
   
   //debuging - refresh aidebugq() if required
   //if ($AIDebugTeam >= 0)
   //   aiDebugQ($AIDebugTeam);
}
  
function AIClearObjective(%objective)
{
   %count = ClientGroup.getCount();
   for(%i = 0; %i < %count; %i++)
   {
		%cl = ClientGroup.getObject(%i);
      if (%cl.objective == %objective)
         AIUnassignClient(%cl);
   }
   
   //debuging - refresh aidebugq() if required
   //if ($AIDebugTeam >= 0)
   //   aiDebugQ($AIDebugTeam);
}
 
//------------------------------
//TASKS AND OBJECTIVES

function AIDefendLocation::initFromObjective(%task, %objective, %client)
{
	//initialize the task vars from the objective
   %task.baseWeight = %client.objectiveWeight;
	%task.targetObject = %objective.targetObjectId;
	if (%objective.Location !$= "")
	   %task.location = %objective.location;
	else
	   %task.location = %objective.targetObjectId.getWorldBoxCenter();

   %task.equipment = %objective.equipment;
	%task.buyEquipmentSet = %objective.buyEquipmentSet;
	%task.desiredEquipment = %objective.desiredEquipment;
	%task.issuedByClient = %objective.issuedByClientId;
	%task.chat = %objective.chat;

	//initialize other task vars
	%task.sendMsg = true;
	%task.sendMsgTime = 0;
   
   %task.engageTarget = -1;
}

function AIDefendLocation::assume(%task, %client)
{
   %task.setWeightFreq(15);
   %task.setMonitorFreq(15);
   %client.inPerimeter = false;
	%client.needEquipment = AINeedEquipment(%task.equipment, %client);

	//even if we don't *need* equipemnt, see if we should buy some... 
	if (! %client.needEquipment && %task.buyEquipmentSet !$= "")
	{
		//see if we could benefit from inventory
		%needArmor = AIMustUseRegularInvStation(%task.desiredEquipment, %client);
		%result = AIFindClosestInventory(%client, %needArmor);
		%closestInv = getWord(%result, 0);
		%closestDist = getWord(%result, 1);
		if (AINeedEquipment(%task.desiredEquipment, %client) && %closestInv > 0)
		{
			%result = AIFindClosestEnemy(%client, 200, $AIClientLOSTimeout);
	      %closestEnemy = getWord(%result, 0);
		   %closestEnemydist = getWord(%result, 1);

			if (%closestEnemy <= 0 || (%closestEnemyDist > %closestDist * 1.5))
				%client.needEquipment = true;
		}
	}

	//mark the current time for the buy inventory state machine
	%task.buyInvTime = getSimTime();

	//set a flag to determine if the objective should be re-aquired when the object is destroyed
	%task.reassignOnDestroyed = false;
}

function AIDefendLocation::retire(%task, %client)
{
	%task.engageVehicle = -1;
	%client.setTargetObject(-1);
}

function AIDefendLocation::weight(%task, %client)
{
	//update the task weight
	if (%task == %client.objectiveTask)
		%task.baseWeight = %client.objectiveWeight;

	%player = %client.player;
	if (!isObject(%player))
		return;

   %hasMissile = (%player.getInventory("MissileLauncher") > 0) && (%player.getInventory("MissileLauncherAmmo") > 0);

	//if we're defending with a missile launcher, our first priority is to take out vehicles...
	//see if we're already attacking a vehicle...
	if (%task.engageVehicle > 0 && isObject(%task.engageVehicle) && %hasMissile)
	{
		//set the weight
	   %task.setWeight(%task.baseWeight);
		return;
	}

	//search for a new vehicle to attack
	%task.engageVehicle = -1;
   %losTimeout = $AIClientMinLOSTime + ($AIClientLOSTimeout * %client.getSkillLevel());
	%result = AIFindClosestEnemyPilot(%client, 300, %losTimeout);
	%pilot = getWord(%result, 0);
	%pilotDist = getWord(%result, 1);

	//if we've got missiles, and a vehicle to attack...
	if (%hasMissile && AIClientIsAlive(%pilot))
	{
		%task.engageVehicle = %pilot.vehicleMounted;
		%client.needEquipment = false;
	}

	//otherwise look for a regular enemy to fight...
	else
	{
	   %result = AIFindClosestEnemyToLoc(%client, %task.location, 100, %losTimeout);
	   %closestEnemy = getWord(%result, 0);
	   %closestdist = getWord(%result, 1);
	   
	   //see if we found someone
	   if (%closestEnemy > 0)
	      %task.engageTarget = %closestEnemy;
	   else
		{
	      %task.engageTarget = -1;

			//see if someone is near me...
			%result = AIFindClosestEnemy(%client, 100, %losTimeout);
		   %closestEnemy = getWord(%result, 0);
		   %closestdist = getWord(%result, 1);
			if (%closestEnemy <= 0 || %closestDist > 70)
				%client.setEngageTarget(-1);
		}
	}

	//set the weight
   %task.setWeight(%task.baseWeight);
}

function AIDefendLocation::monitor(%task, %client)
{
   //first, buy the equipment
   if (%client.needEquipment)
   {
	   %task.setMonitorFreq(5);
		if (%task.equipment !$= "")
			%equipmentList = %task.equipment;
		else
			%equipmentList = %task.desiredEquipment;
      %result = AIBuyInventory(%client, %equipmentList, %task.buyEquipmentSet, %task.buyInvTime);
		if (%result $= "InProgress")
			return;
		else if (%result $= "Finished")
		{
		   %task.setMonitorFreq(15);
			%client.needEquipment = false;
		}
		else if (%result $= "Failed")
		{
	      //if this task is the objective task, choose a new objective
	      if (%task == %client.objectiveTask)
	      {
	         AIUnassignClient(%client);
	         Game.AIChooseGameObjective(%client);
	      }
			return;
		}
   }
	//if we made it past the inventory buying, reset the inv time
	%task.buyInvTime = getSimTime();

	//chat
	if (%task.sendMsg)
	{
		if (%task.sendMsgTime == 0)
			%task.sendMsgTime = getSimTime();
		else if (getSimTime() - %task.sendMsgTime > 7000)
		{
			%task.sendMsg = false;
		   if (%client.isAIControlled())
			{
				if (%task.chat !$= "")
				{
					%chatMsg = getWord(%task.chat, 0);
					%chatTemplate = getWord(%task.chat, 1);
					if (%chatTemplate !$= "")
						AIMessageThreadTemplate(%chatTemplate, %chatMsg, %client, -1);
					else
						AIMessageThread(%task.chat, %client, -1);
				}
				else if (%task.targetObject > 0)
				{
					%type = %task.targetObject.getDataBlock().getName();
					if (%type $= "Flag")
						AIMessageThreadTemplate("DefendBase", "ChatSelfDefendFlag", %client, -1);
					else if (%type $= "GeneratorLarge")
						AIMessageThreadTemplate("DefendBase", "ChatSelfDefendGenerator", %client, -1);
					else if (%type $= "StationVehicle")
						AIMessageThreadTemplate("DefendBase", "ChatSelfDefendVehicle", %client, -1);
					else if (%type $= "SensorLargePulse")
						AIMessageThreadTemplate("DefendBase", "ChatSelfDefendSensors", %client, -1);
					else if (%type $= "SensorMediumPulse")
						AIMessageThreadTemplate("DefendBase", "ChatSelfDefendSensors", %client, -1);
					else if (%type $= "TurretBaseLarge")
						AIMessageThreadTemplate("DefendBase", "ChatSelfDefendTurrets", %client, -1);
				}
			}
		}
	}

	//if the defend location task has an object, set the "reset" flag
	if (%task == %client.objectiveTask && isObject(%task.targetObject))
	{
		if (%task.targetObject.getDamageState() !$= "Destroyed")
			%task.reassignOnDestroyed = true;
		else
		{
			if (%task.reassignOnDestroyed)
			{
	         AIUnassignClient(%client);
	         Game.AIChooseGameObjective(%client);
				return;
			}
		}
	}

	//first, check for a vehicle to engage
	if (%task.engageVehicle > 0 && isObject(%task.engageVehicle))
	{
		%client.stop();
		%client.clearStep();
		%client.setEngageTarget(-1);
		%client.setTargetObject(%task.engageVehicle, 300, "Missile");
	}
	else
	{
		//clear the target vehicle...
		%client.setTargetObject(-1);

	   //see if we're engaging a player
	   if (%client.getEngageTarget() > 0)
	   {
	      //too far, or killed the enemy - return home
	      if (%client.getStepStatus() !$= "InProgress" || %distance > 75)
	      {
	         %client.setEngageTarget(-1);
	         %client.stepMove(%task.location, 8.0);
	      } 
	   }
	   
	   //else see if we have a target to begin attacking
	   else if (%task.engageTarget > 0)
	      %client.stepEngage(%task.engageTarget);
	      
	   //else move to a random location around where we are defending
	   else if (%client.getStepName() !$= "AIStepIdlePatrol")
		{
			%dist = VectorDist(%client.player.getWorldBoxCenter(), %task.location);
			if (%dist < 10)
			{
				//dissolve the human control link and re-evaluate the weight
				if (%task == %client.objectiveTask)
				{
					if (aiHumanHasControl(%task.issuedByClient, %client))
					{
						aiReleaseHumanControl(%client.controlByHuman, %client);

						//should re-evaluate the current objective weight
						%inventoryStr = AIFindClosestInventories(%client);
						%client.objectiveWeight = %client.objective.weight(%client, %client.objectiveLevel, 0, %inventoryStr);
					}
				}

		      %client.stepIdle(%task.location);
			}
			else
		      %client.stepMove(%task.location, 8.0);
		}
	}

   //see if we're supposed to be engaging anyone...
   if (!AIClientIsAlive(%client.getEngageTarget()) && AIClientIsAlive(%client.shouldEngage))
      %client.setEngageTarget(%client.shouldEngage);
}

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

function AIAttackLocation::initFromObjective(%task, %objective, %client)
{
	//initialize the task vars from the objective
   %task.baseWeight = %client.objectiveWeight;
   %task.location = %objective.location;
   %task.equipment = %objective.equipment;
	%task.buyEquipmentSet = %objective.buyEquipmentSet;
	%task.desiredEquipment = %objective.desiredEquipment;
	%task.issuedByClient = %objective.issuedByClientId;
	%task.chat = %objective.chat;

	//initialize other task vars
	%task.sendMsg = true;
	%task.sendMsgTime = 0;
   %task.engageTarget = -1;
}

function AIAttackLocation::assume(%task, %client)
{
   %task.setWeightFreq(30);
   %task.setMonitorFreq(30);
	%client.needEquipment = AINeedEquipment(%task.equipment, %client);

	//even if we don't *need* equipemnt, see if we should buy some... 
	if (! %client.needEquipment && %task.buyEquipmentSet !$= "")
	{
		//see if we could benefit from inventory
		%needArmor = AIMustUseRegularInvStation(%task.desiredEquipment, %client);
		%result = AIFindClosestInventory(%client, %needArmor);
		%closestInv = getWord(%result, 0);
		%closestDist = getWord(%result, 1);
		if (AINeedEquipment(%task.desiredEquipment, %client) && %closestInv > 0)
		{
			%result = AIFindClosestEnemyToLoc(%client, %task.location, 50, $AIClientLOSTimeout);
	      %closestEnemy = getWord(%result, 0);

			if (%closestEnemy <= 0)
				%client.needEquipment = true;
		}
	}

	//mark the current time for the buy inventory state machine
	%task.buyInvTime = getSimTime();

	%task.snipeLocation = "";
	%task.hideLocation = "";
	%task.moveToPosition = true;
	%task.moveToSnipe = false;
	%task.nextSnipeTime = 0;
}

function AIAttackLocation::retire(%task, %client)
{
}

function AIAttackLocation::weight(%task, %client)
{
	//update the task weight
	if (%task == %client.objectiveTask)
		%task.baseWeight = %client.objectiveWeight;

	//if we're a sniper, we're going to cheat, and see if there are clients near the attack location
   %losTimeout = $AIClientMinLOSTime + ($AIClientLOSTimeout * %client.getSkillLevel());
	%distToLoc = VectorDist(%client.player.getWorldBoxCenter(), %task.location);
	if (%client.player.getInventory(SniperRifle) > 0 && %client.player.getInventory(EnergyPack) > 0 && %distToLoc > 60)
		%result = AIFindClosestEnemyToLoc(%client, %task.location, 50, $AIClientLOSTimeout, true);

	//otherwise, do the search normally.  (cheat ignores LOS)...
	else
		%result = AIFindClosestEnemyToLoc(%client, %task.location, 50, %losTimeout, false);

   %closestEnemy = getWord(%result, 0);
   %closestdist = getWord(%result, 1);
   %task.setWeight(%task.baseWeight);
   
   //see if we found someone
   if (%closestEnemy > 0)
      %task.engageTarget = %closestEnemy;
   else
      %task.engageTarget = -1;
}

function AIAttackLocation::monitor(%task, %client)
{
   //first, buy the equipment
   if (%client.needEquipment)
   {
	   %task.setMonitorFreq(5);
		if (%task.equipment !$= "")
			%equipmentList = %task.equipment;
		else
			%equipmentList = %task.desiredEquipment;
      %result = AIBuyInventory(%client, %equipmentList, %task.buyEquipmentSet, %task.buyInvTime);
		if (%result $= "InProgress")
			return;
		else if (%result $= "Finished")
		{
		   %task.setMonitorFreq(30);
			%client.needEquipment = false;
		}
		else if (%result $= "Failed")
		{
	      //if this task is the objective task, choose a new objective
	      if (%task == %client.objectiveTask)
	      {
	         AIUnassignClient(%client);
	         Game.AIChooseGameObjective(%client);
	      }
			return;
		}
   }
	//if we made it past the inventory buying, reset the inv time
	%task.buyInvTime = getSimTime();

	//chat
	if (%task.sendMsg)
	{
		if (%task.sendMsgTime == 0)
			%task.sendMsgTime = getSimTime();
		else if (getSimTime() - %task.sendMsgTime > 7000)
		{
			%task.sendMsg = false;
		   if (%client.isAIControlled())
			{
				if (%task.chat !$= "")
				{
					%chatMsg = getWord(%task.chat, 0);
					%chatTemplate = getWord(%task.chat, 1);
					if (%chatTemplate !$= "")
						AIMessageThreadTemplate(%chatTemplate, %chatMsg, %client, -1);
					else
						AIMessageThread(%task.chat, %client, -1);
				}
				else
					AIMessageThreadTemplate("AttackBase", "ChatSelfAttack", %client, -1);
			}
		}
	}

   //how far are we from the location we're defending
   %myPos = %client.player.getWorldBoxCenter();
   %distance = %client.getPathDistance(%task.location);
	if (%distance < 0)
		%distance = 32767;

	if (%client.player.getInventory(SniperRifle) > 0 && %client.player.getInventory(EnergyPack) > 0)
	{
		//first, find an LOS location
		if (%task.snipeLocation $= "")
		{
			%task.snipeLocation = %client.getLOSLocation(%task.location, 150, 250);
			%task.hideLocation = %client.getHideLocation(%task.location, VectorDist(%task.location, %task.snipeLocation), %task.snipeLocation, 1); 
			%client.stepMove(%task.hideLocation, 4.0);
			%task.moveToPosition = true;
		}
		else
		{
			//see if we can acquire a target
			%energy = %client.player.getEnergyPercent();
			%distToSnipe = VectorDist(%task.snipelocation, %client.player.getWorldBoxCenter());
			%distToHide = VectorDist(%task.hidelocation, %client.player.getWorldBoxCenter());

			//until we're in position, we can move using the AIModeExpress, after that, we only want to walk...
			if (%task.moveToPosition)
			{
				if (%distToHide < 4.0)
				{
					//dissolve the human control link
					if (%task == %client.objectiveTask)
					{
						if (aiHumanHasControl(%task.issuedByClient, %client))
						{
							aiReleaseHumanControl(%client.controlByHuman, %client);

							//should re-evaluate the current objective weight
							%inventoryStr = AIFindClosestInventories(%client);
							%client.objectiveWeight = %client.objective.weight(%client, %client.objectiveLevel, 0, %inventoryStr);
						}
					}

					%task.moveToPosition = false;
				}
			}

			else if (%task.moveToSnipe)
			{
				if (%energy > 0.75 && %client.getStepStatus() $= "Finished")
				{
					%client.stepMove(%task.snipeLocation, 4.0, $AIModeWalk);
					%client.setEngageTarget(%task.engageTarget);
				}
				else if (%energy < 0.4)
				{
					%client.setEngageTarget(-1);
					%client.stepMove(%task.hideLocation, 4.0);
					%task.nextSnipeTime = getSimTime() + 4000 + (getRandom() * 4000);
					%task.moveToSnipe = false;
				}
			}

			else if (%energy > 0.5 && %task.engageTarget > 0 && getSimTime() > %task.nextSnipeTime)
			{ 
			   %client.stepRangeObject(%task.engageTarget.player.getWorldBoxCenter(), "BasicSniperShot", 150, 250, %task.snipelocation);
				%client.aimAt(%task.engageTarget.player.getWorldBoxCenter(), 8000);
				%task.moveToSnipe = true;
			}
		}
	}
	else
	{
	   //else see if we have a target to begin attacking
	   if (%client.getEngageTarget() <= 0 && %task.engageTarget > 0)
	      %client.stepEngage(%task.engageTarget);

	   //else move to the location we're defending
	   else if (%client.getEngageTarget() <= 0)
		{
	      %client.stepMove(%task.location, 8.0);
			if (VectorDist(%client.player.position, %task.location) < 10)
			{
				//dissolve the human control link
				if (%task == %client.objectiveTask)
				{
					if (aiHumanHasControl(%task.issuedByClient, %client))
					{
						aiReleaseHumanControl(%client.controlByHuman, %client);

						//should re-evaluate the current objective weight
						%inventoryStr = AIFindClosestInventories(%client);
						%client.objectiveWeight = %client.objective.weight(%client, %client.objectiveLevel, 0, %inventoryStr);
					}
				}
			}
		}
	}

   //see if we're supposed to be engaging anyone...
   if (!AIClientIsAlive(%client.getEngageTarget()) && AIClientIsAlive(%client.shouldEngage))
      %client.setEngageTarget(%client.shouldEngage);
}

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

function AIAttackPlayer::initFromObjective(%task, %objective, %client)
{
   %task.baseWeight = %client.objectiveWeight;
   %task.targetClient = %objective.targetClientId;
   %task.equipment = %objective.equipment;
	%task.buyEquipmentSet = %objective.buyEquipmentSet;
	%task.desiredEquipment = %objective.desiredEquipment;
	%task.issuedByClient = %objective.issuedByClientId;
}

function AIAttackPlayer::assume(%task, %client)
{
   %task.setWeightFreq(15);
   %task.setMonitorFreq(15);
   %client.needEquipment = AINeedEquipment(%task.equipment, %client);
	if (! %client.needEquipment)
	   %client.stepEngage(%task.targetClient);

	//even if we don't *need* equipemnt, see if we should buy some... 
	if (! %client.needEquipment && %task.buyEquipmentSet !$= "")
	{
		//see if we could benefit from inventory
		%needArmor = AIMustUseRegularInvStation(%task.desiredEquipment, %client);
		%result = AIFindClosestInventory(%client, %needArmor);
		%closestInv = getWord(%result, 0);
		%closestDist = getWord(%result, 1);
		if (AINeedEquipment(%task.desiredEquipment, %client) && %closestInv > 0)
		{
			%distToTarg = %client.getPathDistance(%task.targetClient.player.getWorldBoxCenter());
			if (%distToTarg < 0 || %distToTarg > 100)
				%client.needEquipment = true;
		}
	}

	//mark the current time for the buy inventory state machine
	%task.buyInvTime = getSimTime();
}

function AIAttackPlayer::retire(%task, %client)
{
	//dissolve the human control link
	if (%task == %client.objectiveTask)
		aiReleaseHumanControl(%client.controlByHuman, %client);
}

function AIAttackPlayer::weight(%task, %client)
{
	//update the task weight
	if (%task == %client.objectiveTask)
		%task.baseWeight = %client.objectiveWeight;

   %task.setWeight(%task.baseWeight);
}

function AIAttackPlayer::monitor(%task, %client)
{
   //first, buy the equipment
   if (%client.needEquipment)
   {
	   %task.setMonitorFreq(5);
		if (%task.equipment !$= "")
			%equipmentList = %task.equipment;
		else
			%equipmentList = %task.desiredEquipment;
      %result = AIBuyInventory(%client, %equipmentList, %task.buyEquipmentSet, %task.buyInvTime);
		if (%result $= "InProgress")
			return;
		else if (%result $= "Finished")
		{
		   %task.setMonitorFreq(15);
			%client.needEquipment = false;
		   %client.stepEngage(%task.targetClient);
		}
		else if (%result $= "Failed")
		{
	      //if this task is the objective task, choose a new objective
	      if (%task == %client.objectiveTask)
	      {
	         AIUnassignClient(%client);
	         Game.AIChooseGameObjective(%client);
	      }
			return;
		}
   }
	//if we made it past the inventory buying, reset the inv time
	%task.buyInvTime = getSimTime();

	//cheap hack for now...   make the bot always know where you are...
	%client.clientDetected(%task.targetClient);

	//make sure we're still attacking...
   if (%client.getStepName() !$= "AIStepEngage")
	   %client.stepEngage(%task.targetClient);

	//make sure we're still attacking the right target
	%client.setEngageTarget(%task.targetClient);

   if (%client.getStepStatus() !$= "InProgress" && %task == %client.objectiveTask)
	{
      AIUnassignClient(%client);
      Game.AIChooseGameObjective(%client);
	}
}

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

function AITouchObject::initFromObjective(%task, %objective, %client)
{
   %task.baseWeight = %client.objectiveWeight;
   %task.targetObject = %objective.targetObjectId;
	%task.mode = %objective.mode;
	if (%objective.mode $= "FlagCapture")
		%task.location = %objective.location;
	else if(%objective.mode $= "TouchFlipFlop")
      %task.location = %objective.location;
   else
		%task.location = "";
   %task.equipment = %objective.equipment;
	%task.buyEquipmentSet = %objective.buyEquipmentSet;
	%task.desiredEquipment = %objective.desiredEquipment;
	%task.issuedByClient = %objective.issuedByClientId;

	%task.sendMsgTime = 0;
	if (%task.mode $= "FlagGrab")
		%task.sendMsg = true;
	else
		%task.sendMsg = false;
}

function AITouchObject::assume(%task, %client)
{
   %task.setWeightFreq(15);
   %task.setMonitorFreq(15);
   %task.engageTarget = 0;
   %client.needEquipment = AINeedEquipment(%task.equipment, %client);

	//even if we don't *need* equipemnt, see if we should buy some... 
	if (! %client.needEquipment && (%task.mode $= "FlagGrab" || %task.mode $= "TouchFlipFlop") && %task.buyEquipmentSet !$= "")
	{
		//see if we could benefit from inventory
		%needArmor = AIMustUseRegularInvStation(%task.desiredEquipment, %client);
		%result = AIFindClosestInventory(%client, %needArmor);
		%closestInv = getWord(%result, 0);
		%closestDist = getWord(%result, 1);
		if (AINeedEquipment(%task.desiredEquipment, %client) && %closestInv > 0)
		{
			//find where we are
			%clientPos = %client.player.getWorldBoxCenter();
			%distToObject = %client.getPathDistance(%task.location);
			if (%distToObject < 0 || %closestDist < %distToObject)
				%client.needEquipment = true;
		}
	}

	//mark the current time for the buy inventory state machine
	%task.buyInvTime = getSimTime();
}

function AITouchObject::retire(%task, %client)
{
}

function AITouchObject::weight(%task, %client)
{
	//update the task weight
	if (%task == %client.objectiveTask)
		%task.baseWeight = %client.objectiveWeight;

   //see if we can find someone to shoot at...
   if (%client.getEngageTarget() <= 0)
   {
      %losTimeout = $AIClientMinLOSTime + ($AIClientLOSTimeout * %client.getSkillLevel());
      %myLocation = %client.player.getWorldBoxCenter();
		%result = AIFindClosestEnemy(%client, 40, %losTimeout);
      %task.engageTarget = getWord(%result, 0);
   }
   
   %task.setWeight(%task.baseWeight);
}

function AITouchObject::monitor(%task, %client)
{
   //first, buy the equipment
   if (%client.needEquipment)
   {
	   %task.setMonitorFreq(5);
		if (%task.equipment !$= "")
			%equipmentList = %task.equipment;
		else
			%equipmentList = %task.desiredEquipment;
      %result = AIBuyInventory(%client, %equipmentList, %task.buyEquipmentSet, %task.buyInvTime);
		if (%result $= "InProgress")
			return;
		else if (%result $= "Finished")
		{
		   %task.setMonitorFreq(15);
			%client.needEquipment = false;
		}
		else if (%result $= "Failed")
		{
	      //if this task is the objective task, choose a new objective
	      if (%task == %client.objectiveTask)
	      {
	         AIUnassignClient(%client);
	         Game.AIChooseGameObjective(%client);
	      }
			return;
		}
   }
	//if we made it past the inventory buying, reset the inv time
	%task.buyInvTime = getSimTime();

	//chat
	if (%task.sendMsg)
	{
		if (%task.sendMsgTime == 0)
			%task.sendMsgTime = getSimTime();
		else if (getSimTime() - %task.sendMsgTime > 7000)
		{
			%task.sendMsg = false;
		   if (%client.isAIControlled())
			{
				if (%task.mode $= "FlagGrab")
					AIMessageThreadTemplate("AttackBase", "ChatSelfAttackFlag", %client, -1);
			}
		}
	}

   //keep updating the position, in case the flag is flying through the air...
	if (%task.location !$= "")
		%touchPos = %task.location;
	else
	   %touchPos = %task.targetObject.getWorldBoxCenter();
      
   //see if we need to engage a new target
   %engageTarget = %client.getEngageTarget();
   if (!AIClientIsAlive(%engageTarget) && %task.engageTarget > 0)
      %client.setEngageTarget(%task.engageTarget);
   
   //else see if we should abandon the engagement
   else if (AIClientIsAlive(%engageTarget))
   {
      %myPos = %client.player.getWorldBoxCenter();
      %testPos = %engageTarget.player.getWorldBoxCenter();
      %distance = %client.getPathDistance(%testPos);
      if (%distance < 0 || %distance > 70)
         %client.setEngageTarget(-1);
   }

	//see if we have completed our objective
   if (%task == %client.objectiveTask)
   {
		%completed = false;
		switch$ (%task.mode)
		{
	      case "TouchFlipFlop":
	         if (%task.targetObject.team == %client.team)
					%completed = true;
	      case "FlagGrab":
				if (!%task.targetObject.isHome)
					%completed = true;
	      case "FlagDropped":
				if ((%task.targetObject.isHome) || (%task.targetObject.carrier !$= ""))
					%completed = true;
	      case "FlagCapture":
				if (%task.targetObject.carrier != %client.player)
					%completed = true;
		}
		if (%completed)
		{
	      AIUnassignClient(%client);
	      Game.AIChooseGameObjective(%client);
			return;
		}
	}

	if (%task.mode $= "FlagCapture")
	{
		%homeFlag = $AITeamFlag[%client.team];

		//if we're within range of the flag's home position, and the flag isn't home, start idling...
		if (VectorDist(%client.player.position, %touchPos) < 40 && !%homeFlag.isHome)
		{
	   	if (%client.getStepName() !$= "AIStepIdlePatrol")
				%client.stepIdle(%touchPos);
		}
		else
		   %client.stepMove(%touchPos, 0.25);
	}
	else
	   %client.stepMove(%touchPos, 0.25);

	if (VectorDist(%client.player.position, %touchPos) < 10)
	{
		//dissolve the human control link
		if (%task == %client.objectiveTask)
		{
			if (aiHumanHasControl(%task.issuedByClient, %client))
			{
				aiReleaseHumanControl(%client.controlByHuman, %client);

				//should re-evaluate the current objective weight
				%inventoryStr = AIFindClosestInventories(%client);
				%client.objectiveWeight = %client.objective.weight(%client, %client.objectiveLevel, 0, %inventoryStr);
			}
		}
	}

   //see if we're supposed to be engaging anyone...
   if (!AIClientIsAlive(%client.getEngageTarget()) && AIClientIsAlive(%client.shouldEngage))
      %client.setEngageTarget(%client.shouldEngage);
}

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

function AIEscortPlayer::initFromObjective(%task, %objective, %client)
{
   %task.baseWeight = %client.objectiveWeight;
   %task.targetClient = %objective.targetClientId;
   %task.equipment = %objective.equipment;
	%task.buyEquipmentSet = %objective.buyEquipmentSet;
	%task.desiredEquipment = %objective.desiredEquipment;
	%task.issuedByClient = %objective.issuedByClientId;
	%task.forceClient = %objective.forceClientId;
}

function AIEscortPlayer::assume(%task, %client)
{
   %task.setWeightFreq(15);
   %task.setMonitorFreq(15);
	%task.rangedTarget = false;
	if (%task == %client.objectiveTask && %client == %task.forceClient && %task.issuedByClient == %task.targetClient)
	{
	   %client.needEquipment = false;
      %client.mountVehicle = false;
	}
	else
	{
	   %client.needEquipment = AINeedEquipment(%task.equipment, %client);
		if (! %client.needEquipment)
		   %client.stepEscort(%task.targetClient);

		//even if we don't *need* equipemnt, see if we should buy some... 
		if (! %client.needEquipment && %task.buyEquipmentSet !$= "")
		{
			//see if we could benefit from inventory
			%needArmor = AIMustUseRegularInvStation(%task.desiredEquipment, %client);
			%result = AIFindClosestInventory(%client, %needArmor);
			%closestInv = getWord(%result, 0);
			%closestDist = getWord(%result, 1);
			if (AINeedEquipment(%task.desiredEquipment, %client) && %closestInv > 0)
			{
				//find where we are
				%clientPos = %client.player.getWorldBoxCenter();
				%targPos = %task.targetClient.player.getWorldBoxCenter();
				%distToTarg = %client.getPathDistance(%targPos);
				
				if (%closestDist < 50 && (%distToTarg < 0 || %distToTarg > 100))
					%client.needEquipment = true;
			}
		}
	}

	//mark the current time for the buy inventory state machine
	%task.buyInvTime = getSimTime();
}

function AIEscortPlayer::retire(%task, %client)
{
	%client.clearStep();
   if(%client.player.isMounted())
      AIDisembarkVehicle(%client);

	//clear the target object
	%client.setTargetObject(-1);
}

function AIEscortPlayer::weight(%task, %client)
{
	//update the task weight
	if (%task == %client.objectiveTask)
		%task.baseWeight = %client.objectiveWeight;

   //make sure we still have someone to escort
   if (!AiClientIsAlive(%task.targetClient))
   {
		%task.setWeight(0);
      return;
   }

   //always shoot at the closest person to the client being escorted
   %targetPos = %task.targetClient.player.getWorldBoxCenter();
   %losTimeout = $AIClientMinLOSTime + ($AIClientLOSTimeout * %client.getSkillLevel());
	%result = AIFindClosestEnemyToLoc(%client, %targetPos, 50, %losTimeout);
   %task.engageTarget = getWord(%result, 0);
   if (!AIClientIsAlive(%task.engageTarget))
   {
      if (AIClientIsAlive(%task.targetClient.lastDamageClient, %losTimeout) && getSimTime() - %task.targetClient.lastDamageTime < %losTimeout)
         %task.engageTarget = %task.targetClient.lastDamageClient;
   }
   if (!AIClientIsAlive(%task.engageTarget))
   {
      %myPos = %client.player.getWorldBoxCenter();
		%result = AIFindClosestEnemy(%client, 50, %losTimeout);
      %task.engageTarget = getWord(%result, 0);
   }

	//if both us and the person we're escorting are in a vehicle, set the weight high!
	if (%task.targetClient.player.isMounted() && %client.player.isMounted())
	{
		%vehicle = %client.vehicleMounted;
		if (%vehicle > 0 && isObject(%vehicle) && %vehicle.getDamagePercent() < 0.8)
			%task.setWeight($AIWeightVehicleMountedEscort);
		else
		   %task.setWeight(%task.baseWeight);
	}
	else
	   %task.setWeight(%task.baseWeight);

	//find out if our escortee is lazing a target... 
	%task.missileTarget = -1;
	%targetCount = ServerTargetSet.getCount();
	for (%i = 0; %i < %targetCount; %i++)
	{
		%targ = ServerTargetSet.getObject(%i);
		if (%targ.sourceObject == %task.targetClient.player)
		{
			//find out which item is being targetted...
			%targPoint = %targ.getTargetPoint();
		   InitContainerRadiusSearch(%targPoint, 10, $TypeMasks::TurretObjectType | $TypeMasks::StaticShapeObjectType);
		   %task.missileTarget = containerSearchNext();
			break;
		}
	}
}

function AIEscortPlayer::monitor(%task, %client)
{
   //make sure we still have someone to escort
   if (!AiClientIsAlive(%task.targetClient))
   {
	   if (%task == %client.objectiveTask)
	   {
	      AIUnassignClient(%client);
	      Game.AIChooseGameObjective(%client);
	   }
      return;
   }

   //first, buy the equipment
   if (%client.needEquipment)
   {
	   %task.setMonitorFreq(5);
		if (%task.equipment !$= "")
			%equipmentList = %task.equipment;
		else
			%equipmentList = %task.desiredEquipment;
      %result = AIBuyInventory(%client, %equipmentList, %task.buyEquipmentSet, %task.buyInvTime);
		if (%result $= "InProgress")
			return;
		else if (%result $= "Finished")
		{
		   %task.setMonitorFreq(15);
			%client.needEquipment = false;
		   %client.stepEscort(%task.targetClient);
			%task.buyInvTime = getSimTime();
		}
		else if (%result $= "Failed")
		{
	      //if this task is the objective task, choose a new objective
	      if (%task == %client.objectiveTask)
	      {
	         AIUnassignClient(%client);
	         Game.AIChooseGameObjective(%client);
	      }
			return;
		}
   }
   
	//see if our target is mounted in a vehicle...
	if (%task.targetClient.player.isMounted())
	{
		//find the passenger seat the bot will take
		%vehicle = %task.targetClient.vehicleMounted;
		%node = findAIEmptySeat(%vehicle, %client.player);

		//make sure there is an empty seat
		if (%node < 0 && %client.vehicleMounted != %task.targetClient.vehicleMounted)
		{
	      if (%task == %client.objectiveTask)
	      {
	         AIUnassignClient(%client);
	         Game.AIChooseGameObjective(%client);
	      }
			return;
		}

		//find the passenger seat location...
		%slotPosition = %vehicle.getSlotTransform(%node);

		//make sure we're in the correct armor - assault tanks cannot have a heavy...
		if (%task.targetClient.vehicleMounted.getDataBlock().getName() $= "AssaultVehicle")
		{
			//if the bot is in a heavy, break off the escort...
			if (%client.player.getArmorSize() $= "Heavy")
			{
		      if (%task == %client.objectiveTask)
		      {
		         AIUnassignClient(%client);
		         Game.AIChooseGameObjective(%client);
		      }
				return;
			}

			//throw away any packs that won't fit
			if (%client.player.getInventory(InventoryDeployable) > 0)
				%client.player.throwPack();
			else if (%client.player.getInventory(TurretIndoorDeployable) > 0)
				%client.player.throwPack();
			else if (%client.player.getInventory(TurretOutdoorDeployable) > 0)
				%client.player.throwPack();
		}
		
		if (%client.player.isMounted())
		{
			//make sure it's the same vehicle :)
			if (%client.vehicleMounted != %vehicle)
				AIDisembarkVehicle(%client);
		}
		else
		{
			//mount the vehicle
			%client.stepMove(%slotPosition, 0.25, $AIModeMountVehicle);
		}
	}
	else
	{
		//disembark if we're mounted, but our target isn't (anymore)
		if (%client.player.isMounted())
			AIDisembarkVehicle(%client);
	}
   
	//see if we're supposed to be mortaring/missiling something...
   %hasMortar = (%client.player.getInventory("Mortar") > 0) && (%client.player.getInventory("MortarAmmo") > 0);
   %hasMissile = (%client.player.getInventory("MissileLauncher") > 0) && (%client.player.getInventory("MissileLauncherAmmo") > 0);
   if (!isObject(%task.engageTarget) && isobject(%task.missileTarget) && %task.missileTarget.getDamageState() !$= "Destroyed" && (%hasMortar || %hasMissile))
	{
		if (%task.rangedTarget)
		{
			%client.stop();
			%client.clearStep();
			%client.setEngageTarget(-1);
			if (%hasMortar)
            %client.setTargetObject(%task.missileTarget, 250, "Mortar");
			else
				%client.setTargetObject(%task.missileTarget, 500, "MissileNoLock");
		}
		else if (%client.getStepName() !$= "AIStepRangeObject")
		{
			if (%hasMortar)
	         %client.stepRangeObject(%task.missileTarget, "MortarShot", 100, 200);
			else
				%client.stepRangeObject(%task.missileTarget, "BasicTargeter", 50, 500);
		}
		else if (%client.getStepStatus() $= "Finished")
			%task.rangedTarget = true;
	}
	else
	{
		%task.rangedTarget = false;
		%client.setTargetObject(-1);
	   if (%client.getStepName() !$= "AIStepEscort")
			%client.stepEscort(%task.targetClient);
	}

   //make sure we're still shooting...
   %client.setEngageTarget(%task.engageTarget);

   //see if we're supposed to be engaging anyone...
   if (!AIClientIsAlive(%client.getEngageTarget()) && AIClientIsAlive(%client.shouldEngage))
      %client.setEngageTarget(%client.shouldEngage);
}

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

function AIAttackObject::initFromObjective(%task, %objective, %client)
{
   %task.baseWeight = %client.objectiveWeight;
   %task.targetObject = %objective.targetObjectId;
   %task.equipment = %objective.equipment;
	%task.buyEquipmentSet = %objective.buyEquipmentSet;
	%task.desiredEquipment = %objective.desiredEquipment;
	%task.issuedByClient = %objective.issuedByClientId;

	//initialize other task vars
	%task.sendMsg = true;
	%task.sendMsgTime = 0;
}

function AIAttackObject::assume(%task, %client)
{
   %task.setWeightFreq(15);
   %task.setMonitorFreq(5);
   %client.needEquipment = AINeedEquipment(%task.equipment, %client);

	//even if we don't *need* equipemnt, see if we should buy some... 
	if (! %client.needEquipment && %task.buyEquipmentSet !$= "")
	{
		//see if we could benefit from inventory
		%needArmor = AIMustUseRegularInvStation(%task.desiredEquipment, %client);
		%result = AIFindClosestInventory(%client, %needArmor);
		%closestInv = getWord(%result, 0);
		%closestDist = getWord(%result, 1);
		if (AINeedEquipment(%task.desiredEquipment, %client) && %closestInv > 0)
		{
			//find where we are
			%clientPos = %client.player.getWorldBoxCenter();
			%objPos = %task.targetObject.getWorldBoxCenter();
			%distToObject = %client.getPathDistance(%objPos);
			
			if (%distToObject < 0 || %closestDist < %distToObject)
				%client.needEquipment = true;
		}
	}

	//mark the current time for the buy inventory state machine
	%task.buyInvTime = getSimTime();
}

function AIAttackObject::retire(%task, %client)
{
   %client.setTargetObject(-1);
}

function AIAttackObject::weight(%task, %client)
{
	//update the task weight
	if (%task == %client.objectiveTask)
		%task.baseWeight = %client.objectiveWeight;

   //let the monitor decide when to stop attacking
   %task.setWeight(%task.baseWeight);
}

function AIAttackObject::monitor(%task, %client)
{
   //first, buy the equipment
   if (%client.needEquipment)
   {
	   %task.setMonitorFreq(5);
		if (%task.equipment !$= "")
			%equipmentList = %task.equipment;
		else
			%equipmentList = %task.desiredEquipment;
      %result = AIBuyInventory(%client, %equipmentList, %task.buyEquipmentSet, %task.buyInvTime);
		if (%result $= "InProgress")
			return;
		else if (%result $= "Finished")
		{
		   %task.setMonitorFreq(15);
			%client.needEquipment = false;
		}
		else if (%result $= "Failed")
		{
	      //if this task is the objective task, choose a new objective
	      if (%task == %client.objectiveTask)
	      {
	         AIUnassignClient(%client);
	         Game.AIChooseGameObjective(%client);
	      }
			return;
		}
   }
	//if we made it past the inventory buying, reset the inv time
	%task.buyInvTime = getSimTime();
   
	//chat
	if (%task.sendMsg)
	{
		if (%task.sendMsgTime == 0)
			%task.sendMsgTime = getSimTime();
		else if (getSimTime() - %task.sendMsgTime > 7000)
		{
			%task.sendMsg = false;
		   if (%client.isAIControlled())
			{
				if (%task.chat !$= "")
				{
					%chatMsg = getWord(%task.chat, 0);
					%chatTemplate = getWord(%task.chat, 1);
					if (%chatTemplate !$= "")
						AIMessageThreadTemplate(%chatTemplate, %chatMsg, %client, -1);
					else
						AIMessageThread(%task.chat, %client, -1);
				}
				else if (%task.targetObject > 0)
				{
					%type = %task.targetObject.getDataBlock().getName();
					if (%type $= "GeneratorLarge")
						AIMessageThreadTemplate("AttackBase", "ChatSelfAttackGenerator", %client, -1);
					else if (%type $= "SensorLargePulse")
						AIMessageThreadTemplate("AttackBase", "ChatSelfAttackSensors", %client, -1);
					else if (%type $= "SensorMediumPulse")
						AIMessageThreadTemplate("AttackBase", "ChatSelfAttackSensors", %client, -1);
					else if (%type $= "TurretBaseLarge")
						AIMessageThreadTemplate("AttackBase", "ChatSelfAttackTurrets", %client, -1);
					else if (%type $= "StationVehicle")
						AIMessageThreadTemplate("AttackBase", "ChatSelfAttackVehicle", %client, -1);
				}
			}
		}
	}

   //set the target object
   if (isObject(%task.targetObject) && %task.targetObject.getDamageState() !$= "Destroyed")
   {
      %client.setTargetObject(%task.targetObject, 40, "Destroy");
   
      //move towards the object until we're within range
      if (! %client.targetInRange())
         %client.stepMove(%task.targetObject.getWorldBoxCenter(), 8.0);
      else
		{
			//dissolve the human control link
			if (%task == %client.objectiveTask)
				aiReleaseHumanControl(%client.controlByHuman, %client);

         %client.stop();
		}
   }
   else
   {
      %client.setTargetObject(-1);
      %client.stop();
      
      //if this task is the objective task, choose a new objective
      if (%task == %client.objectiveTask)
      {
         AIUnassignClient(%client);
         Game.AIChooseGameObjective(%client);
      }
   }
}

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

function AIRepairObject::initFromObjective(%task, %objective, %client)
{
   %task.baseWeight = %client.objectiveWeight;
   %task.targetObject = %objective.targetObjectId;
	//need to force this objective to only require a repair pack
   //%task.equipment = %objective.equipment;
   %task.equipment = "RepairPack";
	%task.buyEquipmentSet = %objective.buyEquipmentSet;
	%task.desiredEquipment = %objective.desiredEquipment;
	%task.issuedByClient = %objective.issuedByClientId;

	%task.deployed = %objective.deployed;
	if (%task.deployed)
	{
		%task.location = %objective.position;
		%task.deployDirection = MatrixMulVector("0 0 0 " @ getWords(%objective.getTransform(), 3, 6), "0 1 0");
		%task.deployDirection = VectorNormalize(%task.deployDirection);
	}
}

function AIRepairObject::assume(%task, %client)
{
   %task.setWeightFreq(15);
   %task.setMonitorFreq(15);
   %client.needEquipment = AINeedEquipment(%task.equipment, %client);
   
   //clear the target object, and range it
   %client.setTargetObject(-1);
	if (! %client.needEquipment)
	{
		if (%task.deployed)
		{
		   %task.repairLocation = VectorAdd(%task.location,VectorScale(%task.deployDirection, -4.0));
			%client.stepMove(%task.repairLocation, 0.25);
		}
		else
		   %client.stepRangeObject(%task.targetObject, "DefaultRepairBeam", 3, 8);
	}

	//mark the current time for the buy inventory state machine
	%task.buyInvTime = getSimTime();
	%task.needToRangeTime = 0;
	%task.pickupRepairPack = -1;
	%task.usingInv = false;

	//set a tag to help the repairPack.cs script fudge acquiring a target
	%client.repairObject = %task.targetObject;
}

function AIRepairObject::retire(%task, %client)
{
   %client.setTargetObject(-1);
	%client.repairObject = -1;
}

function AIRepairObject::weight(%task, %client)
{
	//update the task weight
	if (%task == %client.objectiveTask)
		%task.baseWeight = %client.objectiveWeight;

   //let the monitor decide when to stop repairing
   %task.setWeight(%task.baseWeight);
}

function AIRepairObject::monitor(%task, %client)
{
   //first, buy the equipment
   if (%client.needEquipment)
   {
	   %task.setMonitorFreq(5);

		//first, see if we still need a repair pack
		if (%client.player.getInventory(RepairPack) > 0)
		{
			%client.needEquipment = false;
		   %task.setMonitorFreq(15);

			//if this is to repair a deployed object, walk to the deploy point...
			if (%task.deployed)
			{
			   %task.repairLocation = VectorAdd(%task.location,VectorScale(%task.deployDirection, -4.0));
				%client.stepMove(%task.repairLocation, 0.25);
			}
			//otherwise, we'll need to range it...
			else
			   %client.stepRangeObject(%task.targetObject, "DefaultRepairBeam", 3, 8);
		}
		else
		{
			// check to see if there's a repair pack nearby
			%closestRepairPack = -1;
			%closestRepairDist = 32767;

			//search the AIItemSet for a repair pack (someone might have dropped one...)
			%itemCount = $AIItemSet.getCount();
			for (%i = 0; %i < %itemCount; %i++)
			{
				%item = $AIItemSet.getObject(%i);
				if (%item.getDataBlock().getName() $= "RepairPack" && !%item.isHidden())
				{
					%dist = %client.getPathDistance(%item.getWorldBoxCenter());
					if (%dist > 0 && %dist < %closestRepairDist)
					{
						%closestRepairPack = %item;
						%closestRepairDist = %dist;
					}
				}
			}

			//choose whether we're picking up the closest pack, or buying from an inv station...
			if ((isObject(%closestRepairPack) && %closestRepairPack != %task.pickupRepairPack) || (%task.buyInvTime != %client.buyInvTime))
			{
				%task.pickupRepairPack = %closestRepairPack;

				//initialize the inv buying
				%task.buyInvTime = getSimTime();
				AIBuyInventory(%client, "RepairPack", %task.buyEquipmentSet, %task.buyInvTime);

				//now decide which is closer
				if (isObject(%closestRepairPack))
				{
					if (isObject(%client.invToUse))
					{
						%dist = %client.getPathDistance(%item.position);
						if (%dist > %closestRepairDist)
							%task.usingInv = true;
						else
							%task.usingInv = false;
					}
					else
					%task.usingInv = false;
				}
				else
					%task.usingInv = true;
			}

			//now see if we found a closer repair pack
			if (!%task.usingInv)
			{
				%client.stepMove(%task.pickupRepairPack.position, 0.25);
				%distToPack = %client.getPathDistance(%task.pickupRepairPack.position);
				if (%distToPack < 10 && %client.player.getMountedImage($BackpackSlot) > 0)
					%client.player.throwPack();

				//and we're finished until we actually have a repair pack...
				return;
			}
			else
			{
		      %result = AIBuyInventory(%client, "RepairPack", %task.buyEquipmentSet, %task.buyInvTime);
				if (%result $= "InProgress")
					return;
				else if (%result $= "Finished")
				{
					%client.needEquipment = false;
				   %task.setMonitorFreq(15);

					//if this is to repair a deployed object, walk to the deploy point...
					if (%task.deployed)
					{
					   %task.repairLocation = VectorAdd(%task.location,VectorScale(%task.deployDirection, -4.0));
						%client.stepMove(%task.repairLocation, 0.25);
					}
					//otherwise, we'll need to range it...
					else
					   %client.stepRangeObject(%task.targetObject, "DefaultRepairBeam", 3, 8);
				}
				else if (%result $= "Failed")
				{
			      //if this task is the objective task, choose a new objective
			      if (%task == %client.objectiveTask)
			      {
			         AIUnassignClient(%client);
			         Game.AIChooseGameObjective(%client);
			      }
					return;
				}
			}
		}
   }
	//if we made it past the inventory buying, reset the inv time
	%task.buyInvTime = getSimTime();
   
	//chat
	if (%task.sendMsg)
	{
		if (%task.sendMsgTime == 0)
			%task.sendMsgTime = getSimTime();
		else if (getSimTime() - %task.sendMsgTime > 7000)
		{
			%task.sendMsg = false;
		   if (%client.isAIControlled())
			{
				if (%task.chat !$= "")
				{
					%chatMsg = getWord(%task.chat, 0);
					%chatTemplate = getWord(%task.chat, 1);
					if (%chatTemplate !$= "")
						AIMessageThreadTemplate(%chatTemplate, %chatMsg, %client, -1);
					else
						AIMessageThread(%task.chat, %client, -1);
				}
				else if (%task.targetObject > 0)
				{
					%type = %task.targetObject.getDataBlock().getName();
					if (%type $= "GeneratorLarge")
						AIMessageThreadTemplate("RepairBase", "ChatSelfRepairGenerator", %client, -1);
					else if (%type $= "StationVehicle")
						AIMessageThreadTemplate("RepairBase", "ChatSelfRepairVehicle", %client, -1);
					else if (%type $= "SensorLargePulse")
						AIMessageThreadTemplate("RepairBase", "ChatSelfRepairSensors", %client, -1);
					else if (%type $= "SensorMediumPulse")
						AIMessageThreadTemplate("RepairBase", "ChatSelfRepairSensors", %client, -1);
					else if (%type $= "TurretBaseLarge")
						AIMessageThreadTemplate("RepairBase", "ChatSelfRepairTurrets", %client, -1);
				}
			}
		}
	}

   //set the target object
   if (%task.targetObject.getDamagePercent() > 0)
   {
      //make sure we still have equipment
		%client.needEquipment = AINeedEquipment(%task.equipment, %client);
      if (%client.needEquipment)
      {
         //if this task is the objective task, choose a new objective
         if (%task == %client.objectiveTask)
			{
            AIUnassignClient(%client);
	         Game.AIChooseGameObjective(%client);
				return;
			}
      }
      
		if (%task.deployed)
		{
			//see if we're within range of the deploy location
		   %clLoc = %client.player.position;
		   %distance = VectorDist(%clLoc, %task.repairLocation);
			%dist2D = VectorDist(%client.player.position, getWords(%task.repairLocation, 0, 1) SPC getWord(%client.player.position, 2));

			//set the aim when we get near the target...  this will be overwritten when we're actually trying to deploy
 			if (%distance < 10 && %dist2D < 10)
 		      %client.aimAt(%task.location, 1000);

			//see if we're at the deploy location
		   if ((%client.pathDistRemaining(20) > %distance + 0.25) || %dist2D > 0.3)
			{
	         %client.setTargetObject(-1);
		      %client.stepMove(%task.repairLocation, 0.25);
			}
			else
			{
				%client.stop();
	         %client.setTargetObject(%task.targetObject, 8.0, "Repair");
			}
		}
		else
		{
			%currentTime = getSimTime();
			if (%currentTime > %task.needToRangeTime)
			{
				//force a rangeObject every 10 seconds...
				%task.needToRangeTime = %currentTime + 6000;
				%client.setTargetObject(-1);
			   %client.stepRangeObject(%task.targetObject, "DefaultRepairBeam", 3, 8);
			}

	      //if we've ranged the object, start repairing, else unset the object
	      else if (%client.getStepStatus() $= "Finished")
			{
				//dissolve the human control link
				if (%task == %client.objectiveTask)
					aiReleaseHumanControl(%client.controlByHuman, %client);

	         %client.setTargetObject(%task.targetObject, 8.0, "Repair");
			}
	      else
	         %client.setTargetObject(-1);
		}
   }
   else
   {
      %client.setTargetObject(-1);
      
      //if this task is the objective task, choose a new objective
      if (%task == %client.objectiveTask)
		{
         AIUnassignClient(%client);
			Game.AIChooseGameObjective(%client);
		}
   }
}

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

function AILazeObject::initFromObjective(%task, %objective, %client)
{
   %task.baseWeight = %client.objectiveWeight;
   %task.targetObject = %objective.targetObjectId;
   %task.equipment = %objective.equipment;
	%task.buyEquipmentSet = %objective.buyEquipmentSet;
	%task.desiredEquipment = %objective.desiredEquipment;
	%task.issuedByClient = %objective.issuedByClientId;
	%task.msgAck = true;
	%task.msgFire = true;
}

function AILazeObject::assume(%task, %client)
{
   %task.setWeightFreq(30);
   %task.setMonitorFreq(30);
   %client.needEquipment = AINeedEquipment(%task.equipment, %client);
   
   //clear the target object, and range it
   %client.setTargetObject(-1);
	if (! %client.needEquipment)
	   %client.stepRangeObject(%task.targetObject, "BasicTargeter", 80, 300, %task.issuedByClient.player.getWorldBoxCenter());

	//set up some task vars
	%task.celebrate = false;
	%task.waitTimerMS = 0;

	//mark the current time for the buy inventory state machine
	%task.buyInvTime = getSimTime();
}

function AILazeObject::retire(%task, %client)
{
   %client.setTargetObject(-1);
}

function AILazeObject::weight(%task, %client)
{
	//update the task weight
	if (%task == %client.objectiveTask)
		%task.baseWeight = %client.objectiveWeight;

   //let the monitor decide when to stop lazing
   %task.setWeight(%task.baseWeight);
}

function AILazeObject::monitor(%task, %client)
{
   //first, buy the equipment
   if (%client.needEquipment)
   {
	   %task.setMonitorFreq(5);
		if (%task.equipment !$= "")
			%equipmentList = %task.equipment;
		else
			%equipmentList = %task.desiredEquipment;
      %result = AIBuyInventory(%client, %equipmentList, %task.buyEquipmentSet, %task.buyInvTime);
		if (%result $= "InProgress")
			return;
		else if (%result $= "Finished")
		{
		   %task.setMonitorFreq(30);
			%client.needEquipment = false;
		   %client.stepRangeObject(%task.targetObject, "BasicTargeter", 80, 300);
		}
		else if (%result $= "Failed")
		{
	      //if this task is the objective task, choose a new objective
	      if (%task == %client.objectiveTask)
	      {
	         AIUnassignClient(%client);
	         Game.AIChooseGameObjective(%client);
	      }
			return;
		}
   }
	//if we made it past the inventory buying, reset the inv time
	%task.buyInvTime = getSimTime();
   
   //set the target object
   if (isObject(%task.targetObject) && %task.targetObject.getDamageState() !$= "Destroyed")
   {
      //make sure we still have equipment
		%client.needEquipment = AINeedEquipment(%task.equipment, %client);
      if (%client.needEquipment)
      {
         //if this task is the objective task, choose a new objective
         if (%task == %client.objectiveTask)
			{
            AIUnassignClient(%client);
	         Game.AIChooseGameObjective(%client);
				return;
			}
      }
      
		//look to see if anyone else is also targetting...
		%foundTarget = false;
		%numTargets = ServerTargetSet.getCount();
		for (%i = 0; %i < %numTargets; %i++)
		{
			%targ = ServerTargetSet.getObject(%i);
			if (%targ.sourceObject != %client.player)
			{
				%targDist = VectorDist(%targ.getTargetPoint(), %task.targetObject.getWorldBoxCenter());
				if (%targDist < 10)
				{
					%foundTarget = true;
					break;
				}
			}
		}

		if (%foundTarget)
		{
	      //if this task is the objective task, choose a new objective
	      if (%task == %client.objectiveTask)
	         AIUnassignClient(%client);
		}

      else if (%client.getStepStatus() $= "Finished")
		{
			//dissolve the human control link
			if (%task == %client.objectiveTask)
				aiReleaseHumanControl(%client.controlByHuman, %client);

         %client.setTargetObject(%task.targetObject, 300, "Laze");
			%task.celebrate = true;
			%task.waitTimerMS = 0;

			//make sure we only say "fire..." once
			if (%task.msgFire)
			{
				AIMessageThread("FireOnTarget", %client, -1);
				%task.msgFire = false;
			}
		}
      else
		{
			%client.aimAt(%task.targetObject.getWorldBoxCenter(), 1000);
         %client.setTargetObject(-1);
		}
   }
   else
   {
      %client.setTargetObject(-1);

		if (%task.celebrate)
		{
			if (%task.waitTimerMS == 0)
			{
				//add in a "woohoo"!  :)
				//choose the animation range
				%minCel = 3;
				%maxCel = 8;

				//pick a random sound
				if (getRandom() > 0.25)
					%sound = "gbl.awesome";
				else if (getRandom() > 0.5)
					%sound = "gbl.thanks";
				else if (getRandom() > 0.75)
					%sound = "gbl.nice";
				else
					%sound = "gbl.rock";
			  	%randTime = mFloor(getRandom() * 500) + 1;
			   schedule(%randTime, %client, "AIPlayAnimSound", %client, %task.targetObject.getWorldBoxCenter(), %sound, %minCel, %maxCel, 0);

				//set the timer
            %task.waitTimerMS = getSimTime();
			}

			//else see if the celebration period is over
			else if (getSimTime() - %task.waitTimerMS > 3000)
				%task.celebrate = false;
		}
		else
		{
	      //if this task is the objective task, choose a new objective
	      if (%task == %client.objectiveTask)
			{
	         AIUnassignClient(%client);
				Game.AIChooseGameObjective(%client);
			}
		}
   }
}

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

function AIMortarObject::initFromObjective(%task, %objective, %client)
{
   %task.baseWeight = %client.objectiveWeight;
   %task.targetObject = %objective.targetObjectId;
   %task.equipment = %objective.equipment;
	%task.buyEquipmentSet = %objective.buyEquipmentSet;
	%task.desiredEquipment = %objective.desiredEquipment;
	%task.issuedByClient = %objective.issuedByClientId;
	%task.mode = %task.targetObject.getDataBlock().getName();

	%task.sendMsgTime = 0;
	%task.sendMsg = true;
}

function AIMortarObject::assume(%task, %client)
{
   %task.setWeightFreq(30);
   %task.setMonitorFreq(30);
   %task.state = moveToRange;
   %task.waitForTargetter = true;
	%task.celebrate = false;
   %task.waitTimerMS = 0;
	%task.targetAcquired = false;
	%task.sayAcquired = true;
   %client.needEquipment = AINeedEquipment(%task.equipment, %client);

	//even if we don't *need* equipemnt, see if we should buy some... 
	if (! %client.needEquipment && %task.buyEquipmentSet !$= "")
	{
		//see if we could benefit from inventory
		%needArmor = AIMustUseRegularInvStation(%task.desiredEquipment, %client);
		%result = AIFindClosestInventory(%client, %needArmor);
		%closestInv = getWord(%result, 0);
		%closestDist = getWord(%result, 1);
		if (AINeedEquipment(%task.desiredEquipment, %client) && %closestInv > 0)
		{
			//find where we are
			%clientPos = %client.player.getWorldBoxCenter();
			%objPos = %task.targetObject.getWorldBoxCenter();
			%distToObject = %client.getPathDistance(%objPos);
			
			if (%distToObject < 0 || %closestDist < %distToObject)
				%client.needEquipment = true;
		}
	}

	//mark the current time for the buy inventory state machine
	%task.buyInvTime = getSimTime();
}

function AIMortarObject::retire(%task, %client)
{
   %client.setTargetObject(-1);
   
   //remove the associated lazeObjective
   if (%task.targetterObjective)
   {
      AIClearObjective(%task.targetterObjective);
      %task.targetterObjective.delete();
      %task.targetterObjective = "";
   }
}

function AIMortarObject::weight(%task, %client)
{
	//update the task weight
	if (%task == %client.objectiveTask)
		%task.baseWeight = %client.objectiveWeight;

   //let the monitor decide when to stop mortaring
   %task.setWeight(%task.baseWeight);
}

function AIMortarObject::monitor(%task, %client)
{
   //first, buy the equipment
   if (%client.needEquipment)
   {
	   %task.setMonitorFreq(5);
		if (%task.equipment !$= "")
			%equipmentList = %task.equipment;
		else
			%equipmentList = %task.desiredEquipment;
      %result = AIBuyInventory(%client, %equipmentList, %task.buyEquipmentSet, %task.buyInvTime);
		if (%result $= "InProgress")
			return;
		else if (%result $= "Finished")
		{
		   %task.setMonitorFreq(30);
			%client.needEquipment = false;
		}
		else if (%result $= "Failed")
		{
	      //if this task is the objective task, choose a new objective
	      if (%task == %client.objectiveTask)
	      {
	         AIUnassignClient(%client);
	         Game.AIChooseGameObjective(%client);
	      }
			return;
		}
   }
	//if we made it past the inventory buying, reset the inv time
	%task.buyInvTime = getSimTime();
   
	//chat
	if (%task.sendMsg)
	{
		if (%task.sendMsgTime == 0)
			%task.sendMsgTime = getSimTime();
		else if (getSimTime() - %task.sendMsgTime > 7000)
		{
			%task.sendMsg = false;
		   if (%client.isAIControlled())
			{
				if (%task.chat !$= "")
				{
					%chatMsg = getWord(%task.chat, 0);
					%chatTemplate = getWord(%task.chat, 1);
					if (%chatTemplate !$= "")
						AIMessageThreadTemplate(%chatTemplate, %chatMsg, %client, -1);
					else
						AIMessageThread(%task.chat, %client, -1);
				}
				else if (%task.targetObject > 0)
				{
					%type = %task.targetObject.getDataBlock().getName();
					if (%type $= "GeneratorLarge")
						AIMessageThreadTemplate("AttackBase", "ChatSelfAttackGenerator", %client, -1);
					else if (%type $= "SensorLargePulse")
						AIMessageThreadTemplate("AttackBase", "ChatSelfAttackSensors", %client, -1);
					else if (%type $= "SensorMediumPulse")
						AIMessageThreadTemplate("AttackBase", "ChatSelfAttackSensors", %client, -1);
					else if (%type $= "TurretBaseLarge")
						AIMessageThreadTemplate("AttackBase", "ChatSelfAttackTurrets", %client, -1);
					else if (%type $= "StationVehicle")
						AIMessageThreadTemplate("AttackBase", "ChatSelfAttackVehicle", %client, -1);
				}
			}
		}
	}

   //make sure we still have something to destroy
   if (isObject(%task.targetObject) && %task.targetObject.getDamageState() !$= "Destroyed")
   {
      %clientPos = %client.player.getWorldBoxCenter();
      %targetPos = %task.targetObject.getWorldBoxCenter();
      %distance = %client.getPathDistance(%targetPos);
		if (%distance < 0)
			%distance = 32767;
      
      //make sure we still have equipment
		%client.needEquipment = AINeedEquipment(%task.equipment, %client);
      if (%client.needEquipment)
      {
         //if this task is the objective task, choose a new objective
         if (%task == %client.objectiveTask)
			{
            AIUnassignClient(%client);
	         Game.AIChooseGameObjective(%client);
				return;
			}
      }
      
      //next move to within 220 
      else if (%distance > 220)
      {
         %client.setTargetObject(-1);
         %client.stepMove(%task.targetObject.getWorldBoxCenter(), 15);
      }
      
      //now start ask for someone to laze the target, and start a 20 sec timer
      else if (%task.waitForTargetter)
      {
         //see if we've started the timer
         if (%task.waitTimerMS == 0)
         {
				//range the object
	         %client.stepRangeObject(%task.targetObject, "MortarShot", 100, 200);

            //now ask for a targeter...
				%targetType = %task.targetObject.getDataBlock().getName();
				if (%targetType $= "TurretBaseLarge")
					AIMessageThread("ChatCmdTargetTurret", %client, -1);
				else if (%targetType $= "SensorLargePulse")
					AIMessageThread("ChatCmdTargetSensors", %client, -1);
				else if (%targetType $= "SensorMediumPulse")
					AIMessageThread("ChatCmdTargetSensors", %client, -1);
				else
					AIMessageThread("ChatNeedTarget", %client, -1);

            %task.waitTimerMS = getSimTime();

            //create the objective
				if (! %task.targetterObjective)
				{
	            %task.targetterObjective = new AIObjective(AIOLazeObject)
	                              {
										      dataBlock = "AIObjectiveMarker";
	                                 weightLevel1 = $AIWeightLazeObject[1];
	                                 weightLevel2 = $AIWeightLazeObject[2];
	                                 description = "Laze the " @ %task.targetObject.getName();
	                                 targetObjectId = %task.targetObject;
												issuedByClientId = %client;
	                                 offense = true;
	                                 equipment = "TargetingLaser";
	                              };
			      MissionCleanup.add(%task.targetterObjective);
	            $ObjectiveQ[%client.team].add(%task.targetterObjective);
				}
				%task.targetterObjective.lastLazedTime = 0;

				//remove the escort (want a targetter instead)
				if (%client.escort)
				{
			      AIClearObjective(%client.escort);
			      %client.escort.delete();
			      %client.escort = "";
				}
         }									
         else
         {
            %elapsedTime = getSimTime() - %task.waitTimerMS;
				if (%task.targetterObjective.group > 0)
					%targetter = %task.targetterObjective.group.clientLevel[1];
				else
	            %targetter = %task.targetterObjective.clientLevel[1];

				//see if we can find a target near our objective
				%task.targetAcquired = false;
				%numTargets = ServerTargetSet.getCount();
				for (%i = 0; %i < %numTargets; %i++)
				{
					%targ = ServerTargetSet.getObject(%i);
					%targDist = VectorDist(%targ.getTargetPoint(), %task.targetObject.getWorldBoxCenter());
					if (%targDist < 20)
					{
						%task.targetAcquired = true;
						break;
					}
				}

				if (%task.targetAcquired)
            {
               %task.waitForTargetter = false;
					%task.waitTimerMS = 0;
					%task.celebrate = true;
					%task.sayAcquired = false;
					AIMessageThread("ChatTargetAcquired", %client, -1);
            }

				//else see if we've run out of time
				else if ((! %targetter || ! %targetter.isAIControlled()) && %elapsedTime > 20000)
				{
               %task.waitForTargetter = false;
					%task.waitTimerMS = 0;
					%task.celebrate = true;
				}  
         }
      }
      
      //now we should finally be attacking with or without a targetter
      //eventually, the target will be destroyed, or we'll run out of ammo...
      else
      {
			//dissolve the human control link
			if (%task == %client.objectiveTask)
				aiReleaseHumanControl(%client.controlByHuman, %client);

			//see if we didn't acquired a spotter along the way
			if (%task.targetterObjective.group > 0)
	         %targetter = %task.targetterObjective.group.clientLevel[1];
			else
	         %targetter = %task.targetterObjective.clientLevel[1];
			if (! %task.targetAcquired && AIClientIsAlive(%targetter) && %targetter.isAIControlled())
			{
				%client.setTargetObject(-1);
		      %task.waitForTargetter = true;
			}
			else
			{
				//see if we can find a target near our objective
				if (! %task.targetAcquired)
				{
					%numTargets = ServerTargetSet.getCount();
					for (%i = 0; %i < %numTargets; %i++)
					{
						%targ = ServerTargetSet.getObject(%i);
						%targDist = VectorDist(%targ.getTargetPoint(), %task.targetObject.getWorldBoxCenter());
						if (%targDist < 20)
						{
							%task.targetAcquired = true;
							break;
						}
					}
					//see if we found a target (must be by a human)
					if (%task.targetAcquired && %task.sayAcquired)
					{
						%task.sayAcquired = false;
						AIMessageThread("ChatTargetAcquired", %client, -1);
					}
				}
				
	         //set the target object, and keep attacking it
	         if (%client.getStepStatus() $= "Finished")
	            %client.setTargetObject(%task.targetObject, 250, "Mortar");
	         else
	            %client.setTargetObject(-1);
			}
      }
   }

	//the target must have been destroyed  :)
   else
   {
		//dissolve the human control link
		if (%task == %client.objectiveTask)
			aiReleaseHumanControl(%client.controlByHuman, %client);

      %client.setTargetObject(-1);
      %client.clearStep();
      %client.stop();

		if (%task.celebrate)
		{
			if (%task.waitTimerMS == 0)
			{
				//client animation "woohoo"!  :)
				//choose the animation range
				%minCel = 3;
				%maxCel = 8;

				//pick a random sound
				if (getRandom() > 0.25)
					%sound = "gbl.awesome";
				else if (getRandom() > 0.5)
					%sound = "gbl.thanks";
				else if (getRandom() > 0.75)
					%sound = "gbl.nice";
				else
					%sound = "gbl.rock";
			  	%randTime = mFloor(getRandom() * 500) + 1;
			   schedule(%randTime, %client, "AIPlayAnimSound", %client, %task.targetObject.getWorldBoxCenter(), %sound, %minCel, %maxCel, 0);

				//team message
				AIMessageThread("ChatEnemyTurretsDestroyed", %client, -1);

				//set the timer
            %task.waitTimerMS = getSimTime();
			}

			//else see if the celebration period is over
			else if (getSimTime() - %task.waitTimerMS > 3000)
				%task.celebrate = false;
		}
		else
		{
	      //if this task is the objective task, choose a new objective
	      if (%task == %client.objectiveTask)
			{
	         AIUnassignClient(%client);
				Game.AIChooseGameObjective(%client);
			}
		}
   }
}

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

function AIDeployEquipment::initFromObjective(%task, %objective, %client)
{
	//initialize the task vars from the objective
   %task.baseWeight = %client.objectiveWeight;
   %task.location = %objective.location;
   %task.equipment = %objective.equipment;
	%task.buyEquipmentSet = %objective.buyEquipmentSet;
	%task.desiredEquipment = %objective.desiredEquipment;
	%task.issuedByClient = %objective.issuedByClientId;
	%task.chat = %objective.chat;

	//initialize other task vars
	%task.sendMsg = true;
	%task.sendMsgTime = 0;

	//use the Y-axis of the rotation as the desired direction of deployement,
	//and calculate a walk to point 3 m behind the deploy point. 
	%task.deployDirection = MatrixMulVector("0 0 0 " @ getWords(%objective.getTransform(), 3, 6), "0 1 0");
	%task.deployDirection = VectorNormalize(%task.deployDirection);
}

function AIDeployEquipment::assume(%task, %client)
{
   %task.setWeightFreq(15);
   %task.setMonitorFreq(15);
	
	%client.needEquipment = AINeedEquipment(%task.equipment, %client);
   
   //mark the current time for the buy inventory state machine
	%task.buyInvTime = getSimTime();

	%task.passes = 0;
	%task.deployAttempts = 0;
	%task.checkObstructed = false;
	%task.waitMove = 0;
}

function AIDeployEquipment::retire(%task, %client)
{
}

function AIDeployEquipment::weight(%task, %client)
{
	//update the task weight
	if (%task == %client.objectiveTask)
		%task.baseWeight = %client.objectiveWeight;

   %task.setWeight(%task.baseWeight);
}

function findTurretDeployPoint(%client, %location, %attempt)
{
   %player = %client.player;
	if (!isObject(%player))
		return "0 0 0";

	%feetPos = posFromTransform(%player.getTransform());
	%temp = VectorSub(%location, %feetPos);
   %temp2 = getWord(%temp, 0) @ " " @ getWord(%temp, 1) @ " 0";
	%facingVector = VectorNormalize(%temp2);
	%aimPoint = VectorAdd(%feetPos, %facingVector);
	//assume that there will be 10 attempts
	%height = getWord(%location, 2) + 1.0 - (0.2 * %attempt);
   %aimAt = getWord(%aimPoint, 0) @ " " @ getWord(%aimPoint, 1) @ " " @ %height;
   return %aimAt;
}

function AIDeployEquipment::monitor(%task, %client)
{
   //first, buy the equipment
   if (%client.needEquipment)
   {
	   %task.setMonitorFreq(5);
		if (%task.equipment !$= "")
			%equipmentList = %task.equipment;
		else
			%equipmentList = %task.desiredEquipment;
      %result = AIBuyInventory(%client, %equipmentList, %task.buyEquipmentSet, %task.buyInvTime);
		if (%result $= "InProgress")
			return;
		else if (%result $= "Finished")
		{	
		   %task.setMonitorFreq(30);
         %client.needEquipment = false;
         //if we made it past the inventory buying, reset the inv time
	      %task.buyInvTime = getSimTime();
      }
      else if (%result $= "Failed")
		{
	      //if this task is the objective task, choose a new objective
	      if (%task == %client.objectiveTask)
	      {
	         AIUnassignClient(%client);
	         Game.AIChooseGameObjective(%client);
	      }
			return;
		}
   }

	//chat
	if (%task.sendMsg)
	{
		if (%task.sendMsgTime == 0)
			%task.sendMsgTime = getSimTime();
		else if (getSimTime() - %task.sendMsgTime > 7000)
		{
			%task.sendMsg = false;
		   if (%client.isAIControlled())
			{
				if (%task.chat !$= "")
				{
					%chatMsg = getWord(%task.chat, 0);
					%chatTemplate = getWord(%task.chat, 1);
					if (%chatTemplate !$= "")
						AIMessageThreadTemplate(%chatTemplate, %chatMsg, %client, -1);
					else
						AIMessageThread(%task.chat, %client, -1);
				}
			}
		}
	}

   //see if we're supposed to be engaging anyone...
   if (AIClientIsAlive(%client.shouldEngage))
   {
	   %hasLOS = %client.hasLOSToClient(%client.shouldEngage);
	   %losTime = %client.getClientLOSTime(%client.shouldEngage);
	   if (%hasLOS || %losTime < 1000)
         %client.setEngageTarget(%client.shouldEngage);
      else
         %client.setEngageTarget(-1);
   }
   else
      %client.setEngageTarget(-1);

	//calculate the deployFromLocation
	%factor = -1 * (3 - (%task.passes * 0.5));
   %task.deployFromLocation = VectorAdd(%task.location,VectorScale(%task.deployDirection, %factor));

	//see if we're within range of the deploy location
   %clLoc = %client.player.position;
   %distance = VectorDist(%clLoc, %task.deployFromLocation);
	%dist2D = VectorDist(%client.player.position, getWords(%task.deployFromLocation, 0, 1) SPC getWord(%client.player.position, 2));

	//set the aim when we get near the target...  this will be overwritten when we're actually trying to deploy
	if (%distance < 10 && %dist2D < 10)
      %client.aimAt(%task.location, 1000);

   if ((%client.pathDistRemaining(20) > %distance + 0.25) || %dist2D > 0.5)
	{
		%task.deployAttempts = 0;
		%task.checkObstructed = false;
		%task.waitMove = 0;
      %client.stepMove(%task.deployFromLocation, 0.25);
	   %task.setMonitorFreq(15);
		return;
	}
   
	if (%task.deployAttempts < 10 && %task.passes < 5 && !AIClientIsAlive(%client.getEngageTarget()))
	{
		//dissolve the human control link
		if (%task == %client.objectiveTask)
			aiReleaseHumanControl(%client.controlByHuman, %client);

	   %task.setMonitorFreq(3);
      %client.stop();
		if (%task.deployAttempts == 0)
			%deployPoint = %task.location;
		else
	      %deployPoint = findTurretDeployPoint(%client, %task.location, %task.deployAttempts);
      if(%deployPoint !$= "")
      {
         // we have possible point
         %task.deployAttempts++;
         %client.aimAt(%deployPoint, 2000);

			//try to deploy the backpack
			%client.deployPack = true;
         %client.lastDeployedObject = -1;
         %client.player.use(Backpack);
         
         // check if pack deployed
         if (isObject(%client.lastDeployedObject))
			{
				//see if there's a "repairObject" objective for the newly deployed thingy...
				if (%task == %client.objectiveTask)
				{
					%deployedObject = %client.lastDeployedObject;

					//search the current objective group and search for a "repair Object" task...
					%objective = %client.objective;

					//delete any previously associated "AIORepairObject" objective
					if (isObject(%objective.repairObjective))
					{
						AIClearObjective(%objective.repairObjective);
						%objective.repairObjective.delete();
						%objective.repairObjective = "";
					}

					//add the repair objective
	            %objective.repairObjective = new AIObjective(AIORepairObject)
		                              {
											      dataBlock = "AIObjectiveMarker";
		                                 weightLevel1 = %objective.weightLevel1 - 60;
		                                 weightLevel2 = 0;
		                                 description = "Repair the " @ %deployedObject.getDataBlock().getName();
													targetObjectId = %deployedObject;
													issuedByClientId = %client;
		                                 offense = false;
													defense = true;
		                                 equipment = "RepairPack";
		                              };
					%objective.repairObjective.deployed = true;
					%objective.repairObjective.setTransform(%objective.getTransform());
					%objective.repairObjective.group = %objective.group;
			      MissionCleanup.add(%objective.repairObjective);
	            $ObjectiveQ[%client.team].add(%objective.repairObjective);

					//finally, unassign the client so he'll go do something else...
			      AIUnassignClient(%client);
					Game.AIChooseGameObjective(%client);
				}

				//finished
				return;
			}
      }
	}
	else if (!%task.checkObstructed)
	{
		%task.checkObstructed = true;

	   //see if anything is in our way
	   InitContainerRadiusSearch(%task.location, 4, $TypeMasks::MoveableObjectType | $TypeMasks::VehicleObjectType |
																					                      $TypeMasks::PlayerObjectType);
	   %objSrch = containerSearchNext();
		if (%objSrch == %client.player)
		   %objSrch = containerSearchNext();
	   if (%objSrch)
			AIMessageThread("ChatMove", %client, -1);
	}
	else if (%task.waitMove < 5 && %task.passes < 5)
	{
		%task.waitMove++;

		//try another pass at deploying 
		if (%task.waitMove == 5)
		{
			%task.waitMove = 0;
			%task.passes++;
			%task.deployAttempts = 0;

			//see if we're *right* underneath the deploy point
			%deployDist2D = VectorDist(getWords(%client.player.position, 0, 1) @ "0", getWords(%task.location, 0, 1) @ "0");
			if (%deployDist2D < 0.25)
			{
				%client.pressjump();
				%client.deployPack = true;
	         %client.player.use(Backpack);

	         // check if pack deployed
	         if(%client.player.getMountedImage($BackpackSlot) == 0)
				{
					//don't add a "repairObject" objective for ceiling turrets
					if (%task == %client.objectiveTask)
					{
						AIUnassignClient(%client);
						Game.AIChooseGameObjective(%client);
					}
				}
			}
		}
	}
	else
	{
		//find a new assignment - and remove this one from the Queue
      if (%task == %client.objectiveTask)
		{
			error(%client SPC "from team" SPC %client.team SPC "is invalidating objective:" SPC %client.objective SPC "UNABLE TO DEPLOY EQUIPMENT");
			%client.objective.isInvalid = true;
	      AIUnassignClient(%client);
			Game.AIChooseGameObjective(%client);
		}
	}
}

//------------------------------
//AI Objective functions
function ClientHasAffinity(%objective, %client)
{
   if (%objective.offense && %client.offense)
      return true;
   else if (%objective.defense && !%client.offense)
      return true;
   else
      return false;
}

function ClientHasRequiredEquipment(%objective, %client)
{
   return true;
}

function AIODefault::weight(%objective, %client, %level, %inventoryStr)
{
   //make sure the player is still alive!!!!!
   if (! AIClientIsAlive(%client))
      return 0;

   //set the base weight
   switch (%level)
   {
      case 1:
         %weight = %objective.weightLevel1;
      case 2:
         %weight = %objective.weightLevel2;
      case 3:
         %weight = %objective.weightLevel3;
      default:
         %weight = %objective.weightLevel4;
   }
   
   //check Affinity
   if (ClientHasAffinity(%objective, %client))
      %weight += 40;

	//if the objective doesn't require any equipment, it automatically get's the +100...
	if (%objective.equipment $= "" && %objective.desiredEquipment $= "")
		%weight += 100;
	else
	{
		//check equipment requirement
		%needEquipment = AINeedEquipment(%objective.equipment, %client);
	      
	   //check Required equipment
	   if (%objective.equipment !$= "" && !%needEquipment)
	      %weight += 100;

		//figure out the percentage of desired equipment the bot has
		else if (%objective.desiredEquipment !$= "")
		{
			%count = getWordCount(%objective.desiredEquipment);
			%itemCount = 0;
			for (%i = 0; %i < %count; %i++)
			{
				%item = getWord(%objective.desiredEquipment, %i);
				if (!AINeedEquipment(%item, %client))
					%itemCount++;
			}

			//add to the weight
			%weight += mFloor((%itemCount / %count) * 75);
		}
	}
      
   //find the distance to target
   if (%objective.targetClientId !$= "" || %objective.targetObjectId !$= "")
   {
      if (AIClientIsAlive(%objective.targetClientId))
      {   
         %targetPos = %objective.targetClientId.player.getWorldBoxCenter();
      }
      else if (VectorDist(%objective.location, "0 0 0") > 1)
         %targetPos = %objective.location;
      else
      {   
         if(%objective.targetObjectId > 0)
            %targetPos = %objective.targetObjectId.getWorldBoxCenter();
      }
   }

	//make sure the destination is accessible
   %distance = %client.getPathDistance(%targetPos);
	if (%distance < 0)
		return 0;

	%closestInvIsRemote = (getWordCount(%inventoryStr) == 4);
	%closestInv = getWord(%inventoryStr, 0);
	%closestDist = getWord(%inventoryStr, 1);
	%closestRemoteInv = getWord(%inventoryStr, 2);
	%closestRemoteDist = getWord(%inventoryStr, 3);
	
   //if we need equipment, the distance is from the client, to an inv, then to the target
 	if (%needEquipment)
 	{
		//if we need a regular inventory station, and one doesn't exist, exit
		if (!isObject(%closestInv) && %needArmor)
			return 0;

		//find the closest inv based on whether we require armor (from a regular inv station)
		if (!%closestInvIsRemote)
		{
			%needArmor = false;
			%weightDist = %closestDist;
			%weightInv = %closestInv;
		}
		else
		{
	 		%needArmor = AIMustUseRegularInvStation(%objective.equipment, %client);
			if (%needArmor)
			{
				%weightDist = %closestDist;
				%weightInv = %closestInv;
			}
			else
			{
				%weightDist = %closestRemoteDist;
				%weightInv = %closestRemoteInv;
			}
		}

		//if we don't need armor, and there's no inventory station, see if the equipment we need
		//is something we can pick up off the ground (likely this would be a repair pack...)
		if (%weightDist >= 32767)
		{
  			%itemType = getWord(%objective.equipment, 0);
  			%found = false;
  			%itemCount = $AIItemSet.getCount();
  			for (%i = 0; %i < %itemCount; %i++)
  			{
  				%item = $AIItemSet.getObject(%i);
  				if (%item.getDataBlock().getName() $= %itemType && !%item.isHidden())
  				{
					%weightDist = %client.getPathDistance(%item.getWorldBoxCenter());
					if (%weightDist > 0)
					{
						%weightInv = %item;  //set the var so the distance function will work...
	  					%found = true;
	  					break;
					}
  				}
  			}
  			if (! %found)
  				return 0;
  		}
 
		//now find the distance used for weighting the objective
		%tempDist = AIGetPathDistance(%targetPos, %weightInv.getWorldBoxCenter());
		if (%tempDist < 0)
			%tempDist = 32767;
		%distance = %weightDist + %tempDist;
 	}
   
   //see if we're within 200 m
   if (%distance < 200)
      %weight += 30;
      
   //see if we're within 90 m
   if (%distance < 90)
      %weight += 30;

   //see if we're within 45 m
   if (%distance < 45)
      %weight += 30;
      
   //see if we're within 20 m
   if (%distance < 20)
      %weight += 30;
   
   //final return, since we've made it through all the rest
   return %weight;
}

function AIODefault::QuickWeight(%objective, %client, %level, %minWeight)
{
	//can't do a quick weight when re-evaluating a client's current objective
	if (%client.objective == %objective)
		return true;

	//do a quick check to disqualify this objective if it can't meet the minimum weight
	switch (%level)
	{
	   case 1:
	      %testWeight = %objective.weightLevel1;
	   case 2:
	      %testWeight = %objective.weightLevel2;
	   case 3:
	      %testWeight = %objective.weightLevel3;
	   default:
	      %testWeight = %objective.weightLevel4;
	}
	if (%testWeight + 260 < %minWeight)
		return false;
	else
		return true;
}

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

function AIODefendLocation::weight(%this, %client, %level, %minWeight, %inventoryStr)
{
   // if were playing CnH, check who owns this
   if (%this.targetObjectId > 0)
	{
   	if (!isObject(%this.targetObjectId) || %this.targetObjectId.isHidden() || %this.targetObjectId.team != %client.team)
	      return 0;
	}
   
   //make sure the player is still alive!!!!!
   if (! AIClientIsAlive(%client))
      return 0;

	//do a quick check to disqualify this objective if it can't meet the minimum weight
	if (!AIODefault::QuickWeight(%this, %client, %level, %minWeight))
	{
		if (%this.targetObjectId > 0 && %this.issuedByClientId == %client.controlByHuman)
		{
			if ($AIWeightHumanIssuedCommand < %minWeight)
				return 0;
		}
		else
			return 0;
	}

	%weight = AIODefault::weight(%this, %client, %level, %inventoryStr);

   //if the object has been destroyed, reduce the weight
	if (%this.targetObjectId > 0)
	{

		//see if we were forced on the objective
		if (%this.issuedByClientId == %client.controlByHuman && %weight < $AIWeightHumanIssuedCommand)
			%weight = $AIWeightHumanIssuedCommand;

		//else see if the object has been destroyed
	   else if (!isObject(%this.targetObjectId) || %this.targetObjectId.getDamageState() $= "Destroyed")
			%weight -= 320;
	}

	return %weight;
}

function AIODefendLocation::assignClient(%this, %client)
{
   %client.objectiveTask = %client.addTask(AIDefendLocation);
   %client.objectiveTask.initFromObjective(%this, %client);
}

function AIODefendLocation::unassignClient(%this, %client)
{
   %client.removeTask(%client.objectiveTask);
   %client.objectiveTask = "";
}

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

function AIOAttackLocation::weight(%this, %client, %level, %minWeight, %inventoryStr)
{
   //make sure the player is still alive!!!!!
   if (! AIClientIsAlive(%client))
      return 0;
      
	//now, if this bot is linked to a human who has issued this command, up the weight
	if (%this.issuedByClientId == %client.controlByHuman)
	{
		//make sure we have the potential to reach the minWeight
		if (!AIODefault::QuickWeight(%this, %client, %level, %minWeight))
		{
			if ($AIWeightHumanIssuedCommand < %minWeight)
				return 0;
			else
				%weight = $AIWeightHumanIssuedCommand;
		}
		else
		{
			// calculate the default...
			%weight = AIODefault::weight(%this, %client, %level, %inventoryStr);
			if (%weight < $AIWeightHumanIssuedCommand)
				%weight = $AIWeightHumanIssuedCommand;
		}
	}
	else
	{
		//make sure we have the potential to reach the minWeight
		if (!AIODefault::QuickWeight(%this, %client, %level, %minWeight))
			return 0;

		// calculate the default...
		%weight = AIODefault::weight(%this, %client, %level, %inventoryStr);
	}

	return %weight;
}

function AIOAttackLocation::assignClient(%this, %client)
{
   %client.objectiveTask = %client.addTask(AIAttackLocation);
   %client.objectiveTask.initFromObjective(%this, %client);
}

function AIOAttackLocation::unassignClient(%this, %client)
{
   %client.removeTask(%client.objectiveTask);
   %client.objectiveTask = "";
}

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

function AIOTouchObject::weight(%this, %client, %level, %minWeight, %inventoryStr)
{
   //make sure the player is still alive!!!!!
   if (! AIClientIsAlive(%client))
      return 0;

	if (!AIODefault::QuickWeight(%this, %client, %level, %minWeight))
		return 0;

   switch$ (%this.mode)
   {
      case "TouchFlipFlop":
         if(%this.targetObjectId.team == %client.team || %this.targetObjectId.isHidden())
				return 0;
			else
				return AIODefault::weight(%this, %client, %level, %inventoryStr);
      case "FlagGrab":
			if (! %this.targetObjectId.isHome)
				return 0;
			else
	         return AIODefault::weight(%this, %client, %level, %inventoryStr);
      case "FlagDropped":
			if ((%this.targetObjectId.isHome) || (%this.targetObjectId.carrier !$= ""))
				return 0;
			else
	         return AIODefault::weight(%this, %client, %level, %inventoryStr);
      case "FlagCapture":
			if (%this.targetObjectId.carrier != %client.player)
            return 0;
         else
			{
				//find our home flag location
				%homeTeam = %client.team;
				%homeFlag = $AITeamFlag[%homeTeam];
				%this.location = %homeFlag.originalPosition;
				return AIODefault::weight(%this, %client, %level, %inventoryStr);
			}
   }
   return 0;
}

function AIOTouchObject::assignClient(%this, %client)
{
   %client.objectiveTask = %client.addTask(AITouchObject);
   %client.objectiveTask.initFromObjective(%this, %client);

   //create an AIOEscortPlayer objective to help out, if required
   if (%this.mode $= "FlagGrab")
   {
      %client.escort = new AIObjective(AIOEscortPlayer)
                        {
							      dataBlock = "AIObjectiveMarker";
                           weightLevel1 = $AIWeightEscortOffense[1];
                           weightLevel2 = $AIWeightEscortOffense[2];
                           description = "Escort " @ getTaggedString(%client.name);
                           targetClientId = %client;
                           offense = true;
									desiredEquipment = "EnergyPack";
									buyEquipmentSet = "LightEnergyELF";
                        };
      MissionCleanup.add(%client.escort);
      $ObjectiveQ[%client.team].add(%client.escort);
   }
   
   else if (%this.mode $= "FlagCapture")
   {
      %client.escort = new AIObjective(AIOEscortPlayer)
                        {
							      dataBlock = "AIObjectiveMarker";
                           weightLevel1 = $AIWeightEscortCapper[1];
                           weightLevel2 = $AIWeightEscortCapper[2];
                           description = "Escort " @ getTaggedString(%client.name);
                           targetClientId = %client;
                           offense = true;
									desiredEquipment = "EnergyPack";
									buyEquipmentSet = "LightEnergyDefault";
                        };
      MissionCleanup.add(%client.escort);
      $ObjectiveQ[%client.team].add(%client.escort);
   }
}

function AIOTouchObject::unassignClient(%this, %client)
{
   //kill the escort objective
   if (%client.escort)
   {
      AIClearObjective(%client.escort);
      %client.escort.delete();
      %client.escort = "";
   }
   
   %client.removeTask(%client.objectiveTask);
   %client.objectiveTask = "";
}

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

function AIOAttackPlayer::weight(%this, %client, %level, %minWeight, %inventoryStr)
{
   //make sure the player is still alive!!!!!
   if (! AIClientIsAlive(%client))
      return 0;

	//if we're attacking the flag carrier, make sure a flag carrier exists
	if (%this.mode $= "FlagCarrier")
	{
		if (%this.targetObjectId.carrier $= "")
			return 0;
		else
			%this.targetClientId = %this.targetObjectId.carrier.client;
	}
      
	//now, if this bot is linked to a human who has issued this command, up the weight
	if (%this.issuedByClientId == %client.controlByHuman)
	{
		//make sure we have the potential to reach the minWeight
		if (!AIODefault::QuickWeight(%this, %client, %level, %minWeight))
		{
			if ($AIWeightHumanIssuedCommand < %minWeight)
				return 0;
			else
				%weight = $AIWeightHumanIssuedCommand;
		}
		else
		{
			// calculate the default...
			%weight = AIODefault::weight(%this, %client, %level, %inventoryStr);
			if (%weight < $AIWeightHumanIssuedCommand)
				%weight = $AIWeightHumanIssuedCommand;
		}
	}
	else
	{
		//make sure we have the potential to reach the minWeight
		if (!AIODefault::QuickWeight(%this, %client, %level, %minWeight))
			return 0;

		// calculate the default...
		%weight = AIODefault::weight(%this, %client, %level, %inventoryStr);
	}

	return %weight;
}

function AIOAttackPlayer::assignClient(%this, %client)
{
   %client.objectiveTask = %client.addTask(AIAttackPlayer);
   %client.objectiveTask.initFromObjective(%this, %client);
}

function AIOAttackPlayer::unassignClient(%this, %client)
{
   %client.removeTask(%client.objectiveTask);
   %client.objectiveTask = "";
}

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

function AIOEscortPlayer::weight(%this, %client, %level, %minWeight, %inventoryStr)
{
   //make sure the player is still alive!!!!!
   if (! AIClientIsAlive(%client) || ! AIClientIsAlive(%this.targetClientId))
      return 0;

   //can't escort yourself
   if (%client == %this.targetClientId)
      return 0;

	//make sure the class is appropriate
	if (%this.forceClientId <= 0 && %this.issuedByClientId != %client.controlByHuman)
	{
		%targArmor = %this.targetClientId.player.getArmorSize();
      %myArmor = %client.player.getArmorSize();
      
		if ((%targArmor $= "Light" && %myArmor !$= "Light") || %myArmor $= "Heavy")
	      return 0;
	}

	//can't bump a forced client from level 1
	if (%this.forceClientId > 0 && %this.forceClientId != %client && %level == 1)
      return 0;

	//if this bot is linked to a human who has issued this command, up the weight
	if (%this.issuedByClientId == %client.controlByHuman)
	{
		//make sure we have the potential to reach the minWeight
		if (!AIODefault::QuickWeight(%this, %client, %level, %minWeight))
		{
			if ($AIWeightHumanIssuedEscort < %minWeight)
				return 0;
			else
				%weight = $AIWeightHumanIssuedEscort;
		}
		else
		{
			// calculate the default...
			%weight = AIODefault::weight(%this, %client, %level, %inventoryStr);
			if (%weight < $AIWeightHumanIssuedEscort)
				%weight = $AIWeightHumanIssuedEscort;
		}
	}
	else
	{
		//make sure we have the potential to reach the minWeight
		if (!AIODefault::QuickWeight(%this, %client, %level, %minWeight))
			return 0;

		// calculate the default...
		%weight = AIODefault::weight(%this, %client, %level, %inventoryStr);
	}

	return %weight;
}

function AIOEscortPlayer::assignClient(%this, %client)
{
   %client.objectiveTask = %client.addTask(AIEscortPlayer);
   %client.objectiveTask.initFromObjective(%this, %client);
}

function AIOEscortPlayer::unassignClient(%this, %client)
{
   %client.removeTask(%client.objectiveTask);
   %client.objectiveTask = "";
}

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

function AIOAttackObject::weight(%this, %client, %level, %minWeight, %inventoryStr)
{
   // if were playing CnH, check who owns this
   if (!isObject(%this.targetObjectId) || %this.targetObjectId.isHidden() || %this.targetObjectId.team == %client.team)
      return 0;      
   
   //make sure the player is still alive!!!!!
   if (! AIClientIsAlive(%client))
      return 0;
      
   //no need to attack if the object is already destroyed
   if (!isObject(%this.targetObjectId) || %this.targetObjectId.getDamageState() $= "Destroyed")
      return 0;
   else
	{
		//if this bot is linked to a human who has issued this command, up the weight
		if (%this.issuedByClientId == %client.controlByHuman)
		{
			//make sure we have the potential to reach the minWeight
			if (!AIODefault::QuickWeight(%this, %client, %level, %minWeight))
			{
				if ($AIWeightHumanIssuedCommand < %minWeight)
					return 0;
				else
					%weight = $AIWeightHumanIssuedCommand;
			}
			else
			{
				// calculate the default...
				%weight = AIODefault::weight(%this, %client, %level, %inventoryStr);
				if (%weight < $AIWeightHumanIssuedCommand)
					%weight = $AIWeightHumanIssuedCommand;
			}
		}
		else
		{
			//make sure we have the potential to reach the minWeight
			if (!AIODefault::QuickWeight(%this, %client, %level, %minWeight))
				return 0;

			// calculate the default...
			%weight = AIODefault::weight(%this, %client, %level, %inventoryStr);
		}

		return %weight;
	}
}

function AIOAttackObject::assignClient(%this, %client)
{
   %client.objectiveTask = %client.addTask(AIAttackObject);
   %client.objectiveTask.initFromObjective(%this, %client);
}

function AIOAttackObject::unassignClient(%this, %client)
{
   %client.removeTask(%client.objectiveTask);
   %client.objectiveTask = "";
}

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

function AIORepairObject::weight(%this, %client, %level, %minWeight, %inventoryStr)
{
   // if were playing CnH, check who owns this
   if (!isObject(%this.targetObjectId) || %this.targetObjectId.isHidden() || %this.targetObjectId.team != %client.team)
      return 0;      
   
   //make sure the player is still alive!!!!!
   if (! AIClientIsAlive(%client))
      return 0;
      
   //no need to repair if the object isn't in need
   if (!isObject(%this.targetObjectId) || %this.targetObjectId.getDamagePercent() <= 0)
      return 0;
   else
	{
		//if this bot is linked to a human who has issued this command, up the weight
		if (%this.issuedByClientId == %client.controlByHuman)
		{
			//make sure we have the potential to reach the minWeight
			if (!AIODefault::QuickWeight(%this, %client, %level, %minWeight))
			{
				if ($AIWeightHumanIssuedCommand < %minWeight)
					return 0;
				else
					%weight = $AIWeightHumanIssuedCommand;
			}
			else
			{
				// calculate the default...
				%weight = AIODefault::weight(%this, %client, %level, %inventoryStr);
				if (%weight < $AIWeightHumanIssuedCommand)
					%weight = $AIWeightHumanIssuedCommand;
			}
		}
		else
		{
			//make sure we have the potential to reach the minWeight
			if (!AIODefault::QuickWeight(%this, %client, %level, %minWeight))
				return 0;

			// calculate the default...
			%weight = AIODefault::weight(%this, %client, %level, %inventoryStr);
		}

		return %weight;
	}
}

function AIORepairObject::assignClient(%this, %client)
{
   %client.objectiveTask = %client.addTask(AIRepairObject);
   %client.objectiveTask.initFromObjective(%this, %client);
}

function AIORepairObject::unassignClient(%this, %client)
{
   %client.removeTask(%client.objectiveTask);
   %client.objectiveTask = "";
}

//------------------------------
   
function AIOLazeObject::weight(%this, %client, %level, %minWeight, %inventoryStr)
{
   //make sure the player is still alive!!!!!
   if (! AIClientIsAlive(%client))
      return 0;
      
	//see if it's already being lazed
	%numTargets = ServerTargetSet.getCount();
	for (%i = 0; %i < %numTargets; %i++)
	{
		%targ = ServerTargetSet.getObject(%i);
		if (%targ.sourceObject != %client.player)
		{
			%targDist = VectorDist(%targ.getTargetPoint(), %this.targetObjectId.getWorldBoxCenter());
			if (%targDist < 10)
			{
				%this.lastLazedTime = getSimTime();
				%this.lastLazedClient = %targ.sourceObject.client;
				break;
 			}
		}
	}

   //no need to laze if the object is already destroyed
   if (!isObject(%this.targetObjectId) || %this.targetObjectId.getDamageState() $= "Destroyed")
      return 0;
	else if (%this.targetObjectId.isHidden() || %this.targetObjectId.team != %client.team)
      return 0;      
	else if (getSimTime() - %this.lastLazedTime <= 15000 && %this.lastLazedClient != %client)
		return 0;
   else
	{
	   //set the base weight
	   switch (%level)
	   {
	      case 1:
	         %weight = %this.weightLevel1;
	      case 2:
	         %weight = %this.weightLevel2;
	      case 3:
	         %weight = %this.weightLevel3;
	      default:
	         %weight = %this.weightLevel4;
	   }
   
	   //check Affinity
	   if (ClientHasAffinity(%this, %client))
	      %weight += 100;
      
		//for now, do not deviate from the current assignment to laze a target, if you don't
		//already have a targeting laser.
		%needEquipment = AINeedEquipment(%this.equipment, %client);
		if (!%needEquipment)
			%weight += 100;
		else if (!aiHumanHasControl(%client.controlByHuman, %client))
			return 0;

		//see if this client is close to the issuing client
		if (%this.issuedByClientId > 0)
		{
			if (! AIClientIsAlive(%this.issuedByClientId))
				return 0;

		   %distance = %client.getPathDistance(%this.issuedByClientId.player.getWorldBoxCenter());
			if (%distance < 0)
				%distance = 32767;

		   //see if we're within 200 m
		   if (%distance < 200)
		      %weight += 30;

		   //see if we're within 90 m
		   if (%distance < 90)
		      %weight += 30;

		   //see if we're within 45 m
		   if (%distance < 45)
		      %weight += 30;
		}

		//now, if this bot is linked to a human who has issued this command, up the weight
		if (%this.issuedByClientId == %client.controlByHuman && %weight < $AIWeightHumanIssuedCommand)
			%weight = $AIWeightHumanIssuedCommand;

		return %weight;
	}
}

function AIOLazeObject::assignClient(%this, %client)
{
   %client.objectiveTask = %client.addTask(AILazeObject);
   %client.objectiveTask.initFromObjective(%this, %client);
}

function AIOLazeObject::unassignClient(%this, %client)
{
   %client.removeTask(%client.objectiveTask);
   %client.objectiveTask = "";
}

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

function AIOMortarObject::weight(%this, %client, %level, %minWeight, %inventoryStr)
{
   // if were playing CnH, check who owns this
   if (!isObject(%this.targetObjectId) || %this.targetObjectId.isHidden() || %this.targetObjectId.team == %client.team)
      return 0;
   
   //make sure the player is still alive!!!!!
   if (! AIClientIsAlive(%client))
      return 0;
      
   //no need to attack if the object is already destroyed
   if (%this.targetObjectId.getDamageState() $= "Destroyed")
      return 0;
   else
	{
		//if this bot is linked to a human who has issued this command, up the weight
		if (%this.issuedByClientId == %client.controlByHuman)
		{
			//make sure we have the potential to reach the minWeight
			if (!AIODefault::QuickWeight(%this, %client, %level, %minWeight))
			{
				if ($AIWeightHumanIssuedCommand < %minWeight)
					return 0;
				else
					%weight = $AIWeightHumanIssuedCommand;
			}
			else
			{
				// calculate the default...
				%weight = AIODefault::weight(%this, %client, %level, %inventoryStr);
				if (%weight < $AIWeightHumanIssuedCommand)
					%weight = $AIWeightHumanIssuedCommand;
			}
		}
		else
		{
			//make sure we have the potential to reach the minWeight
			if (!AIODefault::QuickWeight(%this, %client, %level, %minWeight))
				return 0;

			// calculate the default...
			%weight = AIODefault::weight(%this, %client, %level, %inventoryStr);
		}

		return %weight;
	}
}

function AIOMortarObject::assignClient(%this, %client)
{
   %client.objectiveTask = %client.addTask(AIMortarObject);
   %client.objectiveTask.initFromObjective(%this, %client);
   
   //create the escort objective (require a targeting laser in this case...)
   %client.escort = new AIObjective(AIOEscortPlayer)
                     {
						      dataBlock = "AIObjectiveMarker";
                        weightLevel1 = $AIWeightEscortOffense[1];
                        weightLevel2 = $AIWeightEscortOffense[2];
                        description = "Escort " @ getTaggedString(%client.name);
                        targetClientId = %client;
                        offense = true;
                        equipment = "TargetingLaser";
								buyEquipmentSet = "LightEnergyDefault";
                     };
   MissionCleanup.add(%client.escort);
   $ObjectiveQ[%client.team].add(%client.escort);
}

function AIOMortarObject::unassignClient(%this, %client)
{
   //kill the escort objective
   if (%client.escort)
   {
      AIClearObjective(%client.escort);
      %client.escort.delete();
      %client.escort = "";
   }
   
   %client.removeTask(%client.objectiveTask);
   %client.objectiveTask = "";
}

//------------------------------------------------------------------------
//If the function ShapeBaseImageData::testInvalidDeployConditions() changes at all, those changes need to be reflected here
function AIODeployEquipment::weight(%this, %client, %level, %minWeight, %inventoryStr)
{
   //make sure the player is still alive!!!!!
   if (! AIClientIsAlive(%client))
      return 0;

	//make sure the deploy objective is valid
	if (%this.isInvalid)
		return 0;

	//first, make sure we haven't deployed too many...
 	if (%this.equipment $= "TurretOutdoorDeployable" || %this.equipment $= "TurretIndoorDeployable")
      %maxAllowed = countTurretsAllowed(%this.equipment);
   else
      %maxAllowed = $TeamDeployableMax[%this.equipment];

	if ($TeamDeployedCount[%client.team, %this.equipment] >= %maxAllowed)
		return 0;

	//now make sure there are no other items in the way...
  	InitContainerRadiusSearch(%this.location, $MinDeployableDistance, $TypeMasks::VehicleObjectType |
  	                                             $TypeMasks::MoveableObjectType |
  	                                             $TypeMasks::StaticShapeObjectType |
  	                                             $TypeMasks::TSStaticShapeObjectType | 
  	                                             $TypeMasks::ForceFieldObjectType |
  	                                             $TypeMasks::ItemObjectType | 
  	                                             $TypeMasks::PlayerObjectType | 
  	                                             $TypeMasks::TurretObjectType);
	%objSearch = containerSearchNext();

	//make sure we're not invalidating the deploy location with the client's own player object
	if (%objSearch == %client.player)
		%objSearch = containerSearchNext();

	//did we find an object which would block deploying the equipment?
	if (isObject(%objSearch))
		return 0;

 	//now run individual checks based on the equipment type...
 	if (%this.equipment $= "TurretIndoorDeployable")
 	{
 		//check if there's another turret close to the deploy location
 	   InitContainerRadiusSearch(%this.location, $TurretIndoorSpaceRadius, $TypeMasks::StaticShapeObjectType);
 	   %found = containerSearchNext();
 	   if (isObject(%found))
 	   {
 			%foundName = %found.getDataBlock().getName();
 			if ((%foundName $= TurretDeployedFloorIndoor) || (%foundName $= "TurretDeployedWallIndoor") || (%foundName $= "TurretDeployedCeilingIndoor") || (%foundName $= "TurretDeployedOutdoor"))
 				return 0;
 		}
 
 		//now see if there are too many turrets in the area...
 	   %highestDensity = 0;
 	   InitContainerRadiusSearch(%this.location, $TurretIndoorSphereRadius, $TypeMasks::StaticShapeObjectType);
 	   %found = containerSearchNext();
 	   while (isObject(%found))
 	   {
 	      %foundName = %found.getDataBlock().getName();
 	      if ((%foundName $= "TurretDeployedFloorIndoor") || (%foundName $= "TurretDeployedWallIndoor") || (%foundName $= "TurretDeployedCeilingIndoor") || (%foundName $= "TurretDeployedOutdoor"))
 	      {
 				//found one
 				%numTurretsNearby++;
 
 				%nearbyDensity = testNearbyDensity(%found, $TurretIndoorSphereRadius);
 				if (%nearbyDensity > %highestDensity)
 					%highestDensity = %nearbyDensity;     
 	      }
 			%found = containerSearchNext();
 	   }
 
 	   if (%numTurretsNearby > %highestDensity)
 	      %highestDensity = %numTurretsNearby;
 
 		//now see if the area is already saturated
 		if (%highestDensity > $TurretIndoorMaxPerSphere)
 			return 0;
 	}
 
 	else if (%this.equipment $= "TurretOutdoorDeployable")
 	{
 		//check if there's another turret close to the deploy location
 	   InitContainerRadiusSearch(%this.location, $TurretOutdoorSpaceRadius, $TypeMasks::StaticShapeObjectType);
 	   %found = containerSearchNext();
 	   if (isObject(%found))
 		{
 			%foundName = %found.getDataBlock().getName();     
 			if ((%foundName $= "TurretDeployedFloorIndoor") || (%foundName $= "TurretDeployedWallIndoor") || (%foundName $= "TurretDeployedCeilingIndoor") || (%foundName $= "TurretDeployedOutdoor"))
 				return 0;
 		}
 
 		//now see if there are too many turrets in the area...
 	   %highestDensity = 0;
 	   InitContainerRadiusSearch(%this.location, $TurretOutdoorSphereRadius, $TypeMasks::StaticShapeObjectType);
 	   %found = containerSearchNext();
 	   while (isObject(%found))
 	   {
 	      %foundName = %found.getDataBlock().getName();
 	      if ((%foundName $= "TurretDeployedFloorIndoor") || (%foundName $= "TurretDeployedWallIndoor") || (%foundName $= "TurretDeployedCeilingIndoor") || (%foundName $= "TurretDeployedOutdoor"))
 	      {
 				//found one
 				%numTurretsNearby++;
 
 				%nearbyDensity = testNearbyDensity(%found, $TurretOutdoorSphereRadius);
 				if (%nearbyDensity > %highestDensity)
 					%highestDensity = %nearbyDensity;     
 	      }
 	     %found = containerSearchNext();
 	   }
 
 		if (%numTurretsNearby > %highestDensity)
 			%highestDensity = %numTurretsNearby;
 
 		//now see if the area is already saturated
 		if (%highestDensity > $TurretOutdoorMaxPerSphere)
 			return 0;
 	}

	//check equipment requirement
	%needEquipment = AINeedEquipment(%this.equipment, %client);
	
   //if don't need equipment, see if we've past the "point of no return", and should continue regardless
 	if (! %needEquipment)
 	{
 		%needArmor = AIMustUseRegularInvStation(%this.equipment, %client);
 		%result = AIFindClosestInventory(%client, %needArmor);
 		%closestInv = getWord(%result, 0);
 		%closestDist = getWord(%result, 1);
 
 		//if we're too far from the inv to go back, or we're too close to the deploy location, force continue
 		if (%closestDist > 50 && VectorDist(%client.player.getWorldBoxCenter(), %task.location) < 50)
 		{
 			%weight = AIODefault::weight(%this, %client, %level, %inventoryStr);
 			if (%weight < $AIWeightContinueDeploying)
 				%weight = $AIWeightContinueDeploying;
 			return %weight;
 		}
 	}

	//if this bot is linked to a human who has issued this command, up the weight
	if (%this.issuedByClientId == %client.controlByHuman)
	{
		//make sure we have the potential to reach the minWeight
		if (!AIODefault::QuickWeight(%this, %client, %level, %minWeight))
		{
			if ($AIWeightHumanIssuedCommand < %minWeight)
				return 0;
			else
				%weight = $AIWeightHumanIssuedCommand;
		}
		else
		{
			// calculate the default...
			%weight = AIODefault::weight(%this, %client, %level, %inventoryStr);
			if (%weight < $AIWeightHumanIssuedCommand)
				%weight = $AIWeightHumanIssuedCommand;
		}
	}
	else
	{
		//make sure we have the potential to reach the minWeight
		if (!AIODefault::QuickWeight(%this, %client, %level, %minWeight))
			return 0;

		// calculate the default...
		%weight = AIODefault::weight(%this, %client, %level, %inventoryStr);
	}

	return %weight;
}

function AIODeployEquipment::assignClient(%this, %client)
{
   %client.objectiveTask = %client.addTask(AIDeployEquipment);
   %task = %client.objectiveTask;
   %task.initFromObjective(%this, %client);
}

function AIODeployEquipment::unassignClient(%this, %client)
{
   %client.removeTask(%client.objectiveTask);
   %client.objectiveTask = "";
}

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