builder.cpp

Go to the documentation of this file.
00001 /* $Id: builder.cpp 26213 2008-04-28 16:54:29Z mog $ */
00002 /*
00003    Copyright (C) 2004 - 2008 by Philippe Plantier <ayin@anathas.org>
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 builder.cpp
00016 //! Terrain builder.
00017 
00018 #include "global.hpp"
00019 
00020 #include "array.hpp"
00021 #include "builder.hpp"
00022 #include "config.hpp"
00023 #include "log.hpp"
00024 #include "pathutils.hpp"
00025 #include "terrain.hpp"
00026 #include "util.hpp"
00027 #include "serialization/string_utils.hpp"
00028 
00029 #include <cassert>
00030 #include <climits>
00031 
00032 #define ERR_NG LOG_STREAM(err, engine)
00033 #define DEBUG_NG LOG_STREAM(info, engine)
00034 
00035 /** The tile width used when using basex and basey. This is not,
00036  * necessarily, the tile width in pixels, this is totally
00037  * arbitrary. However, it will be set to 72 for convenience.
00038  */
00039 static const int TILEWIDTH = 72;
00040 /** The position of unit graphics in a tile. Graphics whose y
00041  * position is below this value are considered background for
00042  * this tile; graphics whose y position is above this value are
00043  * considered foreground.
00044  */
00045 static const int UNITPOS = 36 + 18;
00046 /** The allowed interval for the base-y position. The possible values are from
00047  * -BASE_Y_INTERVAL to BASE_Y_INTERVAL-1
00048  */
00049 static const int BASE_Y_INTERVAL = 100000;
00050 
00051 terrain_builder::rule_image::rule_image(int layer, int x, int y, bool global_image, int cx, int cy) :
00052     layer(layer),
00053     basex(x),
00054     basey(y),
00055     variants(),
00056     global_image(global_image),
00057     center_x(cx),
00058     center_y(cy)
00059 {}
00060 
00061 terrain_builder::tile::tile() :
00062     flags(),
00063     images(),
00064     images_foreground(),
00065     images_background(),
00066     last_tod("invalid_tod")
00067 {}
00068 
00069 void terrain_builder::tile::add_image_to_cache(const std::string &tod, ordered_ri_list::const_iterator itor)
00070 {
00071     rule_image_variantlist::const_iterator tod_variant =
00072         itor->second->variants.find(tod);
00073 
00074     if(tod_variant == itor->second->variants.end())
00075         tod_variant = itor->second->variants.find("");
00076 
00077     if(tod_variant != itor->second->variants.end()) {
00078         //calculate original y-value and layer from list index
00079         int layer = itor->first / BASE_Y_INTERVAL;
00080         int basey = itor->first % BASE_Y_INTERVAL;
00081 
00082         if (basey < 0)
00083             basey += BASE_Y_INTERVAL/2;
00084         else
00085             basey -= BASE_Y_INTERVAL/2;
00086 
00087         if(layer < 0 || (layer == 0 && basey < UNITPOS)) {
00088             images_background.push_back(tod_variant->second.image);
00089         } else {
00090             images_foreground.push_back(tod_variant->second.image);
00091         }
00092     }
00093 }
00094 
00095 void terrain_builder::tile::rebuild_cache(const std::string &tod)
00096 {
00097     images_background.clear();
00098     images_foreground.clear();
00099 
00100     ordered_ri_list::const_iterator itor;
00101     for(itor = images.begin(); itor != images.end(); ++itor) {
00102         add_image_to_cache(tod, itor);
00103     }
00104 }
00105 
00106 void terrain_builder::tile::clear()
00107 {
00108     flags.clear();
00109     images.clear();
00110     images_foreground.clear();
00111     images_background.clear();
00112     last_tod = "invalid_tod";
00113 }
00114 
00115 void terrain_builder::tilemap::reset()
00116 {
00117     for(std::vector<tile>::iterator it = map_.begin(); it != map_.end(); ++it)
00118         it->clear();
00119 }
00120 
00121 bool terrain_builder::tilemap::on_map(const gamemap::location &loc) const
00122 {
00123     if(loc.x < -2 || loc.y < -2 || loc.x > (x_ + 1) || loc.y > (y_ + 1)) {
00124         return false;
00125     }
00126 
00127     return true;
00128     
00129 }
00130 
00131 terrain_builder::tile& terrain_builder::tilemap::operator[](const gamemap::location &loc)
00132 {
00133     assert(on_map(loc));
00134 
00135     return map_[(loc.x + 2) + (loc.y + 2) * (x_ + 4)];
00136 }
00137 
00138 const terrain_builder::tile& terrain_builder::tilemap::operator[] (const gamemap::location &loc) const
00139 {
00140     assert(on_map(loc));
00141 
00142     return map_[(loc.x + 2) + (loc.y + 2) * (x_ + 4)];
00143 }
00144 
00145 terrain_builder::terrain_builder(const config& cfg, const config& level,
00146         const gamemap& map, const std::string& offmap_image) :
00147     map_(map), 
00148     tile_map_(map.w(), map.h()),
00149     terrain_by_type_(),
00150     building_rules_()
00151 {
00152     // Make sure there's nothing left in the cache,
00153     // since it might give problems (or not?)
00154     //image::flush_cache();
00155 
00156     parse_config(cfg);
00157     parse_config(level);
00158     add_off_map_rule(offmap_image);
00159 
00160     build_terrains();
00161 }
00162 
00163 const terrain_builder::imagelist *terrain_builder::get_terrain_at(const gamemap::location &loc,
00164         const std::string &tod, const ADJACENT_TERRAIN_TYPE terrain_type)
00165 {
00166     if(!tile_map_.on_map(loc))
00167         return NULL;
00168 
00169     tile& tile_at = tile_map_[loc];
00170 
00171     if(tod != tile_at.last_tod) {
00172         tile_at.rebuild_cache(tod);
00173         tile_at.last_tod = tod;
00174     }
00175 
00176     if(terrain_type == ADJACENT_BACKGROUND) {
00177         if(!tile_at.images_background.empty())
00178             return &tile_at.images_background;
00179     }
00180 
00181     if(terrain_type == ADJACENT_FOREGROUND) {
00182         if(!tile_at.images_foreground.empty())
00183             return &tile_at.images_foreground;
00184     }
00185 
00186     return NULL;
00187 }
00188 
00189 bool terrain_builder::update_animation(const gamemap::location &loc)
00190 {
00191     if(!tile_map_.on_map(loc))
00192         return false;
00193 
00194     imagelist& bg = tile_map_[loc].images_background;
00195     imagelist& fg = tile_map_[loc].images_foreground;
00196     bool changed = false;
00197 
00198     imagelist::iterator itor = bg.begin();
00199     for(; itor != bg.end(); ++itor) {
00200         if(itor->need_update())
00201             changed = true;
00202         itor->update_last_draw_time();
00203     }
00204 
00205     itor = fg.begin();
00206     for(; itor != fg.end(); ++itor) {
00207         if(itor->need_update())
00208             changed = true;
00209         itor->update_last_draw_time();
00210     }
00211 
00212     return changed;
00213 }
00214 
00215 //! @todo TODO: rename this function
00216 void terrain_builder::rebuild_terrain(const gamemap::location &loc)
00217 {
00218     if (tile_map_.on_map(loc)) {
00219         tile& btile = tile_map_[loc];
00220         // btile.images.clear();
00221         btile.images_foreground.clear();
00222         btile.images_background.clear();
00223         const std::string filename =
00224             map_.get_terrain_info(map_.get_terrain(loc)).minimap_image();
00225         animated<image::locator> img_loc;
00226         img_loc.add_frame(100,image::locator("terrain/" + filename + ".png"));
00227         img_loc.start_animation(0, true);
00228         btile.images_background.push_back(img_loc);
00229         
00230         //Combine base and overlay image if neccessary
00231         if(map_.get_terrain_info(map_.get_terrain(loc)).is_combined()) {
00232             const std::string filename_ovl =
00233                 map_.get_terrain_info(map_.get_terrain(loc)).minimap_image_overlay();
00234             animated<image::locator> img_loc_ovl;
00235             img_loc_ovl.add_frame(100,image::locator("terrain/" + filename_ovl + ".png"));
00236             img_loc_ovl.start_animation(0, true);
00237             btile.images_background.push_back(img_loc_ovl);
00238         }
00239     }
00240 }
00241 
00242 void terrain_builder::rebuild_all()
00243 {
00244     tile_map_.reset();
00245     terrain_by_type_.clear();
00246     build_terrains();
00247 }
00248 
00249 bool terrain_builder::rule_valid(const building_rule &rule) const
00250 {
00251     // If the rule has no constraints, it is invalid
00252     if(rule.constraints.empty())
00253         return false;
00254 
00255     // Checks if all the images referenced by the current rule are valid.
00256     // If not, this rule will not match.
00257     rule_imagelist::const_iterator image;
00258     constraint_set::const_iterator constraint;
00259     rule_image_variantlist::const_iterator variant;
00260 
00261     for(constraint = rule.constraints.begin();
00262             constraint != rule.constraints.end(); ++constraint) {
00263         for(image = constraint->second.images.begin();
00264                 image != constraint->second.images.end();
00265                 ++image) {
00266 
00267             for(variant = image->variants.begin(); variant != image->variants.end(); ++variant) {
00268                 std::string s = variant->second.image_string;
00269                 s = s.substr(0, s.find_first_of(",:"));
00270 
00271                 if(!image::exists("terrain/" + s + ".png"))
00272                     return false;
00273             }
00274         }
00275     }
00276 
00277     return true;
00278 }
00279 
00280 bool terrain_builder::start_animation(building_rule &rule)
00281 {
00282     rule_imagelist::iterator image;
00283     constraint_set::iterator constraint;
00284     rule_image_variantlist::iterator variant;
00285 
00286     for(constraint = rule.constraints.begin();
00287             constraint != rule.constraints.end(); ++constraint) {
00288 
00289         for(image = constraint->second.images.begin();
00290                 image != constraint->second.images.end();
00291                 ++image) {
00292 
00293             for(variant = image->variants.begin(); variant != image->variants.end(); ++variant) {
00294 
00295                 animated<image::locator>::anim_description image_vector;
00296                 std::vector<std::string> items = utils::split(variant->second.image_string);
00297                 std::vector<std::string>::const_iterator itor = items.begin();
00298                 for(; itor != items.end(); ++itor) {
00299                     const std::vector<std::string>& items = utils::split(*itor, ':');
00300                     std::string str;
00301                     int time;
00302 
00303                     if(items.size() > 1) {
00304                         str = items.front();
00305                         time = atoi(items.back().c_str());
00306                     } else {
00307                         str = *itor;
00308                         time = 100;
00309                     }
00310                     if(image->global_image) {
00311                         image_vector.push_back(animated<image::locator>::frame_description(time,image::locator("terrain/" + str + ".png",constraint->second.loc, image->center_x, image->center_y)));
00312                     } else {
00313                         image_vector.push_back(animated<image::locator>::frame_description(time,image::locator("terrain/" + str + ".png")));
00314                     }
00315 
00316                 }
00317 
00318                 animated<image::locator> th(image_vector);
00319 
00320                 variant->second.image = th;
00321                 variant->second.image.start_animation(0, true);
00322             }
00323         }
00324     }
00325 
00326     return true;
00327 }
00328 
00329 terrain_builder::terrain_constraint terrain_builder::rotate(const terrain_builder::terrain_constraint &constraint, int angle)
00330 {
00331     static const struct { int ii; int ij; int ji; int jj; }  rotations[6] =
00332         { {  1, 0, 0,  1 }, {  1,  1, -1, 0 }, { 0,  1, -1, -1 },
00333           { -1, 0, 0, -1 }, { -1, -1,  1, 0 }, { 0, -1,  1,  1 } };
00334 
00335     // The following array of matrices is intended to rotate the (x,y)
00336     // coordinates of a point in a wesnoth hex (and wesnoth hexes are not
00337     // regular hexes :) ).
00338     // The base matrix for a 1-step rotation with the wesnoth tile shape
00339     // is:
00340     //
00341     // r = s^-1 * t * s
00342     //
00343     // with s = [[ 1   0         ]
00344     //           [ 0   -sqrt(3)/2 ]]
00345     //
00346     // and t =  [[ -1/2       sqrt(3)/2 ]
00347     //           [ -sqrt(3)/2  1/2        ]]
00348     //
00349     // With t being the rotation matrix (pi/3 rotation), and s a matrix
00350     // that transforms the coordinates of the wesnoth hex to make them
00351     // those of a regular hex.
00352     //
00353     // (demonstration left as an exercise for the reader)
00354     //
00355     // So we have
00356     //
00357     // r = [[ 1/2  -3/4 ]
00358     //      [ 1    1/2  ]]
00359     //
00360     // And the following array contains I(2), r, r^2, r^3, r^4, r^5
00361     // (with r^3 == -I(2)), which are the successive rotations.
00362     static const struct {
00363         double xx;
00364         double xy;
00365         double yx;
00366         double yy;
00367     } xyrotations[6] = {
00368         { 1.,         0.,  0., 1.    },
00369         { 1./2. , -3./4.,  1., 1./2. },
00370         { -1./2., -3./4.,   1, -1./2.},
00371         { -1.   ,     0.,  0., -1.   },
00372         { -1./2.,  3./4., -1., -1./2.},
00373         { 1./2. ,  3./4., -1., 1./2. },
00374     };
00375 
00376     assert(angle >= 0);
00377 
00378     angle %= 6;
00379     terrain_constraint ret = constraint;
00380 
00381     // Vector i is going from n to s, vector j is going from ne to sw.
00382     int vi = ret.loc.y - ret.loc.x/2;
00383     int vj = ret.loc.x;
00384 
00385     int ri = rotations[angle].ii * vi + rotations[angle].ij * vj;
00386     int rj = rotations[angle].ji * vi + rotations[angle].jj * vj;
00387 
00388     ret.loc.x = rj;
00389     ret.loc.y = ri + (rj >= 0 ? rj/2 : (rj-1)/2);
00390 
00391     for (rule_imagelist::iterator itor = ret.images.begin();
00392             itor != ret.images.end(); ++itor) {
00393 
00394         double vx, vy, rx, ry;
00395 
00396         vx = double(itor->basex) - double(TILEWIDTH)/2;
00397         vy = double(itor->basey) - double(TILEWIDTH)/2;
00398 
00399         rx = xyrotations[angle].xx * vx + xyrotations[angle].xy * vy;
00400         ry = xyrotations[angle].yx * vx + xyrotations[angle].yy * vy;
00401 
00402         itor->basex = int(rx + TILEWIDTH/2);
00403         itor->basey = int(ry + TILEWIDTH/2);
00404 
00405         //std::cerr << "Rotation: from " << vx << ", " << vy << " to " << itor->basex <<
00406         //  ", " << itor->basey << "\n";
00407     }
00408 
00409     return ret;
00410 }
00411 
00412 void terrain_builder::replace_token(std::string &s, const std::string &token, const std::string &replacement)
00413 {
00414     size_t pos;
00415 
00416     if(token.empty()) {
00417         ERR_NG << "empty token in replace_token\n";
00418         return;
00419     }
00420     while((pos = s.find(token)) != std::string::npos) {
00421         s.replace(pos, token.size(), replacement);
00422     }
00423 }
00424 
00425 void terrain_builder::replace_token(terrain_builder::rule_image &image, const std::string &token, const std::string &replacement)
00426 {
00427     rule_image_variantlist::iterator itor;
00428 
00429     for(itor = image.variants.begin(); itor != image.variants.end(); ++itor) {
00430         replace_token(itor->second, token, replacement);
00431     }
00432 }
00433 
00434 void terrain_builder::replace_token(terrain_builder::rule_imagelist &list, const std::string &token, const std::string &replacement)
00435 {
00436     rule_imagelist::iterator itor;
00437 
00438     for(itor = list.begin(); itor != list.end(); ++itor) {
00439         replace_token(*itor, token, replacement);
00440     }
00441 }
00442 
00443 void terrain_builder::replace_token(terrain_builder::building_rule &rule, const std::string &token, const std::string& replacement)
00444 {
00445     constraint_set::iterator cons;
00446 
00447     for(cons = rule.constraints.begin(); cons != rule.constraints.end(); ++cons) {
00448         // Transforms attributes
00449         std::vector<std::string>::iterator flag;
00450 
00451         for(flag = cons->second.set_flag.begin(); flag != cons->second.set_flag.end(); flag++) {
00452             replace_token(*flag, token, replacement);
00453         }
00454         for(flag = cons->second.no_flag.begin(); flag != cons->second.no_flag.end(); flag++) {
00455             replace_token(*flag, token, replacement);
00456         }
00457         for(flag = cons->second.has_flag.begin(); flag != cons->second.has_flag.end(); flag++) {
00458             replace_token(*flag, token, replacement);
00459         }
00460         replace_token(cons->second.images, token, replacement);
00461     }
00462 
00463     //replace_token(rule.images, token, replacement);
00464 }
00465 
00466 terrain_builder::building_rule terrain_builder::rotate_rule(const terrain_builder::building_rule &rule,
00467     int angle, const std::vector<std::string>& rot)
00468 {
00469     building_rule ret;
00470     if(rot.size() != 6) {
00471         ERR_NG << "invalid rotations\n";
00472         return ret;
00473     }
00474     ret.location_constraints = rule.location_constraints;
00475     ret.probability = rule.probability;
00476     ret.precedence = rule.precedence;
00477 
00478     constraint_set tmp_cons;
00479     constraint_set::const_iterator cons;
00480     for(cons = rule.constraints.begin(); cons != rule.constraints.end(); ++cons) {
00481         const terrain_constraint &rcons = rotate(cons->second, angle);
00482 
00483         tmp_cons[rcons.loc] = rcons;
00484     }
00485 
00486     // Normalize the rotation, so that it starts on a positive location
00487     int minx = INT_MAX;
00488     int miny = INT_MAX;
00489 
00490     constraint_set::iterator cons2;
00491     for(cons2 = tmp_cons.begin(); cons2 != tmp_cons.end(); ++cons2) {
00492         minx = minimum<int>(cons2->second.loc.x, minx);
00493         miny = minimum<int>(2*cons2->second.loc.y + (cons2->second.loc.x & 1), miny);
00494     }
00495 
00496     if((miny & 1) && (minx & 1) && (minx < 0))
00497         miny += 2;
00498     if(!(miny & 1) && (minx & 1) && (minx > 0))
00499         miny -= 2;
00500 
00501     for(cons2 = tmp_cons.begin(); cons2 != tmp_cons.end(); ++cons2) {
00502         // Adjusts positions
00503         cons2->second.loc += gamemap::location(-minx, -((miny-1)/2));
00504         ret.constraints[cons2->second.loc] = cons2->second;
00505     }
00506 
00507     for(int i = 0; i < 6; ++i) {
00508         int a = (angle+i) % 6;
00509         std::string token = "@R";
00510         push_back(token,'0' + i);
00511         replace_token(ret, token, rot[a]);
00512     }
00513 
00514     return ret;
00515 }
00516 
00517 void terrain_builder::add_images_from_config(rule_imagelist& images, const config &cfg, bool global, int dx, int dy)
00518 {
00519     const config::child_list& cimages = cfg.get_children("image");
00520 
00521 
00522     for(config::child_list::const_iterator img = cimages.begin(); img != cimages.end(); ++img) {
00523 
00524         const std::string &name = (**img)["name"];
00525         const int layer = lexical_cast_default<int>((**img)["layer"], 0);
00526 
00527         int basex = 0, basey = 0;
00528         if((**img)["base"].empty()) {
00529             basex = TILEWIDTH / 2 + dx;
00530             basey = TILEWIDTH / 2 + dy;
00531         } else {
00532             std::vector<std::string> base = utils::split((**img)["base"]);
00533 
00534             if(base.size() >= 2) {
00535                 basex = atoi(base[0].c_str());
00536                 basey = atoi(base[1].c_str());
00537             }
00538         }
00539 
00540         int center_x = -1, center_y = -1;
00541         if( !(**img)["center"].empty()) {
00542             std::vector<std::string> center = utils::split((**img)["center"]);
00543 
00544             if(center.size() >= 2) {
00545                 center_x = atoi(center[0].c_str());
00546                 center_y = atoi(center[1].c_str());
00547             }
00548         }
00549 
00550         images.push_back(rule_image(layer, basex - dx, basey - dy, global, center_x, center_y));
00551 
00552         // Adds the main (default) variant of the image, if present
00553         images.back().variants.insert(std::pair<std::string, rule_image_variant>("", rule_image_variant(name,"")));
00554 
00555         // Adds the other variants of the image
00556         const config::child_list& variants = (**img).get_children("variant");
00557 
00558         for(config::child_list::const_iterator variant = variants.begin();
00559                 variant != variants.end(); ++variant) {
00560             const std::string &name = (**variant)["name"];
00561             const std::string &tod = (**variant)["tod"];
00562 
00563             images.back().variants.insert(std::pair<std::string, rule_image_variant>(tod, rule_image_variant(name,tod)));
00564 
00565         }
00566     }
00567 }
00568 
00569 void terrain_builder::add_constraints(
00570         terrain_builder::constraint_set& constraints,
00571         const gamemap::location& loc,
00572         const t_translation::t_match& type, const config& global_images)
00573 {
00574     if(constraints.find(loc) == constraints.end()) {
00575         // The terrain at the current location did not exist, so create it
00576         constraints[loc] = terrain_constraint(loc);
00577     }
00578 
00579     if(!type.terrain.empty()) {
00580         constraints[loc].terrain_types_match = type;
00581     }
00582 
00583     int x = loc.x * TILEWIDTH * 3 / 4;
00584     int y = loc.y * TILEWIDTH + (loc.x % 2) * TILEWIDTH / 2;
00585     add_images_from_config(constraints[loc].images, global_images, true, x, y);
00586 }
00587 
00588 void terrain_builder::add_constraints(terrain_builder::constraint_set &constraints,
00589         const gamemap::location& loc, const config& cfg, const config& global_images)
00590 
00591 {
00592     add_constraints(constraints, loc, t_translation::t_match(cfg["type"], t_translation::WILDCARD), global_images);
00593 
00594     terrain_constraint& constraint = constraints[loc];
00595 
00596     std::vector<std::string> item_string = utils::split(cfg["set_flag"]);
00597     constraint.set_flag.insert(constraint.set_flag.end(),
00598             item_string.begin(), item_string.end());
00599 
00600     item_string = utils::split(cfg["has_flag"]);
00601     constraint.has_flag.insert(constraint.has_flag.end(),
00602             item_string.begin(), item_string.end());
00603 
00604     item_string = utils::split(cfg["no_flag"]);
00605     constraint.no_flag.insert(constraint.no_flag.end(),
00606             item_string.begin(), item_string.end());
00607 
00608     add_images_from_config(constraint.images, cfg, false);
00609 }
00610 
00611 void terrain_builder::parse_mapstring(const std::string &mapstring,
00612         struct building_rule &br, anchormap& anchors,
00613         const config& global_images)
00614 {
00615 
00616     const t_translation::t_map map = t_translation::read_builder_map(mapstring);
00617 
00618     // If there is an empty map leave directly.
00619     // Determine after conversion, since a
00620     // non-empty string can return an empty map.
00621     if(map.empty()) {
00622         return;
00623     }
00624 
00625     int lineno = (map[0][0] == t_translation::NONE_TERRAIN) ? 1 : 0;
00626     int x = lineno;
00627     int y = 0;
00628     for(size_t y_off = 0; y_off < map.size(); ++y_off) {
00629         for(size_t x_off = x; x_off < map[y_off].size(); ++x_off) {
00630 
00631             const t_translation::t_terrain terrain = map[y_off][x_off];
00632 
00633             if(terrain.base == t_translation::TB_DOT) {
00634                 // Dots are simple placeholders,
00635                 // which do not represent actual terrains.
00636             } else if (terrain.overlay != 0 ) {
00637                 anchors.insert(std::pair<int, gamemap::location>(terrain.overlay, gamemap::location(x, y)));
00638             } else if (terrain.base == t_translation::TB_STAR) {
00639                 add_constraints(br.constraints, gamemap::location(x, y), t_translation::STAR, global_images);
00640             } else {
00641                     ERR_NG << "Invalid terrain (" << t_translation::write_terrain_code(terrain) << ") in builder map\n";
00642                     assert(false); 
00643                     return;
00644             }
00645         x += 2;
00646         }
00647 
00648         if(lineno % 2 == 1) {
00649             ++y;
00650             x = 0;
00651         } else {
00652             x = 1;
00653         }
00654         ++lineno;
00655     }
00656 }
00657 
00658 void terrain_builder::add_rule(building_ruleset& rules, building_rule &rule)
00659 {
00660     if(rule_valid(rule)) {
00661         start_animation(rule);
00662         rules.insert(std::pair<int, building_rule>(rule.precedence, rule));
00663     }
00664 }
00665 
00666 void terrain_builder::add_rotated_rules(building_ruleset& rules, building_rule& tpl, const std::string &rotations)
00667 {
00668     if(rotations.empty()) {
00669         // Adds the parsed built terrain to the list
00670 
00671         add_rule(rules, tpl);
00672     } else {
00673         const std::vector<std::string>& rot = utils::split(rotations, ',');
00674 
00675         for(size_t angle = 0; angle < rot.size(); angle++) {
00676             building_rule rule = rotate_rule(tpl, angle, rot);
00677             add_rule(rules, rule);
00678         }
00679     }
00680 }
00681 
00682 void terrain_builder::parse_config(const config &cfg)
00683 {
00684     log_scope("terrain_builder::parse_config");
00685 
00686     // Parses the list of building rules (BRs)
00687     const config::child_list& brs = cfg.get_children("terrain_graphics");
00688 
00689     for(config::child_list::const_iterator br = brs.begin(); br != brs.end(); ++br) {
00690         building_rule pbr; // Parsed Building rule
00691 
00692         // add_images_from_config(pbr.images, **br);
00693 
00694         if(!((**br)["x"].empty() || (**br)["y"].empty()))
00695             pbr.location_constraints = gamemap::location(atoi((**br)["x"].c_str())-1, atoi((**br)["y"].c_str())-1);
00696 
00697         pbr.probability = (**br)["probability"].empty() ? -1 : atoi((**br)["probability"].c_str());
00698         pbr.precedence = (**br)["precedence"].empty() ? 0 : atoi((**br)["precedence"].c_str());
00699 
00700         // Mapping anchor indices to anchor locations.
00701         anchormap anchors;
00702 
00703         // Parse the map= , if there is one (and fill the anchors list)
00704         parse_mapstring((**br)["map"], pbr, anchors, **br);
00705 
00706         // Parses the terrain constraints (TCs)
00707         config::child_list tcs((*br)->get_children("tile"));
00708 
00709         for(config::child_list::const_iterator tc = tcs.begin(); tc != tcs.end(); tc++) {
00710             // Adds the terrain constraint to the current built terrain's list
00711             // of terrain constraints, if it does not exist.
00712             gamemap::location loc;
00713             if((**tc)["x"].size()) {
00714                 loc.x = atoi((**tc)["x"].c_str());
00715             }
00716             if((**tc)["y"].size()) {
00717                 loc.y = atoi((**tc)["y"].c_str());
00718             }
00719             if(!(**tc)["loc"].empty()) {
00720                 std::vector<std::string> sloc = utils::split((**tc)["loc"]);
00721                 if(sloc.size() == 2) {
00722                     loc.x = atoi(sloc[0].c_str());
00723                     loc.y = atoi(sloc[1].c_str());
00724                 }
00725             }
00726             if(loc.valid()) {
00727                 add_constraints(pbr.constraints, loc, **tc, **br);
00728             }
00729             if((**tc)["pos"].size()) {
00730                 int pos = atoi((**tc)["pos"].c_str());
00731                 if(anchors.find(pos) == anchors.end()) {
00732                     LOG_STREAM(warn, engine) << "Invalid anchor!\n";
00733                     continue;
00734                 }
00735 
00736                 std::pair<anchormap::const_iterator, anchormap::const_iterator> range =
00737                     anchors.equal_range(pos);
00738 
00739                 for(; range.first != range.second; range.first++) {
00740                     loc = range.first->second;
00741                     add_constraints(pbr.constraints, loc, **tc, **br);
00742                 }
00743             }
00744         }
00745 
00746         const std::string global_set_flag = (**br)["set_flag"];
00747         const std::string global_no_flag = (**br)["no_flag"];
00748         const std::string global_has_flag = (**br)["has_flag"];
00749 
00750         for(constraint_set::iterator constraint = pbr.constraints.begin(); constraint != pbr.constraints.end();
00751             constraint++) {
00752 
00753             if(global_set_flag.size())
00754                 constraint->second.set_flag.push_back(global_set_flag);
00755 
00756             if(global_no_flag.size())
00757                 constraint->second.no_flag.push_back(global_no_flag);
00758 
00759             if(global_has_flag.size())
00760                 constraint->second.has_flag.push_back(global_has_flag);
00761 
00762         }
00763 
00764         // Handles rotations
00765         const std::string rotations = (**br)["rotations"];
00766 
00767         add_rotated_rules(building_rules_, pbr, rotations);
00768 
00769     }
00770 
00771 // Debug output for the terrain rules
00772 #if 0
00773     std::cerr << "Built terrain rules: \n";
00774 
00775     building_ruleset::const_iterator rule;
00776     for(rule = building_rules_.begin(); rule != building_rules_.end(); ++rule) {
00777         std::cerr << ">> New rule: image_background = " 
00778             << "\n>> Location " << rule->second.location_constraints
00779             << "\n>> Probability " << rule->second.probability 
00780             << "\n>> Precedence " << rule->second.precedence << '\n';
00781 
00782         for(constraint_set::const_iterator constraint = rule->second.constraints.begin();
00783             constraint != rule->second.constraints.end(); ++constraint) {
00784 
00785             std::cerr << ">>>> New constraint: location = (" << constraint->second.loc
00786                       << "), terrain types = '" << t_translation::write_list(constraint->second.terrain_types_match.terrain) << "'\n";
00787 
00788             std::vector<std::string>::const_iterator flag;
00789 
00790             for(flag  = constraint->second.set_flag.begin(); flag != constraint->second.set_flag.end(); ++flag) {
00791                 std::cerr << ">>>>>> Set_flag: " << *flag << "\n";
00792             }
00793 
00794             for(flag = constraint->second.no_flag.begin(); flag != constraint->second.no_flag.end(); ++flag) {
00795                 std::cerr << ">>>>>> No_flag: " << *flag << "\n";
00796             }
00797         }
00798 
00799     }
00800 #endif
00801 
00802 }
00803 
00804 void terrain_builder::add_off_map_rule(const std::string& image)
00805 {
00806     // Build a config object
00807     config cfg;
00808 
00809     cfg.add_child("terrain_graphics");
00810     config *item = cfg.child("terrain_graphics");
00811 
00812     (*item).add_child("tile");
00813     config *tile = (*item).child("tile");
00814     (*tile)["x"] = "0";
00815     (*tile)["y"] = "0";
00816     (*tile)["type"] = t_translation::write_terrain_code(t_translation::OFF_MAP_USER);
00817 
00818     (*tile).add_child("image");
00819     config *tile_image = (*tile).child("image");
00820     (*tile_image)["layer"] = "-1000";
00821     (*tile_image)["name"] = image;
00822 
00823     (*item)["probability"] = "100";
00824     (*item)["no_flag"] = "base";
00825     (*item)["set_flag"] = "base";
00826 
00827     // Parse the object
00828     parse_config(cfg);
00829 }
00830 
00831 bool terrain_builder::rule_matches(const terrain_builder::building_rule &rule,
00832         const gamemap::location &loc, const int rule_index, const constraint_set::const_iterator type_checked) const
00833 {
00834     if(rule.location_constraints.valid() && rule.location_constraints != loc) {
00835         return false;
00836     }
00837 
00838     if(rule.probability != -1) {
00839         unsigned int a = (loc.x + 92872973) ^ 918273;
00840         unsigned int b = (loc.y + 1672517) ^ 128123;
00841         unsigned int c = (rule_index + 127390) ^ 13923787;
00842         unsigned int abc = a*b*c + a*b + b*c + a*c + a + b + c;
00843         unsigned int random = (abc*abc) % 100;
00844 
00845         if(random > static_cast<unsigned int>(rule.probability)) {
00846             return false;
00847         }
00848     }
00849 
00850     for(constraint_set::const_iterator cons = rule.constraints.begin();
00851             cons != rule.constraints.end(); ++cons) {
00852 
00853         // Translated location
00854         const gamemap::location tloc = loc + cons->second.loc;
00855 
00856         if(!tile_map_.on_map(tloc)) {
00857             return false;
00858         }
00859 
00860         //std::cout << "testing..." << builder_letter(map_.get_terrain(tloc))
00861 
00862         // check if terrain matches except if we already know that it does
00863         if(cons != type_checked &&
00864                 !terrain_matches(map_.get_terrain(tloc), cons->second.terrain_types_match)) {
00865             return false;
00866         }
00867 
00868         const tile& btile = tile_map_[tloc];
00869 
00870         std::vector<std::string>::const_iterator itor;
00871         for(itor = cons->second.no_flag.begin(); itor != cons->second.no_flag.end(); ++itor) {
00872 
00873             // If a flag listed in "no_flag" is present, the rule does not match
00874             if(btile.flags.find(*itor) != btile.flags.end()) {
00875                 return false;
00876             }
00877         }
00878         for(itor = cons->second.has_flag.begin(); itor != cons->second.has_flag.end(); ++itor) {
00879 
00880             // If a flag listed in "has_flag" is not present, this rule does not match
00881             if(btile.flags.find(*itor) == btile.flags.end()) {
00882                 return false;
00883             }
00884         }
00885     }
00886 
00887     return true;
00888 }
00889 
00890 void terrain_builder::apply_rule(const terrain_builder::building_rule &rule, const gamemap::location &loc)
00891 {
00892     for(constraint_set::const_iterator constraint = rule.constraints.begin();
00893             constraint != rule.constraints.end(); ++constraint) {
00894 
00895         rule_imagelist::const_iterator img;
00896         const gamemap::location tloc = loc + constraint->second.loc;
00897         if(!tile_map_.on_map(tloc)) {
00898             return;
00899         }
00900 
00901         tile& btile = tile_map_[tloc];
00902 
00903         // We want to order the images by layer first and base-y second,
00904         // so we sort by layer*BASE_Y_INTERVAL + BASE_Y_INTERVAL/2 + basey
00905         // Thus, allowed values for basey are from -50000 to 49999
00906         for(img = constraint->second.images.begin(); img != constraint->second.images.end(); ++img) {
00907             btile.images.insert(std::pair<int, const rule_image*>(
00908                                     img->layer*BASE_Y_INTERVAL + BASE_Y_INTERVAL/2 + img->basey, &*img));
00909         }
00910 
00911         // Sets flags
00912         for(std::vector<std::string>::const_iterator itor = constraint->second.set_flag.begin();
00913                 itor != constraint->second.set_flag.end(); itor++) {
00914             btile.flags.insert(*itor);
00915         }
00916 
00917     }
00918 }
00919 
00920 void terrain_builder::build_terrains()
00921 {
00922     log_scope("terrain_builder::build_terrains");
00923 
00924     // Builds the terrain_by_type_ cache
00925     for(int x = -2; x <= map_.w(); ++x) {
00926         for(int y = -2; y <= map_.h(); ++y) {
00927             const gamemap::location loc(x,y);
00928             const t_translation::t_terrain t = map_.get_terrain(loc);
00929 
00930             terrain_by_type_[t].push_back(loc);
00931         }
00932     }
00933 
00934     int rule_index = 0;
00935     building_ruleset::const_iterator r;
00936 
00937     for(r = building_rules_.begin(); r != building_rules_.end(); ++r) {
00938 
00939         const building_rule& rule = r->second;
00940 
00941         // Find the constraint that contains the less terrain of all terrain rules.
00942         // We will keep a track of the matching terrains of this constraint
00943         // and later try to apply the rule only on them
00944         size_t min_size = INT_MAX;
00945         t_translation::t_list min_types;
00946         constraint_set::const_iterator min_constraint = rule.constraints.end();
00947 
00948         for(constraint_set::const_iterator constraint = rule.constraints.begin();
00949                 constraint != rule.constraints.end(); ++constraint) {
00950 
00951             const t_translation::t_match& match = constraint->second.terrain_types_match;
00952             t_translation::t_list matching_types;
00953             size_t constraint_size = 0;
00954 
00955             for (terrain_by_type_map::iterator type_it = terrain_by_type_.begin();
00956                      type_it != terrain_by_type_.end(); type_it++) {
00957 
00958                 const t_translation::t_terrain t = type_it->first;
00959                 if (terrain_matches(t, match)) {
00960                     const size_t match_size = type_it->second.size();
00961                     constraint_size += match_size;
00962                     if (constraint_size >= min_size) {
00963                         break; // not a minimum, bail out
00964                     }
00965                     matching_types.push_back(t);
00966                 }
00967             }
00968 
00969             if (constraint_size < min_size) {
00970                 min_size = constraint_size;
00971                 min_types = matching_types;
00972                 min_constraint = constraint;
00973                 if (min_size == 0) {
00974                     // a constraint is never matched on this map
00975                     // we break with a empty type list
00976                     break;
00977                 }
00978             }
00979         }
00980 
00981         //NOTE: if min_types is not empty, we have found a valid min_constraint;
00982         for(t_translation::t_list::const_iterator t = min_types.begin();
00983                 t != min_types.end(); ++t) {
00984 
00985             const std::vector<gamemap::location>* locations = &terrain_by_type_[*t];
00986 
00987             for(std::vector<gamemap::location>::const_iterator itor = locations->begin();
00988                     itor != locations->end(); ++itor) {
00989                 const gamemap::location loc = *itor - min_constraint->second.loc;
00990 
00991                 if(rule_matches(rule, loc, rule_index, min_constraint)) {
00992                     apply_rule(rule, loc);
00993                 }
00994             }
00995         }
00996 
00997         rule_index++;
00998     }
00999 }

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