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 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); } }
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. */ }
/** * @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); } }