/** * Is this a valid tile for an entity with this movement type? */ bool MapCollision::is_valid_tile(const int& tile_x, const int& tile_y, MOVEMENTTYPE movement_type, bool is_hero) const { // outside the map isn't valid if (is_outside_map(tile_x,tile_y)) return false; if(is_hero) { if(colmap[tile_x][tile_y] == BLOCKS_ENEMIES && !ENABLE_ALLY_COLLISION) return true; } else if(colmap[tile_x][tile_y] == BLOCKS_ENEMIES) return false; // occupied by an entity isn't valid if (colmap[tile_x][tile_y] == BLOCKS_ENTITIES) return false; // intangible creatures can be everywhere if (movement_type == MOVEMENT_INTANGIBLE) return true; // flying creatures can't be in walls if (movement_type == MOVEMENT_FLYING) { return (!(colmap[tile_x][tile_y] == BLOCKS_ALL || colmap[tile_x][tile_y] == BLOCKS_ALL_HIDDEN)); } if (colmap[tile_x][tile_y] == MAP_ONLY || colmap[tile_x][tile_y] == MAP_ONLY_ALT) return true; // normal creatures can only be in empty spaces return (colmap[tile_x][tile_y] == BLOCKS_NONE); }
/** * A map space is a wall if it contains a wall blocking type (normal or hidden) * A position outside the map boundary is a wall */ bool MapCollision::is_wall(const float& x, const float& y) const { // bounds check const int tile_x = int(x); const int tile_y = int(y); if (is_outside_map(tile_x, tile_y)) return true; // collision type check return (colmap[tile_x][tile_y] == BLOCKS_ALL || colmap[tile_x][tile_y] == BLOCKS_ALL_HIDDEN); }
/** * A map space is empty if it contains no blocking type * A position outside the map boundary is not empty */ bool MapCollision::is_empty(const float& x, const float& y) const { // map bounds check const int tile_x = int(x); const int tile_y = int(y); if (is_outside_map(tile_x, tile_y)) return false; // collision type check return (colmap[tile_x][tile_y] == BLOCKS_NONE || colmap[tile_x][tile_y] == MAP_ONLY || colmap[tile_x][tile_y] == MAP_ONLY_ALT); }
bool MapCollision::line_of_movement(const float& x1, const float& y1, const float& x2, const float& y2, MOVEMENTTYPE movement_type) { if (is_outside_map(x2, y2)) return false; // intangible entities can always move if (movement_type == MOVEMENT_INTANGIBLE) return true; // if the target is blocking, clear it temporarily int tile_x = int(x2); int tile_y = int(y2); bool target_blocks = false; int target_blocks_type = colmap[tile_x][tile_y]; if (colmap[tile_x][tile_y] == BLOCKS_ENTITIES || colmap[tile_x][tile_y] == BLOCKS_ENEMIES) { target_blocks = true; unblock(x2,y2); } bool has_movement = line_check(x1, y1, x2, y2, CHECK_MOVEMENT, movement_type); if (target_blocks) block(x2,y2, target_blocks_type == BLOCKS_ENEMIES); return has_movement; }
/** * Compute a path from (x1,y1) to (x2,y2) * Store waypoint inside path * limit is the maximum number of explored node * @return true if a path is found */ bool MapCollision::compute_path(FPoint start_pos, FPoint end_pos, std::vector<FPoint> &path, MOVEMENTTYPE movement_type, unsigned int limit) { if (is_outside_map(end_pos.x, end_pos.y)) return false; if (limit == 0) limit = std::max(map_size.x, map_size.y); // path must be empty if (!path.empty()) path.clear(); // convert start & end to MapCollision precision Point start = map_to_collision(start_pos); Point end = map_to_collision(end_pos); // if the target square has an entity, temporarily clear it to compute the path bool target_blocks = false; int target_blocks_type = colmap[end.x][end.y]; if (colmap[end.x][end.y] == BLOCKS_ENTITIES || colmap[end.x][end.y] == BLOCKS_ENEMIES) { target_blocks = true; unblock(end_pos.x, end_pos.y); } Point current = start; AStarNode* node = new AStarNode(start); node->setActualCost(0); node->setEstimatedCost((float)calcDist(start,end)); node->setParent(current); AStarContainer open(map_size.x, map_size.y, limit); AStarCloseContainer close(map_size.x, map_size.y, limit); open.add(node); while (!open.isEmpty() && (unsigned)close.getSize() < limit) { node = open.get_shortest_f(); current.x = node->getX(); current.y = node->getY(); close.add(node); open.remove(node); if ( current.x == end.x && current.y == end.y) break; //path found ! //limit evaluated nodes to the size of the map std::list<Point> neighbours = node->getNeighbours(map_size.x, map_size.y); // for every neighbour of current node for (std::list<Point>::iterator it=neighbours.begin(); it != neighbours.end(); ++it) { Point neighbour = *it; // do not exceed the node limit when adding nodes if ((unsigned)open.getSize() >= limit) { break; } // if neighbour is not free of any collision, skip it if (!is_valid_tile(neighbour.x,neighbour.y,movement_type, false)) continue; // if nabour is already in close, skip it if(close.exists(neighbour)) continue; // if neighbour isn't inside open, add it as a new Node if(!open.exists(neighbour)) { AStarNode* newNode = new AStarNode(neighbour.x,neighbour.y); newNode->setActualCost(node->getActualCost()+(float)calcDist(current,neighbour)); newNode->setParent(current); newNode->setEstimatedCost((float)calcDist(neighbour,end)); open.add(newNode); } // else, update it's cost if better else { AStarNode* i = open.get(neighbour.x, neighbour.y); if (node->getActualCost()+(float)calcDist(current,neighbour) < i->getActualCost()) { Point pos(i->getX(), i->getY()); Point parent_pos(node->getX(), node->getY()); open.updateParent(pos, parent_pos, node->getActualCost()+(float)calcDist(current,neighbour)); } } } } if (current.x != end.x || current.y != end.y) { //couldnt find the target so map a path to the closest node found node = close.get_shortest_h(); current.x = node->getX(); current.y = node->getY(); while (current.x != start.x || current.y != start.y) { path.push_back(collision_to_map(current)); current = close.get(current.x, current.y)->getParent(); } } else { // store path from end to start path.push_back(collision_to_map(end)); while (current.x != start.x || current.y != start.y) { path.push_back(collision_to_map(current)); current = close.get(current.x, current.y)->getParent(); } } // reblock target if needed if (target_blocks) block(end_pos.x, end_pos.y, target_blocks_type == BLOCKS_ENEMIES); return !path.empty(); }
bool MapCollision::is_outside_map(const float& tile_x, const float& tile_y) const { return is_outside_map((int)(tile_x), (int)(tile_y)); }
bool MapCollision::is_outside_map(const float& tile_x, const float& tile_y) const { return is_outside_map(static_cast<int>(tile_x), static_cast<int>(tile_y)); }