
function aiAddHumanObjective(%client, %objective, %targetClient, %fromCmdMap)
{
	//first, make sure the objective Q for the given team exists
	if (!isObject($ObjectiveQ[%client.team]))
		return 0;

	//if a target client is specified, create a link if we can...
	if (%targetClient > 0)
	{
		if (aiHumanHasControl(%client, %targetClient) || (%targetClient.isAIControlled() && !aiAlreadyControlled(%targetClient)))
			aiSetHumanControl(%client, %targetClient);
		else
			return 0;
	}

	//special (hacky) case here for AIOBombLocation objectives
	if (%objective.getName() $= "AIOBombLocation")
	{
		if (!isObject($AIBombLocationSet))
			return 0;

		%objective.team = %client.team;
		$AIBombLocationSet.add(%objective);
		return %objective;
	}

	//parse the type of objective, and see if it already exists...
	%useThisObjective = -1;
	%objQ = $ObjectiveQ[%client.team];
	if (%objective.getName() $= "AIOEscortPlayer" || %objective.getName() $= "AIOAttackPlayer")
	{
		//first, make sure the objective is legit
		if (!AIClientIsAlive(%objective.targetClientId))
			return 0;

		//fill in the description:
		%objective.description = %objective.getName() SPC getTaggedString(%objective.targetClientId.name);

		//now see if the objective already exists...
		%count = %objQ.getCount();
		for (%i = 0; %i < %count; %i++)
		{
			%obj = %objQ.getObject(%i);
			if (%obj.getName() $= %objective.getName())
			{
				//if we've found a previously existing version, use it instead of the new objective
				if (%obj.issuedByClientId == %client && %obj.targetClientId == %objective.targetClientId)
				{
					%useThisObjective = %obj;
					break;
				}
			}
		}
	}

	else if (%objective.getName() $= "AIODefendLocation" || %objective.getName() $= "AIOAttackLocation")
	{
		//make sure it's a valid objective
		if (%objective.location $= "" || %objective.location $= "0 0 0")
			return 0;

		//fill in the description:
		%objective.description = %objective.getName() @ " at" SPC %objective.location;

		//look for a duplicate...
		%count = %objQ.getCount();
		for (%i = 0; %i < %count; %i++)
		{
			%obj = %objQ.getObject(%i);
			if (%obj.getName() $= %objective.getName())
			{
				if (%obj.issuedByClientId == %client && VectorDist(%objective.location, %obj.location) < 30)
				{
					%useThisObjective = %obj;
					break;
				}
			}
		}
	}

	else if (%objective.getName() $= "AIOAttackObject" || %objective.getName() $= "AIORepairObject" ||
				%objective.getName() $= "AIOLazeObject" || %objective.getName() $= "AIOMortarObject" ||
				%objective.getName() $= "AIOTouchObject")
	{
		//make sure it's a valid objective
		if (!isObject(%objective.targetObjectId))
			return 0;

		//fill in the description:
		%objective.description = %objective.getName() SPC %objective.targetObjectId.getDataBlock().getName();

		//look for a duplicate...
		%count = %objQ.getCount();
		for (%i = 0; %i < %count; %i++)
		{
			%obj = %objQ.getObject(%i);
			if (%obj.getName() $= %objective.getName())
			{
				if (%obj.issuedByClientId == %client && %objective.targetObjectId == %obj.targetObjectId)
				{
					%useThisObjective = %obj;
					break;
				}
			}
		}
	}
	else if (%objective.getName() $= "AIODeployEquipment")
	{
		//make sure it's a valid objective
		if (%objective.location $= "" || %objective.location $= "0 0 0" || %objective.equipment $= "")
			return 0;

		//fill in the description:
		%objective.description = %objective.getName() SPC %objective.equipment SPC "at" SPC %objective.location;

		//look for a duplicate...
		%count = %objQ.getCount();
		for (%i = 0; %i < %count; %i++)
		{
			%obj = %objQ.getObject(%i);
			if (%obj.getName() $= %objective.getName())
			{
				if (%obj.issuedByClientId == %client && VectorDist(%objective.location, %obj.location) < 8 && %objective.equipment == %obj.equipment)
				{
					%useThisObjective = %obj;
					break;
				}
			}
		}
	}

	//if we found a previously added objective, delete the submitted one
	if (%useThisObjective > 0)
	{
		%objective.delete();
	}

	//otherwise add it to the objective Q
	else
	{
		%useThisObjective = %objective;
	 	$ObjectiveQ[%client.team].add(%useThisObjective);
      %objective.shouldAcknowledge = true;
		//now that it's been added, see if anyone picks it up within 10 seconds
		schedule(10000, %objective, "AIReassessHumanObjective", %objective); 
	}

	//now see if we're supposed to force the target to the objective
	//the link will have already been checked at the top...
	if (%targetClient > 0)
	{
		//if we were previously assigned to an objective which was forced on this client, we need to delete it...
		%prevObjective = %targetClient.objective;
		if (%prevObjective != %useThisObjective && %prevObjective.issuedByClientId == %targetClient.controlByHuman)
		{
	      AIClearObjective(%prevObjective);
	      %prevObjective.delete();
		}

		//if the command is an escort command, issue it at the forced escort weight instead
		%forcedWeight = $AIWeightHumanIssuedCommand;
		if (%useThisObjective.getName() $= "AIOEscortPlayer")
			%forcedWeight = $AIWeightHumanIssuedEscort;

		//reweight the client's objective
		%testWeight = 0;
		if (isObject(%client.objective))
			%testWeight = %client.objective.weight(%client, %client.objectiveLevel, 0, %inventoryStr);
		if (%testWeight <= 0 || %testWeight > %client.objectiveWeight)
			%client.objectiveWeight = %testWeight;

		//see if we should force the objective
		if (%targetClient.objectiveWeight <= %forcedWeight)
		{
			AIForceObjective(%targetClient, %useThisObjective, %forcedWeight);

			//clearing the prev objective will undo the link - re-do it here
			aiSetHumanControl(%client, %targetClient);

			//send the "command accepted response"
			if (!%fromCmdMap)
			{
				if (isObject(%client.player) && VectorDist(%targetClient.player.position, %client.player.position) <= 40)
				   schedule(250, %targetClient.player, "AIPlayAnimSound", %targetClient, %client.player.getWorldBoxCenter(), "cmd.acknowledge", 1, 1, 0);
			}
			else
				serverCmdAcceptTask(%targetClient, %client, -1, %useThisObjective.ackDescription);
		}
		else
		{
			//send the "command declined response"
			if (!%fromCmdMap)
			{
				if (isObject(%client.player) && VectorDist(%targetClient.player.position, %client.player.position) <= 40)
            {
				   schedule(250, %targetClient.player, "AIPlayAnimSound", %targetClient, %client.player.getWorldBoxCenter(), "cmd.decline", -1, -1, 0);
               schedule(2000, %client.player, "AIRespondToEvent", %client, 'ChatCmdWhat', %targetClient);
            }
			}
			else
				serverCmdDeclineTask(%targetClient, %client, %useThisObjective.ackDescription);
		}
	}

	//return the objective used, so the calling function can know whether to delete the parameter one...
	return %useThisObjective;
}

function AIReassessHumanObjective(%objective)
{
	if (%objective.issuedByHuman)
	{
		//see if there's anyone still assigned to this objective
		if (!AIClientIsAlive(%objective.clientLevel1) && !AIClientIsAlive(%objective.clientLevel2) && !AIClientIsAlive(%objective.clientLevel3))
		{
			AIClearObjective(%objective);
			%objective.delete();
		}

		//else reassess this objective in another 10 seconds
		else
			schedule(10000, %objective, "AIReassessHumanObjective", %objective); 
	}
}

function aiAttemptHumanControl(%humanClient, %aiClient)
{
	if (!aiAlreadyControlled(%aiClient))
	{
		aiSetHumanControl(%humanClient, %aiClient);
		return true;
	}
	else
		return false;
}

function aiHumanHasControl(%humanClient, %aiClient)
{
	if (AIClientIsAlive(%humanClient) && AIClientIsAlive(%aiClient))
		if (%humanClient.controlAI == %aiClient && %aiClient.controlByHuman == %humanClient)
			return true;

	return false;
}

function aiAlreadyControlled(%aiClient)
{
	if (AIClientIsAlive(%aiClient) && AIClientIsAlive(%aiClient.controlByHuman))
		if (%aiClient.controlByHuman.controlAI == %aiClient)
			return true;

	return false;
}

function aiReleaseHumanControl(%humanClient, %aiClient)
{
	//make sure they were actually linked
	if (!aiHumanHasControl(%humanClient, %aiClient))
		return;

	aiBreakHumanControl(%humanClient, %aiClient);
}

function aiSetHumanControl(%humanClient, %aiClient)
{
	//make sure it's from a human to an ai
	if (%humanClient.isAIControlled() || !%aiClient.isAIControlled())
		return;

	//these checks should be redundant, but...
	if (%humanClient.controlAI > 0)
		aiBreakHumanControl(%humanClient, %humanClient.controlAI);

	if (%aiClient.controlByHuman > 0)
		aiBreakHumanControl(%aiClient.controlByHuman, %aiClient);

	//create the link
	%humanClient.controlAI = %aiClient;
	%aiClient.controlByHuman = %humanClient;
}

function aiBreakHumanControl(%humanClient, %aiClient)
{
	//make sure they were actually linked
	if (!aiHumanHasControl(%humanClient, %aiClient))
		return;

	//for now, just break the link, and worry about unassigning objectives later...
	%humanClient.controlAI = "";
	%aiClient.controlByHuman = "";
}