builder.cpp

Go to the documentation of this file.
00001 /* $Id: builder.cpp 53296 2012-02-28 10:31:13Z fendrin $ */
00002 /*
00003    Copyright (C) 2004 - 2012 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 as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 
00016 /**
00017  * @file
00018  * Terrain builder.
00019  */
00020 
00021 #include "builder.hpp"
00022 
00023 #include "foreach.hpp"
00024 #include "loadscreen.hpp"
00025 #include "log.hpp"
00026 #include "map.hpp"
00027 #include "serialization/string_utils.hpp"
00028 #include "image.hpp"
00029 
00030 static lg::log_domain log_engine("engine");
00031 #define ERR_NG LOG_STREAM(err, log_engine)
00032 #define WRN_NG LOG_STREAM(warn, log_engine)
00033 
00034 terrain_builder::building_ruleset terrain_builder::building_rules_;
00035 const config* terrain_builder::rules_cfg_ = NULL;
00036 
00037 terrain_builder::rule_image::rule_image(int layer, int x, int y, bool global_image, int cx, int cy) :
00038     layer(layer),
00039     basex(x),
00040     basey(y),
00041     variants(),
00042     global_image(global_image),
00043     center_x(cx),
00044     center_y(cy)
00045 {}
00046 
00047 terrain_builder::tile::tile() :
00048     flags(),
00049     images(),
00050     images_foreground(),
00051     images_background(),
00052     last_tod("invalid_tod"),
00053     sorted_images(false)
00054 {}
00055 
00056 void terrain_builder::tile::rebuild_cache(const std::string& tod, logs* log)
00057 {
00058     images_background.clear();
00059     images_foreground.clear();
00060 
00061     if(!sorted_images){
00062         //sort images by their layer (and basey)
00063         //but use stable to keep the insertion order in equal cases
00064         std::stable_sort(images.begin(), images.end());
00065         sorted_images = true;
00066     }
00067 
00068     foreach(const rule_image_rand& ri, images){
00069         bool is_background = ri->is_background();
00070 
00071         imagelist& img_list = is_background ? images_background : images_foreground;
00072 
00073         foreach(const rule_image_variant& variant, ri->variants){
00074             if(!variant.tods.empty() && variant.tods.find(tod) == variant.tods.end())
00075                 continue;
00076 
00077             //need to break parity pattern in RNG
00078             /** @todo improve this */
00079             unsigned int rnd = ri.rand / 7919; //just the 1000th prime
00080             const animated<image::locator>& anim = variant.images[rnd % variant.images.size()];
00081 
00082             bool is_empty = true;
00083             for(size_t i = 0; i < anim.get_frames_count(); ++i) {
00084                 if(!image::is_empty_hex(anim.get_frame(i))) {
00085                     is_empty = false;
00086                     break;
00087                 }
00088             }
00089 
00090             if(is_empty)
00091                 continue;
00092 
00093             img_list.push_back(anim);
00094 
00095             if(variant.random_start)
00096                 img_list.back().set_animation_time(ri.rand % img_list.back().get_animation_duration());
00097 
00098             if(log) {
00099                 log->push_back(std::make_pair(&ri, &variant));
00100             }
00101 
00102             break; // found a matching variant
00103         }
00104     }
00105 }
00106 
00107 void terrain_builder::tile::clear()
00108 {
00109     flags.clear();
00110     images.clear();
00111     sorted_images = false;
00112     images_foreground.clear();
00113     images_background.clear();
00114     last_tod = "invalid_tod";
00115 }
00116 
00117 static unsigned int get_noise(const map_location& loc, unsigned int index){
00118     unsigned int a = (loc.x + 92872973) ^ 918273;
00119     unsigned int b = (loc.y + 1672517) ^ 128123;
00120     unsigned int c = (index + 127390) ^ 13923787;
00121     unsigned int abc = a*b*c + a*b + b*c + a*c + a + b + c;
00122     return abc*abc;
00123 }
00124 
00125 void terrain_builder::tilemap::reset()
00126 {
00127     for(std::vector<tile>::iterator it = tiles_.begin(); it != tiles_.end(); ++it)
00128         it->clear();
00129 }
00130 
00131 void terrain_builder::tilemap::reload(int x, int y)
00132 {
00133     x_ = x;
00134     y_ = y;
00135     std::vector<terrain_builder::tile> new_tiles((x + 4) * (y + 4));
00136     tiles_.swap(new_tiles);
00137     reset();
00138 }
00139 
00140 bool terrain_builder::tilemap::on_map(const map_location &loc) const
00141 {
00142     if(loc.x < -2 || loc.y < -2 || loc.x > (x_ + 1) || loc.y > (y_ + 1)) {
00143         return false;
00144     }
00145 
00146     return true;
00147 
00148 }
00149 
00150 terrain_builder::tile& terrain_builder::tilemap::operator[](const map_location &loc)
00151 {
00152     assert(on_map(loc));
00153 
00154     return tiles_[(loc.x + 2) + (loc.y + 2) * (x_ + 4)];
00155 }
00156 
00157 const terrain_builder::tile& terrain_builder::tilemap::operator[] (const map_location &loc) const
00158 {
00159     assert(on_map(loc));
00160 
00161     return tiles_[(loc.x + 2) + (loc.y + 2) * (x_ + 4)];
00162 }
00163 
00164 terrain_builder::terrain_builder(const config& level,
00165         const gamemap* m, const std::string& offmap_image) :
00166     map_(m),
00167     tile_map_(m ? map().w() : 0, m ? map().h() :0),
00168     terrain_by_type_()
00169 {
00170     image::precache_file_existence("terrain/");
00171 
00172     if(building_rules_.empty() && rules_cfg_){
00173         //off_map first to prevent some default rule seems to block it
00174         add_off_map_rule(offmap_image);
00175         // parse global terrain rules
00176         parse_global_config(*rules_cfg_);
00177     } else {
00178         // use cached global rules but clear local rules
00179         flush_local_rules();
00180     }
00181 
00182     // parse local rules
00183     parse_config(level);
00184 
00185     if (m)
00186         build_terrains();
00187 }
00188 
00189 void terrain_builder::flush_local_rules()
00190 {
00191     building_ruleset::iterator i = building_rules_.begin();
00192     for(; i != building_rules_.end();){
00193         if (i->local)
00194             building_rules_.erase(i++);
00195         else
00196             ++i;
00197     }
00198 }
00199 
00200 void terrain_builder::set_terrain_rules_cfg(const config& cfg)
00201 {
00202     rules_cfg_ = &cfg;
00203     // use the swap trick to clear the rules cache and get a fresh one.
00204     // because simple clear() seems to cause some progressive memory degradation.
00205     building_ruleset empty;
00206     std::swap(building_rules_, empty);
00207 }
00208 
00209 void terrain_builder::reload_map()
00210 {
00211     tile_map_.reload(map().w(), map().h());
00212     terrain_by_type_.clear();
00213     build_terrains();
00214 }
00215 
00216 void terrain_builder::change_map(const gamemap* m)
00217 {
00218     map_ = m;
00219     reload_map();
00220 }
00221 
00222 const terrain_builder::imagelist *terrain_builder::get_terrain_at(const map_location &loc,
00223         const std::string &tod, const TERRAIN_TYPE terrain_type)
00224 {
00225     if(!tile_map_.on_map(loc))
00226         return NULL;
00227 
00228     tile& tile_at = tile_map_[loc];
00229 
00230     if(tod != tile_at.last_tod) {
00231         tile_at.rebuild_cache(tod);
00232         tile_at.last_tod = tod;
00233     }
00234 
00235     const imagelist& img_list = (terrain_type == BACKGROUND) ?
00236             tile_at.images_background : tile_at.images_foreground;
00237 
00238     if(!img_list.empty()) {
00239         return &img_list;
00240     }
00241 
00242     return NULL;
00243 }
00244 
00245 bool terrain_builder::update_animation(const map_location &loc)
00246 {
00247     if(!tile_map_.on_map(loc))
00248         return false;
00249 
00250     bool changed = false;
00251 
00252     tile& btile = tile_map_[loc];
00253 
00254     foreach(animated<image::locator>& a, btile.images_background) {
00255         if(a.need_update())
00256             changed = true;
00257         a.update_last_draw_time();
00258     }
00259     foreach(animated<image::locator>& a, btile.images_foreground) {
00260         if(a.need_update())
00261             changed = true;
00262         a.update_last_draw_time();
00263     }
00264 
00265     return changed;
00266 }
00267 
00268 /** @todo TODO: rename this function */
00269 void terrain_builder::rebuild_terrain(const map_location &loc)
00270 {
00271     if (tile_map_.on_map(loc)) {
00272         tile& btile = tile_map_[loc];
00273         // btile.images.clear();
00274         btile.images_foreground.clear();
00275         btile.images_background.clear();
00276         const std::string filename =
00277             map().get_terrain_info(map().get_terrain(loc)).minimap_image();
00278         animated<image::locator> img_loc;
00279         img_loc.add_frame(100,image::locator("terrain/" + filename + ".png"));
00280         img_loc.start_animation(0, true);
00281         btile.images_background.push_back(img_loc);
00282 
00283         //Combine base and overlay image if necessary
00284         if(map().get_terrain_info(map().get_terrain(loc)).is_combined()) {
00285             const std::string filename_ovl =
00286                 map().get_terrain_info(map().get_terrain(loc)).minimap_image_overlay();
00287             animated<image::locator> img_loc_ovl;
00288             img_loc_ovl.add_frame(100,image::locator("terrain/" + filename_ovl + ".png"));
00289             img_loc_ovl.start_animation(0, true);
00290             btile.images_background.push_back(img_loc_ovl);
00291         }
00292     }
00293 }
00294 
00295 void terrain_builder::rebuild_all()
00296 {
00297     tile_map_.reset();
00298     terrain_by_type_.clear();
00299     build_terrains();
00300 }
00301 
00302 static bool image_exists(const std::string& name)
00303 {
00304     bool precached = name.find("..") == std::string::npos;
00305 
00306     if(precached && image::precached_file_exists(name)) {
00307         return true;
00308     } else if(image::exists(name)) {
00309         return true;
00310     }
00311 
00312     return false;
00313 }
00314 
00315 static std::vector<std::string> get_variations(const std::string& base, const std::string& variations)
00316 {
00317     /** @todo optimize this function */
00318     std::vector<std::string> res;
00319     if(variations.empty()){
00320         res.push_back(base);
00321         return res;
00322     }
00323     std::string::size_type pos = base.find("@V", 0);
00324     if(pos == std::string::npos) {
00325         res.push_back(base);
00326         return res;
00327     }
00328     std::vector<std::string> vars = utils::split(variations, ';', 0);
00329 
00330     foreach(const std::string& v, vars){
00331         res.push_back(base);
00332         std::string::size_type pos = 0;
00333         while ((pos = res.back().find("@V", pos)) != std::string::npos) {
00334             res.back().replace(pos, 2, v);
00335             pos += v.size();
00336         }
00337     }
00338     return res;
00339 }
00340 
00341 bool terrain_builder::load_images(building_rule &rule)
00342 {
00343     // If the rule has no constraints, it is invalid
00344     if(rule.constraints.empty())
00345         return false;
00346 
00347     // Parse images and animations data
00348     // If one is not valid, return false.
00349     foreach(terrain_constraint &constraint, rule.constraints)
00350     {
00351         foreach(rule_image& ri, constraint.images)
00352         {
00353             foreach(rule_image_variant& variant, ri.variants)
00354             {
00355 
00356                 std::vector<std::string> var_strings = get_variations(variant.image_string, variant.variations);
00357                 foreach(const std::string& var, var_strings)
00358                 {
00359                     /** @todo improve this, 99% of terrains are not animated. */
00360                     std::vector<std::string> frames = utils::parenthetical_split(var,',');
00361                     animated<image::locator> res;
00362 
00363                     foreach(const std::string& frame, frames)
00364                     {
00365                         const std::vector<std::string> items = utils::split(frame, ':');
00366                         const std::string& str = items.front();
00367 
00368                         const size_t tilde = str.find('~');
00369                         bool has_tilde = tilde != std::string::npos;
00370                         const std::string filename = "terrain/" + (has_tilde ? str.substr(0,tilde) : str);
00371 
00372                         if(!image_exists(filename)){
00373                             continue; // ignore missing frames
00374                         }
00375 
00376                         const std::string modif = (has_tilde ? str.substr(tilde+1) : "");
00377 
00378                         int time = 100;
00379                         if(items.size() > 1) {
00380                             time = atoi(items.back().c_str());
00381                         }
00382                         image::locator locator;
00383                         if(ri.global_image) {
00384                             locator = image::locator(filename, constraint.loc, ri.center_x, ri.center_y, modif);
00385                         } else {
00386                             locator = image::locator(filename, modif);
00387                         }
00388                         res.add_frame(time, locator);
00389                     }
00390                     if(res.get_frames_count() == 0)
00391                         break; // no valid images, don't register it
00392 
00393                     res.start_animation(0, true);
00394                     variant.images.push_back(res);
00395                 }
00396                 if(variant.images.empty())
00397                     return false; //no valid images, rule is invalid
00398             }
00399         }
00400     }
00401 
00402     return true;
00403 }
00404 
00405 void terrain_builder::rotate(terrain_constraint &ret, int angle)
00406 {
00407     static const struct { int ii; int ij; int ji; int jj; }  rotations[6] =
00408         { {  1, 0, 0,  1 }, {  1,  1, -1, 0 }, { 0,  1, -1, -1 },
00409           { -1, 0, 0, -1 }, { -1, -1,  1, 0 }, { 0, -1,  1,  1 } };
00410 
00411     // The following array of matrices is intended to rotate the (x,y)
00412     // coordinates of a point in a wesnoth hex (and wesnoth hexes are not
00413     // regular hexes :) ).
00414     // The base matrix for a 1-step rotation with the wesnoth tile shape
00415     // is:
00416     //
00417     // r = s^-1 * t * s
00418     //
00419     // with s = [[ 1   0         ]
00420     //           [ 0   -sqrt(3)/2 ]]
00421     //
00422     // and t =  [[ -1/2       sqrt(3)/2 ]
00423     //           [ -sqrt(3)/2  1/2        ]]
00424     //
00425     // With t being the rotation matrix (pi/3 rotation), and s a matrix
00426     // that transforms the coordinates of the wesnoth hex to make them
00427     // those of a regular hex.
00428     //
00429     // (demonstration left as an exercise for the reader)
00430     //
00431     // So we have
00432     //
00433     // r = [[ 1/2  -3/4 ]
00434     //      [ 1    1/2  ]]
00435     //
00436     // And the following array contains I(2), r, r^2, r^3, r^4, r^5
00437     // (with r^3 == -I(2)), which are the successive rotations.
00438     static const struct {
00439         double xx;
00440         double xy;
00441         double yx;
00442         double yy;
00443     } xyrotations[6] = {
00444         { 1.,         0.,  0., 1.    },
00445         { 1./2. , -3./4.,  1., 1./2. },
00446         { -1./2., -3./4.,   1, -1./2.},
00447         { -1.   ,     0.,  0., -1.   },
00448         { -1./2.,  3./4., -1., -1./2.},
00449         { 1./2. ,  3./4., -1., 1./2. },
00450     };
00451 
00452     assert(angle >= 0);
00453 
00454     angle %= 6;
00455 
00456     // Vector i is going from n to s, vector j is going from ne to sw.
00457     int vi = ret.loc.y - ret.loc.x/2;
00458     int vj = ret.loc.x;
00459 
00460     int ri = rotations[angle].ii * vi + rotations[angle].ij * vj;
00461     int rj = rotations[angle].ji * vi + rotations[angle].jj * vj;
00462 
00463     ret.loc.x = rj;
00464     ret.loc.y = ri + (rj >= 0 ? rj/2 : (rj-1)/2);
00465 
00466     for (rule_imagelist::iterator itor = ret.images.begin();
00467             itor != ret.images.end(); ++itor) {
00468 
00469         double vx, vy, rx, ry;
00470 
00471         vx = double(itor->basex) - double(TILEWIDTH)/2;
00472         vy = double(itor->basey) - double(TILEWIDTH)/2;
00473 
00474         rx = xyrotations[angle].xx * vx + xyrotations[angle].xy * vy;
00475         ry = xyrotations[angle].yx * vx + xyrotations[angle].yy * vy;
00476 
00477         itor->basex = int(rx + TILEWIDTH/2);
00478         itor->basey = int(ry + TILEWIDTH/2);
00479 
00480         //std::cerr << "Rotation: from " << vx << ", " << vy << " to " << itor->basex <<
00481         //  ", " << itor->basey << "\n";
00482     }
00483 }
00484 
00485 void terrain_builder::replace_rotate_tokens(std::string &s, int angle,
00486     const std::vector<std::string> &replacement)
00487 {
00488     std::string::size_type pos = 0;
00489     while ((pos = s.find("@R", pos)) != std::string::npos) {
00490         if (pos + 2 >= s.size()) return;
00491         unsigned i = s[pos + 2] - '0' + angle;
00492         if (i >= 6) i -= 6;
00493         if (i >= 6) { pos += 2; continue; }
00494         const std::string &r = replacement[i];
00495         s.replace(pos, 3, r);
00496         pos += r.size();
00497     }
00498 }
00499 
00500 void terrain_builder::replace_rotate_tokens(rule_image &image, int angle,
00501     const std::vector<std::string> &replacement)
00502 {
00503     foreach(rule_image_variant& variant, image.variants) {
00504         replace_rotate_tokens(variant, angle, replacement);
00505     }
00506 }
00507 
00508 void terrain_builder::replace_rotate_tokens(rule_imagelist &list, int angle,
00509     const std::vector<std::string> &replacement)
00510 {
00511     foreach (rule_image &img, list) {
00512         replace_rotate_tokens(img, angle, replacement);
00513     }
00514 }
00515 
00516 void terrain_builder::replace_rotate_tokens(building_rule &rule, int angle,
00517     const std::vector<std::string> &replacement)
00518 {
00519     foreach (terrain_constraint &cons, rule.constraints)
00520     {
00521         // Transforms attributes
00522         foreach (std::string &flag, cons.set_flag) {
00523             replace_rotate_tokens(flag, angle, replacement);
00524         }
00525         foreach (std::string &flag, cons.no_flag) {
00526             replace_rotate_tokens(flag, angle, replacement);
00527         }
00528         foreach (std::string &flag, cons.has_flag) {
00529             replace_rotate_tokens(flag, angle, replacement);
00530         }
00531         replace_rotate_tokens(cons.images, angle, replacement);
00532     }
00533 
00534     //replace_rotate_tokens(rule.images, angle, replacement);
00535 }
00536 
00537 void terrain_builder::rotate_rule(building_rule &ret, int angle,
00538     const std::vector<std::string> &rot)
00539 {
00540     if (rot.size() != 6) {
00541         ERR_NG << "invalid rotations\n";
00542         return;
00543     }
00544 
00545     foreach (terrain_constraint &cons, ret.constraints) {
00546         rotate(cons, angle);
00547     }
00548 
00549     // Normalize the rotation, so that it starts on a positive location
00550     int minx = INT_MAX;
00551     int miny = INT_MAX;
00552 
00553     foreach (const terrain_constraint &cons, ret.constraints) {
00554         minx = std::min<int>(cons.loc.x, minx);
00555         miny = std::min<int>(2 * cons.loc.y + (cons.loc.x & 1), miny);
00556     }
00557 
00558     if((miny & 1) && (minx & 1) && (minx < 0))
00559         miny += 2;
00560     if(!(miny & 1) && (minx & 1) && (minx > 0))
00561         miny -= 2;
00562 
00563     foreach (terrain_constraint &cons, ret.constraints) {
00564         cons.loc.legacy_sum_assign(map_location(-minx, -((miny - 1) / 2)));
00565     }
00566 
00567     replace_rotate_tokens(ret, angle, rot);
00568 }
00569 
00570 terrain_builder::rule_image_variant::rule_image_variant(const std::string &image_string, const std::string& variations, const std::string& tod, bool random_start) :
00571         image_string(image_string),
00572         variations(variations),
00573         images(),
00574         tods(),
00575         random_start(random_start)
00576 {
00577     if(!tod.empty()) {
00578         const std::vector<std::string> tod_list = utils::split(tod);
00579         tods.insert(tod_list.begin(), tod_list.end());
00580     }
00581 }
00582 
00583 void terrain_builder::add_images_from_config(rule_imagelist& images, const config &cfg, bool global, int dx, int dy)
00584 {
00585     foreach (const config &img, cfg.child_range("image"))
00586     {
00587         int layer = img["layer"];
00588 
00589         int basex = TILEWIDTH / 2 + dx, basey = TILEWIDTH / 2 + dy;
00590         if (const config::attribute_value *base_ = img.get("base")) {
00591             std::vector<std::string> base = utils::split(*base_);
00592             if(base.size() >= 2) {
00593                 basex = atoi(base[0].c_str());
00594                 basey = atoi(base[1].c_str());
00595             }
00596         }
00597 
00598         int center_x = -1, center_y = -1;
00599         if (const config::attribute_value *center_ = img.get("center")) {
00600             std::vector<std::string> center = utils::split(*center_);
00601             if(center.size() >= 2) {
00602                 center_x = atoi(center[0].c_str());
00603                 center_y = atoi(center[1].c_str());
00604             }
00605         }
00606 
00607         images.push_back(rule_image(layer, basex - dx, basey - dy, global, center_x, center_y));
00608 
00609         // Adds the other variants of the image
00610         foreach (const config &variant, img.child_range("variant"))
00611         {
00612             const std::string &name = variant["name"];
00613             const std::string &variations = img["variations"];
00614             const std::string &tod = variant["tod"];
00615             bool random_start = variant["random_start"].to_bool(true);
00616 
00617             images.back().variants.push_back(rule_image_variant(name, variations, tod, random_start));
00618         }
00619 
00620         // Adds the main (default) variant of the image at the end,
00621         // (will be used only if previous variants don't match)
00622         const std::string &name = img["name"];
00623         const std::string &variations = img["variations"];
00624         bool random_start = img["random_start"].to_bool(true);
00625         images.back().variants.push_back(rule_image_variant(name, variations, random_start));
00626     }
00627 }
00628 
00629 terrain_builder::terrain_constraint &terrain_builder::add_constraints(
00630         terrain_builder::constraint_set& constraints,
00631         const map_location& loc,
00632         const t_translation::t_match& type, const config& global_images)
00633 {
00634     terrain_constraint *cons = NULL;
00635     foreach (terrain_constraint &c, constraints) {
00636         if (c.loc == loc) {
00637             cons = &c;
00638             break;
00639         }
00640     }
00641 
00642     if (!cons) {
00643         // The terrain at the current location did not exist, so create it
00644         constraints.push_back(terrain_constraint(loc));
00645         cons = &constraints.back();
00646     }
00647 
00648     if(!type.terrain.empty()) {
00649         cons->terrain_types_match = type;
00650     }
00651 
00652     int x = loc.x * TILEWIDTH * 3 / 4;
00653     int y = loc.y * TILEWIDTH + (loc.x % 2) * TILEWIDTH / 2;
00654     add_images_from_config(cons->images, global_images, true, x, y);
00655 
00656     return *cons;
00657 }
00658 
00659 void terrain_builder::add_constraints(terrain_builder::constraint_set &constraints,
00660         const map_location& loc, const config& cfg, const config& global_images)
00661 
00662 {
00663     terrain_constraint& constraint = add_constraints(constraints, loc,
00664         t_translation::t_match(cfg["type"], t_translation::WILDCARD), global_images);
00665 
00666 
00667     std::vector<std::string> item_string = utils::split(cfg["set_flag"]);
00668     constraint.set_flag.insert(constraint.set_flag.end(),
00669             item_string.begin(), item_string.end());
00670 
00671     item_string = utils::split(cfg["has_flag"]);
00672     constraint.has_flag.insert(constraint.has_flag.end(),
00673             item_string.begin(), item_string.end());
00674 
00675     item_string = utils::split(cfg["no_flag"]);
00676     constraint.no_flag.insert(constraint.no_flag.end(),
00677             item_string.begin(), item_string.end());
00678 
00679     item_string = utils::split(cfg["set_no_flag"]);
00680     constraint.set_flag.insert(constraint.set_flag.end(),
00681             item_string.begin(), item_string.end());
00682     constraint.no_flag.insert(constraint.no_flag.end(),
00683             item_string.begin(), item_string.end());
00684 
00685 
00686     add_images_from_config(constraint.images, cfg, false);
00687 }
00688 
00689 void terrain_builder::parse_mapstring(const std::string &mapstring,
00690         struct building_rule &br, anchormap& anchors,
00691         const config& global_images)
00692 {
00693 
00694     const t_translation::t_map map = t_translation::read_builder_map(mapstring);
00695 
00696     // If there is an empty map leave directly.
00697     // Determine after conversion, since a
00698     // non-empty string can return an empty map.
00699     if(map.empty()) {
00700         return;
00701     }
00702 
00703     int lineno = (map[0][0] == t_translation::NONE_TERRAIN) ? 1 : 0;
00704     int x = lineno;
00705     int y = 0;
00706     for(size_t y_off = 0; y_off < map.size(); ++y_off) {
00707         for(size_t x_off = x; x_off < map[y_off].size(); ++x_off) {
00708 
00709             const t_translation::t_terrain terrain = map[y_off][x_off];
00710 
00711             if(terrain.base == t_translation::TB_DOT) {
00712                 // Dots are simple placeholders,
00713                 // which do not represent actual terrains.
00714             } else if (terrain.overlay != 0 ) {
00715                 anchors.insert(std::pair<int, map_location>(terrain.overlay, map_location(x, y)));
00716             } else if (terrain.base == t_translation::TB_STAR) {
00717                 add_constraints(br.constraints, map_location(x, y), t_translation::STAR, global_images);
00718             } else {
00719                     ERR_NG << "Invalid terrain (" << t_translation::write_terrain_code(terrain) << ") in builder map\n";
00720                     assert(false);
00721                     return;
00722             }
00723         x += 2;
00724         }
00725 
00726         if(lineno % 2 == 1) {
00727             ++y;
00728             x = 0;
00729         } else {
00730             x = 1;
00731         }
00732         ++lineno;
00733     }
00734 }
00735 
00736 void terrain_builder::add_rule(building_ruleset &rules, building_rule &rule)
00737 {
00738     if(load_images(rule)) {
00739         rules.insert(rule);
00740     }
00741 }
00742 
00743 void terrain_builder::add_rotated_rules(building_ruleset &rules, building_rule &tpl,
00744     const std::string &rotations)
00745 {
00746     if(rotations.empty()) {
00747         // Adds the parsed built terrain to the list
00748 
00749         add_rule(rules, tpl);
00750     } else {
00751         const std::vector<std::string>& rot = utils::split(rotations, ',');
00752 
00753         for(size_t angle = 0; angle < rot.size(); ++angle) {
00754             /* Only 5% of the rules have valid images, so most of
00755                them will be discarded. If the ratio was higher,
00756                it would be more efficient to insert a copy of the
00757                template rule into the ruleset, modify it in place,
00758                and remove it if invalid. But since the ratio is so
00759                low, the speedup is not worth the extra multiset
00760                manipulations. */
00761             building_rule rule = tpl;
00762             rotate_rule(rule, angle, rot);
00763             add_rule(rules, rule);
00764         }
00765     }
00766 }
00767 
00768 void terrain_builder::parse_config(const config &cfg, bool local)
00769 {
00770     log_scope("terrain_builder::parse_config");
00771 
00772     // Parses the list of building rules (BRs)
00773     foreach (const config &br, cfg.child_range("terrain_graphics"))
00774     {
00775         building_rule pbr; // Parsed Building rule
00776         pbr.local = local;
00777 
00778         // add_images_from_config(pbr.images, **br);
00779 
00780         pbr.location_constraints =
00781             map_location(br["x"].to_int() - 1, br["y"].to_int() - 1);
00782 
00783         pbr.probability = br["probability"].to_int(100);
00784 
00785         // Mapping anchor indices to anchor locations.
00786         anchormap anchors;
00787 
00788         // Parse the map= , if there is one (and fill the anchors list)
00789         parse_mapstring(br["map"], pbr, anchors, br);
00790 
00791         // Parses the terrain constraints (TCs)
00792         foreach (const config &tc, br.child_range("tile"))
00793         {
00794             // Adds the terrain constraint to the current built terrain's list
00795             // of terrain constraints, if it does not exist.
00796             map_location loc;
00797             if (const config::attribute_value *v = tc.get("x")) {
00798                 loc.x = *v;
00799             }
00800             if (const config::attribute_value *v = tc.get("y")) {
00801                 loc.y = *v;
00802             }
00803             if (const config::attribute_value *v = tc.get("loc")) {
00804                 std::vector<std::string> sloc = utils::split(*v);
00805                 if(sloc.size() == 2) {
00806                     loc.x = atoi(sloc[0].c_str());
00807                     loc.y = atoi(sloc[1].c_str());
00808                 }
00809             }
00810             if(loc.valid()) {
00811                 add_constraints(pbr.constraints, loc, tc, br);
00812             }
00813             if (const config::attribute_value *v = tc.get("pos")) {
00814                 int pos = *v;
00815                 if(anchors.find(pos) == anchors.end()) {
00816                     WRN_NG << "Invalid anchor!\n";
00817                     continue;
00818                 }
00819 
00820                 std::pair<anchormap::const_iterator, anchormap::const_iterator> range =
00821                     anchors.equal_range(pos);
00822 
00823                 for(; range.first != range.second; ++range.first) {
00824                     loc = range.first->second;
00825                     add_constraints(pbr.constraints, loc, tc, br);
00826                 }
00827             }
00828         }
00829 
00830         const std::vector<std::string> global_set_flag = utils::split(br["set_flag"]);
00831         const std::vector<std::string> global_no_flag = utils::split(br["no_flag"]);
00832         const std::vector<std::string> global_has_flag = utils::split(br["has_flag"]);
00833         const std::vector<std::string> global_set_no_flag = utils::split(br["set_no_flag"]);
00834 
00835         foreach (terrain_constraint &constraint, pbr.constraints)
00836         {
00837             constraint.set_flag.insert(constraint.set_flag.end(),
00838                 global_set_flag.begin(), global_set_flag.end());
00839             constraint.no_flag.insert(constraint.no_flag.end(),
00840                 global_no_flag.begin(), global_no_flag.end());
00841             constraint.has_flag.insert(constraint.has_flag.end(),
00842                 global_has_flag.begin(), global_has_flag.end());
00843             constraint.set_flag.insert(constraint.set_flag.end(),
00844                 global_set_no_flag.begin(), global_set_no_flag.end());
00845             constraint.no_flag.insert(constraint.no_flag.end(),
00846                 global_set_no_flag.begin(), global_set_no_flag.end());
00847         }
00848 
00849         // Handles rotations
00850         const std::string &rotations = br["rotations"];
00851 
00852         pbr.precedence = br["precedence"];
00853 
00854         add_rotated_rules(building_rules_, pbr, rotations);
00855 
00856         loadscreen::increment_progress();
00857     }
00858 
00859 // Debug output for the terrain rules
00860 #if 0
00861     std::cerr << "Built terrain rules: \n";
00862 
00863     building_ruleset::const_iterator rule;
00864     for(rule = building_rules_.begin(); rule != building_rules_.end(); ++rule) {
00865         std::cerr << ">> New rule: image_background = "
00866             << "\n>> Location " << rule->second.location_constraints
00867             << "\n>> Probability " << rule->second.probability
00868 
00869         for(constraint_set::const_iterator constraint = rule->second.constraints.begin();
00870             constraint != rule->second.constraints.end(); ++constraint) {
00871 
00872             std::cerr << ">>>> New constraint: location = (" << constraint->second.loc
00873                       << "), terrain types = '" << t_translation::write_list(constraint->second.terrain_types_match.terrain) << "'\n";
00874 
00875             std::vector<std::string>::const_iterator flag;
00876 
00877             for(flag  = constraint->second.set_flag.begin(); flag != constraint->second.set_flag.end(); ++flag) {
00878                 std::cerr << ">>>>>> Set_flag: " << *flag << "\n";
00879             }
00880 
00881             for(flag = constraint->second.no_flag.begin(); flag != constraint->second.no_flag.end(); ++flag) {
00882                 std::cerr << ">>>>>> No_flag: " << *flag << "\n";
00883             }
00884         }
00885 
00886     }
00887 #endif
00888 
00889 }
00890 
00891 void terrain_builder::add_off_map_rule(const std::string& image)
00892 {
00893     // Build a config object
00894     config cfg;
00895 
00896     config &item = cfg.add_child("terrain_graphics");
00897 
00898     config &tile = item.add_child("tile");
00899     tile["x"] = 0;
00900     tile["y"] = 0;
00901     tile["type"] = t_translation::write_terrain_code(t_translation::OFF_MAP_USER);
00902 
00903     config &tile_image = tile.add_child("image");
00904     tile_image["layer"] = -1000;
00905     tile_image["name"] = image;
00906 
00907     item["probability"] = 100;
00908     item["no_flag"] = "base";
00909     item["set_flag"] = "base";
00910 
00911     // Parse the object
00912     parse_global_config(cfg);
00913 }
00914 
00915 bool terrain_builder::rule_matches(const terrain_builder::building_rule &rule,
00916         const map_location &loc, const terrain_constraint *type_checked) const
00917 {
00918     if(rule.location_constraints.valid() && rule.location_constraints != loc) {
00919         return false;
00920     }
00921 
00922     if(rule.probability != 100) {
00923         unsigned int random = get_noise(loc, rule.get_hash()) % 100;
00924         if(random > static_cast<unsigned int>(rule.probability)) {
00925             return false;
00926         }
00927     }
00928 
00929     foreach (const terrain_constraint &cons, rule.constraints)
00930     {
00931         // Translated location
00932         const map_location tloc = loc.legacy_sum(cons.loc);
00933 
00934         if(!tile_map_.on_map(tloc)) {
00935             return false;
00936         }
00937 
00938         //std::cout << "testing..." << builder_letter(map().get_terrain(tloc))
00939 
00940         // check if terrain matches except if we already know that it does
00941         if (&cons != type_checked && !terrain_matches(map().get_terrain(tloc), cons.terrain_types_match)) {
00942             return false;
00943         }
00944 
00945         const std::set<std::string> &flags = tile_map_[tloc].flags;
00946 
00947         foreach (const std::string &s, cons.no_flag) {
00948             // If a flag listed in "no_flag" is present, the rule does not match
00949             if (flags.find(s) != flags.end()) {
00950                 return false;
00951             }
00952         }
00953         foreach (const std::string &s, cons.has_flag) {
00954             // If a flag listed in "has_flag" is not present, this rule does not match
00955             if (flags.find(s) == flags.end()) {
00956                 return false;
00957             }
00958         }
00959     }
00960 
00961     return true;
00962 }
00963 
00964 void terrain_builder::apply_rule(const terrain_builder::building_rule &rule, const map_location &loc)
00965 {
00966     unsigned int rand_seed = get_noise(loc, rule.get_hash());
00967 
00968     foreach (const terrain_constraint &constraint, rule.constraints)
00969     {
00970         const map_location tloc = loc.legacy_sum(constraint.loc);
00971         if(!tile_map_.on_map(tloc)) {
00972             return;
00973         }
00974 
00975         tile& btile = tile_map_[tloc];
00976 
00977         foreach (const rule_image &img, constraint.images) {
00978             btile.images.push_back(tile::rule_image_rand(&img, rand_seed));
00979         }
00980 
00981         // Sets flags
00982         foreach (const std::string &flag, constraint.set_flag) {
00983             btile.flags.insert(flag);
00984         }
00985 
00986     }
00987 }
00988 
00989 // copied from text_surface::hash()
00990 // but keep it separated because the needs are different
00991 // and changing it will modify the map random variations
00992 static unsigned int hash_str(const std::string& str)
00993 {
00994     unsigned int h = 0;
00995     for(std::string::const_iterator it = str.begin(), it_end = str.end(); it != it_end; ++it)
00996         h = ((h << 9) | (h >> (sizeof(int) * 8 - 9))) ^ (*it);
00997     return h;
00998 }
00999 
01000 unsigned int terrain_builder::building_rule::get_hash() const
01001 {
01002     if(hash_ != DUMMY_HASH)
01003         return hash_;
01004 
01005     foreach(const terrain_constraint &constraint, constraints) {
01006         foreach(const rule_image& ri, constraint.images) {
01007             foreach(const rule_image_variant& variant, ri.variants) {
01008                 // we will often hash the same string, but that seems fast enough
01009                 hash_ += hash_str(variant.image_string);
01010             }
01011         }
01012     }
01013 
01014     //don't use the reserved dummy hash
01015     if(hash_ == DUMMY_HASH)
01016         hash_ = 105533;  // just a random big prime number
01017 
01018     return hash_;
01019 }
01020 
01021 void terrain_builder::build_terrains()
01022 {
01023     log_scope("terrain_builder::build_terrains");
01024 
01025     // Builds the terrain_by_type_ cache
01026     for(int x = -2; x <= map().w(); ++x) {
01027         for(int y = -2; y <= map().h(); ++y) {
01028             const map_location loc(x,y);
01029             const t_translation::t_terrain t = map().get_terrain(loc);
01030 
01031             terrain_by_type_[t].push_back(loc);
01032         }
01033     }
01034 
01035     foreach (const building_rule &rule, building_rules_)
01036     {
01037         // Find the constraint that contains the less terrain of all terrain rules.
01038         // We will keep a track of the matching terrains of this constraint
01039         // and later try to apply the rule only on them
01040         size_t min_size = INT_MAX;
01041         t_translation::t_list min_types;
01042         const terrain_constraint *min_constraint = NULL;
01043 
01044         foreach (const terrain_constraint &constraint, rule.constraints)
01045         {
01046             const t_translation::t_match& match = constraint.terrain_types_match;
01047             t_translation::t_list matching_types;
01048             size_t constraint_size = 0;
01049 
01050             for (terrain_by_type_map::iterator type_it = terrain_by_type_.begin();
01051                      type_it != terrain_by_type_.end(); ++type_it) {
01052 
01053                 const t_translation::t_terrain t = type_it->first;
01054                 if (terrain_matches(t, match)) {
01055                     const size_t match_size = type_it->second.size();
01056                     constraint_size += match_size;
01057                     if (constraint_size >= min_size) {
01058                         break; // not a minimum, bail out
01059                     }
01060                     matching_types.push_back(t);
01061                 }
01062             }
01063 
01064             if (constraint_size < min_size) {
01065                 min_size = constraint_size;
01066                 min_types = matching_types;
01067                 min_constraint = &constraint;
01068                 if (min_size == 0) {
01069                     // a constraint is never matched on this map
01070                     // we break with a empty type list
01071                     break;
01072                 }
01073             }
01074         }
01075 
01076         //NOTE: if min_types is not empty, we have found a valid min_constraint;
01077         for(t_translation::t_list::const_iterator t = min_types.begin();
01078                 t != min_types.end(); ++t) {
01079 
01080             const std::vector<map_location>* locations = &terrain_by_type_[*t];
01081 
01082             for(std::vector<map_location>::const_iterator itor = locations->begin();
01083                     itor != locations->end(); ++itor) {
01084                 const map_location loc = itor->legacy_difference(min_constraint->loc);
01085 
01086                 if(rule_matches(rule, loc, min_constraint)) {
01087                     apply_rule(rule, loc);
01088                 }
01089             }
01090         }
01091 
01092     }
01093 }
01094 
01095 terrain_builder::tile* terrain_builder::get_tile(const map_location &loc)
01096 {
01097     if(tile_map_.on_map(loc))
01098         return &(tile_map_[loc]);
01099     return NULL;
01100 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Wed May 23 2012 01:02:34 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs