pathfind/pathfind.cpp

Go to the documentation of this file.
00001 /* $Id: pathfind.cpp 54129 2012-05-08 19:03:00Z anonymissimus $ */
00002 /*
00003    Copyright (C) 2003 by David White <dave@whitevine.net>
00004    Copyright (C) 2005 - 2012 by Guillaume Melquiond <guillaume.melquiond@gmail.com>
00005    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00006 
00007    This program is free software; you can redistribute it and/or modify
00008    it under the terms of the GNU General Public License as published by
00009    the Free Software Foundation; either version 2 of the License, or
00010    (at your option) any later version.
00011    This program is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY.
00013 
00014    See the COPYING file for more details.
00015 */
00016 
00017 /**
00018  * @file
00019  * Various pathfinding functions and utilities.
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     // Iterate out 50 hexes from loc
00055     for (int distance = 0; distance < 50; ++distance) {
00056         if (pending_tiles_to_check.empty())
00057             return map_location();
00058         //Copy over the hexes to check and clear the old set
00059         std::set<map_location> tiles_checking;
00060         tiles_checking.swap(pending_tiles_to_check);
00061         //Iterate over all the hexes we need to check
00062         foreach (const map_location &loc, tiles_checking)
00063         {
00064             //If this area is not a castle but should, skip it.
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             //If the unit can't reach the tile and we have searched
00069             //an area of at least radius 10 (arbitrary), skip the tile.
00070             //Neccessary for cases such as an unreachable
00071             //starting hex surrounded by 6 other unreachable hexes, in which case
00072             //the algorithm would not even search distance==1
00073             //even if there's a reachable hex for distance==2.
00074             if (pass_check_and_unreachable && distance > 10) continue;
00075             //If the hex is empty and we do either no pass check or the hex is reachable, return it.
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                 // Add the tile to be checked if it hasn't already been and
00083                 // isn't being checked.
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  * Determines if a given location is in an enemy zone of control.
00098  *
00099  * @param current_team  The moving team (only ZoC of enemies of this team are considered).
00100  * @param loc           The location to check.
00101  * @param viewing_team  Only units visible to this team are considered.
00102  * @param see_all       If true, all units are considered (and viewing_team is ignored).
00103  *
00104  * @return true iff a visible enemy exerts zone of control over loc.
00105  */
00106 bool pathfind::enemy_zoc(team const &current_team, map_location const &loc,
00107                          team const &viewing_team, bool see_all)
00108 {
00109     // Check the adjacent tiles.
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     // No adjacent tiles had an enemy exerting ZoC over loc.
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      * If equal to search_counter, the node is off the list.
00133      * If equal to search_counter + 1, the node is on the list.
00134      * Otherwise it is outdated.
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  * Creates a list of routes that a unit can traverse from the provided location.
00182  * (This is called when creating pathfind::paths and descendant classes.)
00183  *
00184  * @param map[in]              The gamemap to use (for identifying terrain).
00185  * @param units[in]            Currently unused.
00186  * @param u[in]                The unit whose moves and movement type will be used.
00187  * @param loc[in]              The location at which to begin the routes.
00188  * @param move_left[in]        The number of movement points left for the current turn.
00189  * @param destinations[out]    The traversable routes.
00190  * @param edges[out]           The hexes (possibly off-map) adjacent to those in
00191  *                             destinations. (It is permissible for this to contain
00192  *                             some hexes that are also in destinations.)
00193  * @param teams[in]            The teams of the game (for recognizing enemies).
00194  * @param force_ignore_zoc[in] Set to true to completely ignore zones of control.
00195  * @param allow_teleport[in]   Set to true to consider teleportation abilities.
00196  * @param turns_left[in]       The number of additional turns of movement to use,
00197  *                             in addition to the current turn.
00198  * @param viewing_team[in]     Usually the current team, except for "show enemy
00199  *                             moves", etc. Relevant if allowing teleports or
00200  *                             if not ignoring units.
00201  * @param see_all[in]          Set to true to remove unit visibility from consideration.
00202  * @param ignore_units[in]     Set to true if units should never obstruct paths
00203  *                             (implies ignoring ZoC as well).
00204  * @param vision[in]           Set if the move_costs or the vision_costs are used.
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 &current_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             // Classic Dijkstra allow to skip chosen nodes (with next.in==search_counter)
00260             // But the cost function and hex grid allow to also skip visited nodes:
00261             // if next was visited, then we already have a path 'src-..-n2-next'
00262             // - n2 was chosen before n, meaning that it is nearer to src.
00263             // - the cost of 'n-next' can't be smaller than 'n2-next' because
00264             //   cost is independent of direction and we don't have more MP at n
00265             //   (important because more MP may allow to avoid waiting next turn)
00266             // Thus, 'src-..-n-next' can't be shorter.
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             // if already in the priority queue then we just update it, else push it.
00327             if (in_list) { // never happen see next_visited above
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     // Build the routes for every map_location that we reached.
00337     // The ordering must be compatible with map_location::operator<.
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  * Returns the path going from the source point (included) to the
00381  * destination point @a j (excluded).
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  * Construct a list of paths for the specified unit.
00407  *
00408  * This function is used for several purposes, including showing a unit's
00409  * potential moves and generating currently possible paths.
00410  * @param map              The gamemap to use (for identifying terrain).
00411  * @param units            The unit_map to use (for identifying units).
00412  * @param u                The unit whose moves and movement type will be used.
00413  * @param teams            The teams of the game (for recognizing enemies).
00414  * @param force_ignore_zoc Set to true to completely ignore zones of control.
00415  * @param allow_teleport   Set to true to consider teleportation abilities.
00416  * @param viewing_team     Usually the current team, except for "show enemy moves", etc.
00417  * @param additional_turns The number of turns to account for, in addition to the current.
00418  * @param see_all          Set to true to remove unit visibility from consideration.
00419  * @param ignore_units     Set to true if units should never obstruct paths (implies ignoring ZoC as well).
00420  */
00421 pathfind::paths::paths(gamemap const &map, unit_map const &/*units*/,
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  * Virtual destructor to support child classes.
00438  */
00439 pathfind::paths::~paths()
00440 {
00441 }
00442 
00443 /**
00444  * Constructs a list of vision paths for a unit.
00445  *
00446  * This is used to construct a list of hexes that the indicated unit can see.
00447  * It differs from pathfinding in that it will only ever go out one turn,
00448  * and that it will also collect a set of border hexes (the "one hex beyond"
00449  * movement to which vision extends).
00450  * @param map        The gamemap to use (for identifying terrain).
00451  * @param viewer     The unit doing the viewing.
00452  * @param loc        The location from which the viewing occurs
00453  *                   (does not have to be the unit's location).
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     // Finding routes: ignore ZoC, disallow teleports, zero turns left,
00463     // (viewing team), see all, and ignore units.
00464     // (The "see all" setting does not currently matter since teleports are
00465     // not allowed and units are ignored. If something changes to make it
00466     // significant, I might have incorrectly guessed the appropriate value.)
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 /// Default destructor
00473 pathfind::vision_path::~vision_path()
00474 {
00475 }
00476 
00477 
00478 /**
00479  * Constructs a list of jamming paths for a unit.
00480  *
00481  * This is used to construct a list of hexes that the indicated unit can jam.
00482  * It differs from pathfinding in that it will only ever go out one turn.
00483  * @param map        The gamemap to use (for identifying terrain).
00484  * @param jammer     The unit doing the jamming.
00485  * @param loc        The location from which the jamming occurs
00486  *                   (does not have to be the unit's location).
00487  */
00488 pathfind::jamming_path::jamming_path(gamemap const &map, const unit& jammer,
00489                                    map_location const &loc)
00490     : paths()//, edges()
00491 {
00492     const team & jammer_team = (*resources::teams)[jammer.side()-1];
00493 
00494     const int jamming_range = jammer.jamming();
00495 
00496     // Finding routes: ignore ZoC, disallow teleports, zero turns left,
00497     // (jamming team), see all, and ignore units.
00498     // (The "see all" setting does not currently matter since teleports are
00499     // not allowed and units are ignored. If something changes to make it
00500     // significant, I might have incorrectly guessed the appropriate value.)
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 /// Default destructor
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         // move_cost of the next step is irrelevant for the last step
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             // check if we stop an a village and so maybe capture it
00539             // if it's an enemy unit and a fogged village, we assume a capture
00540             // (if he already owns it, we can't know that)
00541             // if it's not an enemy, we can always know if he owns the village
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; // finished and we used dummy move_cost
00552 
00553             movement = u.total_movement();
00554             if(move_cost > movement) {
00555                 return res; //we can't reach destination
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     // loc is shrouded, consider it impassable
00587     // NOTE: This is why AI must avoid to use shroud
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     // Pathfinding heuristic: the cost must be at least 1
00594     VALIDATE(terrain_cost >= 1, _("Terrain with a movement cost less than 1 encountered."));
00595 
00596     // total MP is not enough to move on this terrain: impassable
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         // We can't traverse visible enemy and we also prefer empty hexes
00606         // (less blocking in multi-turn moves and better when exploring fog,
00607         // because we can't stop on a friend)
00608 
00609         if (other_unit)
00610         {
00611             if (teams_[unit_.side() - 1].is_enemy(other_unit->side()))
00612                 return getNoPathValue();
00613             else
00614                 // This value will be used with the defense_subcost (see below)
00615                 // The 1 here means: consider occupied hex as a -1% defense
00616                 // (less important than 10% defense because friends may move)
00617                 other_unit_subcost = 1;
00618         }
00619     }
00620 
00621     // Compute how many movement points are left in the game turn
00622     // needed to reach the previous hex.
00623     // total_movement_ is not zero, thanks to the pathfinding heuristic
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     // this will sum all different costs of this move
00629     int move_cost = 0;
00630 
00631     // Suppose that we have only 2 remaining MP and want to move onto a hex
00632     // costing 3 MP. We don't have enough MP now, so we must end our turn here,
00633     // thus spend our remaining MP by waiting (next turn, with full MP, we will
00634     // be able to move on that hex)
00635     if (remaining_movement < terrain_cost) {
00636         move_cost += remaining_movement;
00637         remaining_movement = total_movement_; // we consider having full MP now
00638     }
00639 
00640     // check ZoC
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         // entering ZoC cost all remaining MP
00645         move_cost += remaining_movement;
00646     } else {
00647         // empty hex, pay only the terrain cost
00648         move_cost += terrain_cost;
00649     }
00650 
00651     // We will add a tiny cost based on terrain defense, so the pathfinding
00652     // will prefer good terrains between 2 with the same MP cost
00653     // Keep in mind that defense_modifier is inverted (= 100 - defense%)
00654     const int defense_subcost = ignore_defense_ ? 0 : unit_.defense_modifier(terrain);
00655 
00656     // We divide subcosts by 100 * 100, because defense is 100-based and
00657     // we don't want any impact on move cost for less then 100-steps path
00658     // (even ~200 since mean defense is around ~50%)
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 // This is an simplified version of shortest_path_calculator (see above for explanation)
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Fri May 25 2012 01:03:07 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs