map_manip.cpp

Go to the documentation of this file.
00001 /* $Id: map_manip.cpp 26769 2008-05-22 13:21:04Z mog $ */
00002 /*
00003   Copyright (C) 2003 - 2008 by David White <dave@whitevine.net>
00004   Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006   This program is free software; you can redistribute it and/or modify
00007   it under the terms of the GNU General Public License version 2
00008   or at your option any later version.
00009   This program is distributed in the hope that it will be useful,
00010   but WITHOUT ANY WARRANTY.
00011 
00012   See the COPYING file for more details.
00013 */
00014 
00015 //! @file editor/map_manip.cpp
00016 //! Operations on a game-map.
00017 
00018 #include "map_manip.hpp"
00019 
00020 #include "../gettext.hpp"
00021 #include "../map.hpp"
00022 #include "../config.hpp"
00023 #include "../construct_dialog.hpp"
00024 #include "../util.hpp"
00025 #include "../wml_exception.hpp"
00026 #include "serialization/string_utils.hpp"
00027 
00028 #include <algorithm>
00029 #include <cassert>
00030 #include <map>
00031 #include <set>
00032 #include <sstream>
00033 #include <vector>
00034 
00035 std::string editormap::resize(const size_t width, const size_t height,
00036     const int x_offset, const int y_offset,
00037     const bool do_expand, t_translation::t_terrain filler)
00038 {
00039 
00040     const unsigned old_w = static_cast<unsigned>(w());
00041     const unsigned old_h = static_cast<unsigned>(h());
00042 
00043     // no changes leave
00044     if(old_w == width && old_h == height && x_offset == 0 && y_offset == 0 ) {
00045         return "";
00046     }
00047 
00048     if(do_expand) {
00049         filler = t_translation::NONE_TERRAIN;
00050     }
00051 
00052     // Determine the amount of resizing is required
00053     const int left_resize = -x_offset;
00054     const int right_resize = (width - old_w) + x_offset;
00055     const int top_resize = -y_offset;
00056     const int bottom_resize = (height - old_h) + y_offset;
00057 
00058 //  std::cerr << "resize y " << left_resize << " x " << right_resize
00059 //      << " top " << top_resize << " bottom " << bottom_resize << '\n';
00060 
00061     if(right_resize > 0) {
00062         add_tiles_right(right_resize, filler);
00063     } else if(right_resize < 0) {
00064         remove_tiles_right(-right_resize);
00065     }
00066 
00067     if(bottom_resize > 0) {
00068         add_tiles_bottom(bottom_resize, filler);
00069     } else if(bottom_resize < 0) {
00070         remove_tiles_bottom(-bottom_resize);
00071     }
00072 
00073     if(left_resize > 0) {
00074         add_tiles_left(left_resize, filler);
00075     } else if(left_resize < 0) {
00076         remove_tiles_left(-left_resize);
00077     }
00078 
00079     if(top_resize > 0) {
00080         add_tiles_top(top_resize, filler);
00081     } else if(top_resize < 0) {
00082         remove_tiles_top(-top_resize);
00083     }
00084 
00085     // fix the starting positions
00086     if(x_offset || y_offset) {
00087         for(size_t i = 0; i < MAX_PLAYERS+1; ++i) {
00088             if(startingPositions_[i] != gamemap::location()) {
00089                 startingPositions_[i].x -= x_offset;
00090                 startingPositions_[i].y -= y_offset;
00091             }
00092         }
00093     }
00094 
00095 
00096     return write();
00097 }
00098 
00099 std::string editormap::flip(const map_editor::FLIP_AXIS axis)
00100 {
00101     if(axis !=  map_editor::FLIP_X && axis != map_editor::FLIP_Y) {
00102         return "";
00103     }
00104 
00105     if(axis == map_editor::FLIP_X) {
00106         // Due to the hexes we need some mirror tricks when mirroring over the
00107         // X axis. We resize the map and fill it. The odd columns will be extended
00108         // with the data in row 0 the even columns are extended with the data in
00109         // the last row
00110         const size_t middle = (tiles_[0].size() / 2); // the middle if reached we flipped all
00111         const size_t end = tiles_[0].size() - 1; // the last row _before_ resizing
00112         for(size_t x = 0; x < tiles_.size(); ++x) {
00113             if(x % 2) {
00114                 // odd lines
00115                 tiles_[x].resize(tiles_[x].size() + 1, tiles_[x][0]);
00116 
00117                 for(size_t y1 = 0, y2 = end; y1 < middle; ++y1, --y2) {
00118                     swap_starting_position(x, y1, x, y2);
00119                     std::swap(tiles_[x][y1], tiles_[x][y2]);
00120                 }
00121             } else {
00122                 // even lines
00123                 tiles_[x].resize(tiles_[x].size() + 1, tiles_[x][end]);
00124 
00125                 for(size_t y1 = 0, y2 = end + 1; y1 < middle; ++y1, --y2) {
00126                     swap_starting_position(x, y1, x, y2);
00127                     std::swap(tiles_[x][y1], tiles_[x][y2]);
00128                 }
00129 
00130             }
00131         }
00132     } else { // FLIP_Y
00133         // Flipping on the Y axis requires no resize,
00134         // so the code is much simpler.
00135         const size_t middle = (tiles_.size() / 2);
00136         const size_t end = tiles_.size() - 1;
00137         for(size_t y = 0; y < tiles_[0].size(); ++y) {
00138             for(size_t x1 = 0, x2 = end; x1 < middle; ++x1, --x2) {
00139                 swap_starting_position(x1, y, x2, y);
00140                 std::swap(tiles_[x1][y], tiles_[x2][y]);
00141             }
00142         }
00143     }
00144 
00145     return write();
00146 }
00147 
00148 void editormap::set_starting_position(const int pos, const location loc) {
00149     startingPositions_[pos] = loc;
00150 }
00151 
00152 void editormap::swap_starting_position(const size_t x1, const size_t y1,
00153     const size_t x2, const size_t y2)
00154 {
00155     const int pos1 = is_starting_position(location(x1, y1));
00156     const int pos2 = is_starting_position(location(x2, y2));
00157 
00158     if(pos1 != -1) {
00159         set_starting_position(pos1 + 1, location(x2, y2));
00160     }
00161 
00162     if(pos2 != -1) {
00163         set_starting_position(pos2 + 1, location(x1, y1));
00164     }
00165 }
00166 
00167 void editormap::add_tiles_right(
00168     const unsigned count, const t_translation::t_terrain& filler)
00169 {
00170     for(size_t x = 1; x <= count; ++x) {
00171         t_translation::t_list one_row(tiles_[1].size());
00172         for(size_t y = 0; y < tiles_[1].size(); ++y) {
00173             one_row[y] =
00174                 filler != t_translation::NONE_TERRAIN ?
00175                 filler :
00176                 tiles_[x - 1][y];
00177 
00178             assert(one_row[y] != t_translation::NONE_TERRAIN);
00179         }
00180         tiles_.push_back(one_row);
00181     }
00182 }
00183 
00184 void editormap::add_tiles_left(
00185     const unsigned count, const t_translation::t_terrain& filler)
00186 {
00187     for(size_t i = 1; i <= count; ++i) {
00188         t_translation::t_list one_row(tiles_[1].size());
00189         for(size_t y = 0; y < tiles_[1].size(); ++y) {
00190             one_row[y] =
00191                 filler != t_translation::NONE_TERRAIN ?
00192                 filler :
00193                 tiles_[0][y];
00194 
00195             assert(one_row[y] != t_translation::NONE_TERRAIN);
00196         }
00197         tiles_.insert(tiles_.begin(), 1, one_row);
00198         clear_border_cache();
00199     }
00200 }
00201 
00202 void editormap::remove_tiles_right(const unsigned count)
00203 {
00204     if(count > tiles_.size()) {
00205         std::stringstream sstr;
00206         sstr << _("Can't resize the map, the requested size is bigger "
00207             "than the maximum, size=") << count << _(" maximum=")
00208             << tiles_.size();
00209 
00210         throw incorrect_format_exception(sstr.str().c_str());
00211     }
00212 
00213     tiles_.resize(tiles_.size() - count);
00214 }
00215 
00216 void editormap::remove_tiles_left(const unsigned count)
00217 {
00218     if(count > tiles_.size()) {
00219         std::stringstream sstr;
00220         sstr << _("Can't resize the map, the requested size is bigger "
00221             "than the maximum, size=") << count << _(" maximum=")
00222             << tiles_.size();
00223 
00224         throw incorrect_format_exception(sstr.str().c_str());
00225     }
00226 
00227     tiles_.erase(tiles_.begin(), tiles_.begin() + count);
00228 }
00229 
00230 void editormap::add_tiles_top(
00231     const unsigned count, const t_translation::t_terrain& filler)
00232 {
00233     for(size_t i =  1; i <= count; ++i) {
00234         for(size_t y = 0; y < tiles_.size(); ++y) {
00235             t_translation::t_terrain terrain =
00236                 filler != t_translation::NONE_TERRAIN ?
00237                 filler :
00238                 tiles_[y][0];
00239 
00240             assert(terrain != t_translation::NONE_TERRAIN);
00241             tiles_[y].insert(tiles_[y].begin(), 1, terrain);
00242             clear_border_cache();
00243         }
00244     }
00245 }
00246 
00247 void editormap::add_tiles_bottom(
00248     const unsigned count, const t_translation::t_terrain& filler)
00249 {
00250     for(size_t i =  1; i <= count; ++i) {
00251         for(size_t x = 0; x < tiles_.size(); ++x) {
00252             t_translation::t_terrain terrain =
00253                 filler != t_translation::NONE_TERRAIN ?
00254                 filler :
00255                 tiles_[x][i - 1];
00256 
00257             assert(terrain != t_translation::NONE_TERRAIN);
00258             tiles_[x].push_back(terrain);
00259         }
00260     }
00261 }
00262 
00263 void editormap::remove_tiles_top(const unsigned count)
00264 {
00265     if(count > tiles_[0].size()) {
00266         std::stringstream sstr;
00267         sstr << _("Can't resize the map, the requested size is bigger "
00268             "than the maximum, size=") << count << _(" maximum=")
00269             << tiles_[0].size();
00270 
00271         throw incorrect_format_exception(sstr.str().c_str());
00272     }
00273 
00274     for(size_t x = 0; x < tiles_.size(); ++x) {
00275         tiles_[x].erase(tiles_[x].begin(), tiles_[x].begin() + count);
00276     }
00277 }
00278 
00279 void editormap::remove_tiles_bottom(const unsigned count)
00280 {
00281     if(count > tiles_[0].size()) {
00282         std::stringstream sstr;
00283         sstr << _("Can't resize the map, the requested size is bigger "
00284             "than the maximum, size=") << count << _(" maximum=")
00285             << tiles_[0].size();
00286 
00287         throw incorrect_format_exception(sstr.str().c_str());
00288     }
00289 
00290     for(size_t x = 0; x < tiles_.size(); ++x) {
00291         tiles_[x].erase(tiles_[x].end() - count, tiles_[x].end());
00292     }
00293 
00294 }
00295 
00296 namespace map_editor {
00297 
00298 std::vector<gamemap::location> get_tiles(const gamemap &map,
00299                                          const gamemap::location& a,
00300                                          const unsigned int radius) {
00301     const unsigned int distance = radius - 1;
00302     std::vector<gamemap::location> res;
00303     res.push_back(a);
00304     for (unsigned int d = 1; d <= distance; d++) {
00305         gamemap::location loc = a;
00306         unsigned int i;
00307         // Get the starting point.
00308         for (i = 1; i <= d; i++) {
00309             loc = loc.get_direction(gamemap::location::NORTH, 1);
00310         }
00311         // Get all the tiles clockwise with distance d.
00312         const gamemap::location::DIRECTION direction[6] =
00313             {gamemap::location::SOUTH_EAST, gamemap::location::SOUTH, gamemap::location::SOUTH_WEST,
00314              gamemap::location::NORTH_WEST, gamemap::location::NORTH, gamemap::location::NORTH_EAST};
00315         for (i = 0; i < 6; i++) {
00316             for (unsigned int j = 1; j <= d; j++) {
00317                 loc = loc.get_direction(direction[i], 1);
00318                 if (map.on_board(loc, true)) {
00319                     res.push_back(loc);
00320                 }
00321             }
00322         }
00323     }
00324     return res;
00325 }
00326 
00327 void flood_fill(gamemap &map, const gamemap::location &start_loc,
00328                 const t_translation::t_terrain fill_with, terrain_log *log)
00329 {
00330     t_translation::t_terrain terrain_to_fill = map.get_terrain(start_loc);
00331     if (fill_with == terrain_to_fill) {
00332         return;
00333     }
00334     std::set<gamemap::location> to_fill = get_component(map, start_loc);
00335     std::set<gamemap::location>::iterator it;
00336     for (it = to_fill.begin(); it != to_fill.end(); it++) {
00337         gamemap::location loc = *it;
00338         if (log != NULL) {
00339             log->push_back(std::make_pair(loc, terrain_to_fill));
00340         }
00341         if (fill_with.base == t_translation::NO_LAYER) {
00342             map.set_terrain(loc, fill_with, gamemap::OVERLAY);
00343         }
00344         else {
00345             map.set_terrain(loc, fill_with);
00346         }
00347     }
00348 }
00349 
00350 std::set<gamemap::location> get_component(const gamemap &map,
00351         const gamemap::location &start_loc)
00352 {
00353     t_translation::t_terrain terrain_to_fill = map.get_terrain(start_loc);
00354     std::set<gamemap::location> to_fill;
00355     std::set<gamemap::location> filled;
00356     std::set<gamemap::location>::iterator it;
00357     // Insert the start location in a set.
00358     // Chose an element in the set, mark this element,
00359     // and add all adjacent elements that are not marked.
00360     // Continue until the set is empty.
00361     to_fill.insert(start_loc);
00362     while (!to_fill.empty()) {
00363         it = to_fill.begin();
00364         gamemap::location loc = *it;
00365         to_fill.erase(it);
00366         filled.insert(loc);
00367         // Find all adjacent tiles with the terrain that should
00368         // be filled and add these to the to_fill vector.
00369         std::vector<gamemap::location> adj = get_tiles(map, loc, 2);
00370         for (std::vector<gamemap::location>::iterator it2 = adj.begin();
00371              it2 != adj.end(); it2++) {
00372             if (map.on_board(*it2, true) && map.get_terrain(*it2) == terrain_to_fill
00373                 && filled.find(*it2) == filled.end()) {
00374                 to_fill.insert(*it2);
00375             }
00376         }
00377     }
00378     return filled;
00379 }
00380 
00381 std::string resize_map(editormap &map, const unsigned new_w,
00382     const unsigned new_h, const int off_x, const int off_y,
00383     const bool do_expand, const t_translation::t_terrain fill_with)
00384 {
00385     return map.resize(new_w, new_h, off_x, off_y, do_expand, fill_with);
00386 }
00387 
00388 
00389 std::string flip_map(editormap &map, const FLIP_AXIS axis) {
00390     return map.flip(axis);
00391 }
00392 
00393 bool valid_mapdata(const std::string &data, const config &cfg) {
00394     bool res = data.size() != 0;
00395     // Create a map and see if we get an exception. Not very efficient,
00396     // but simple as things are implemented now.
00397     try {
00398         const gamemap m(cfg, data);
00399         // Having a zero size map may cause floating point exceptions
00400         // at some places later on.
00401         res = m.w() != 0 && m.h() != 0;
00402     }
00403     catch (gamemap::incorrect_format_exception) {
00404         res = false;
00405     } catch(twml_exception& e) {
00406         std::cerr << "WML exception:\nUser message: " 
00407             << e.user_message << "\nDev message: " << e.dev_message << '\n';
00408         return false;
00409     }
00410     return res;
00411 }
00412 
00413 std::string new_map(const size_t width, const size_t height, const t_translation::t_terrain filler)
00414 {
00415     const t_translation::t_list column(height, filler);
00416     const t_translation::t_map map(width, column);
00417 
00418     return gamemap::default_map_header + t_translation::write_game_map(map);
00419 
00420 }
00421 
00422 } // end namespace map_editor
00423 

Generated by doxygen 1.5.5 on 23 May 2008 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs