00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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
00063
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
00078
00079 unsigned int rnd = ri.rand / 7919;
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;
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
00160 add_off_map_rule(offmap_image);
00161
00162 parse_global_config(*rules_cfg_);
00163 } else {
00164
00165 flush_local_rules();
00166 }
00167
00168
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
00189
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
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
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
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
00298
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
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
00337 if(rule.constraints.empty())
00338 return false;
00339
00340
00341
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
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;
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;
00385
00386 res.start_animation(0, true);
00387 variant.images.push_back(res);
00388 }
00389 if(variant.images.empty())
00390 return false;
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
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430
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
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
00474
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
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
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
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
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
00614
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
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
00690
00691
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
00706
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
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
00748
00749
00750
00751
00752
00753
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
00766 foreach (const config &br, cfg.child_range("terrain_graphics"))
00767 {
00768 building_rule pbr;
00769 pbr.local = local;
00770
00771
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
00779 anchormap anchors;
00780
00781
00782 parse_mapstring(br["map"], pbr, anchors, br);
00783
00784
00785 foreach (const config &tc, br.child_range("tile"))
00786 {
00787
00788
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
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
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
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
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
00925 const map_location tloc = loc.legacy_sum(cons.loc);
00926
00927 if(!tile_map_.on_map(tloc)) {
00928 return false;
00929 }
00930
00931
00932
00933
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
00942 if (flags.find(s) != flags.end()) {
00943 return false;
00944 }
00945 }
00946 foreach (const std::string &s, cons.has_flag) {
00947
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
00975 foreach (const std::string &flag, constraint.set_flag) {
00976 btile.flags.insert(flag);
00977 }
00978
00979 }
00980 }
00981
00982
00983
00984
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
01002 hash_ += hash_str(variant.image_string);
01003 }
01004 }
01005 }
01006
01007
01008 if(hash_ == DUMMY_HASH)
01009 hash_ = 105533;
01010
01011 return hash_;
01012 }
01013
01014 void terrain_builder::build_terrains()
01015 {
01016 log_scope("terrain_builder::build_terrains");
01017
01018
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
01031
01032
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;
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
01063
01064 break;
01065 }
01066 }
01067 }
01068
01069
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 }