// Complexity: O(nr lg(nr) + nr^2), where n = locs.size(), r = radius. // The nr^2 term is bounded by the size of the board. void get_tiles_radius(const gamemap& map, const std::vector<map_location>& locs, size_t radius, std::set<map_location>& result, bool with_border) { // Make sure the provided locations are included. // This would be needed in case some of the provided locations are off-map. // It also allows simpler processing if the radius is zero. // For efficiency, do this first since locs is potentially unsorted. result.insert(locs.begin(), locs.end()); if ( radius != 0 && !locs.empty() ) { const int border = with_border ? map.border_size() : 0; column_ranges collected_tiles; // Collect the hexes within the desired disks into collected_tiles. // This maps each x-value to a set of ranges of y-values that // are covered by the disks around each element of locs. // (So the data size at this point is proportional to the number // of x-values involved, which is O(nr). The lg(nr) factor comes // from the data being sorted.) get_column_ranges(collected_tiles, locs, radius, -border, map.w() + border); // Now that all the tiles have been collected, add them to result. // (There are O(nr^2) hexes to add.) By collecting before adding, each // hex will be processed only once, even when disks overlap. This is // how we can get good performance if there is significant overlap, and // how the work required can be bound by the size of the board. ranges_to_tiles(result, collected_tiles, -border, map.h() + border); } }
void gamemap::overlay(const gamemap& m, const config& rules_cfg, int xpos, int ypos, bool border) { const config::const_child_itors &rules = rules_cfg.child_range("rule"); int actual_border = (m.border_size() == border_size()) && border ? border_size() : 0; const int xstart = std::max<int>(-actual_border, -xpos - actual_border); const int ystart = std::max<int>(-actual_border, -ypos - actual_border - ((xpos & 1) ? 1 : 0)); const int xend = std::min<int>(m.w() + actual_border, w() + actual_border - xpos); const int yend = std::min<int>(m.h() + actual_border, h() + actual_border - ypos); for(int x1 = xstart; x1 < xend; ++x1) { for(int y1 = ystart; y1 < yend; ++y1) { const int x2 = x1 + xpos; const int y2 = y1 + ypos + ((xpos & 1) && (x1 & 1) ? 1 : 0); const t_translation::t_terrain t = m[x1][y1 + m.border_size_]; const t_translation::t_terrain current = (*this)[x2][y2 + border_size_]; if(t == t_translation::FOGGED || t == t_translation::VOID_TERRAIN) { continue; } // See if there is a matching rule config::const_child_iterator rule = rules.first; for( ; rule != rules.second; ++rule) { static const std::string src_key = "old", dst_key = "new"; const config &cfg = *rule; const t_translation::t_list& src = t_translation::read_list(cfg[src_key]); if(!src.empty() && t_translation::terrain_matches(current, src) == false) { continue; } const t_translation::t_list& dst = t_translation::read_list(cfg[dst_key]); if(!dst.empty() && t_translation::terrain_matches(t, dst) == false) { continue; } break; } if (rule != rules.second) { const config &cfg = *rule; const t_translation::t_list& terrain = t_translation::read_list(cfg["terrain"]); terrain_type_data::tmerge_mode mode = terrain_type_data::BOTH; if (cfg["layer"] == "base") { mode = terrain_type_data::BASE; } else if (cfg["layer"] == "overlay") { mode = terrain_type_data::OVERLAY; } t_translation::t_terrain new_terrain = t; if(!terrain.empty()) { new_terrain = terrain[0]; } if (!cfg["use_old"].to_bool()) { set_terrain(map_location(x2, y2), new_terrain, mode, cfg["replace_if_failed"].to_bool()); } } else { set_terrain(map_location(x2,y2),t); } } } for(const map_location* pos = m.startingPositions_; pos != m.startingPositions_ + sizeof(m.startingPositions_)/sizeof(*m.startingPositions_); ++pos) { if(pos->valid()) { startingPositions_[pos - m.startingPositions_] = *pos; } } }