Tripoint Path::step(int n) { if (n < 0 || n >= path.size()) { return Tripoint(-1, -1, -1); } return path[n]; }
Path Pathfinder::path_a_star(Tripoint start, Tripoint end) { int x_size = map.get_size_x(); int y_size = map.get_size_y(); int z_size = map.get_size_z(); start.x -= map.x_offset; start.y -= map.y_offset; start.z -= map.z_offset; end.x -= map.x_offset; end.y -= map.y_offset; end.z -= map.z_offset; if (x_size == 0 || y_size == 0 || z_size == 0) { debugmsg("A* generated; %s => %s (size %d, %d, %d)", start.str().c_str(), end.str().c_str(), x_size, y_size, z_size); return Path(); } std::vector<Tripoint> open_points; A_star_status status[x_size][y_size][z_size]; int gscore[x_size][y_size][z_size]; int hscore[x_size][y_size][z_size]; Tripoint parent[x_size][y_size][z_size]; if (border > 0) { int x0 = (start.x < end.x ? start.x : end.x); int y0 = (start.y < end.y ? start.y : end.y); int z0 = (start.z < end.z ? start.z : end.z); int x1 = (start.x > end.x ? start.x : end.x); int y1 = (start.y > end.y ? start.y : end.y); int z1 = (start.z > end.z ? start.z : end.z); set_bounds(x0 - border, y0 - border, z0 - border, x1 + border, y1 + border, z1 + border); } // Init everything to 0 for (int x = 0; x < x_size; x++) { for (int y = 0; y < y_size; y++) { for (int z = 0; z < z_size; z++) { status[x][y][z] = ASTAR_NONE; gscore[x][y][z] = 0; hscore[x][y][z] = 0; parent[x][y][z] = Tripoint(-1, -1, -1); } } } status[start.x][start.y][start.z] = ASTAR_OPEN; open_points.push_back(start); bool done = false; while (!done && !open_points.empty()) { // 1) Find the lowest cost in open_points, and set (current) to that point // (if multiple points are tied, randomly select one) int lowest_cost = -1, point_index = -1; Tripoint current; int current_g = 0; std::vector<int> lowest_indices; for (int i = 0; i < open_points.size(); i++) { Tripoint p = open_points[i]; int score = gscore[p.x][p.y][p.z] + hscore[p.x][p.y][p.z]; if (i == 0 || score < lowest_cost) { lowest_cost = score; lowest_indices.clear(); lowest_indices.push_back(i); } else if (score == lowest_cost) { lowest_indices.push_back(i); } } if (lowest_indices.empty()) { // Should never happen point_index = 0; } else { point_index = lowest_indices[ rng(0, lowest_indices.size() - 1) ]; } current = open_points[point_index]; current_g = gscore[current.x][current.y][current.z]; // 2) Check if (current) is the endpoint if (current == end) { done = true; } else { // 3) Set (current) to be closed open_points.erase(open_points.begin() + point_index); status[current.x][current.y][current.z] = ASTAR_CLOSED; // 4) Examine all adjacent points on the same z-level for (int x = current.x - 1; x <= current.x + 1; x++) { for (int y = current.y - 1; y <= current.y + 1; y++) { if (x == current.x && y == current.y) { y++; // Skip the current tile } int z = current.z; // If it's not diagonal, or diagonals are allowed... // ...and if it's in-bounds and not blocked... if ((allow_diag || x == current.x || y == current.y) && (in_bounds(x, y, z) && !map.blocked(x, y, z))) { int g = current_g + map.get_cost(x, y, z); // If it's unexamined, make it open and set its values if (status[x][y][z] == ASTAR_NONE) { status[x][y][z] = ASTAR_OPEN; gscore[x][y][z] = g; if (allow_diag) { hscore[x][y][z] = map.get_cost(x, y, z) * rl_dist(x, y, z, end.x, end.y, end.z); } else { hscore[x][y][z] = map.get_cost(x, y, z) * manhattan_dist(x, y, z, end.x, end.y, end.z); } parent[x][y][z] = current; open_points.push_back( Tripoint(x, y, z) ); // Otherwise, if it's open and we're a better parent, make us the parent } else if (status[x][y][z] == ASTAR_OPEN && g < gscore[x][y][z]) { gscore[x][y][z] = g; parent[x][y][z] = current; } } } } // 5. Examine adjacent points on adjacent Z-levels // TODO: Allow diagonal movement across Z-levels? For flying monsters? // Examine above if (map.allow_z_up(current)) { int z = current.z + 1; int x = current.x, y = current.y; if ((in_bounds(x, y, z) && !map.blocked(x, y, z))) { int g = current_g + map.get_cost(x, y, z); // If it's unexamined, make it open and set its values if (status[x][y][z] == ASTAR_NONE) { status[x][y][z] = ASTAR_OPEN; gscore[x][y][z] = g; if (allow_diag) { hscore[x][y][z] = map.get_cost(x, y, z) * rl_dist(x, y, z, end.x, end.y, end.z); } else { hscore[x][y][z] = map.get_cost(x, y, z) * manhattan_dist(x, y, z, end.x, end.y, end.z); } parent[x][y][z] = current; open_points.push_back( Tripoint(x, y, z) ); // If it's open and we're a better parent, make us the parent } else if (status[x][y][z] == ASTAR_OPEN && g < gscore[x][y][z]) { gscore[x][y][z] = g; parent[x][y][z] = current; } } } // Examine below (code duplication, sorry mom) if (map.allow_z_down(current)) { int z = current.z - 1; int x = current.x, y = current.y; if ((in_bounds(x, y, z) && !map.blocked(x, y, z))) { int g = current_g + map.get_cost(x, y, z); // If it's unexamined, make it open and set its values if (status[x][y][z] == ASTAR_NONE) { debugmsg("A*'d over Z-level"); status[x][y][z] = ASTAR_OPEN; gscore[x][y][z] = g; if (allow_diag) { hscore[x][y][z] = map.get_cost(x, y, z) * rl_dist(x, y, z, end.x, end.y, end.z); } else { hscore[x][y][z] = map.get_cost(x, y, z) * manhattan_dist(x, y, z, end.x, end.y, end.z); } parent[x][y][z] = current; open_points.push_back( Tripoint(x, y, z) ); // If it's open and we're a better parent, make us the parent } else if (status[x][y][z] == ASTAR_OPEN && g < gscore[x][y][z]) { gscore[x][y][z] = g; parent[x][y][z] = current; } } } } } Path ret; if (open_points.empty()) { return ret; } Tripoint cur = end; ret.add_step(cur, map.get_cost(cur)); while (parent[cur.x][cur.y][cur.z] != start) { cur = parent[cur.x][cur.y][cur.z]; ret.add_step(cur, map.get_cost(cur)); } ret.reverse(); // Add the offsets back in. ret.offset(map.x_offset, map.y_offset, map.z_offset); return ret; }
Path Pathfinder::path_line(Tripoint start, Tripoint end) { Path ret; Tripoint cur = start; bool done = false; while (!done) { bool picked_next = false; if (cur == end) { done = true; // Prioritize vertical movement over lateral } else if (end.z < start.z && !map.blocked(cur.x, cur.y, cur.z - 1)) { picked_next = true; cur.z--; } else if (end.z > start.z && !map.blocked(cur.x, cur.y, cur.z + 1)) { picked_next = true; cur.z++; } else { Tripoint options[5]; for (int i = 0; i < 5; i++) { options[i] = cur; } bool x_diff_bigger = ( abs(end.x - cur.x) > abs(end.y - cur.y) ); int best_x_move = cur.x, alt_x_move = cur.x, worst_x_move = cur.x;; if (end.x > cur.x) { best_x_move++; worst_x_move--; } else if (end.x < cur.x) { best_x_move--; worst_x_move++; } else { int alt = 2 * rng(0, 1) - 1; // -1 or 1 alt_x_move += alt; worst_x_move += -1 * alt; } int best_y_move = cur.y, alt_y_move = cur.y, worst_y_move = cur.y;; if (end.y > cur.y) { best_y_move++; worst_y_move--; } else if (end.y < cur.y) { best_y_move--; worst_y_move++; } else { int alt = 2 * rng(0, 1) - 1; // -1 or 1 alt_y_move += alt; worst_y_move += -1 * alt; } options[0] = Tripoint(best_x_move, best_y_move, cur.z); if (x_diff_bigger) { options[1] = Tripoint(best_x_move, alt_y_move, cur.z); options[2] = Tripoint(alt_x_move, best_y_move, cur.z); options[3] = Tripoint(best_x_move, worst_y_move, cur.z); options[4] = Tripoint(worst_x_move, best_y_move, cur.z); } else { options[1] = Tripoint(alt_x_move, best_y_move, cur.z); options[2] = Tripoint(best_x_move, alt_y_move, cur.z); options[3] = Tripoint(worst_x_move, best_y_move, cur.z); options[4] = Tripoint(best_x_move, worst_y_move, cur.z); } for (int i = 0; i < 5 && !picked_next; i++) { if (!map.blocked( options[i] ) && in_bounds( options[i] )) { picked_next = true; cur = options[i]; } } } // Lateral movement if (!picked_next) { // Couldn't reach our target using this stupid algo! done = true; } else { ret.add_step(cur, map.get_cost(cur)); if (cur.x == end.x && cur.y == end.y) { cur = end; ret.add_step(cur, map.get_cost(cur)); } } } // while (!done) if (cur != end) { // We didn't make it :( return Path(); } return ret; }
Tripoint Pathfinder::get_step(Path_type type, int x0, int y0, int z0, int x1, int y1, int z1) { return get_step(type, Tripoint(x0, y0, z0), Tripoint(x1, y1, z1)); }
Tripoint Pathfinder::get_step(Path_type type, Point start, Point end) { return get_step(type, Tripoint(start.x, start.y, 0), Tripoint(end.x, end.y, 0)); }
Path Pathfinder::get_path(Path_type type, int x0, int y0, int x1, int y1) { return get_path(type, Tripoint(x0, y0, 0), Tripoint(x1, y1, 0)); }
Tripoint Entity::get_position() { return Tripoint(posx, posy, posz); }