bool can_generate(const gamemap& map, const std::vector<team>& teams, const unit_map& units, const unit& u, const map_location& loc) { if (!map.on_board(loc)) { return false; } if (u.movement_cost(map[loc]) == unit_movement_type::UNREACHABLE) { return false; } unit_map::const_iterator it = units.find(loc, false); if (it.valid() && !it->can_stand(u)) { return false; } map_location locs[6]; get_adjacent_tiles(loc, locs); for (int i = 0; i != 6; ++i) { if (!map.on_board(locs[i])) { continue; } if (u.movement_cost(map[locs[i]]) != unit_movement_type::UNREACHABLE) { return true; } } return false; }
map_location pathfind::find_vacant_tile(const gamemap& map, const unit_map& units, const map_location& loc, pathfind::VACANT_TILE_TYPE vacancy, const unit* pass_check) { if (!map.on_board(loc)) return map_location(); std::set<map_location> pending_tiles_to_check, tiles_checked; pending_tiles_to_check.insert(loc); // Iterate out 50 hexes from loc for (int distance = 0; distance < 50; ++distance) { if (pending_tiles_to_check.empty()) return map_location(); //Copy over the hexes to check and clear the old set std::set<map_location> tiles_checking; tiles_checking.swap(pending_tiles_to_check); //Iterate over all the hexes we need to check foreach (const map_location &loc, tiles_checking) { //If this area is not a castle but should, skip it. if (vacancy == pathfind::VACANT_CASTLE && !map.is_castle(loc)) continue; const bool pass_check_and_unreachable = pass_check && pass_check->movement_cost(map[loc]) == unit_movement_type::UNREACHABLE; //If the unit can't reach the tile and we have searched //an area of at least radius 10 (arbitrary), skip the tile. //Neccessary for cases such as an unreachable //starting hex surrounded by 6 other unreachable hexes, in which case //the algorithm would not even search distance==1 //even if there's a reachable hex for distance==2. if (pass_check_and_unreachable && distance > 10) continue; //If the hex is empty and we do either no pass check or the hex is reachable, return it. if (units.find(loc) == units.end() && !pass_check_and_unreachable) return loc; map_location adjs[6]; get_adjacent_tiles(loc,adjs); foreach (const map_location &loc, adjs) { if (!map.on_board(loc)) continue; // Add the tile to be checked if it hasn't already been and // isn't being checked. if (tiles_checked.find(loc) == tiles_checked.end() && tiles_checking.find(loc) == tiles_checking.end()) { pending_tiles_to_check.insert(loc); } } } tiles_checked.swap(tiles_checking); } return map_location(); }
void get_tiles_radius(gamemap const &map, std::vector<map_location> const &locs, size_t radius, std::set<map_location> &res, xy_pred *pred) { typedef std::set<map_location> location_set; location_set not_visited(locs.begin(), locs.end()), must_visit, filtered_out; ++radius; for(;;) { location_set::const_iterator it = not_visited.begin(), it_end = not_visited.end(); std::copy(it,it_end,std::inserter(res,res.end())); for(; it != it_end; ++it) { map_location adj[6]; get_adjacent_tiles(*it, adj); for(size_t i = 0; i != 6; ++i) { map_location const &loc = adj[i]; if(map.on_board(loc) && !res.count(loc) && !filtered_out.count(loc)) { if(!pred || (*pred)(loc)) { must_visit.insert(loc); } else { filtered_out.insert(loc); } } } } if(--radius == 0 || must_visit.empty()) { break; } not_visited.swap(must_visit); must_visit.clear(); } }
boost::optional<std::string> game_board::replace_map(const gamemap & newmap) { boost::optional<std::string> ret = boost::optional<std::string> (); /* Remember the locations where a village is owned by a side. */ std::map<map_location, int> villages; for(const auto& village : map_->villages()) { const int owner = village_owner(village); if(owner != -1) { villages[village] = owner; } } for (unit_map::iterator itor = units_.begin(); itor != units_.end(); ) { if (!newmap.on_board(itor->get_location())) { if (!try_add_unit_to_recall_list(itor->get_location(), itor.get_shared_ptr())) { *ret = std::string("replace_map: Cannot add a unit that would become off-map to the recall list\n"); } units_.erase(itor++); } else { ++itor; } } /* Disown villages that are no longer villages. */ for(const auto& village : villages) { if(!newmap.is_village(village.first)) { teams_[village.second].lose_village(village.first); } } *map_ = newmap; return ret; }
map_location pathfind::find_vacant_tile(const gamemap& map, const unit_map& units, const map_location& loc, pathfind::VACANT_TILE_TYPE vacancy, const unit* pass_check) { if (!map.on_board(loc)) return map_location(); std::set<map_location> pending_tiles_to_check, tiles_checked; pending_tiles_to_check.insert(loc); // Iterate out 50 hexes from loc for (int distance = 0; distance < 50; ++distance) { if (pending_tiles_to_check.empty()) return map_location(); //Copy over the hexes to check and clear the old set std::set<map_location> tiles_checking; tiles_checking.swap(pending_tiles_to_check); //Iterate over all the hexes we need to check foreach (const map_location &loc, tiles_checking) { //If the unit cannot reach this area or it's not a castle but should, skip it. if ((vacancy == pathfind::VACANT_CASTLE && !map.is_castle(loc)) || (pass_check && pass_check->movement_cost(map[loc]) == unit_movement_type::UNREACHABLE)) continue; //If the hex is empty, return it. if (units.find(loc) == units.end()) return loc; map_location adjs[6]; get_adjacent_tiles(loc,adjs); foreach (const map_location &loc, adjs) { if (!map.on_board(loc)) continue; // Add the tile to be checked if it hasn't already been and // isn't being checked. if (tiles_checked.find(loc) == tiles_checked.end() && tiles_checking.find(loc) == tiles_checking.end()) { pending_tiles_to_check.insert(loc); } } } tiles_checked.swap(tiles_checking); } return map_location(); }
map_location pathfind::find_vacant_tile(const gamemap& map, const std::vector<team>& teams, const unit_map& units, const map_location& loc, const unit* pass_check) { if (!map.on_board(loc)) return map_location(); std::set<map_location> pending_tiles_to_check, tiles_checked; pending_tiles_to_check.insert(loc); // Iterate out 50 hexes from loc for (int distance = 0; distance < 50; ++distance) { //Copy over the hexes to check and clear the old set std::set<map_location> tiles_checking; tiles_checking.swap(pending_tiles_to_check); //Iterate over all the hexes we need to check BOOST_FOREACH (const map_location &loc, tiles_checking) { tiles_checked.insert(loc); // If the unit cannot reach this area or it's not a castle but should, skip it. if (!pass_check || can_generate(map, teams, units, *pass_check, loc)) { // If the hex is empty, return it. if (units.find(loc) == units.end()) { return loc; } } map_location adjs[6]; get_adjacent_tiles(loc, adjs); BOOST_FOREACH (const map_location &loc, adjs) { if (!map.on_board(loc)) continue; // Add the tile to be checked if it hasn't already been and // isn't being checked. if (tiles_checked.find(loc) == tiles_checked.end()) { pending_tiles_to_check.insert(loc); } } } }
bool recruit_result::test_suitable_recruit_location(const gamemap &map, const unit_map &units, const unit &my_leader, bool) { recruit_location_ = where_; //if we have not-on-board location, such as null_location, then the caller wants us to recruit on 'any' possible tile. if (!map.on_board(recruit_location_)) { recruit_location_ = pathfind::find_vacant_tile(map, units, my_leader.get_location(), pathfind::VACANT_CASTLE); } if (!can_recruit_on(map, my_leader.get_location(), recruit_location_)) { set_error(E_BAD_RECRUIT_LOCATION); return false; } return true; }
map_location find_vacant_tile(const gamemap& map, const unit_map& units, const map_location& loc, VACANT_TILE_TYPE vacancy, const unit* pass_check) { std::set<map_location> pending_tiles_to_check; std::set<map_location> tiles_checked; pending_tiles_to_check.insert( loc ); // Iterate out 50 hexes from loc for (int distance = 0; distance < 50; ++distance) { if (pending_tiles_to_check.empty()) return map_location(); //Copy over the hexes to check and clear the old set std::set<map_location> tiles_checking = pending_tiles_to_check; std::set<map_location>::const_iterator tc_itor = tiles_checking.begin(); pending_tiles_to_check.clear(); //Iterate over all the hexes we need to check for ( ; tc_itor != tiles_checking.end(); ++tc_itor ) { //If the unit cannot reach this area or it's not a castle but should, skip it. if ((vacancy == VACANT_CASTLE && !map.is_castle(*tc_itor)) || (pass_check && pass_check->movement_cost(map[*tc_itor]) == unit_movement_type::UNREACHABLE)) continue; //If the hex is empty, return it. if (map.on_board(*tc_itor) && units.find(*tc_itor) == units.end()) return (*tc_itor); map_location adjs[6]; get_adjacent_tiles(*tc_itor,adjs); for (int i = 0; i != 6; ++i) { //Add the tile to be checked if it hasn't already been and isn't already //pending to be checked if (pending_tiles_to_check.find(adjs[i]) == pending_tiles_to_check.end() && tiles_checked.find(adjs[i]) == tiles_checked.end() && tiles_checking.find(adjs[i]) == tiles_checking.end()) { pending_tiles_to_check.insert(adjs[i]); } } } tiles_checked = tiles_checking; } return map_location(); }
static int placing_score(const config& side, const gamemap& map, const map_location& pos) { int positions = 0, liked = 0; const t_translation::t_list terrain = t_translation::read_list(side["terrain_liked"]); for(int i = pos.x-8; i != pos.x+8; ++i) { for(int j = pos.y-8; j != pos.y+8; ++j) { const map_location pos(i,j); if(map.on_board(pos)) { ++positions; if(std::count(terrain.begin(),terrain.end(),map[pos])) { ++liked; } } } } return (100*liked)/positions; }
/** * Function that will add to @a result all elements of @a locs, plus all * on-board locations matching @a pred that are connected to elements of * locs by a chain of at most @a radius tiles, each of which matches @a pred. * @a result must be a std::set of locations. */ void get_tiles_radius(gamemap const &map, std::vector<map_location> const &locs, size_t radius, std::set<map_location> &result, bool with_border, xy_pred const &pred) { typedef std::set<map_location> location_set; location_set must_visit, filtered_out; location_set not_visited(locs.begin(), locs.end()); for ( ; radius != 0 && !not_visited.empty(); --radius ) { location_set::const_iterator it = not_visited.begin(); location_set::const_iterator it_end = not_visited.end(); result.insert(it, it_end); for(; it != it_end; ++it) { map_location adj[6]; get_adjacent_tiles(*it, adj); for(size_t i = 0; i != 6; ++i) { map_location const &loc = adj[i]; if ( with_border ? map.on_board_with_border(loc) : map.on_board(loc) ) { if ( !result.count(loc) && !filtered_out.count(loc) ) { if ( pred(loc) ) must_visit.insert(loc); else filtered_out.insert(loc); } } } } not_visited.swap(must_visit); must_visit.clear(); } result.insert(not_visited.begin(), not_visited.end()); }
marked_route mark_route(const plain_route &rt, const std::vector<map_location>& waypoints, const unit &u, const team &viewing_team, const unit_map &units, const std::vector<team> &teams, const gamemap &map) { marked_route res; if (rt.steps.empty()) return res; res.steps = rt.steps; int turns = 0; int movement = u.movement_left(); const team& unit_team = teams[u.side()-1]; bool zoc = false; std::vector<map_location>::const_iterator i = rt.steps.begin(), w = waypoints.begin(); // TODO fix the name confusion with waypoints and route.waypoints for (; i !=rt.steps.end(); i++) { bool last_step = (i+1 == rt.steps.end()); // move_cost of the next step is irrelevant for the last step assert(last_step || map.on_board(*(i+1))); const int move_cost = last_step ? 0 : u.movement_cost(map[*(i+1)]); bool capture = false; bool pass_here = false; if (w != waypoints.end() && *i == *w) { w++; pass_here = true; } if (last_step || zoc || move_cost > movement) { // check if we stop an a village and so maybe capture it // if it's an enemy unit and a fogged village, we assume a capture // (if he already owns it, we can't know that) // if it's not an enemy, we can always know if he owns the village bool capture = map.is_village(*i) && ( !unit_team.owns_village(*i) || (viewing_team.is_enemy(u.side()) && viewing_team.fogged(*i)) ); ++turns; bool invisible = u.invisible(*i,units,teams,false); res.waypoints[*i] = marked_route::waypoint(turns, pass_here, zoc, capture, invisible); if (last_step) break; // finished and we used dummy move_cost movement = u.total_movement(); if(move_cost > movement) { return res; //we can't reach destination } } else if (pass_here) { bool invisible = u.invisible(*i,units,teams,false); res.waypoints[*i] = marked_route::waypoint(0, pass_here, zoc, false, invisible); } zoc = enemy_zoc(units, teams, *(i + 1), viewing_team,u.side()) && !u.get_ability_bool("skirmisher", *(i+1)); if (zoc || capture) { movement = 0; } else { movement -= move_cost; } } return res; }
surface getMinimap(int w, int h, const gamemap &map, const team *vw) { const int scale = 8; DBG_DP << "creating minimap " << int(map.w()*scale*0.75) << "," << map.h()*scale << "\n"; const size_t map_width = map.w()*scale*3/4; const size_t map_height = map.h()*scale; if(map_width == 0 || map_height == 0) { return surface(NULL); } surface minimap(create_neutral_surface(map_width, map_height)); if(minimap == NULL) return surface(NULL); typedef mini_terrain_cache_map cache_map; cache_map *normal_cache = &mini_terrain_cache; cache_map *fog_cache = &mini_fogged_terrain_cache; for(int y = 0; y != map.total_height(); ++y) { for(int x = 0; x != map.total_width(); ++x) { surface surf(NULL); const map_location loc(x,y); if(map.on_board(loc)) { const bool shrouded = (vw != NULL && vw->shrouded(loc)); // shrouded hex are not considered fogged (no need to fog a black image) const bool fogged = (vw != NULL && !shrouded && vw->fogged(loc)); const t_translation::t_terrain terrain = shrouded ? t_translation::VOID_TERRAIN : map[loc]; const terrain_type& terrain_info = map.get_terrain_info(terrain); bool need_fogging = false; cache_map* cache = fogged ? fog_cache : normal_cache; cache_map::iterator i = cache->find(terrain); if (fogged && i == cache->end()) { // we don't have the fogged version in cache // try the normal cache and ask fogging the image cache = normal_cache; i = cache->find(terrain); need_fogging = true; } if(i == cache->end()) { std::string base_file = "terrain/" + terrain_info.minimap_image() + ".png"; surface tile = get_image(base_file,image::HEXED); //Compose images of base and overlay if necessary // NOTE we also skip overlay when base is missing (to avoid hiding the error) if(tile != NULL && map.get_terrain_info(terrain).is_combined()) { std::string overlay_file = "terrain/" + terrain_info.minimap_image_overlay() + ".png"; surface overlay = get_image(overlay_file,image::HEXED); if(overlay != NULL && overlay != tile) { surface combined = create_neutral_surface(tile->w, tile->h); SDL_Rect r = create_rect(0,0,0,0); sdl_blit(tile, NULL, combined, &r); r.x = std::max(0, (tile->w - overlay->w)/2); r.y = std::max(0, (tile->h - overlay->h)/2); //blit_surface needs neutral surface surface overlay_neutral = make_neutral_surface(overlay); blit_surface(overlay_neutral, NULL, combined, &r); tile = combined; } } surf = scale_surface_sharp(tile, scale, scale); i = normal_cache->insert(cache_map::value_type(terrain,surf)).first; } surf = i->second; if (need_fogging) { surf = adjust_surface_color(surf,-50,-50,-50); fog_cache->insert(cache_map::value_type(terrain,surf)); } // we need a balanced shift up and down of the hexes. // if not, only the bottom half-hexes are clipped // and it looks asymmetrical. // also do 1-pixel shift because the scaling // function seems to do it with its rounding SDL_Rect maprect = create_rect( x * scale * 3 / 4 - 1 , y * scale + scale / 4 * (is_odd(x) ? 1 : -1) - 1 , 0 , 0); if(surf != NULL) sdl_blit(surf, NULL, minimap, &maprect); } } } double wratio = w*1.0 / minimap->w; double hratio = h*1.0 / minimap->h; double ratio = std::min<double>(wratio, hratio); minimap = scale_surface_sharp(minimap, static_cast<int>(minimap->w * ratio), static_cast<int>(minimap->h * ratio)); DBG_DP << "done generating minimap\n"; return minimap; }
surface getMinimap(int w, int h, const gamemap &map, const team *vw) { const int scale = 8; DBG_DP << "creating minimap " << int(map.w()*scale*0.75) << "," << int(map.h()*scale) << "\n"; const size_t map_width = map.w()*scale*3/4; const size_t map_height = map.h()*scale; if(map_width == 0 || map_height == 0) { return surface(NULL); } surface minimap(create_neutral_surface(map_width, map_height)); if(minimap == NULL) return surface(NULL); typedef mini_terrain_cache_map cache_map; cache_map *normal_cache = &mini_terrain_cache; cache_map *fog_cache = &mini_fogged_terrain_cache; for(int y = 0; y != map.total_height(); ++y) { for(int x = 0; x != map.total_width(); ++x) { surface surf(NULL); const map_location loc(x,y); if(map.on_board(loc)) { const bool shrouded = vw != NULL && vw->shrouded(loc); // shrouded hex are not considered fogged (no need to fog a black image) const bool fogged = vw != NULL && !shrouded && vw->fogged(loc); const t_translation::t_terrain terrain = shrouded ? t_translation::VOID_TERRAIN : map[loc]; bool need_fogging = false; cache_map* cache = fogged ? fog_cache : normal_cache; cache_map::iterator i = cache->find(terrain); if (fogged && i == cache->end()) { // we don't have the fogged version in cache // try the normal cache and ask fogging the image cache = normal_cache; i = cache->find(terrain); need_fogging = true; } if(i == cache->end()) { surface tile(get_image("terrain/" + map.get_terrain_info(terrain).minimap_image() + ".png",image::HEXED)); if(tile == 0) { utils::string_map symbols; symbols["terrain"] = t_translation::write_terrain_code(terrain); const std::string msg = vgettext("Could not get image for terrain: $terrain.", symbols); VALIDATE(false, msg); } //Compose images of base and overlay if neccessary if(map.get_terrain_info(terrain).is_combined()) { surface overlay(get_image("terrain/" + map.get_terrain_info(terrain).minimap_image_overlay() + ".png", image::HEXED)); if(overlay != 0 && overlay != tile) { surface combined = create_compatible_surface(tile, tile->w, tile->h); SDL_Rect r; r.x = 0; r.y = 0; SDL_BlitSurface(tile, NULL, combined, &r); r.x = std::max(0, (tile->w - overlay->w)/2); r.y = std::max(0, (tile->h - overlay->h)/2); if ((overlay->flags & SDL_RLEACCEL) == 0) { blit_surface(overlay, NULL, combined, &r); } else { WRN_DP << map.get_terrain_info(terrain).minimap_image_overlay() << ".png overlay is RLE-encoded, creating a neutral surface\n"; surface overlay_neutral = make_neutral_surface(overlay); blit_surface(overlay_neutral, NULL, combined, &r); } tile = combined; } } surf = surface(scale_surface_blended(tile,scale,scale)); VALIDATE(surf != NULL, _("Error creating or aquiring an image.")); i = normal_cache->insert(cache_map::value_type(terrain,surf)).first; } surf = i->second; if (need_fogging) { surf = surface(adjust_surface_colour(surf,-50,-50,-50)); fog_cache->insert(cache_map::value_type(terrain,surf)); } VALIDATE(surf != NULL, _("Error creating or aquiring an image.")); // we need a balanced shift up and down of the hexes. // if not, only the bottom half-hexes are clipped // and it looks asymmetrical. // also do 1-pixel shift because the scaling // function seems to do it with its rounding SDL_Rect maprect = {x * scale*3/4 - 1, y*scale + scale/4 * (is_odd(x) ? 1 : -1) - 1, 0, 0}; SDL_BlitSurface(surf, NULL, minimap, &maprect); } } } double wratio = w*1.0 / minimap->w; double hratio = h*1.0 / minimap->h; double ratio = std::min<double>(wratio, hratio); minimap = scale_surface(minimap, static_cast<int>(minimap->w * ratio), static_cast<int>(minimap->h * ratio)); DBG_DP << "done generating minimap\n"; return minimap; }