ePathFinderStatus cPath::CalculationStep(cChunk & a_Chunk) { m_Chunk = &a_Chunk; if (m_Status != ePathFinderStatus::CALCULATING) { return m_Status; } if (m_BadChunkFound) { FinishCalculation(ePathFinderStatus::PATH_NOT_FOUND); return m_Status; } if (m_StepsLeft == 0) { AttemptToFindAlternative(); } else { --m_StepsLeft; int i; for (i = 0; i < CALCULATIONS_PER_STEP; ++i) { if (StepOnce()) // StepOnce returns true when no more calculation is needed. { break; // if we're here, m_Status must have changed either to PATH_FOUND or PATH_NOT_FOUND. } } m_Chunk = nullptr; } return m_Status; }
bool cPath::StepOnce() { cPathCell * CurrentCell = OpenListPop(); // Path not reachable. if (CurrentCell == nullptr) { AttemptToFindAlternative(); return true; } // Path found. if (CurrentCell->m_Location == m_Destination) { BuildPath(); FinishCalculation(ePathFinderStatus::PATH_FOUND); return true; } // Calculation not finished yet // Check if we have a new NearestPoint. if ((m_Destination - CurrentCell->m_Location).Length() < 5) { if (m_Rand.NextInt(4) == 0) { m_NearestPointToTarget = CurrentCell; } } else if (CurrentCell->m_H < m_NearestPointToTarget->m_H) { m_NearestPointToTarget = CurrentCell; } // process a currentCell by inspecting all neighbors. // Now we start checking adjacent cells. // If true, no need to do more checks in that direction bool DoneEast = false, DoneWest = false, DoneNorth = false, DoneSouth = false; // If true, we can walk in that direction without changing height // This is used for deciding if to calculate diagonals bool WalkableEast = false, WalkableWest = false, WalkableNorth = false, WalkableSouth = false; // If we can jump without hitting the ceiling if (BodyFitsIn(CurrentCell->m_Location + Vector3i(0, 1, 0), CurrentCell->m_Location)) { // For ladder climbing ProcessIfWalkable(CurrentCell->m_Location + Vector3i(0, 1, 0), CurrentCell, JUMP_G_COST); // Check east-up if (ProcessIfWalkable(CurrentCell->m_Location + Vector3i(1, 1, 0), CurrentCell, JUMP_G_COST)) { DoneEast = true; } // Check west-up if (ProcessIfWalkable(CurrentCell->m_Location + Vector3i(-1, 1, 0), CurrentCell, JUMP_G_COST)) { DoneWest = true; } // Check north-up if (ProcessIfWalkable(CurrentCell->m_Location + Vector3i(0, 1, -1), CurrentCell, JUMP_G_COST)) { DoneNorth = true; } // Check south-up if (ProcessIfWalkable(CurrentCell->m_Location + Vector3i(0, 1, 1), CurrentCell, JUMP_G_COST)) { DoneSouth = true; } } // Check North, South, East, West at our own height or below. We are willing to jump up to 3 blocks down. if (!DoneEast) { for (int y = 0; y >= -3; --y) { if (ProcessIfWalkable(CurrentCell->m_Location + Vector3i(1, y, 0), CurrentCell, NORMAL_G_COST)) { DoneEast = true; if (y == 0) { WalkableEast = true; } break; } } } if (!DoneWest) { for (int y = 0; y >= -3; --y) { if (ProcessIfWalkable(CurrentCell->m_Location + Vector3i(-1, y, 0), CurrentCell, NORMAL_G_COST)) { DoneWest = true; if (y == 0) { WalkableWest = true; } break; } } } if (!DoneSouth) { for (int y = 0; y >= -3; --y) { if (ProcessIfWalkable(CurrentCell->m_Location + Vector3i(0, y, 1), CurrentCell, NORMAL_G_COST)) { DoneWest = true; if (y == 0) { WalkableSouth = true; } break; } } } if (!DoneNorth) { for (int y = 0; y >= -3; --y) { if (ProcessIfWalkable(CurrentCell->m_Location + Vector3i(0, y, -1), CurrentCell, NORMAL_G_COST)) { DoneNorth = true; if (y == 0) { WalkableNorth = true; } break; } } } // Check diagonals if (WalkableNorth && WalkableEast) { ProcessIfWalkable(CurrentCell->m_Location + Vector3i(1, 0, -1), CurrentCell, DIAGONAL_G_COST); } if (WalkableNorth && WalkableWest) { ProcessIfWalkable(CurrentCell->m_Location + Vector3i(-1, 0, -1), CurrentCell, DIAGONAL_G_COST); } if (WalkableSouth && WalkableEast) { ProcessIfWalkable(CurrentCell->m_Location + Vector3i(1, 0, 1), CurrentCell, DIAGONAL_G_COST); } if (WalkableSouth && WalkableWest) { ProcessIfWalkable(CurrentCell->m_Location + Vector3i(-1, 0, 1), CurrentCell, DIAGONAL_G_COST); } return false; }
bool cPath::StepOnce() { cPathCell * CurrentCell = OpenListPop(); // Path not reachable. if (CurrentCell == nullptr) { AttemptToFindAlternative(); return true; } // Path found. if (CurrentCell->m_Location == m_Destination) { BuildPath(); FinishCalculation(ePathFinderStatus::PATH_FOUND); return true; } // Calculation not finished yet. // Check if we have a new NearestPoint. // TODO I don't like this that much, there should be a smarter way. if ((m_Destination - CurrentCell->m_Location).Length() < 5) { if (m_Rand.NextInt(4) == 0) { m_NearestPointToTarget = CurrentCell; } } else if (CurrentCell->m_H < m_NearestPointToTarget->m_H) { m_NearestPointToTarget = CurrentCell; } // process a currentCell by inspecting all neighbors. // Check North, South, East, West on our height. ProcessIfWalkable(CurrentCell->m_Location + Vector3i(1, 0, 0), CurrentCell, 10); ProcessIfWalkable(CurrentCell->m_Location + Vector3i(-1, 0, 0), CurrentCell, 10); ProcessIfWalkable(CurrentCell->m_Location + Vector3i(0, 0, 1), CurrentCell, 10); ProcessIfWalkable(CurrentCell->m_Location + Vector3i(0, 0, -1), CurrentCell, 10); // Check diagonals on XY plane. // x = -1: west, x = 1: east. for (int x = -1; x <= 1; x += 2) { if (GetCell(CurrentCell->m_Location + Vector3i(x, 0, 0))->m_IsSolid) // If there's a solid our east / west. { if (!GetCell(CurrentCell->m_Location + Vector3i(0, 1, 0))->m_IsSolid) // If there isn't a solid above. { ProcessIfWalkable(CurrentCell->m_Location + Vector3i(x, 1, 0), CurrentCell, JUMP_G_COST); // Check east-up / west-up. } } else { ProcessIfWalkable(CurrentCell->m_Location + Vector3i(x, -1, 0), CurrentCell, 14); // Else check east-down / west-down. } } // Check diagonals on the YZ plane. for (int z = -1; z <= 1; z += 2) { if (GetCell(CurrentCell->m_Location + Vector3i(0, 0, z))->m_IsSolid) // If there's a solid our north / south. { if (!GetCell(CurrentCell->m_Location + Vector3i(0, 1, 0))->m_IsSolid) // If there isn't a solid above. { ProcessIfWalkable(CurrentCell->m_Location + Vector3i(0, 1, z), CurrentCell, JUMP_G_COST); // Check north-up / south-up. } } else { ProcessIfWalkable(CurrentCell->m_Location + Vector3i(0, -1, z), CurrentCell, 14); // Else check north-down / south-down. } } // Check diagonals on the XZ plane. (Normal diagonals, this plane is special because of gravity, etc) for (int x = -1; x <= 1; x += 2) { for (int z = -1; z <= 1; z += 2) { // This condition prevents diagonal corner cutting. if (!GetCell(CurrentCell->m_Location + Vector3i(x, 0, 0))->m_IsSolid && !GetCell(CurrentCell->m_Location + Vector3i(0, 0, z))->m_IsSolid) { // This prevents falling of "sharp turns" e.g. a 1x1x20 rectangle in the air which breaks in a right angle suddenly. if (GetCell(CurrentCell->m_Location + Vector3i(x, -1, 0))->m_IsSolid && GetCell(CurrentCell->m_Location + Vector3i(0, -1, z))->m_IsSolid) { ProcessIfWalkable(CurrentCell->m_Location + Vector3i(x, 0, z), CurrentCell, 14); // 14 is a good enough approximation of sqrt(10 + 10). } } } } return false; }