/// <summary> /// Recursive function to check agents which this agent is waiting on. /// /// There are a few situations where the agent should abort waiting. /// /// 1. When this agent is waiting on an agent which is ultimately waiting for this agent. /// 2. When this agent is waiting for an agent which is involved in a circular conflict either itself, or some subsequent agent. /// 3. When this agent is waiting for a waiting agent which is ultimately waiting for a stationary agent. /// 4. When this agent is waiting for an agent who might be waiting, but its conflict handling setting is lower and therefor will never move again. /// /// Some of these options can be solved just by letting others solve their conflicts first. /// </summary> /// <param name="inNextNode">The next node this agent might traverse to.</param> /// <param name="inObstructingAgent">The obstructing agent encountered.</param> /// <param name="inCheckedAgents">All Checked agents including the obstructed agent.</param> private EConflictStatus CheckWaitingOccuypingAgents(PathNode inNextNode, NavTileAgent inObstructingAgent, List <NavTileAgent> inCheckedAgents, ref EAbortReason refAbortReason) { // Get all agents occupying the tile which the obstructing agent is waiting for, sorted per MovementStatus. Dictionary <EMovementStatus, List <NavTileAgent> > sortedAgents = inObstructingAgent.WaitingForTile.GetSortedOccupyingAgents(inObstructingAgent); List <NavTileAgent> agents; // Check if there are any stationary agents. If so, abort this path. if (sortedAgents.TryGetValue(EMovementStatus.Stationary, out agents)) { refAbortReason = EAbortReason.EncounteredStationaryAgent; return(EConflictStatus.Abort); } else if (sortedAgents.TryGetValue(EMovementStatus.Waiting, out agents)) // Check if there are any waiting agents. { foreach (NavTileAgent agent in agents) { // Is the agent found this agent or has it already been checked? Abort path if (agent == this || inCheckedAgents.Contains(agent)) { refAbortReason = EAbortReason.EncounteredCircularConflict; return(EConflictStatus.Abort); } // Add the agent to the list inCheckedAgents.Add(agent); // Do this again. return(CheckWaitingOccuypingAgents(inNextNode, agent, inCheckedAgents, ref refAbortReason)); } } else // No entries of waiting or stationary agents. { // Set the timer to be used later and specify which tile this agent is waiting for. _didEncounterObstruction = true; _obstructionTimer = _waitAfterFreeTile; _waitingForTile = NavTileManager.Instance.SurfaceManager.Data.GetTileData(inNextNode.TilePosition); } return(EConflictStatus.Processing); }
private void OnPathAbortSetAnimationParameters(EAbortReason inAbortReason, Vector2Int inAbortPosition) { ResetAnimationParameters(); }
/// <summary> /// Function to check whether there is a conflict for this agent with the given parameters. /// </summary> /// <param name="inCurrentNode">The current node the agent is on.</param> /// <param name="inNextNode">The next node the agent might travel to.</param> /// <returns>A conflict status detailing how the conflict is handled.</returns> private EConflictStatus HandleConflict(PathNode inCurrentNode, PathNode inNextNode, out EAbortReason outAbortReason) { outAbortReason = EAbortReason.None; // Check if the next tile is free. TileData nextNodeTileData = NavTileManager.Instance.SurfaceManager.Data.GetTileData(inNextNode.TilePosition); NavTileAgent obstructingAgent; // Check if the next tile is obstructed. if (nextNodeTileData.IsObstructed(this, out obstructingAgent) || (_diagonalAllowed && IsMovingDiagonally(inCurrentNode.TilePosition, inNextNode.TilePosition) && IsPerpendicularObstructed(inCurrentNode.TilePosition, inNextNode.TilePosition, out obstructingAgent))) { // If so, set the position to be on the center of the tile. // The result of this function will stop moving the agent for at least one frame, so we correct it here instead. transform.position = inCurrentNode.WorldPosition; // If the conflict options is WaitOnWaitingAgent or higher, and the obstructing agent is waiting, // check if that agent isn't waiting indefinitely on this agent, itself or if there is some other circular conflict. if (_conflictOption >= EConflictOptions.WaitOnWaitingAgent && obstructingAgent.MovementStatus == EMovementStatus.Waiting) { return(CheckWaitingOccuypingAgents(inNextNode, obstructingAgent, new List <NavTileAgent>() { obstructingAgent }, ref outAbortReason)); } // If the conflict option is WaitingOnTraversingAgent and the obstructing agent is traversing, // set the timer to be used later and specify which tile this agent is waiting for. if (_conflictOption >= EConflictOptions.WaitOnTraversingAgent && obstructingAgent.MovementStatus == EMovementStatus.Traversing) { _didEncounterObstruction = true; _obstructionTimer = _waitAfterFreeTile; _waitingForTile = NavTileManager.Instance.SurfaceManager.Data.GetTileData(inNextNode.TilePosition); return(EConflictStatus.Processing); } // If this agent should abort on any obstruction, or there has been no result from the previous if's, abort the path. if (_conflictOption >= EConflictOptions.AbortOnObstruction || obstructingAgent.MovementStatus != EMovementStatus.Traversing) { outAbortReason = EAbortReason.EncounteredObstruction; return(EConflictStatus.Abort); } } else // No obstruction was found. { // Check if there was an obstruction since the last movement. if (_didEncounterObstruction && _conflictOption >= EConflictOptions.WaitOnTraversingAgent) { // If so, start a timer. _obstructionTimer -= Time.deltaTime; if (_obstructionTimer > 0) { return(EConflictStatus.Processing); } } // Update the tiles this agent should be occupying. UpdateTileOccupancy(inCurrentNode.TilePosition, inNextNode.TilePosition); } // No conflicts were found and everything is handled. Return a Success. return(EConflictStatus.Success); }