00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "global.hpp"
00020
00021 #include "astarnode.hpp"
00022 #include "gamestatus.hpp"
00023 #include "gettext.hpp"
00024 #include "log.hpp"
00025 #include "pathfind.hpp"
00026 #include "util.hpp"
00027 #include "wml_exception.hpp"
00028
00029 #include <cassert>
00030 #include <cmath>
00031 #include <iostream>
00032
00033 #define LOG_PF LOG_STREAM(info, engine)
00034
00035 static gamemap::location find_vacant(const gamemap& map,
00036 const unit_map& units,
00037 const gamemap::location& loc, int depth,
00038 VACANT_TILE_TYPE vacancy,
00039 std::set<gamemap::location>& touched)
00040 {
00041 if(touched.count(loc))
00042 return gamemap::location();
00043
00044 touched.insert(loc);
00045
00046 if (map.on_board(loc) && units.find(loc) == units.end() &&
00047 (vacancy == VACANT_ANY || map.is_castle(loc))) {
00048 return loc;
00049 } else if(depth == 0) {
00050 return gamemap::location();
00051 } else {
00052 gamemap::location adj[6];
00053 get_adjacent_tiles(loc,adj);
00054 for(int i = 0; i != 6; ++i) {
00055 if(!map.on_board(adj[i]) || (vacancy == VACANT_CASTLE && !map.is_castle(adj[i])))
00056 continue;
00057
00058 const gamemap::location res =
00059 find_vacant(map, units, adj[i], depth - 1, vacancy, touched);
00060
00061 if (map.on_board(res))
00062 return res;
00063 }
00064
00065 return gamemap::location();
00066 }
00067 }
00068
00069 gamemap::location find_vacant_tile(const gamemap& map,
00070 const unit_map& units,
00071 const gamemap::location& loc,
00072 VACANT_TILE_TYPE vacancy)
00073 {
00074 for(int i = 1; i != 50; ++i) {
00075 std::set<gamemap::location> touch;
00076 const gamemap::location res = find_vacant(map,units,loc,i,vacancy,touch);
00077 if(map.on_board(res))
00078 return res;
00079 }
00080
00081 return gamemap::location();
00082 }
00083
00084 bool enemy_zoc(gamemap const &map,
00085 unit_map const &units,
00086 std::vector<team> const &teams,
00087 gamemap::location const &loc, team const &viewing_team, unsigned int side, bool see_all)
00088 {
00089 gamemap::location locs[6];
00090 const team ¤t_team = teams[side-1];
00091 get_adjacent_tiles(loc,locs);
00092 for(int i = 0; i != 6; ++i) {
00093 unit_map::const_iterator it;
00094 it = find_visible_unit(units, locs[i], map, teams, viewing_team,see_all);
00095
00096 if (it != units.end() && it->second.side() != side &&
00097 current_team.is_enemy(it->second.side()) && it->second.emits_zoc()) {
00098 return true;
00099 }
00100 }
00101
00102 return false;
00103 }
00104
00105 static void find_routes(const gamemap& map, const unit_map& units,
00106 const unit& u, const gamemap::location& loc,
00107 int move_left, paths::routes_map& routes,
00108 std::vector<team> const &teams,
00109 bool force_ignore_zocs, bool allow_teleport, int turns_left,
00110 bool starting_pos, const team &viewing_team,
00111 bool see_all, bool ignore_units)
00112 {
00113 team const ¤t_team = teams[u.side()-1];
00114
00115
00116 std::vector<gamemap::location> locs(6);
00117 get_adjacent_tiles(loc,&locs[0]);
00118
00119
00120
00121
00122 if (allow_teleport && map.is_village(loc) && current_team.owns_village(loc) &&
00123 (starting_pos || ignore_units ||
00124 find_visible_unit(units, loc, map, teams, viewing_team,see_all) == units.end())) {
00125
00126
00127
00128 const std::set<gamemap::location>& villages = current_team.villages();
00129 for(std::set<gamemap::location>::const_iterator t = villages.begin(); t != villages.end(); ++t) {
00130 if ((see_all || !viewing_team.is_enemy(u.side()) || !viewing_team.fogged(*t))
00131 && (ignore_units || find_visible_unit(units, *t, map, teams, viewing_team, see_all) == units.end())) {
00132 locs.push_back(*t);
00133 }
00134 }
00135 }
00136
00137
00138 for(std::vector<gamemap::location>::const_iterator i=locs.begin(); i != locs.end(); ++i) {
00139 const gamemap::location& currentloc = *i;
00140
00141 if (!map.on_board(currentloc))
00142 continue;
00143
00144
00145 const int move_cost = u.movement_cost(map[currentloc]);
00146 if (move_cost > move_left && (turns_left < 1 || move_cost > u.total_movement()))
00147 continue;
00148
00149 int new_move_left = move_left - move_cost;
00150 int new_turns_left = turns_left;
00151 if (new_move_left < 0) {
00152 --new_turns_left;
00153 new_move_left = u.total_movement() - move_cost;
00154 }
00155 const int new_turns_moves = new_turns_left * u.total_movement();
00156
00157
00158 int old_move_left = -1;
00159 const paths::routes_map::const_iterator old_rt = routes.find(currentloc);
00160 if (old_rt != routes.end())
00161 old_move_left = old_rt->second.move_left;
00162
00163
00164
00165 if(old_move_left >= new_turns_moves + new_move_left)
00166 continue;
00167
00168 if (!ignore_units) {
00169
00170 const unit_map::const_iterator unit_it =
00171 find_visible_unit(units, currentloc, map, teams, viewing_team,see_all);
00172 if (unit_it != units.end() && current_team.is_enemy(unit_it->second.side()))
00173 continue;
00174
00175
00176
00177
00178 if (!force_ignore_zocs && new_move_left > 0
00179 && enemy_zoc(map,units,teams, currentloc, viewing_team,u.side(),see_all)
00180 && !u.get_ability_bool("skirmisher", currentloc)) {
00181 new_move_left = 0;
00182
00183 if(old_move_left >= new_turns_moves + 0)
00184 continue;
00185 }
00186 }
00187
00188 paths::route& src_route = routes[loc];
00189 paths::route& new_route = routes[currentloc];
00190 new_route.steps = src_route.steps;
00191 new_route.steps.push_back(loc);
00192 new_route.move_left = new_turns_moves + new_move_left;
00193
00194 if (new_route.move_left > 0) {
00195 find_routes(map, units, u, currentloc,
00196 new_move_left, routes, teams, force_ignore_zocs,
00197 allow_teleport, new_turns_left, false, viewing_team,
00198 see_all, ignore_units);
00199 }
00200 }
00201 }
00202
00203 paths::paths(gamemap const &map,
00204 unit_map const &units,
00205 gamemap::location const &loc,
00206 std::vector<team> const &teams,
00207 bool force_ignore_zoc,
00208 bool allow_teleport, const team &viewing_team,
00209 int additional_turns, bool see_all, bool ignore_units)
00210 {
00211 const unit_map::const_iterator i = units.find(loc);
00212 if(i == units.end()) {
00213 std::cerr << "unit not found\n";
00214 return;
00215 }
00216
00217 if(i->second.side() < 1 || i->second.side() > teams.size()) {
00218 return;
00219 }
00220
00221 routes[loc].move_left = i->second.movement_left();
00222 find_routes(map,units,i->second,loc,
00223 i->second.movement_left(),routes,teams,force_ignore_zoc,
00224 allow_teleport,additional_turns,true,viewing_team,
00225 see_all, ignore_units);
00226 }
00227
00228 int route_turns_to_complete(const unit& u, paths::route& rt, const team &viewing_team,
00229 const unit_map& units, const std::vector<team>& teams, const gamemap& map)
00230 {
00231 if(rt.steps.empty())
00232 return 0;
00233
00234 int turns = 0;
00235 int movement = u.movement_left();
00236 const team& unit_team = teams[u.side()-1];
00237 bool zoc = false;
00238
00239 for (std::vector<gamemap::location>::const_iterator i = rt.steps.begin();
00240 i !=rt.steps.end(); i++) {
00241 bool last_step = (i+1 == rt.steps.end());
00242
00243
00244 assert(last_step || map.on_board(*(i+1)));
00245 const int move_cost = last_step ? 0 : u.movement_cost(map[*(i+1)]);
00246
00247 if (last_step || zoc || move_cost > movement) {
00248
00249
00250
00251
00252 bool capture = map.is_village(*i) && ( !unit_team.owns_village(*i)
00253 || (viewing_team.is_enemy(u.side()) && viewing_team.fogged(*i)) );
00254
00255 ++turns;
00256
00257 bool invisible = u.invisible(*i,units,teams,false);
00258
00259 rt.waypoints[*i] = paths::route::waypoint(turns, zoc, capture, invisible);
00260
00261 if (last_step) break;
00262
00263 movement = u.total_movement();
00264 if(move_cost > movement) {
00265 return -1;
00266 }
00267 }
00268
00269 zoc = enemy_zoc(map,units,teams, *(i+1), viewing_team,u.side())
00270 && !u.get_ability_bool("skirmisher", *(i+1));
00271
00272 if (zoc) {
00273 movement = 0;
00274 } else {
00275 movement -= move_cost;
00276 }
00277 }
00278
00279 return turns;
00280 }
00281
00282
00283 shortest_path_calculator::shortest_path_calculator(unit const &u, team const &t,
00284 unit_map const &units, std::vector<team> const &teams, gamemap const &map,
00285 bool ignore_unit, bool ignore_defense)
00286 : unit_(u), viewing_team_(t), units_(units), teams_(teams), map_(map),
00287 movement_left_(unit_.movement_left()),
00288 total_movement_(unit_.total_movement()),
00289 ignore_unit_(ignore_unit), ignore_defense_(ignore_defense)
00290 {
00291 }
00292
00293 double shortest_path_calculator::cost(const gamemap::location& ,const gamemap::location& loc, const double so_far) const
00294 {
00295 assert(map_.on_board(loc));
00296
00297
00298
00299 if (viewing_team_.shrouded(loc))
00300 return getNoPathValue();
00301
00302 const t_translation::t_terrain terrain = map_[loc];
00303 int const base_cost = unit_.movement_cost(terrain);
00304
00305 VALIDATE(base_cost >= 1, _("Terrain with a movement cost less than 1 encountered."));
00306
00307
00308 if (total_movement_ < base_cost)
00309 return getNoPathValue();
00310
00311 int other_unit_subcost = 0;
00312 if (!ignore_unit_) {
00313 unit_map::const_iterator
00314 other_unit = find_visible_unit(units_, loc, map_, teams_, viewing_team_);
00315
00316
00317
00318
00319
00320 if (other_unit != units_.end()) {
00321 if (teams_[unit_.side()-1].is_enemy(other_unit->second.side()))
00322 return getNoPathValue();
00323 else
00324
00325
00326
00327 other_unit_subcost = 1;
00328 }
00329 }
00330
00331
00332
00333
00334 int remaining_movement = movement_left_ - static_cast<int>(so_far);
00335 if (remaining_movement < 0)
00336 remaining_movement = total_movement_ - (-remaining_movement) % total_movement_;
00337
00338
00339 int move_cost = base_cost;
00340
00341
00342
00343
00344 bool need_new_turn = base_cost > remaining_movement;
00345
00346
00347 if (need_new_turn)
00348 move_cost += remaining_movement;
00349
00350 if (!ignore_unit_ && enemy_zoc(map_,units_,teams_, loc, viewing_team_, unit_.side())
00351 && !unit_.get_ability_bool("skirmisher", loc)) {
00352
00353
00354 move_cost += need_new_turn ? total_movement_ : remaining_movement;
00355 }
00356
00357
00358
00359
00360 const int defense_subcost = ignore_defense_ ? 0 : unit_.defense_modifier(terrain);
00361
00362
00363
00364
00365 return move_cost + (defense_subcost + other_unit_subcost) / 10000.0;
00366 }
00367
00368 emergency_path_calculator::emergency_path_calculator(const unit& u, const gamemap& map)
00369 : unit_(u), map_(map)
00370 {
00371 }
00372
00373 double emergency_path_calculator::cost(const gamemap::location&,const gamemap::location& loc, const double) const
00374 {
00375 assert(map_.on_board(loc));
00376
00377 return unit_.movement_cost(map_[loc]);
00378 }