00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #define GETTEXT_DOMAIN "wesnoth-editor"
00016
00017 #include "editor/action/action_base.hpp"
00018 #include "editor_map.hpp"
00019 #include "formula_string_utils.hpp"
00020
00021 #include "display.hpp"
00022 #include "filesystem.hpp"
00023 #include "foreach.hpp"
00024 #include "gettext.hpp"
00025 #include "map_exception.hpp"
00026 #include "map_label.hpp"
00027 #include "wml_exception.hpp"
00028
00029
00030 namespace editor {
00031
00032 editor_map_load_exception wrap_exc(const char* type, const std::string& e_msg, const std::string& filename)
00033 {
00034 WRN_ED << type << " error in load map " << filename << ": " << e_msg << "\n";
00035 utils::string_map symbols;
00036 symbols["type"] = type;
00037 const char* error_msg = "There was an error ($type) while loading the file:";
00038 std::string msg = vgettext(error_msg, symbols);
00039 msg += "\n";
00040 msg += e_msg;
00041 return editor_map_load_exception(filename, msg);
00042 }
00043
00044 editor_map::editor_map(const config& terrain_cfg, const display& disp)
00045 : gamemap(terrain_cfg, "")
00046 , selection_()
00047 , labels_(disp, NULL)
00048 , units_()
00049 , teams_()
00050 , tod_manager_()
00051 , state_()
00052 {
00053 }
00054
00055 editor_map::editor_map(const config& terrain_cfg, const config& level, const display& disp)
00056 : gamemap(terrain_cfg, level)
00057 , selection_()
00058 , labels_(disp, NULL)
00059 , units_()
00060 , teams_()
00061 , tod_manager_(level)
00062 , state_()
00063 {
00064 labels_.read(level);
00065
00066 foreach (const config& side, level.child_range("side"))
00067 {
00068 team t;
00069 t.build(side, *this, 100);
00070
00071 t.change_controller("null");
00072
00073
00074 teams_.push_back(t);
00075 foreach (const config &a_unit, side.child_range("unit")) {
00076 map_location loc(a_unit, NULL);
00077 units_.add(loc, unit(a_unit, true));
00078 }
00079 }
00080
00081 sanity_check();
00082 }
00083
00084 editor_map::editor_map(const config& terrain_cfg, const std::string& data, const display& disp)
00085 : gamemap(terrain_cfg, data)
00086 , selection_()
00087 , labels_(disp, NULL)
00088 , units_()
00089 , teams_()
00090 , tod_manager_()
00091 , state_()
00092 {
00093 sanity_check();
00094 }
00095
00096 editor_map editor_map::from_string(const config& terrain_cfg, const std::string& data, const display& disp)
00097 {
00098 try {
00099 return editor_map(terrain_cfg, data, disp);
00100 } catch (incorrect_map_format_error& e) {
00101 throw wrap_exc("format", e.message, "");
00102 } catch (twml_exception& e) {
00103 throw wrap_exc("wml", e.user_message, "");
00104 } catch (config::error& e) {
00105 throw wrap_exc("config", e.message, "");
00106 }
00107 }
00108
00109 editor_map::editor_map(
00110 const config& terrain_cfg
00111 , size_t width
00112 , size_t height
00113 , t_translation::t_terrain filler
00114 , const display& disp)
00115 : gamemap(
00116 terrain_cfg
00117 , t_translation::write_game_map(t_translation::t_map(
00118 width + 2
00119 , t_translation::t_list(height + 2, filler))))
00120 , selection_()
00121 , labels_(disp, NULL)
00122 , units_()
00123 , teams_()
00124 , tod_manager_()
00125 , state_()
00126 {
00127 sanity_check();
00128 }
00129
00130 editor_map::editor_map(const gamemap& map, const display& disp)
00131 : gamemap(map)
00132 , selection_()
00133 , labels_(disp, NULL)
00134 , units_()
00135 , teams_()
00136 , tod_manager_()
00137 , state_()
00138 {
00139 sanity_check();
00140 }
00141
00142 editor_map::~editor_map()
00143 {
00144 }
00145
00146 void editor_map::sanity_check()
00147 {
00148 int errors = 0;
00149 if (total_width() != static_cast<int>(tiles_.size())) {
00150 ERR_ED << "total_width is " << total_width() << " but tiles_.size() is " << tiles_.size() << "\n";
00151 ++errors;
00152 }
00153 if (total_height() != static_cast<int>(tiles_[0].size())) {
00154 ERR_ED << "total_height is " << total_height() << " but tiles_[0].size() is " << tiles_.size() << "\n";
00155 ++errors;
00156 }
00157 if (w() + 2 * border_size() != total_width()) {
00158 ERR_ED << "h is " << h_ << " and border_size is " << border_size() << " but total_width is " << total_width() << "\n";
00159 ++errors;
00160 }
00161 if (h() + 2 * border_size() != total_height()) {
00162 ERR_ED << "w is " << w_ << " and border_size is " << border_size() << " but total_height is " << total_height() << "\n";
00163 ++errors;
00164 }
00165 for (size_t i = 1; i < tiles_.size(); ++i) {
00166 if (tiles_[i].size() != tiles_[0].size()) {
00167 ERR_ED << "tiles_[ " << i << "] has size() " << tiles_[i].size() << " but tiles[0] has size() " << tiles_[0].size() << "\n";
00168 ++errors;
00169 }
00170 }
00171 foreach (const map_location& loc, selection_) {
00172 if (!on_board_with_border(loc)) {
00173 ERR_ED << "Off-map tile in selection: " << loc << "\n";
00174 }
00175 }
00176 if (errors) {
00177 throw editor_map_integrity_error();
00178 }
00179 }
00180
00181 std::set<map_location> editor_map::get_contiguous_terrain_tiles(const map_location& start) const
00182 {
00183 t_translation::t_terrain terrain = get_terrain(start);
00184 std::set<map_location> result;
00185 std::deque<map_location> queue;
00186 result.insert(start);
00187 queue.push_back(start);
00188
00189 do {
00190 map_location adj[6];
00191 get_adjacent_tiles(queue.front(), adj);
00192 for (int i = 0; i < 6; ++i) {
00193 if (on_board_with_border(adj[i]) && get_terrain(adj[i]) == terrain
00194 && result.find(adj[i]) == result.end()) {
00195 result.insert(adj[i]);
00196 queue.push_back(adj[i]);
00197 }
00198 }
00199 queue.pop_front();
00200 } while (!queue.empty());
00201 return result;
00202 }
00203
00204 std::set<map_location> editor_map::set_starting_position_labels(display& disp)
00205 {
00206 std::set<map_location> label_locs;
00207 std::string label = _("Player");
00208 label += " ";
00209 for (int i = 1; i <= gamemap::MAX_PLAYERS; i++) {
00210 if (startingPositions_[i].valid()) {
00211 disp.labels().set_label(startingPositions_[i], label + lexical_cast<std::string>(i));
00212 label_locs.insert(startingPositions_[i]);
00213 }
00214 }
00215 return label_locs;
00216 }
00217
00218 bool editor_map::in_selection(const map_location& loc) const
00219 {
00220 return selection_.find(loc) != selection_.end();
00221 }
00222
00223 bool editor_map::add_to_selection(const map_location& loc)
00224 {
00225 return on_board_with_border(loc) ? selection_.insert(loc).second : false;
00226 }
00227
00228 bool editor_map::remove_from_selection(const map_location& loc)
00229 {
00230 return selection_.erase(loc) != 0;
00231 }
00232
00233 void editor_map::clear_selection()
00234 {
00235 selection_.clear();
00236 }
00237
00238 void editor_map::invert_selection()
00239 {
00240 std::set<map_location> new_selection;
00241 for (int x = -1; x < w() + 1; ++x) {
00242 for (int y = -1; y < h() + 1; ++y) {
00243 if (selection_.find(map_location(x, y)) == selection_.end()) {
00244 new_selection.insert(map_location(x, y));
00245 }
00246 }
00247 }
00248 selection_.swap(new_selection);
00249 }
00250
00251 void editor_map::select_all()
00252 {
00253 clear_selection();
00254 invert_selection();
00255 }
00256
00257 bool editor_map::everything_selected() const
00258 {
00259 LOG_ED << selection_.size() << " " << total_width() * total_height() << "\n";
00260 return static_cast<int>(selection_.size()) == total_width() * total_height();
00261 }
00262
00263 void editor_map::resize(int width, int height, int x_offset, int y_offset,
00264 t_translation::t_terrain filler)
00265 {
00266 int old_w = w();
00267 int old_h = h();
00268 if (old_w == width && old_h == height && x_offset == 0 && y_offset == 0) {
00269 return;
00270 }
00271
00272
00273 const int left_resize = -x_offset;
00274 const int right_resize = (width - old_w) + x_offset;
00275 const int top_resize = -y_offset;
00276 const int bottom_resize = (height - old_h) + y_offset;
00277
00278 if(right_resize > 0) {
00279 expand_right(right_resize, filler);
00280 } else if(right_resize < 0) {
00281 shrink_right(-right_resize);
00282 }
00283 if(bottom_resize > 0) {
00284 expand_bottom(bottom_resize, filler);
00285 } else if(bottom_resize < 0) {
00286 shrink_bottom(-bottom_resize);
00287 }
00288 if(left_resize > 0) {
00289 expand_left(left_resize, filler);
00290 } else if(left_resize < 0) {
00291 shrink_left(-left_resize);
00292 }
00293 if(top_resize > 0) {
00294 expand_top(top_resize, filler);
00295 } else if(top_resize < 0) {
00296 shrink_top(-top_resize);
00297 }
00298
00299
00300 if(x_offset || y_offset) {
00301 for(size_t i = 0; i < MAX_PLAYERS+1; ++i) {
00302 if(startingPositions_[i] != map_location()) {
00303 startingPositions_[i].x -= x_offset;
00304 startingPositions_[i].y -= y_offset;
00305 }
00306 }
00307 }
00308 sanity_check();
00309 }
00310
00311 editor_map editor_map::mask_to(const editor_map& target) const
00312 {
00313 if (target.w() != w() || target.h() != h()) {
00314 throw editor_action_exception(_("The size of the target map is different from the current map"));
00315 }
00316 editor_map mask(target);
00317 map_location iter;
00318 for (iter.x = -border_size(); iter.x < w() + border_size(); ++iter.x) {
00319 for (iter.y = -border_size(); iter.y < h() + border_size(); ++iter.y) {
00320 if (target.get_terrain(iter) == get_terrain(iter)) {
00321 mask.set_terrain(iter, t_translation::FOGGED);
00322 }
00323 }
00324 }
00325 return mask;
00326 }
00327
00328 bool editor_map::same_size_as(const gamemap& other) const
00329 {
00330 return h() == other.h()
00331 && w() == other.w();
00332 }
00333
00334 t_translation::t_list editor_map::clone_column(int x, t_translation::t_terrain filler)
00335 {
00336 int h = tiles_[1].size();
00337 t_translation::t_list column(h);
00338 for (int y = 0; y < h; ++y) {
00339 column[y] =
00340 filler != t_translation::NONE_TERRAIN ?
00341 filler :
00342 tiles_[x][y];
00343 assert(column[y] != t_translation::NONE_TERRAIN);
00344 }
00345 return column;
00346 }
00347
00348 void editor_map::expand_right(int count, t_translation::t_terrain filler)
00349 {
00350 int w = tiles_.size();
00351 for (int x = 0; x < count; ++x) {
00352 tiles_.push_back(clone_column(w - 1 , filler));
00353 }
00354 w_ += count;
00355 total_width_ += count;
00356 }
00357
00358 void editor_map::expand_left(int count, t_translation::t_terrain filler)
00359 {
00360 for (int x = 0; x < count; ++x) {
00361 tiles_.insert(tiles_.begin(), 1, clone_column(0, filler));
00362 clear_border_cache();
00363 }
00364 w_ += count;
00365 total_width_ += count;
00366 }
00367
00368 void editor_map::expand_top(int count, t_translation::t_terrain filler)
00369 {
00370 for (int y = 0; y < count; ++y) {
00371 for (int x = 0; x < static_cast<int>(tiles_.size()); ++x) {
00372 t_translation::t_terrain terrain =
00373 filler != t_translation::NONE_TERRAIN ?
00374 filler :
00375 tiles_[x][0];
00376 assert(terrain != t_translation::NONE_TERRAIN);
00377 tiles_[x].insert(tiles_[x].begin(), 1, terrain);
00378 clear_border_cache();
00379 }
00380 }
00381 h_ += count;
00382 total_height_ += count;
00383 }
00384
00385 void editor_map::expand_bottom(int count, t_translation::t_terrain filler)
00386 {
00387 int h = tiles_[1].size();
00388 for (int y = 0; y < count; ++y) {
00389 for (int x = 0; x < static_cast<int>(tiles_.size()); ++x) {
00390 t_translation::t_terrain terrain =
00391 filler != t_translation::NONE_TERRAIN ?
00392 filler :
00393 tiles_[x][h - 1];
00394 assert(terrain != t_translation::NONE_TERRAIN);
00395 tiles_[x].push_back(terrain);
00396 }
00397 }
00398 h_ += count;
00399 total_height_ += count;
00400 }
00401
00402 void editor_map::shrink_right(int count)
00403 {
00404 if(count < 0 || count > static_cast<int>(tiles_.size())) {
00405 throw editor_map_operation_exception();
00406 }
00407 tiles_.resize(tiles_.size() - count);
00408 w_ -= count;
00409 total_width_ -= count;
00410 }
00411
00412 void editor_map::shrink_left(int count)
00413 {
00414 if(count < 0 || count > static_cast<int>(tiles_.size())) {
00415 throw editor_map_operation_exception();
00416 }
00417 tiles_.erase(tiles_.begin(), tiles_.begin() + count);
00418 w_ -= count;
00419 total_width_ -= count;
00420 }
00421
00422 void editor_map::shrink_top(int count)
00423 {
00424 if(count < 0 || count > static_cast<int>(tiles_[0].size())) {
00425 throw editor_map_operation_exception();
00426 }
00427 for (size_t x = 0; x < tiles_.size(); ++x) {
00428 tiles_[x].erase(tiles_[x].begin(), tiles_[x].begin() + count);
00429 }
00430 h_ -= count;
00431 total_height_ -= count;
00432 }
00433
00434 void editor_map::shrink_bottom(int count)
00435 {
00436 if(count < 0 || count > static_cast<int>(tiles_[0].size())) {
00437 throw editor_map_operation_exception();
00438 }
00439 for (size_t x = 0; x < tiles_.size(); ++x) {
00440 tiles_[x].erase(tiles_[x].end() - count, tiles_[x].end());
00441 }
00442 h_ -= count;
00443 total_height_ -= count;
00444 }
00445
00446 void editor_map::write(config& cfg) const {
00447 config& map = cfg.add_child("map");
00448 gamemap::write(map);
00449 labels_.write(cfg);
00450 }
00451
00452 }