// Find a route for an DROID to a location in world coordinates FPATH_RETVAL fpathDroidRoute(DROID *psDroid, SDWORD tX, SDWORD tY, FPATH_MOVETYPE moveType) { bool acceptNearest; PROPULSION_STATS *psPropStats = getPropulsionStats(psDroid); // override for AI to blast our way through stuff if (!isHumanPlayer(psDroid->player) && moveType == FMT_MOVE) { moveType = (psDroid->asWeaps[0].nStat == 0) ? FMT_MOVE : FMT_ATTACK; } ASSERT_OR_RETURN(FPR_FAILED, psPropStats != NULL, "invalid propulsion stats pointer"); ASSERT_OR_RETURN(FPR_FAILED, psDroid->type == OBJ_DROID, "We got passed an object that isn't a DROID!"); // Check whether the start and end points of the route are blocking tiles and find an alternative if they are. Position startPos = psDroid->pos; Position endPos = Position(tX, tY, 0); StructureBounds dstStructure = getStructureBounds(worldTile(endPos.xy)->psObject); startPos = findNonblockingPosition(startPos, getPropulsionStats(psDroid)->propulsionType, psDroid->player, moveType); if (!dstStructure.valid()) // If there's a structure over the destination, ignore it, otherwise pathfind from somewhere around the obstruction. { endPos = findNonblockingPosition(endPos, getPropulsionStats(psDroid)->propulsionType, psDroid->player, moveType); } objTrace(psDroid->id, "Want to go to (%d, %d) -> (%d, %d), going (%d, %d) -> (%d, %d)", map_coord(psDroid->pos.x), map_coord(psDroid->pos.y), map_coord(tX), map_coord(tY), map_coord(startPos.x), map_coord(startPos.y), map_coord(endPos.x), map_coord(endPos.y)); switch (psDroid->order.type) { case DORDER_BUILD: case DORDER_LINEBUILD: // build a number of structures in a row (walls + bridges) dstStructure = getStructureBounds(psDroid->order.psStats, psDroid->order.pos, psDroid->order.direction); // Just need to get close enough to build (can be diagonally), do not need to reach the destination tile. // fallthrough case DORDER_HELPBUILD: // help to build a structure case DORDER_DEMOLISH: // demolish a structure case DORDER_REPAIR: acceptNearest = false; break; default: acceptNearest = true; break; } return fpathRoute(&psDroid->sMove, psDroid->id, startPos.x, startPos.y, endPos.x, endPos.y, psPropStats->propulsionType, psDroid->droidType, moveType, psDroid->player, acceptNearest, dstStructure); }
// Only used by fpathTest. static FPATH_RETVAL fpathSimpleRoute(MOVE_CONTROL *psMove, int id, int startX, int startY, int tX, int tY) { return fpathRoute(psMove, id, startX, startY, tX, tY, PROPULSION_TYPE_WHEELED, DROID_WEAPON, FMT_BLOCK, 0, true, getStructureBounds((BASE_OBJECT *)NULL)); }
static FPATH_RETVAL fpathSimpleRoute(MOVE_CONTROL *psMove, int id, int startX, int startY, int tX, int tY) { return fpathRoute(psMove, id, startX, startY, tX, tY, PROPULSION_TYPE_WHEELED, DROID_WEAPON, FMT_MOVE, 0, true); }
// Find a route for an DROID to a location in world coordinates FPATH_RETVAL fpathDroidRoute(DROID* psDroid, SDWORD tX, SDWORD tY, FPATH_MOVETYPE moveType) { bool acceptNearest; PROPULSION_STATS *psPropStats = getPropulsionStats(psDroid); // override for AI to blast our way through stuff if (!isHumanPlayer(psDroid->player) && moveType == FMT_MOVE) { moveType = (psDroid->asWeaps[0].nStat == 0) ? FMT_MOVE : FMT_ATTACK; } ASSERT_OR_RETURN(FPR_FAILED, psPropStats != NULL, "invalid propulsion stats pointer"); ASSERT_OR_RETURN(FPR_FAILED, psDroid->type == OBJ_DROID, "We got passed an object that isn't a DROID!"); // check whether the end point of the route // is a blocking tile and find an alternative if it is if (psDroid->sMove.Status != MOVEWAITROUTE && fpathDroidBlockingTile(psDroid, map_coord(tX), map_coord(tY), moveType)) { // find the nearest non blocking tile to the DROID int minDist = SDWORD_MAX; int nearestDir = NUM_DIR; int dir; objTrace(psDroid->id, "BLOCKED(%d,%d) - trying workaround", map_coord(tX), map_coord(tY)); for (dir = 0; dir < NUM_DIR; dir++) { int x = map_coord(tX) + aDirOffset[dir].x; int y = map_coord(tY) + aDirOffset[dir].y; if (tileOnMap(x, y) && !fpathDroidBlockingTile(psDroid, x, y, moveType)) { // pick the adjacent tile closest to our starting point int tileDist = fpathDistToTile(x, y, psDroid->pos.x, psDroid->pos.y); if (tileDist < minDist) { minDist = tileDist; nearestDir = dir; } } if (dir == NUM_BASIC - 1 && nearestDir != NUM_DIR) { break; // found a solution without checking at greater distance } } if (nearestDir == NUM_DIR) { // surrounded by blocking tiles, give up objTrace(psDroid->id, "route to (%d, %d) failed - target blocked", map_coord(tX), map_coord(tY)); return FPR_FAILED; } else { tX = world_coord(map_coord(tX) + aDirOffset[nearestDir].x) + TILE_UNITS / 2; tY = world_coord(map_coord(tY) + aDirOffset[nearestDir].y) + TILE_UNITS / 2; objTrace(psDroid->id, "Workaround found at (%d, %d)", map_coord(tX), map_coord(tY)); } } switch (psDroid->order) { case DORDER_BUILD: case DORDER_HELPBUILD: // help to build a structure case DORDER_LINEBUILD: // 6 - build a number of structures in a row (walls + bridges) case DORDER_DEMOLISH: // demolish a structure case DORDER_REPAIR: acceptNearest = false; break; default: acceptNearest = true; break; } return fpathRoute(&psDroid->sMove, psDroid->id, psDroid->pos.x, psDroid->pos.y, tX, tY, psPropStats->propulsionType, psDroid->droidType, moveType, psDroid->player, acceptNearest); }