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 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;
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
00174 add_off_map_rule(offmap_image);
00175
00176 parse_global_config(*rules_cfg_);
00177 } else {
00178
00179 flush_local_rules();
00180 }
00181
00182
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
00204
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
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
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
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
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
00344 if(rule.constraints.empty())
00345 return false;
00346
00347
00348
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
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;
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;
00392
00393 res.start_animation(0, true);
00394 variant.images.push_back(res);
00395 }
00396 if(variant.images.empty())
00397 return false;
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
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437
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
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
00481
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
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
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
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
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
00621
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
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
00697
00698
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
00713
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
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
00755
00756
00757
00758
00759
00760
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
00773 foreach (const config &br, cfg.child_range("terrain_graphics"))
00774 {
00775 building_rule pbr;
00776 pbr.local = local;
00777
00778
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
00786 anchormap anchors;
00787
00788
00789 parse_mapstring(br["map"], pbr, anchors, br);
00790
00791
00792 foreach (const config &tc, br.child_range("tile"))
00793 {
00794
00795
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
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
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
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
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
00932 const map_location tloc = loc.legacy_sum(cons.loc);
00933
00934 if(!tile_map_.on_map(tloc)) {
00935 return false;
00936 }
00937
00938
00939
00940
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
00949 if (flags.find(s) != flags.end()) {
00950 return false;
00951 }
00952 }
00953 foreach (const std::string &s, cons.has_flag) {
00954
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
00982 foreach (const std::string &flag, constraint.set_flag) {
00983 btile.flags.insert(flag);
00984 }
00985
00986 }
00987 }
00988
00989
00990
00991
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
01009 hash_ += hash_str(variant.image_string);
01010 }
01011 }
01012 }
01013
01014
01015 if(hash_ == DUMMY_HASH)
01016 hash_ = 105533;
01017
01018 return hash_;
01019 }
01020
01021 void terrain_builder::build_terrains()
01022 {
01023 log_scope("terrain_builder::build_terrains");
01024
01025
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
01038
01039
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;
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
01070
01071 break;
01072 }
01073 }
01074 }
01075
01076
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 }