/// <summary>
		/// Gets the platform that a ledge is a part of.
		/// </summary>
		/// <returns>The platform that a ledge is a part of.</returns>
		/// <param name="ledge">The ledge to get a platform for.</param>
		private GameObject GetLedgePlatform(LedgeNode ledge)
		{
			if (ledge.transform.parent.parent != null)
			{
				return ledge.transform.parent.parent.gameObject;
			}
			else
			{
				return ledge.transform.parent.gameObject;
			}
		}
		/// <summary>
		/// Picks an action for the character to do every tick.
		/// </summary>
		/// <param name="controller">The controller for the character.</param>
		public void ChooseAction(AIController controller)
		{
			if (DEBUGLINES)
			{
				foreach (LedgeNode ledge in ledges)
				{
					foreach (LedgeNode adjacent in ledge.adjacentEdges)
					{
						Debug.DrawLine(ledge.transform.position, adjacent.transform.position, Color.yellow);
					}
				}
			}
			if (target == null) {
				controller.SetRunInDirection(0);
				return;
			}
			Controller targetController = target.GetComponent<Controller>();
			if (targetController != null && targetController.LifeComponent.Health <= 0)
			{
				controller.SetRunInDirection(0);
				return;
			}

			lastSpeed = controller.runSpeed;

			controller.jump = false;
			float currentTargetDistance = targetDistance;
			Vector3 opponentOffset = target.transform.position - controller.transform.position;
			Vector3 targetOffset = opponentOffset;
			float distanceTolerance = targetDistance - 1;
			Vector3 playerCenter = controller.transform.position + Vector3.up * 0.75f;

			RaycastHit under;
			Physics.Raycast(playerCenter, Vector3.down, out under, 30, AIController.LAYERMASK);

			// Check if there is a platform in the way of shooting.
			RaycastHit hit;
			// Find a ledge path to get to the target.
			replanTimer -= Time.deltaTime;
			if (pathTarget != target && currentNode == null)
			{
				replanTimer = 0;
			}
			if (OnSamePlatform(controller.transform, target.transform))
			{
				if (Mathf.Abs(opponentOffset.y) < 5 && (Mathf.Abs(opponentOffset.y) < 0.1f || under.distance < 1))
				{
					currentNode = null;
				}
			}
			else if (replanTimer <= 0)
			{
				replanTimer = REPLANTIME;
				pathTarget = target;
				currentPath = GetPath(GetClosestLedgeNode(controller.transform), GetClosestLedgeNode(target.transform));
				if (currentPath.Count > 0)
				{
					currentPath.Last.Value.YOffset = target.transform.position.y - currentPath.Last.Value.transform.position.y;
					LoadNextLedge();
					currentNode.YOffset = currentNode.transform.position.y - controller.transform.position.y;
					if (currentPath.Count > 0)
					{
						if (GetLedgePlatform(currentPath.First.Value) == GetPlatformUnderneath(controller.transform))
						{
							LoadNextLedge();
						}
						else if (currentPath.Count == 1 && Mathf.Abs(currentNode.transform.position.y - currentPath.First.Value.transform.position.y) < 0.2f)
						{
							currentNode = null;
						}
					}
				}
				else
				{
					currentNode = null;
				}
			}

			if (currentNode != null)
			{
				// Move towards the nearest ledge, jumping if needed.
				Vector3 currentOffset = currentNode.transform.position - controller.transform.position;

				// Go to the next ledge node if possible.
				if (currentNode.YOffset >= 0 && Math.Abs(currentOffset.x) <= LEDGEGRABDISTANCE / 3 && currentOffset.y <= 0  ||
					currentNode.YOffset < 0 && currentOffset.y >= 1.5f)
				{
					LoadNextLedge();
				}
				if (currentNode != null)
				{
					currentOffset = currentNode.transform.position - controller.transform.position;

					currentTargetDistance = 0;
					distanceTolerance = 0.1f;

					if (Mathf.Abs(currentOffset.y) < distanceTolerance)
					{
						currentOffset.y = 0;
					}

					Vector3 platformOffset = currentNode.transform.position - GetLedgePlatform(currentNode).transform.position;

					float ledgeOffset = -0.1f;
					if (currentNode.YOffset > 0 && currentOffset.y > -0.5f ||
						currentNode.YOffset < 0)
					{
						ledgeOffset = LEDGEGRABDISTANCE;
					}
					if (platformOffset.x > 0)
					{
						currentOffset.x += ledgeOffset;
					}
					else
					{
						currentOffset.x -= ledgeOffset;
					}

					controller.jump = currentNode.YOffset >= 0 && Math.Abs(currentOffset.x) <= LEDGEGRABDISTANCE / 2;
					 
					targetOffset = currentOffset;
				}
			}

			if (DEBUGLINES)
			{
				Debug.DrawRay(controller.transform.position, targetOffset, Color.red);
				if (currentNode != null)
				{
					LedgeNode c = currentNode;
					foreach (LedgeNode l in currentPath)
					{
						Debug.DrawLine(c.transform.position, l.transform.position, Color.red);
						c = l;
					}
					Debug.DrawLine(c.transform.position, target.transform.position, Color.red);
				}
			}
				
			LedgeNode closestLedge = null;

			// Check if the AI is falling to its death.
			if (under.collider == null)
			{
				// Find the closest ledge to go to.
				float closestLedgeDistance = Mathf.Infinity;
				foreach (LedgeNode ledge in ledges)
				{
                    if (ledge != null)
                    {
                        float currentDistance = Mathf.Abs(ledge.transform.position.x - controller.transform.position.x);
                        if (currentDistance < closestLedgeDistance && ledge.transform.position.y < controller.transform.position.y + 1)
                        {
                            closestLedge = ledge;
                            closestLedgeDistance = currentDistance;
                        }
                    }
				}
				bool awayFromLedge = false;
				if (closestLedge == null)
				{
					controller.SetRunInDirection(-controller.transform.position.x);
				}
				else {
					float ledgeOffsetX = closestLedge.transform.position.x - controller.transform.position.x;
					if (Mathf.Abs(ledgeOffsetX) > LEDGEGRABDISTANCE)
					{
						controller.SetRunInDirection(ledgeOffsetX);
						awayFromLedge = true;
					}
				}
				controller.jump = true;
				if (awayFromLedge)
				{
					return;
				}
			}

			// Move towards the opponent.
			float horizontalDistance = Mathf.Abs(targetOffset.x);
			if (horizontalDistance > currentTargetDistance)
			{
				controller.SetRunInDirection(targetOffset.x);
			}
			else if (horizontalDistance < currentTargetDistance - distanceTolerance)
			{
				controller.SetRunInDirection(-targetOffset.x);
			}
			else if (opponentOffset == targetOffset && under.collider != null && (controller.ParkourComponent.FacingRight ^ opponentOffset.x > 0))
			{
				controller.ParkourComponent.FacingRight = opponentOffset.x > 0;
			}
			else
			{
				controller.runSpeed = 0;
			}
			if (controller.runSpeed != 0)
			{
				// Don't chase an opponent off the map.
				Vector3 offsetPosition = controller.transform.position;
				offsetPosition.x += controller.runSpeed / 3;
				offsetPosition.y += 0.5f;
				Vector3 offsetPosition3 = offsetPosition;
				offsetPosition3.x += controller.runSpeed;
				if (!Physics.Raycast(offsetPosition, Vector3.down, out hit, 30, AIController.LAYERMASK) &&
					!Physics.Raycast(offsetPosition3, Vector3.down, out hit, 30, AIController.LAYERMASK))
				{
					if (controller.ParkourComponent.Sliding)
					{
						controller.SetRunInDirection(-opponentOffset.x);
					}
					else if (closestLedge == null)
					{
						controller.runSpeed = 0;
					}
					controller.slide = false;
				}
				else
				{
					// Slide if the opponent is far enough away for sliding to be useful.
					controller.ParkourComponent.FacingRight = opponentOffset.x > 0;
					controller.slide = horizontalDistance > 10 && currentNode == null && OnSamePlatform(controller.transform, target.transform);
				}
			}

			if (controller.runSpeed == 0 && Mathf.Abs(opponentOffset.x) < 1 && opponentOffset.y < 0 && target.GetComponent<Controller>() && controller.GetComponent<Rigidbody>().velocity.y <= Mathf.Epsilon)
			{
				// Don't sit on top of the opponent.
				controller.SetRunInDirection(-controller.transform.position.x);
			}

			if (controller.runSpeed > 0 && lastSpeed < 0 || controller.runSpeed < 0 && lastSpeed > 0)
			{
				// Check if the AI turned very recently to avoid thrashing.
				turnTimer -= Time.deltaTime;
				if (turnTimer <= 0) {
					turnTimer = TURNCOOLDOWN;
				} else {
					controller.runSpeed = 0;
				}
			}

			// Jump to reach some tokens.
			if (targetDistance == 0 && controller.runSpeed == 0 && target.GetComponent<ArrowToken>() && opponentOffset == targetOffset) {
				controller.jump = true;
			}

			controller.fastFall = targetOffset.y < -1f;
		}
		/// <summary>
		/// Loads the next ledge node into the current node.
		/// </summary>
		private void LoadNextLedge()
		{
			if (currentPath == null || currentPath.Count == 0)
			{
				currentNode = null;
				replanTimer = 0;
			}
			else
			{
				currentNode = currentPath.First.Value;
				currentPath.RemoveFirst();
			}
		}
		/// <summary>
		/// Constructs a path from a start and end node.
		/// </summary>
		/// <returns>A path from the start to the end node.</returns>
		/// <param name="start">The node to start from.</param>
		/// <param name="end">The node to end at.</param>
		private LinkedList<LedgeNode> GetPath(LedgeNode start, LedgeNode end)
		{
			LinkedList<LedgeNode> path = new LinkedList<LedgeNode>();
			if (start == null || end == null || next[start.index, end.index] == null)
			{
				return path;
			}
			else
			{
				LedgeNode current = start;
				path.AddLast(start);
				while (current != end)
				{
					LedgeNode previous = current;
					current = next[current.index, end.index];
					float offset = current.transform.position.y - previous.transform.position.y;
					if (Mathf.Abs(offset) > 0.2f)
					{
						if (offset > 0)
						{
							current.YOffset = offset;
							if (previous == start && offset < 5)
							{
								path.RemoveFirst();
							}
						}
						else if (offset < 0)
						{
							previous.YOffset = offset;
						}
					}
					if (current != end || GetLedgePlatform(current) != GetLedgePlatform(previous))
					{
						path.AddLast(current);
					}
				}
				return path;
			}
		}