int aspect_attacks::rate_terrain(const unit& u, const map_location& loc) { gamemap &map_ = *resources::game_map; const t_translation::t_terrain terrain = map_.get_terrain(loc); const int defense = u.defense_modifier(terrain); int rating = 100 - defense; const int healing_value = 10; const int friendly_village_value = 5; const int neutral_village_value = 10; const int enemy_village_value = 15; if(map_.gives_healing(terrain) && u.get_ability_bool("regenerate",loc) == false) { rating += healing_value; } if(map_.is_village(terrain)) { int owner = village_owner(loc, *resources::teams) + 1; if(owner == u.side()) { rating += friendly_village_value; } else if(owner == 0) { rating += neutral_village_value; } else { rating += enemy_village_value; } } return rating; }
int default_ai_context_impl::rate_terrain(const unit& u, const map_location& loc) const { const gamemap &map_ = resources::gameboard->map(); const t_translation::terrain_code terrain = map_.get_terrain(loc); const int defense = u.defense_modifier(terrain); int rating = 100 - defense; const int healing_value = 10; const int friendly_village_value = 5; const int neutral_village_value = 10; const int enemy_village_value = 15; if(map_.gives_healing(terrain) && u.get_ability_bool("regenerate", loc, *resources::gameboard) == false) { rating += healing_value; } if(map_.is_village(terrain)) { int owner = resources::gameboard->village_owner(loc) + 1; if(owner == get_side()) { rating += friendly_village_value; } else if(owner == 0) { rating += neutral_village_value; } else { rating += enemy_village_value; } } return rating; }
std::set<map_location> get_teleport_locations(const unit &u, const unit_map &units, const team &viewing_team, bool see_all, bool ignore_units) { std::set<map_location> res; if (!u.get_ability_bool("teleport")) return res; const team ¤t_team = (*resources::teams)[u.side() - 1]; const map_location &loc = u.get_location(); foreach (const map_location &l, current_team.villages()) { // This must be a vacant village (or occupied by the unit) // to be able to teleport. if (!see_all && viewing_team.is_enemy(u.side()) && viewing_team.fogged(l)) continue; if (!ignore_units && l != loc && get_visible_unit(units, l, viewing_team, see_all)) continue; res.insert(l); } return res; }
marked_route mark_route(const plain_route &rt, const std::vector<map_location>& waypoints, const unit &u, const team &viewing_team, const unit_map &units, const std::vector<team> &teams, const gamemap &map) { marked_route res; if (rt.steps.empty()) return res; res.steps = rt.steps; int turns = 0; int movement = u.movement_left(); const team& unit_team = teams[u.side()-1]; bool zoc = false; std::vector<map_location>::const_iterator i = rt.steps.begin(), w = waypoints.begin(); // TODO fix the name confusion with waypoints and route.waypoints for (; i !=rt.steps.end(); i++) { bool last_step = (i+1 == rt.steps.end()); // move_cost of the next step is irrelevant for the last step assert(last_step || map.on_board(*(i+1))); const int move_cost = last_step ? 0 : u.movement_cost(map[*(i+1)]); bool capture = false; bool pass_here = false; if (w != waypoints.end() && *i == *w) { w++; pass_here = true; } if (last_step || zoc || move_cost > movement) { // check if we stop an a village and so maybe capture it // if it's an enemy unit and a fogged village, we assume a capture // (if he already owns it, we can't know that) // if it's not an enemy, we can always know if he owns the village bool capture = map.is_village(*i) && ( !unit_team.owns_village(*i) || (viewing_team.is_enemy(u.side()) && viewing_team.fogged(*i)) ); ++turns; bool invisible = u.invisible(*i,units,teams,false); res.waypoints[*i] = marked_route::waypoint(turns, pass_here, zoc, capture, invisible); if (last_step) break; // finished and we used dummy move_cost movement = u.total_movement(); if(move_cost > movement) { return res; //we can't reach destination } } else if (pass_here) { bool invisible = u.invisible(*i,units,teams,false); res.waypoints[*i] = marked_route::waypoint(0, pass_here, zoc, false, invisible); } zoc = enemy_zoc(units, teams, *(i + 1), viewing_team,u.side()) && !u.get_ability_bool("skirmisher", *(i+1)); if (zoc || capture) { movement = 0; } else { movement -= move_cost; } } return res; }
static void find_routes(const gamemap& map, const unit_map& units, const unit& u, const map_location& loc, int move_left, paths::dest_vect &destinations, std::vector<team> const &teams, bool force_ignore_zocs, bool allow_teleport, int turns_left, const team &viewing_team, bool see_all, bool ignore_units) { const team& current_team = teams[u.side() - 1]; std::set<map_location> teleports; if (allow_teleport) { teleports = get_teleport_locations(u, units, viewing_team, see_all, ignore_units); } const int total_movement = u.total_movement(); std::vector<map_location> locs(6 + teleports.size()); std::copy(teleports.begin(), teleports.end(), locs.begin() + 6); search_counter += 2; if (search_counter == 0) search_counter = 2; static std::vector<node> nodes; nodes.resize(map.w() * map.h()); indexer index(map.w(), map.h()); comp node_comp(nodes); int xmin = loc.x, xmax = loc.x, ymin = loc.y, ymax = loc.y, nb_dest = 1; nodes[index(loc)] = node(move_left, turns_left, map_location::null_location, loc); std::vector<int> pq; pq.push_back(index(loc)); while (!pq.empty()) { node& n = nodes[pq.front()]; std::pop_heap(pq.begin(), pq.end(), node_comp); pq.pop_back(); n.in = search_counter; get_adjacent_tiles(n.curr, &locs[0]); for (int i = teleports.count(n.curr) ? locs.size() : 6; i-- > 0; ) { if (!locs[i].valid(map.w(), map.h())) continue; node& next = nodes[index(locs[i])]; bool next_visited = next.in - search_counter <= 1u; // Classic Dijkstra allow to skip chosen nodes (with next.in==search_counter) // But the cost function and hex grid allow to also skip visited nodes: // if next was visited, then we already have a path 'src-..-n2-next' // - n2 was chosen before n, meaning that it is nearer to src. // - the cost of 'n-next' can't be smaller than 'n2-next' because // cost is independent of direction and we don't have more MP at n // (important because more MP may allow to avoid waiting next turn) // Thus, 'src-..-n-next' can't be shorter. if (next_visited) continue; const int move_cost = u.movement_cost(map[locs[i]]); node t = node(n.movement_left, n.turns_left, n.curr, locs[i]); if (t.movement_left < move_cost) { t.movement_left = total_movement; t.turns_left--; } if (t.movement_left < move_cost || t.turns_left < 0) continue; t.movement_left -= move_cost; if (!ignore_units) { const unit *v = get_visible_unit(units, locs[i], viewing_team, see_all); if (v && current_team.is_enemy(v->side())) continue; if (!force_ignore_zocs && t.movement_left > 0 && enemy_zoc(units, teams, locs[i], viewing_team, u.side(), see_all) && !u.get_ability_bool("skirmisher", locs[i])) { t.movement_left = 0; } } ++nb_dest; int x = locs[i].x; if (x < xmin) xmin = x; if (xmax < x) xmax = x; int y = locs[i].y; if (y < ymin) ymin = y; if (ymax < y) ymax = y; bool in_list = next.in == search_counter + 1; t.in = search_counter + 1; next = t; // if already in the priority queue then we just update it, else push it. if (in_list) { // never happen see next_visited above std::push_heap(pq.begin(), std::find(pq.begin(), pq.end(), index(locs[i])) + 1, node_comp); } else { pq.push_back(index(locs[i])); std::push_heap(pq.begin(), pq.end(), node_comp); } } } // Build the routes for every map_location that we reached. // The ordering must be compatible with map_location::operator<. destinations.reserve(nb_dest); for (int x = xmin; x <= xmax; ++x) { for (int y = ymin; y <= ymax; ++y) { const node &n = nodes[index(map_location(x, y))]; if (n.in - search_counter > 1u) continue; paths::step s = { n.curr, n.prev, n.movement_left + n.turns_left * total_movement }; destinations.push_back(s); } } }