private IEnumerator WalkToNode(PathNode node)
        {
            var targetPosition = node.Position.ToVector3();

            while (targetPosition != transform.position) {

                /* Linearly move player to target cell */
                this.transform.position = Vector3.MoveTowards(transform.position, targetPosition, Speed * Time.deltaTime);

                var _direction = (targetPosition - transform.position).normalized;
                var _lookRotation = Quaternion.LookRotation(_direction);

                //rotate us over time according to speed until we are in the required rotation
                transform.rotation = Quaternion.Slerp(transform.rotation, _lookRotation, Time.deltaTime * RotationSpeed);

                yield return null;
            }
        }
        public IEnumerator FindPath(IntVector3 start, IntVector3 end, Action<bool> callback)
        {
            currentNode = null;
            this.endNode = null;

            this.start = start;
            this.end = end;

            this.openNodes.Clear();
            this.closedNodes.Clear();
            this.finalNodes.Clear();

            /* Create start node and add to open list */
            var startNode = new PathNode() {
                Position = start,
                G = 0,
                F = Manhattan(start, end)
            };

            this.openNodes.Add(startNode);

            while (this.openNodes.Count > 0) {

                /* Sort open nodes by lowest f(G, H) = G + H */
                this.openNodes = this.openNodes.OrderBy( node => node.F ).ToList();

                currentNode = this.openNodes.PopAt(0);
                this.closedNodes.Add(currentNode);

                if (currentNode.Position.Equals(end)) {
                    this.endNode = currentNode;
                    break;
                }

                while (!ContinueSteps && PerformSteps) {
                    yield return null;
                }

                ContinueSteps = false;

                /* Visit all neighbors of our current node */
                foreach(var offset in CellRange.Values2D(false)) {
                    var absPos = currentNode.Position + offset.ToIntVector3(0);

                    /* Continue if we already visited that neighbor */
                    var isClosedNode = this.closedNodes.Find( node => node.Position.Equals(absPos) ) != null;
                    var isWalkable = CellDefinition.IsWalkable(regionManager.GetCellAt(absPos));

                    if (isClosedNode || !isWalkable) {
                        continue;
                    }

                    var tentativeG = currentNode.G + Distance(currentNode.Position, absPos);
                    var openNeighbor = this.openNodes.Find( node => node.Position.Equals(absPos) );

                    if (openNeighbor == null || tentativeG < openNeighbor.G) {
                        if (openNeighbor == null) {
                            openNeighbor = new PathNode();

                            this.openNodes.Add(openNeighbor);
                        }

                        openNeighbor.ParentNode = currentNode;
                        openNeighbor.Position = absPos;
                        openNeighbor.G = tentativeG;
                        openNeighbor.F = openNeighbor.G + Manhattan(absPos, end);
                    }
                }
            }

            callback(this.endNode != null);
        }
        private void DrawFinalPathGizmos()
        {
            var currentNode = this.endNode;

            if (this.endNode == null) {
                return;
            }

            Gizmos.color = Color.red;

            while (true) {
                if (currentNode.ParentNode == null) {
                    break;
                }

                Gizmos.DrawLine(currentNode.Position.ToVector3(), currentNode.ParentNode.Position.ToVector3());

                currentNode = currentNode.ParentNode;
            }
        }
        private void RemoveNodesInSight(PathNode node)
        {
            var heightOffset = new Vector3(0, 0.25f, 0);

            if (node.ParentNode == null) {
                return;
            }

            if (Physics.Linecast(node.Position + heightOffset, node.ParentNode.Position + heightOffset)) {
                RemoveNodesInSight(node.ParentNode);
            } else {
                var parentIndex = this.finalNodes.IndexOf(node.ParentNode);

                if (node.ParentNode == null) {
                    return;
                }

                node.ParentNode = node.ParentNode.ParentNode;
                this.finalNodes.RemoveAt(parentIndex);

                RemoveNodesInSight(node);
            }
        }
        public List<PathNode> ReconstructPath(bool reverse = true)
        {
            var currentNode = endNode;

            if (this.endNode == null) {
                Logger.Warn("Cannot reconstruct path, must find it first.");
                return null;
            }

            while (true) {
                this.finalNodes.Add(currentNode);

                if (currentNode.ParentNode == null) {
                    break;
                }

                currentNode = currentNode.ParentNode;
            }

            if (reverse) {
                this.finalNodes.Reverse();
            }

            return this.finalNodes;
        }