/** * @brief Calculate the TUs after we in the given dir * @param[in] path Pointer to client or server side pathing table (clPathMap, svPathMap) */ void Step::calcNewTUs (const pathing_t *path) { const byte TUsSoFar = RT_AREA_POS(path, fromPos, crouchingState); /* Find the number of TUs used (normally) to move in this direction. */ const byte TUsForMove = Grid_GetTUsForDirection(dir, crouchingState); /* Now add the TUs needed to get to the originating cell. */ TUsAfter = TUsSoFar + TUsForMove; }
/** * @brief Return the needed TUs to walk to a given position * @param[in] path Pointer to client or server side pathing table (clPathMap, svPathMap) * @param[in] to Position to walk to * @param[in] crouchingState Whether the actor is currently crouching, 1 is yes, 0 is no. * @param[in] stored Use the stored mask (the cached move) of the routing data * @return ROUTING_NOT_REACHABLE if the move isn't possible * @return length of move otherwise (TUs) */ pos_t Grid_MoveLength (const pathing_t *path, const pos3_t to, byte crouchingState, bool stored) { /* Confirm bounds */ assert(to[2] < PATHFINDING_HEIGHT); assert(crouchingState == 0 || crouchingState == 1); /* s.a. ACTOR_MAX_STATES */ if (!stored) return RT_AREA_POS(path, to, crouchingState); else return RT_SAREA(path, to[0], to[1], to[2], crouchingState); }
static void Grid_SetMoveData (pathing_t *path, const pos3_t toPos, const int c, const byte length, const int dir, const int oz, const int oc, priorityQueue_t *pqueue) { pos4_t dummy; RT_AREA_TEST_POS(path, toPos, c); RT_AREA_POS(path, toPos, c) = length; /**< Store TUs for this square. */ RT_AREA_FROM_POS(path, toPos, c) = makeDV(dir, oz); /**< Store origination information for this square. */ Vector4Set(dummy, toPos[0], toPos[1], toPos[2], c); /** @todo add heuristic for A* algorithm */ PQueuePush(pqueue, dummy, length); }
/** * @brief Get the direction to use to move to a position (used to reconstruct the path) * @param[in] path Pointer to client or server side pathing table (le->PathMap, svPathMap) * @param[in] toPos The desired location * @param[in] crouchingState Whether the actor is currently crouching, 1 is yes, 0 is no. * @return a direction vector (see dvecs and DIRECTIONS) * @sa Grid_MoveCheck */ int Grid_MoveNext (const pathing_t *path, const pos3_t toPos, byte crouchingState) { const pos_t l = RT_AREA_POS(path, toPos, crouchingState); /**< Get TUs for this square */ /* Check to see if the TUs needed to move here are greater than 0 and less then ROUTING_NOT_REACHABLE */ if (!l || l == ROUTING_NOT_REACHABLE) { /* ROUTING_UNREACHABLE means, not possible/reachable */ return ROUTING_UNREACHABLE; } /* Return the information indicating how the actor got to this cell */ return RT_AREA_FROM_POS(path, toPos, crouchingState); }
/** * @brief Return the needed TUs to walk to a given position * @param[in] path Pointer to client or server side pathing table (clPathMap, svPathMap) * @param[in] to Position to walk to * @param[in] crouchingState Whether the actor is currently crouching, 1 is yes, 0 is no. * @param[in] stored Use the stored mask (the cached move) of the routing data * @return ROUTING_NOT_REACHABLE if the move isn't possible * @return length of move otherwise (TUs) */ pos_t Grid_MoveLength (const pathing_t *path, const pos3_t to, byte crouchingState, qboolean stored) { #ifdef PARANOID if (to[2] >= PATHFINDING_HEIGHT) { Com_DPrintf(DEBUG_PATHING, "Grid_MoveLength: WARNING to[2] = %i(>= HEIGHT)\n", to[2]); return ROUTING_NOT_REACHABLE; } #endif /* Confirm bounds */ assert(to[2] < PATHFINDING_HEIGHT); assert(crouchingState == 0 || crouchingState == 1); /* s.a. ACTOR_MAX_STATES */ if (!stored) return RT_AREA_POS(path, to, crouchingState); else return RT_SAREA(path, to[0], to[1], to[2], crouchingState); }
/** * @brief Checks if a crouched actor could save TUs by standing up, walking and crouching again. * @param[in] path Pointer to client or server side pathing table * @param[in] toPos Desired position */ bool Grid_ShouldUseAutostand (const pathing_t *path, const pos3_t toPos) { const int tusCrouched = RT_AREA_POS(path, toPos, 1); const int tusUpright = RT_AREA_POS(path, toPos, 0); return tusUpright + 2 * TU_CROUCH < tusCrouched; }
/** * @brief Tries to find a path from the given actor(-position) to a given target position * * Unlike Grid_CalcPathing, this function does not neccessarily calculate the TU values for * all positions reachable from 'from'. Instead it tries to find the shortest/fastest path to * the target position. There is no limit to maxTUs. * * @param[in] routing Reference to client or server side routing table (clMap, svMap) * @param[in] actorSize The size of thing to calc the move for (e.g. size=2 means 2x2). * The plan is to have the 'origin' in 2x2 units in the bottom-left (towards the lower coordinates) corner of the 2x2 square. * @param[in,out] path Pointer to client or server side pathing table (clMap, svMap) * @param[in] from The position to start the calculation from. * @param[in] targetPos The position where we want to end up. * @param[in] maxTUs The maximum TUs away from 'from' to calculate move-information for * @param[in] crouchingState Whether the actor is currently crouching, 1 is yes, 0 is no. * @param[in] fb_list Forbidden list (entities are standing at those points) * @param[in] fb_length Length of forbidden list * @sa G_MoveCalc * @sa CL_ConditionalMoveCalc */ bool Grid_FindPath (const Routing &routing, const actorSizeEnum_t actorSize, pathing_t *path, const pos3_t from, const pos3_t targetPos, byte crouchingState, int maxTUs, byte ** fb_list, int fb_length) { bool found = false; int count; priorityQueue_t pqueue; pos4_t epos; /**< Extended position; includes crouching state */ pos3_t pos; /* this is the position of the current actor- so the actor can stand in the cell it is in when pathfinding */ pos3_t excludeFromForbiddenList; /* Confirm bounds */ assert((from[2]) < PATHFINDING_HEIGHT); assert(crouchingState == 0 || crouchingState == 1); /* s.a. ACTOR_MAX_STATES */ /* reset move data */ OBJSET(path->area, ROUTING_NOT_REACHABLE); OBJSET(path->areaFrom, ROUTING_NOT_REACHABLE); path->fblist = fb_list; path->fblength = fb_length; /* Prepare exclusion of starting-location (i.e. this should be ent-pos or le-pos) in Grid_CheckForbidden */ VectorCopy(from, excludeFromForbiddenList); /* set starting position to 0 TUs.*/ RT_AREA_POS(path, from, crouchingState) = 0; PQueueInitialise(&pqueue, 1024); Vector4Set(epos, from[0], from[1], from[2], crouchingState); PQueuePush(&pqueue, epos, 0); count = 0; while (!PQueueIsEmpty(&pqueue)) { PQueuePop(&pqueue, epos); VectorCopy(epos, pos); count++; /* if reaching that square already took too many TUs, * don't bother to reach new squares *from* there. */ const byte usedTUs = RT_AREA_POS(path, pos, crouchingState); if (usedTUs >= maxTUs) continue; for (int dir = 0; dir < PATHFINDING_DIRECTIONS; dir++) { Step step(routing, pos, actorSize, crouchingState, dir); /* Directions 12, 14, and 15 are currently undefined. */ if (dir == 12 || dir == 14 || dir == 15) continue; /* If this is a crouching or crouching move, forget it. */ if (dir == DIRECTION_STAND_UP || dir == DIRECTION_CROUCH) continue; if (!step.init()) continue; /* either dir is irrelevant or something worse happened */ if (!step.isPossible(path)) continue; /* Is this a better move into this cell? */ RT_AREA_TEST_POS(path, step.toPos, step.crouchingState); if (RT_AREA_POS(path, step.toPos, step.crouchingState) <= step.TUsAfter) { continue; /* This move is not optimum. */ } /* Test for forbidden (by other entities) areas. */ /* Do NOT check the forbiddenList. We might find a multi-turn path. */ #if 0 if (Grid_CheckForbidden(excludeFromForbiddenList, step.actorSize, path, step.toPos[0], step.toPos[1], step.toPos[2])) { continue; /* That spot is occupied. */ } #endif /* Store move in pathing table. */ Grid_SetMoveData(path, step.toPos, step.crouchingState, step.TUsAfter, step.dir, step.fromPos[2]); pos4_t dummy; const int dist = step.TUsAfter + (int) (2 * VectorDist(step.toPos, targetPos)); Vector4Set(dummy, step.toPos[0], step.toPos[1], step.toPos[2], step.crouchingState); PQueuePush(&pqueue, dummy, dist); if (VectorEqual(step.toPos, targetPos)) { found = true; break; } } if (found) break; } /* Com_Printf("Loop: %i", count); */ PQueueFree(&pqueue); return found; }
/** * @brief Recalculate the pathing table for the given actor(-position) * * We calculate the table for ALL possible movement states (atm stand and crouch) * to be able to propose smart things like autostand. * @param[in] routing Reference to client or server side routing table (clMap, svMap) * @param[in] actorSize The size of thing to calc the move for (e.g. size=2 means 2x2). * The plan is to have the 'origin' in 2x2 units in the bottom-left (towards the lower coordinates) corner of the 2x2 square. * @param[in,out] path Pointer to client or server side pathing table (clMap, svMap) * @param[in] from The position to start the calculation from. * @param[in] maxTUs The maximum TUs away from 'from' to calculate move-information for * @param[in] fb_list Forbidden list (entities are standing at those points) * @param[in] fb_length Length of forbidden list * @sa G_MoveCalc * @sa CL_ConditionalMoveCalc */ void Grid_CalcPathing (const Routing &routing, const actorSizeEnum_t actorSize, pathing_t *path, const pos3_t from, int maxTUs, byte ** fb_list, int fb_length) { priorityQueue_t pqueue; pos4_t epos; /**< Extended position; includes crouching state */ pos3_t pos; int amst; /* acronym for actor movement state */ /* this is the position of the current actor- so the actor can stand in the cell it is in when pathfinding */ pos3_t excludeFromForbiddenList; /* Confirm bounds */ assert((from[2]) < PATHFINDING_HEIGHT); /* reset move data */ OBJSET(path->area, ROUTING_NOT_REACHABLE); OBJSET(path->areaFrom, ROUTING_NOT_REACHABLE); path->fblist = fb_list; path->fblength = fb_length; maxTUs = std::min(maxTUs, MAX_ROUTE_TUS); /* Prepare exclusion of starting-location (i.e. this should be ent-pos or le-pos) in Grid_CheckForbidden */ VectorCopy(from, excludeFromForbiddenList); for (amst = 0; amst < ACTOR_MAX_STATES; amst++) { /* set starting position to 0 TUs.*/ RT_AREA_POS(path, from, amst) = 0; PQueueInitialise(&pqueue, 1024); Vector4Set(epos, from[0], from[1], from[2], amst); PQueuePush(&pqueue, epos, 0); int count = 0; while (!PQueueIsEmpty(&pqueue)) { int dir; PQueuePop(&pqueue, epos); VectorCopy(epos, pos); count++; /* if reaching that square already took too many TUs, * don't bother to reach new squares *from* there. */ const byte usedTUs = RT_AREA_POS(path, pos, amst); if (usedTUs >= maxTUs) continue; for (dir = 0; dir < PATHFINDING_DIRECTIONS; ++dir) { Step step(routing, pos, actorSize, amst, dir); /* Directions 12, 14, and 15 are currently undefined. */ if (dir == 12 || dir == 14 || dir == 15) continue; /* If this is a crouching or crouching move, forget it. */ if (dir == DIRECTION_STAND_UP || dir == DIRECTION_CROUCH) continue; if (!step.init()) continue; /* either dir is irrelevant or something worse happened */ if (step.isPossible(path)) { /* Is this a better move into this cell? */ RT_AREA_TEST_POS(path, step.toPos, step.crouchingState); if (RT_AREA_POS(path, step.toPos, step.crouchingState) <= step.TUsAfter) { continue; /* This move is not optimum. */ } /* Test for forbidden (by other entities) areas. */ if (Grid_CheckForbidden(excludeFromForbiddenList, step.actorSize, path, step.toPos[0], step.toPos[1], step.toPos[2])) { continue; /* That spot is occupied. */ } /* Store move in pathing table. */ Grid_SetMoveData(path, step.toPos, step.crouchingState, step.TUsAfter, step.dir, step.fromPos[2]); pos4_t dummy; Vector4Set(dummy, step.toPos[0], step.toPos[1], step.toPos[2], step.crouchingState); PQueuePush(&pqueue, dummy, step.TUsAfter); } } } /* Com_Printf("Loop: %i", count); */ PQueueFree(&pqueue); } }
/** * @brief Checks if we can walk in the given direction * First test for opening height availability. Then test for stepup compatibility. Last test for fall. * @note Fliers use this code only when they are walking. * @param[in] path Pointer to client or server side pathing table (clPathMap, svPathMap) * @return false if we can't fly there */ bool Step::checkWalkingDirections (const pathing_t *path) { int nx, ny, nz; int passageHeight; /** @todo falling_height should be replaced with an arbitrary max falling height based on the actor. */ const int fallingHeight = PATHFINDING_MAX_FALL;/**<This is the maximum height that an actor can fall. */ const int stepupHeight = routing.getStepupHeight(actorSize, fromPos[0], fromPos[1], fromPos[2], dir); /**< The actual stepup height without the level flags */ int heightChange; /** @todo actor_stepup_height should be replaced with an arbitrary max stepup height based on the actor. */ int actorStepupHeight = PATHFINDING_MAX_STEPUP; /* This is the standard passage height for all units trying to move horizontally. */ passageHeight = routing.getConn(actorSize, fromPos, dir); if (passageHeight < actorHeight) { #if 0 /** I know this code could be streamlined, but until I understand it myself, plz leave it like it is !*/ int dvFlagsNew = 0; if (!crouchingState /* not in std crouch mode */ && passageHeight >= actorCrouchedHeight) { /* and passage is tall enough for crouching ? */ /* we should try autocrouching */ int dvFlagsOld = getDVflags(RT_AREA_POS(path, fromPos, crouchingState)); int toHeight = routing.getCeiling(actorSize, toPos) - routing.getFloor(actorSize, toPos); int tuCr = Grid_GetTUsForDirection(dir, 1); /* 1 means crouched */ int newTUs = 0; if (toHeight >= actorHeight) { /* can we stand in the new cell ? */ if ((dvFlagsOld & DV_FLAG_AUTOCROUCH) /* already in auto-crouch mode ? */ || (dvFlagsOld & DV_FLAG_AUTOCROUCHED)) { dvFlagsNew |= DV_FLAG_AUTOCROUCHED; /* keep that ! */ newTUs = tuCr + TU_CROUCH; /* TUs for crouching plus getting up */ } else { dvFlagsNew |= DV_FLAG_AUTODIVE; newTUs = tuCr + 2 * TU_CROUCH; /* TUs for crouching plus getting down and up */ } } else { /* we can't stand there */ if (dvFlagsOld & DV_FLAG_AUTOCROUCHED) { dvFlagsNew |= DV_FLAG_AUTOCROUCHED; /* keep that ! */ newTUs = tuCr; /* TUs just for crouching */ } else { dvFlagsNew |= DV_FLAG_AUTOCROUCH; /* get down ! */ newTUs = tuCr + TU_CROUCH; /* TUs for crouching plus getting down */ } } } else #endif return false; /* Passage is not tall enough. */ } /* If we are moving horizontally, use the stepup requirement of the floors. * The new z coordinate may need to be adjusted from stepup. * Also, actor_stepup_height must be at least the cell's positive stepup value to move that direction. */ /* If the actor cannot reach stepup, then we can't go this way. */ if (actorStepupHeight < stepupHeight) { return false; /* Actor cannot stepup high enough. */ } nx = toPos[0]; ny = toPos[1]; nz = toPos[2]; if (routing.isStepUpLevel(actorSize, fromPos, dir) && toPos[2] < PATHFINDING_HEIGHT - 1) { toPos[2]++; /** * @note If you need to know about how pathfinding works, you need to understand the * following brief. It may cause nausea, but is an important concept. * * @brief OK, now some crazy tests: * Because of the grid based nature of this game, each cell can have at most only ONE * floor that can be stood upon. If an actor can walk down a slope that is in the * same level, and actor should be able to walk on (and not fall into) the slope that * decends a game level. BUT it is possible for an actor to be able to crawl under a * floor that can be stood on, with this opening being in the same cell as the floor. * SO to prevent any conflicts, we will move down a floor under the following conditions: * - The STEPDOWN flag is set * - The floor in the immediately adjacent cell is lower than the current floor, but not * more than CELL_HEIGHT units (in QUANT units) below the current floor. * - The actor's stepup value is at least the inverse stepup value. This is the stepup * FROM the cell we are moving towards back into the cell we are starting in. This * ensures that the actor can actually WALK BACK. * If the actor does not have a high enough stepup but meets all the other requirements to * descend the level, the actor will move into a fall state, provided that there is no * floor in the adjacent cell. * * This will prevent actors from walking under a floor in the same cell in order to fall * to the floor beneath. They will need to be able to step down into the cell or will * not be able to use the opening. */ } else if (routing.isStepDownLevel(actorSize, fromPos, dir) && toPos[2] > 0 && actorStepupHeight >= routing.getStepupHeight(actorSize, nx, ny, nz - 1, dir ^ 1)) { toPos[2]--; /* Stepping down into lower cell. */ } heightChange = routing.getFloor(actorSize, toPos) - routing.getFloor(actorSize, fromPos) + (toPos[2] - fromPos[2]) * CELL_HEIGHT; /* If the actor tries to fall more than falling_height, then prohibit the move. */ if (heightChange < -fallingHeight && !hasLadderSupport) { return false; /* Too far a drop without a ladder. */ } /* If we are walking normally, we can fall if we move into a cell that does not * have its STEPDOWN flag set and has a negative floor: * Set heightChange to 0. * The actor enters the cell. * The actor will be forced to fall (dir 13) from the destination cell to the cell below. */ if (routing.getFloor(actorSize, toPos) < 0) { /* We cannot fall if STEPDOWN is defined. */ if (routing.isStepDownLevel(actorSize, fromPos, dir)) { return false; /* There is stepdown from here. */ } heightChange = 0; toPos[2]--; } return true; }
static void Grid_SetMoveData (pathing_t *path, const pos3_t toPos, const int crouch, const byte length, const int dir, const int oldZ) { RT_AREA_TEST_POS(path, toPos, crouch); RT_AREA_POS(path, toPos, crouch) = length; /**< Store TUs for this square. */ RT_AREA_FROM_POS(path, toPos, crouch) = makeDV(dir, oldZ); /**< Store origination information for this square. */ }
/** * @brief Recalculate the pathing table for the given actor(-position) * @param[in] map Pointer to client or server side routing table (clMap, svMap) * @param[in] actorSize The size of thing to calc the move for (e.g. size=2 means 2x2). * The plan is to have the 'origin' in 2x2 units in the bottom-left (towards the lower coordinates) corner of the 2x2 square. * @param[in,out] path Pointer to client or server side pathing table (clMap, svMap) * @param[in] from The position to start the calculation from. * @param[in] distance The maximum TUs away from 'from' to calculate move-information for * @param[in] crouchingState Whether the actor is currently crouching, 1 is yes, 0 is no. * @param[in] fb_list Forbidden list (entities are standing at those points) * @param[in] fb_length Length of forbidden list * @sa Grid_MoveMark * @sa G_MoveCalc * @sa CL_ConditionalMoveCalc */ void Grid_MoveCalc (const routing_t *map, const actorSizeEnum_t actorSize, pathing_t *path, const pos3_t from, byte crouchingState, int distance, byte ** fb_list, int fb_length) { int dir; int count; priorityQueue_t pqueue; pos4_t epos; /**< Extended position; includes crouching state */ pos3_t pos; /* this is the position of the current actor- so the actor can stand in the cell it is in when pathfinding */ pos3_t excludeFromForbiddenList; /* reset move data */ OBJSET(path->area, ROUTING_NOT_REACHABLE); OBJSET(path->areaFrom, ROUTING_NOT_REACHABLE); path->fblist = fb_list; path->fblength = fb_length; if (distance > MAX_ROUTE + 3) /* +3 is added to calc at least one square (diagonal) more */ distance = MAX_ROUTE + 3; /* and later show one step beyond the walkable path in red */ /* Prepare exclusion of starting-location (i.e. this should be ent-pos or le-pos) in Grid_CheckForbidden */ VectorCopy(from, excludeFromForbiddenList); PQueueInitialise(&pqueue, 1024); Vector4Set(epos, from[0], from[1], from[2], crouchingState); PQueuePush(&pqueue, epos, 0); /* Confirm bounds */ assert((from[2]) < PATHFINDING_HEIGHT); assert(crouchingState == 0 || crouchingState == 1); /* s.a. ACTOR_MAX_STATES */ /* set starting position to 0 TUs.*/ RT_AREA_POS(path, from, crouchingState) = 0; Com_DPrintf(DEBUG_PATHING, "Grid_MoveCalc: Start at (%i %i %i) c:%i\n", from[0], from[1], from[2], crouchingState); count = 0; while (!PQueueIsEmpty(&pqueue)) { byte TUsSoFar; PQueuePop(&pqueue, epos); VectorCopy(epos, pos); count++; /**< if reaching that square already took too many TUs, * don't bother to reach new squares *from* there. */ TUsSoFar = RT_AREA_POS(path, pos, crouchingState); if (TUsSoFar >= distance || TUsSoFar >= MAX_MOVELENGTH) continue; for (dir = 0; dir < PATHFINDING_DIRECTIONS; dir++) { /* Directions 12, 14, and 15 are currently undefined. */ if (dir == 12 || dir == 14 || dir == 15) continue; /* If this is a crouching or crouching move, forget it. */ if (dir == DIRECTION_STAND_UP || dir == DIRECTION_CROUCH) continue; Grid_MoveMark(map, excludeFromForbiddenList, actorSize, path, pos, epos[3], dir, &pqueue); } } /* Com_Printf("Loop: %i", count); */ PQueueFree(&pqueue); Com_DPrintf(DEBUG_PATHING, "Grid_MoveCalc: Done\n\n"); }
/** * @param[in] map Pointer to client or server side routing table (clMap, svMap) * @param[in] exclude Exclude this position from the forbidden list check * @param[in] actorSize Give the field size of the actor (e.g. for 2x2 units) to check linked fields as well. * @param[in,out] path Pointer to client or server side pathing table (clMap, svMap) * @param[in] pos Current location in the map. * @param[in] crouchingState Whether the actor is currently crouching, 1 is yes, 0 is no. * @param[in] dir Direction vector index (see DIRECTIONS and dvecs) * @param[in,out] pqueue Priority queue (heap) to insert the now reached tiles for reconsidering * @sa Grid_CheckForbidden */ static void Grid_MoveMark (const routing_t *map, const pos3_t exclude, const actorSizeEnum_t actorSize, pathing_t *path, const pos3_t pos, byte crouchingState, const int dir, priorityQueue_t *pqueue) { step_t step_; step_t *step = &step_; /* temporary solution */ pos3_t toPos; byte TUsSoFar, TUsForMove, TUsAfter; if (!Grid_StepInit(step, map, actorSize, crouchingState, dir)) return; /* either dir is irrelevant or something worse happened */ TUsSoFar = RT_AREA_POS(path, pos, crouchingState); /* Find the number of TUs used (normally) to move in this direction. */ TUsForMove = Grid_GetTUsForDirection(dir, crouchingState); /* calculate the position we would normally end up if moving in the given dir. */ if (!Grid_StepCalcNewPos(step, pos, toPos, dir)) { return; } /* If there is no passageway (or rather lack of a wall) to the desired cell, then return. */ /* If the flier is moving up or down diagonally, then passage height will also adjust */ if (dir >= FLYING_DIRECTIONS) { if (!Grid_StepCheckFlyingDirections(step, pos, toPos, dir)) { return; } } else if (dir < CORE_DIRECTIONS) { /** note that this function may modify toPos ! */ if (!Grid_StepCheckWalkingDirections(step, path, pos, toPos, dir, crouchingState)) { return; } } else { /* else there is no movement that uses passages. */ /* If we are falling, the height difference is the floor value. */ if (!Grid_StepCheckVerticalDirections(step, pos, dir)) { return; } } /* OK, at this point we are certain of a few things: * There is not a wall obstructing access to the destination cell. * If the actor is not a flier, the actor will not rise more than actor_stepup_height or fall more than * falling_height, unless climbing. * * If the actor is a flier, as long as there is a passage, it can be moved through. * There are no floor difference restrictions for fliers, only obstructions. */ /* nz can't move out of bounds */ if (toPos[2] >= PATHFINDING_HEIGHT) toPos[2] = PATHFINDING_HEIGHT - 1; /* Now add the TUs needed to get to the originating cell. */ TUsAfter = TUsSoFar + TUsForMove; /* Is this a better move into this cell? */ RT_AREA_TEST_POS(path, toPos, crouchingState); if (RT_AREA_POS(path, toPos, crouchingState) <= TUsAfter) { return; /* This move is not optimum. */ } /* Test for forbidden (by other entities) areas. */ if (Grid_CheckForbidden(exclude, actorSize, path, toPos[0], toPos[1], toPos[2])) { return; /* That spot is occupied. */ } /* Store move. */ if (pqueue) { Grid_SetMoveData(path, toPos, crouchingState, TUsAfter, dir, pos[2], crouchingState, pqueue); } }