builder.cpp

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

Generated by doxygen 1.5.6 on Thu Feb 10 01:01:19 2011 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs