00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "global.hpp"
00023
00024 #include "pathfind/pathfind.hpp"
00025 #include "pathfind/teleport.hpp"
00026
00027 #include "foreach.hpp"
00028 #include "game_display.hpp"
00029 #include "gettext.hpp"
00030 #include "log.hpp"
00031 #include "map.hpp"
00032 #include "resources.hpp"
00033 #include "team.hpp"
00034 #include "unit.hpp"
00035 #include "unit_map.hpp"
00036 #include "wml_exception.hpp"
00037
00038 #include <iostream>
00039 #include <vector>
00040 #include <algorithm>
00041
00042 static lg::log_domain log_engine("engine");
00043 #define ERR_PF LOG_STREAM(err, log_engine)
00044
00045 map_location pathfind::find_vacant_tile(const gamemap& map,
00046 const unit_map& units,
00047 const map_location& loc,
00048 pathfind::VACANT_TILE_TYPE vacancy,
00049 const unit* pass_check)
00050 {
00051 if (!map.on_board(loc)) return map_location();
00052 std::set<map_location> pending_tiles_to_check, tiles_checked;
00053 pending_tiles_to_check.insert(loc);
00054
00055 for (int distance = 0; distance < 50; ++distance) {
00056 if (pending_tiles_to_check.empty())
00057 return map_location();
00058
00059 std::set<map_location> tiles_checking;
00060 tiles_checking.swap(pending_tiles_to_check);
00061
00062 foreach (const map_location &loc, tiles_checking)
00063 {
00064
00065 if (vacancy == pathfind::VACANT_CASTLE && !map.is_castle(loc)) continue;
00066 const bool pass_check_and_unreachable = pass_check
00067 && pass_check->movement_cost(map[loc]) == unit_movement_type::UNREACHABLE;
00068
00069
00070
00071
00072
00073
00074 if (pass_check_and_unreachable && distance > 10) continue;
00075
00076 if (units.find(loc) == units.end() && !pass_check_and_unreachable) return loc;
00077 map_location adjs[6];
00078 get_adjacent_tiles(loc,adjs);
00079 foreach (const map_location &loc, adjs)
00080 {
00081 if (!map.on_board(loc)) continue;
00082
00083
00084 if (tiles_checked.find(loc) == tiles_checked.end() &&
00085 tiles_checking.find(loc) == tiles_checking.end())
00086 {
00087 pending_tiles_to_check.insert(loc);
00088 }
00089 }
00090 }
00091 tiles_checked.swap(tiles_checking);
00092 }
00093 return map_location();
00094 }
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106 bool pathfind::enemy_zoc(team const ¤t_team, map_location const &loc,
00107 team const &viewing_team, bool see_all)
00108 {
00109
00110 map_location locs[6];
00111 get_adjacent_tiles(loc,locs);
00112 for (int i = 0; i != 6; ++i)
00113 {
00114 const unit *u = get_visible_unit(locs[i], viewing_team, see_all);
00115 if ( u && current_team.is_enemy(u->side()) && u->emits_zoc() )
00116 return true;
00117 }
00118
00119
00120 return false;
00121 }
00122
00123 static unsigned search_counter;
00124
00125 namespace {
00126
00127 struct node {
00128 int movement_left, turns_left;
00129 map_location prev, curr;
00130
00131
00132
00133
00134
00135
00136 unsigned in;
00137
00138 node(int moves, int turns, const map_location &p, const map_location &c)
00139 : movement_left(moves)
00140 , turns_left(turns)
00141 , prev(p)
00142 , curr(c)
00143 , in(0)
00144 {
00145 }
00146
00147 node()
00148 : movement_left(0)
00149 , turns_left(0)
00150 , prev()
00151 , curr()
00152 , in(0)
00153 {
00154 }
00155
00156 bool operator<(const node& o) const {
00157 return turns_left > o.turns_left
00158 || (turns_left == o.turns_left
00159 && movement_left > o.movement_left);
00160 }
00161 };
00162
00163 struct indexer {
00164 int w, h;
00165 indexer(int a, int b) : w(a), h(b) { }
00166 int operator()(const map_location& loc) const {
00167 return loc.y * w + loc.x;
00168 }
00169 };
00170
00171 struct comp {
00172 const std::vector<node>& nodes;
00173 comp(const std::vector<node>& n) : nodes(n) { }
00174 bool operator()(int l, int r) const {
00175 return nodes[r] < nodes[l];
00176 }
00177 };
00178 }
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206 static void find_routes(const gamemap& map, const unit& u, const map_location& loc,
00207 int move_left, pathfind::paths::dest_vect &destinations,
00208 std::set<map_location> *edges, const team ¤t_team,
00209 bool force_ignore_zocs, bool allow_teleport, int turns_left,
00210 const team &viewing_team,
00211 bool see_all, bool ignore_units, pathfind::PATH_TYPE type, const std::map<map_location, int>* jamming_map)
00212 {
00213 pathfind::teleport_map teleports;
00214 if (allow_teleport) {
00215 teleports = pathfind::get_teleport_locations(u, viewing_team, see_all, ignore_units);
00216 }
00217
00218 const int total_movement = move_left;
00219
00220 search_counter += 2;
00221 if (search_counter == 0) search_counter = 2;
00222
00223 static std::vector<node> nodes;
00224 nodes.resize(map.w() * map.h());
00225
00226 indexer index(map.w(), map.h());
00227 comp node_comp(nodes);
00228
00229 int xmin = loc.x, xmax = loc.x, ymin = loc.y, ymax = loc.y, nb_dest = 1;
00230
00231 nodes[index(loc)] = node(move_left, turns_left, map_location::null_location, loc);
00232 std::vector<int> pq;
00233 pq.push_back(index(loc));
00234
00235 while (!pq.empty()) {
00236 node& n = nodes[pq.front()];
00237 std::pop_heap(pq.begin(), pq.end(), node_comp);
00238 pq.pop_back();
00239 n.in = search_counter;
00240
00241 std::set<map_location> allowed_teleports;
00242 teleports.get_adjacents(allowed_teleports, n.curr);
00243 std::vector<map_location> locs(6 + allowed_teleports.size());
00244 std::copy(allowed_teleports.begin(), allowed_teleports.end(), locs.begin() + 6);
00245 get_adjacent_tiles(n.curr, &locs[0]);
00246 for (int i = locs.size(); i-- > 0; ) {
00247 if (!locs[i].valid(map.w(), map.h())) {
00248 if ( edges != NULL )
00249 edges->insert(locs[i]);
00250 continue;
00251 }
00252
00253 if (locs[i] == n.curr) continue;
00254
00255 node& next = nodes[index(locs[i])];
00256
00257 bool next_visited = next.in - search_counter <= 1u;
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267 if (next_visited) continue;
00268
00269 int cost = 1;
00270 switch (type) {
00271 case pathfind::MOVE:
00272 cost = u.movement_cost(map[locs[i]]);
00273 break;
00274 case pathfind::VISION:
00275 cost = u.vision_cost(map[locs[i]]);
00276 if (jamming_map->count(locs[i]) == 1)
00277 cost += (jamming_map->find(locs[i]))->second;
00278 break;
00279 case pathfind::JAMMING:
00280 cost = u.vision_cost(map[locs[i]]);
00281 break;
00282 }
00283
00284 node t = node(n.movement_left, n.turns_left, n.curr, locs[i]);
00285 if (t.movement_left < cost) {
00286 t.movement_left = total_movement;
00287 t.turns_left--;
00288 }
00289
00290 if (t.movement_left < cost || t.turns_left < 0) {
00291 if ( edges != NULL )
00292 edges->insert(t.curr);
00293 continue;
00294 }
00295
00296 t.movement_left -= cost;
00297
00298 if (!ignore_units) {
00299 const unit *v =
00300 get_visible_unit(locs[i], viewing_team, see_all);
00301 if (v && current_team.is_enemy(v->side())) {
00302 if ( edges != NULL )
00303 edges->insert(t.curr);
00304 continue;
00305 }
00306
00307 if (!force_ignore_zocs && t.movement_left > 0
00308 && pathfind::enemy_zoc(current_team, locs[i], viewing_team, see_all)
00309 && !u.get_ability_bool("skirmisher", locs[i])) {
00310 t.movement_left = 0;
00311 }
00312 }
00313
00314 ++nb_dest;
00315 int x = locs[i].x;
00316 if (x < xmin) xmin = x;
00317 if (xmax < x) xmax = x;
00318 int y = locs[i].y;
00319 if (y < ymin) ymin = y;
00320 if (ymax < y) ymax = y;
00321
00322 bool in_list = next.in == search_counter + 1;
00323 t.in = search_counter + 1;
00324 next = t;
00325
00326
00327 if (in_list) {
00328 std::push_heap(pq.begin(), std::find(pq.begin(), pq.end(), index(locs[i])) + 1, node_comp);
00329 } else {
00330 pq.push_back(index(locs[i]));
00331 std::push_heap(pq.begin(), pq.end(), node_comp);
00332 }
00333 }
00334 }
00335
00336
00337
00338 destinations.reserve(nb_dest);
00339 for (int x = xmin; x <= xmax; ++x) {
00340 for (int y = ymin; y <= ymax; ++y)
00341 {
00342 const node &n = nodes[index(map_location(x, y))];
00343 if (n.in - search_counter > 1u) continue;
00344 pathfind::paths::step s =
00345 { n.curr, n.prev, n.movement_left + n.turns_left * total_movement };
00346 destinations.push_back(s);
00347 }
00348 }
00349 }
00350
00351 static pathfind::paths::dest_vect::iterator lower_bound(pathfind::paths::dest_vect &v, const map_location &loc)
00352 {
00353 size_t sz = v.size(), pos = 0;
00354 while (sz)
00355 {
00356 if (v[pos + sz / 2].curr < loc) {
00357 pos = pos + sz / 2 + 1;
00358 sz = sz - sz / 2 - 1;
00359 } else sz = sz / 2;
00360 }
00361 return v.begin() + pos;
00362 }
00363
00364 pathfind::paths::dest_vect::const_iterator pathfind::paths::dest_vect::find(const map_location &loc) const
00365 {
00366 const_iterator i = lower_bound(const_cast<dest_vect &>(*this), loc), i_end = end();
00367 if (i != i_end && i->curr != loc) i = i_end;
00368 return i;
00369 }
00370
00371 void pathfind::paths::dest_vect::insert(const map_location &loc)
00372 {
00373 iterator i = lower_bound(*this, loc), i_end = end();
00374 if (i != i_end && i->curr == loc) return;
00375 paths::step s = { loc, map_location(), 0 };
00376 std::vector<step>::insert(i, s);
00377 }
00378
00379
00380
00381
00382
00383 std::vector<map_location> pathfind::paths::dest_vect::get_path(const const_iterator &j) const
00384 {
00385 std::vector<map_location> path;
00386 if (!j->prev.valid()) {
00387 path.push_back(j->curr);
00388 } else {
00389 const_iterator i = j;
00390 do {
00391 i = find(i->prev);
00392 assert(i != end());
00393 path.push_back(i->curr);
00394 } while (i->prev.valid());
00395 }
00396 std::reverse(path.begin(), path.end());
00397 return path;
00398 }
00399
00400 bool pathfind::paths::dest_vect::contains(const map_location &loc) const
00401 {
00402 return find(loc) != end();
00403 }
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421 pathfind::paths::paths(gamemap const &map, unit_map const &,
00422 const unit& u, std::vector<team> const &teams,
00423 bool force_ignore_zoc, bool allow_teleport, const team &viewing_team,
00424 int additional_turns, bool see_all, bool ignore_units)
00425 : destinations()
00426 {
00427 if (u.side() < 1 || u.side() > int(teams.size())) {
00428 return;
00429 }
00430
00431 find_routes(map, u, u.get_location(), u.movement_left(), destinations, NULL,
00432 teams[u.side()-1], force_ignore_zoc, allow_teleport,
00433 additional_turns, viewing_team, see_all, ignore_units, pathfind::MOVE, NULL);
00434 }
00435
00436
00437
00438
00439 pathfind::paths::~paths()
00440 {
00441 }
00442
00443
00444
00445
00446
00447
00448
00449
00450
00451
00452
00453
00454
00455 pathfind::vision_path::vision_path(gamemap const &map, const unit& viewer,
00456 map_location const &loc, const std::map<map_location, int>& jamming_map)
00457 : paths(), edges()
00458 {
00459 const team & viewer_team = (*resources::teams)[viewer.side()-1];
00460 const int sight_range = viewer.vision();
00461
00462
00463
00464
00465
00466
00467 find_routes(map, viewer, loc, sight_range, destinations, &edges,
00468 viewer_team, true, false, 0, viewer_team, true, true, pathfind::VISION, &jamming_map);
00469
00470 }
00471
00472
00473 pathfind::vision_path::~vision_path()
00474 {
00475 }
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485
00486
00487
00488 pathfind::jamming_path::jamming_path(gamemap const &map, const unit& jammer,
00489 map_location const &loc)
00490 : paths()
00491 {
00492 const team & jammer_team = (*resources::teams)[jammer.side()-1];
00493
00494 const int jamming_range = jammer.jamming();
00495
00496
00497
00498
00499
00500
00501 find_routes(map, jammer, loc, jamming_range, destinations, NULL,
00502 jammer_team, true, false, 0, jammer_team, true, true, pathfind::JAMMING, NULL);
00503 }
00504
00505
00506 pathfind::jamming_path::~jamming_path()
00507 {
00508 }
00509
00510 pathfind::marked_route pathfind::mark_route(const plain_route &rt)
00511 {
00512 marked_route res;
00513
00514 if (rt.steps.empty()) return marked_route();
00515 res.route = rt;
00516
00517 unit_map::const_iterator it = resources::units->find(rt.steps.front());
00518 if (it == resources::units->end()) return marked_route();
00519 unit const& u = *it;
00520
00521 int turns = 0;
00522 int movement = u.movement_left();
00523 const team& unit_team = (*resources::teams)[u.side()-1];
00524 bool zoc = false;
00525
00526 std::vector<map_location>::const_iterator i = rt.steps.begin();
00527
00528 for (; i !=rt.steps.end(); ++i) {
00529 bool last_step = (i+1 == rt.steps.end());
00530
00531
00532 assert(last_step || resources::game_map->on_board(*(i+1)));
00533 const int move_cost = last_step ? 0 : u.movement_cost((*resources::game_map)[*(i+1)]);
00534
00535 team const& viewing_team = (*resources::teams)[resources::screen->viewing_team()];
00536
00537 if (last_step || zoc || move_cost > movement) {
00538
00539
00540
00541
00542 bool capture = resources::game_map->is_village(*i) && ( !unit_team.owns_village(*i)
00543 || (viewing_team.is_enemy(u.side()) && viewing_team.fogged(*i)) );
00544
00545 ++turns;
00546
00547 bool invisible = u.invisible(*i,false);
00548
00549 res.marks[*i] = marked_route::mark(turns, zoc, capture, invisible);
00550
00551 if (last_step) break;
00552
00553 movement = u.total_movement();
00554 if(move_cost > movement) {
00555 return res;
00556 }
00557 }
00558
00559 zoc = enemy_zoc(unit_team, *(i + 1), viewing_team)
00560 && !u.get_ability_bool("skirmisher", *(i+1));
00561
00562 if (zoc) {
00563 movement = 0;
00564 } else {
00565 movement -= move_cost;
00566 }
00567 }
00568
00569 return res;
00570 }
00571
00572 pathfind::shortest_path_calculator::shortest_path_calculator(unit const &u, team const &t,
00573 unit_map const &units, std::vector<team> const &teams, gamemap const &map,
00574 bool ignore_unit, bool ignore_defense, bool see_all)
00575 : unit_(u), viewing_team_(t), units_(units), teams_(teams), map_(map),
00576 movement_left_(unit_.movement_left()),
00577 total_movement_(unit_.total_movement()),
00578 ignore_unit_(ignore_unit), ignore_defense_(ignore_defense),
00579 see_all_(see_all)
00580 {}
00581
00582 double pathfind::shortest_path_calculator::cost(const map_location& loc, const double so_far) const
00583 {
00584 assert(map_.on_board(loc));
00585
00586
00587
00588 if (!see_all_ && viewing_team_.shrouded(loc))
00589 return getNoPathValue();
00590
00591 const t_translation::t_terrain terrain = map_[loc];
00592 int const terrain_cost = unit_.movement_cost(terrain);
00593
00594 VALIDATE(terrain_cost >= 1, _("Terrain with a movement cost less than 1 encountered."));
00595
00596
00597 if (total_movement_ < terrain_cost)
00598 return getNoPathValue();
00599
00600 int other_unit_subcost = 0;
00601 if (!ignore_unit_) {
00602 const unit *other_unit =
00603 get_visible_unit(loc, viewing_team_, see_all_);
00604
00605
00606
00607
00608
00609 if (other_unit)
00610 {
00611 if (teams_[unit_.side() - 1].is_enemy(other_unit->side()))
00612 return getNoPathValue();
00613 else
00614
00615
00616
00617 other_unit_subcost = 1;
00618 }
00619 }
00620
00621
00622
00623
00624 int remaining_movement = movement_left_ - static_cast<int>(so_far);
00625 if (remaining_movement < 0)
00626 remaining_movement = total_movement_ - (-remaining_movement) % total_movement_;
00627
00628
00629 int move_cost = 0;
00630
00631
00632
00633
00634
00635 if (remaining_movement < terrain_cost) {
00636 move_cost += remaining_movement;
00637 remaining_movement = total_movement_;
00638 }
00639
00640
00641 if (!ignore_unit_ && remaining_movement != terrain_cost
00642 && enemy_zoc(teams_[unit_.side()-1], loc, viewing_team_, see_all_)
00643 && !unit_.get_ability_bool("skirmisher", loc)) {
00644
00645 move_cost += remaining_movement;
00646 } else {
00647
00648 move_cost += terrain_cost;
00649 }
00650
00651
00652
00653
00654 const int defense_subcost = ignore_defense_ ? 0 : unit_.defense_modifier(terrain);
00655
00656
00657
00658
00659 return move_cost + (defense_subcost + other_unit_subcost) / 10000.0;
00660 }
00661
00662 pathfind::move_type_path_calculator::move_type_path_calculator(const unit_movement_type& mt, int movement_left, int total_movement, team const &t, gamemap const &map)
00663 : movement_type_(mt), movement_left_(movement_left),
00664 total_movement_(total_movement), viewing_team_(t), map_(map)
00665 {}
00666
00667
00668 double pathfind::move_type_path_calculator::cost(const map_location& loc, const double so_far) const
00669 {
00670 assert(map_.on_board(loc));
00671 if (viewing_team_.shrouded(loc))
00672 return getNoPathValue();
00673
00674 const t_translation::t_terrain terrain = map_[loc];
00675 int const terrain_cost = movement_type_.movement_cost(map_, terrain);
00676
00677 if (total_movement_ < terrain_cost)
00678 return getNoPathValue();
00679
00680 int remaining_movement = movement_left_ - static_cast<int>(so_far);
00681 if (remaining_movement < 0)
00682 remaining_movement = total_movement_ - (-remaining_movement) % total_movement_;
00683
00684 int move_cost = 0;
00685
00686 if (remaining_movement < terrain_cost) {
00687 move_cost += remaining_movement;
00688 }
00689
00690 move_cost += terrain_cost;
00691
00692 return move_cost;
00693 }
00694
00695
00696 pathfind::emergency_path_calculator::emergency_path_calculator(const unit& u, const gamemap& map)
00697 : unit_(u), map_(map)
00698 {}
00699
00700 double pathfind::emergency_path_calculator::cost(const map_location& loc, const double) const
00701 {
00702 assert(map_.on_board(loc));
00703
00704 return unit_.movement_cost(map_[loc]);
00705 }
00706
00707 pathfind::dummy_path_calculator::dummy_path_calculator(const unit&, const gamemap&)
00708 {}
00709
00710 double pathfind::dummy_path_calculator::cost(const map_location&, const double) const
00711 {
00712 return 1.0;
00713 }