bool can_generate(const gamemap& map, const std::vector<team>& teams, const unit_map& units, const unit& u, const map_location& loc) { if (!map.on_board(loc)) { return false; } if (u.movement_cost(map[loc]) == unit_movement_type::UNREACHABLE) { return false; } unit_map::const_iterator it = units.find(loc, false); if (it.valid() && !it->can_stand(u)) { return false; } map_location locs[6]; get_adjacent_tiles(loc, locs); for (int i = 0; i != 6; ++i) { if (!map.on_board(locs[i])) { continue; } if (u.movement_cost(map[locs[i]]) != unit_movement_type::UNREACHABLE) { return true; } } return false; }
bool display_context::unit_can_move(const unit &u) const { if(!u.attacks_left() && u.movement_left()==0) return false; // Units with goto commands that have already done their gotos this turn // (i.e. don't have full movement left) should have red globes. if(u.has_moved() && u.has_goto()) { return false; } const team ¤t_team = get_team(u.side()); map_location locs[6]; get_adjacent_tiles(u.get_location(), locs); for(int n = 0; n != 6; ++n) { if (map().on_board(locs[n])) { const unit_map::const_iterator i = units().find(locs[n]); if (i.valid() && !i->incapacitated() && current_team.is_enemy(i->side())) { return true; } if (u.movement_cost(map()[locs[n]]) <= u.movement_left()) { return true; } } } return false; }
bool texpedite::can_move(const unit& u) { for (size_t i = 0; i < city_.adjacent_size_; i ++) { if (u.movement_cost(map_[city_.adjacent_[i]]) <= u.movement_left()) { return true; } } return false; }
int path_cost(std::vector<map_location> const& path, unit const& u) { if(path.size() < 2) return 0; map_location const& dest = path.back(); if((resources::game_map->is_village(dest) && !resources::teams->at(u.side()-1).owns_village(dest)) || pathfind::enemy_zoc(*resources::teams,dest,resources::teams->at(u.side()-1),u.side())) return u.total_movement(); int result = 0; gamemap const& map = *resources::game_map; BOOST_FOREACH(map_location const& loc, std::make_pair(path.begin()+1,path.end())) result += u.movement_cost(map[loc]); return result; }
int path_cost(std::vector<map_location> const& path, unit const& u) { if(path.size() < 2) return 0; team const& u_team = resources::teams->at(u.side()-1); map_location const& dest = path.back(); if ( (resources::gameboard->map().is_village(dest) && !u_team.owns_village(dest)) || pathfind::enemy_zoc(u_team, dest, u_team) ) return u.total_movement(); int result = 0; gamemap const& map = resources::gameboard->map(); for(map_location const& loc : std::make_pair(path.begin()+1,path.end())) { result += u.movement_cost(map[loc]); } return result; }
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); } } }
bool basic_unit_filter_impl::internal_matches_filter(const unit & u, const map_location& loc, const unit* u2) const { if (!vcfg["name"].blank() && vcfg["name"].t_str() != u.name()) { return false; } if (!vcfg["id"].empty()) { std::vector<std::string> id_list = utils::split(vcfg["id"]); if (std::find(id_list.begin(), id_list.end(), u.id()) == id_list.end()) { return false; } } // Allow 'speaker' as an alternative to id, since people use it so often if (!vcfg["speaker"].blank() && vcfg["speaker"].str() != u.id()) { return false; } if (vcfg.has_child("filter_location")) { if (vcfg.count_children("filter_location") > 1) { FAIL("Encountered multiple [filter_location] children of a standard unit filter. " "This is not currently supported and in all versions of wesnoth would have " "resulted in the later children being ignored. You must use [and] or similar " "to achieve the desired result."); } terrain_filter filt(vcfg.child("filter_location"), &fc_, use_flat_tod_); if (!filt.match(loc)) { return false; } } if(vcfg.has_child("filter_side")) { if (vcfg.count_children("filter_side") > 1) { FAIL("Encountered multiple [filter_side] children of a standard unit filter. " "This is not currently supported and in all versions of wesnoth would have " "resulted in the later children being ignored. You must use [and] or similar " "to achieve the desired result."); } side_filter filt(vcfg.child("filter_side"), &fc_); if(!filt.match(u.side())) return false; } // Also allow filtering on location ranges outside of the location filter if (!vcfg["x"].blank() || !vcfg["y"].blank()){ if(vcfg["x"] == "recall" && vcfg["y"] == "recall") { //locations on the map are considered to not be on a recall list if (fc_.get_disp_context().map().on_board(loc)) { return false; } } else if(vcfg["x"].empty() && vcfg["y"].empty()) { return false; } else if(!loc.matches_range(vcfg["x"], vcfg["y"])) { return false; } } // The type could be a comma separated list of types if (!vcfg["type"].empty()) { std::vector<std::string> types = utils::split(vcfg["type"]); if (std::find(types.begin(), types.end(), u.type_id()) == types.end()) { return false; } } // Shorthand for all advancements of a given type if (!vcfg["type_tree"].empty()) { std::set<std::string> types; for(const std::string type : utils::split(vcfg["type_tree"])) { if(types.count(type)) { continue; } if(const unit_type* ut = unit_types.find(type)) { const auto& tree = ut->advancement_tree(); types.insert(tree.begin(), tree.end()); types.insert(type); } } if(types.find(u.type_id()) == types.end()) { return false; } } // The variation_type could be a comma separated list of types if (!vcfg["variation"].empty()) { std::vector<std::string> types = utils::split(vcfg["variation"]); if (std::find(types.begin(), types.end(), u.variation()) == types.end()) { return false; } } // The has_variation_type could be a comma separated list of types if (!vcfg["has_variation"].empty()) { bool match = false; // If this unit is a variation itself then search in the base unit's variations. const unit_type* const type = u.variation().empty() ? &u.type() : unit_types.find(u.type().base_id()); assert(type); for (const std::string& variation_id : utils::split(vcfg["has_variation"])) { if (type->has_variation(variation_id)) { match = true; break; } } if (!match) return false; } if (!vcfg["ability"].empty()) { bool match = false; for (const std::string& ability_id : utils::split(vcfg["ability"])) { if (u.has_ability_by_id(ability_id)) { match = true; break; } } if (!match) return false; } if (!vcfg["race"].empty()) { std::vector<std::string> races = utils::split(vcfg["race"]); if (std::find(races.begin(), races.end(), u.race()->id()) == races.end()) { return false; } } if (!vcfg["gender"].blank() && string_gender(vcfg["gender"]) != u.gender()) { return false; } if (!vcfg["side"].empty() && vcfg["side"].to_int(-999) != u.side()) { std::vector<std::string> sides = utils::split(vcfg["side"]); const std::string u_side = std::to_string(u.side()); if (std::find(sides.begin(), sides.end(), u_side) == sides.end()) { return false; } } // handle statuses list if (!vcfg["status"].empty()) { bool status_found = false; for (const std::string status : utils::split(vcfg["status"])) { if(u.get_state(status)) { status_found = true; break; } } if(!status_found) { return false; } } if (vcfg.has_child("has_attack")) { const vconfig& weap_filter = vcfg.child("has_attack"); bool has_weapon = false; for(const attack_type& a : u.attacks()) { if(a.matches_filter(weap_filter.get_parsed_config())) { has_weapon = true; break; } } if(!has_weapon) { return false; } } else if (!vcfg["has_weapon"].blank()) { std::string weapon = vcfg["has_weapon"]; bool has_weapon = false; for(const attack_type& a : u.attacks()) { if(a.id() == weapon) { has_weapon = true; break; } } if(!has_weapon) { return false; } } if (!vcfg["role"].blank() && vcfg["role"].str() != u.get_role()) { return false; } if (!vcfg["ai_special"].blank() && ((vcfg["ai_special"].str() == "guardian") != u.get_state(unit::STATE_GUARDIAN))) { return false; } if (!vcfg["canrecruit"].blank() && vcfg["canrecruit"].to_bool() != u.can_recruit()) { return false; } if (!vcfg["recall_cost"].blank() && vcfg["recall_cost"].to_int(-1) != u.recall_cost()) { return false; } if (!vcfg["level"].blank() && vcfg["level"].to_int(-1) != u.level()) { return false; } if (!vcfg["defense"].blank() && vcfg["defense"].to_int(-1) != u.defense_modifier(fc_.get_disp_context().map().get_terrain(loc))) { return false; } if (!vcfg["movement_cost"].blank() && vcfg["movement_cost"].to_int(-1) != u.movement_cost(fc_.get_disp_context().map().get_terrain(loc))) { return false; } // Now start with the new WML based comparison. // If a key is in the unit and in the filter, they should match // filter only => not for us // unit only => not filtered config unit_cfg; // No point in serializing the unit once for each [filter_wml]! for (const vconfig& wmlcfg : vcfg.get_children("filter_wml")) { config fwml = wmlcfg.get_parsed_config(); /* Check if the filter only cares about variables. If so, no need to serialize the whole unit. */ config::all_children_itors ci = fwml.all_children_range(); if (fwml.all_children_count() == 1 && fwml.attribute_count() == 1 && ci.front().key == "variables") { if (!u.variables().matches(ci.front().cfg)) return false; } else { if (unit_cfg.empty()) u.write(unit_cfg); if (!unit_cfg.matches(fwml)) return false; } } for (const vconfig& vision : vcfg.get_children("filter_vision")) { std::set<int> viewers; // Use standard side filter side_filter ssf(vision, &fc_); std::vector<int> sides = ssf.get_teams(); viewers.insert(sides.begin(), sides.end()); bool found = false; for (const int viewer : viewers) { bool fogged = fc_.get_disp_context().teams()[viewer - 1].fogged(loc); bool hiding = u.invisible(loc, fc_.get_disp_context()); bool unit_hidden = fogged || hiding; if (vision["visible"].to_bool(true) != unit_hidden) { found = true; break; } } if (!found) {return false;} } if (vcfg.has_child("filter_adjacent")) { const unit_map& units = fc_.get_disp_context().units(); map_location adjacent[6]; get_adjacent_tiles(loc, adjacent); for (const vconfig& adj_cfg : vcfg.get_children("filter_adjacent")) { int match_count=0; unit_filter filt(adj_cfg, &fc_, use_flat_tod_); config::attribute_value i_adjacent = adj_cfg["adjacent"]; std::vector<map_location::DIRECTION> dirs; if (i_adjacent.blank()) { dirs = map_location::default_dirs(); } else { dirs = map_location::parse_directions(i_adjacent); } std::vector<map_location::DIRECTION>::const_iterator j, j_end = dirs.end(); for (j = dirs.begin(); j != j_end; ++j) { unit_map::const_iterator unit_itor = units.find(adjacent[*j]); if (unit_itor == units.end() || !filt(*unit_itor, u)) { continue; } boost::optional<bool> is_enemy; if (!adj_cfg["is_enemy"].blank()) { is_enemy = adj_cfg["is_enemy"].to_bool(); } if (!is_enemy || *is_enemy == fc_.get_disp_context().teams()[u.side() - 1].is_enemy(unit_itor->side())) { ++match_count; } } static std::vector<std::pair<int,int> > default_counts = utils::parse_ranges("1-6"); config::attribute_value i_count = adj_cfg["count"]; if(!in_ranges(match_count, !i_count.blank() ? utils::parse_ranges(i_count) : default_counts)) { return false; } } } if (!vcfg["find_in"].blank()) { // Allow filtering by searching a stored variable of units if (const game_data * gd = fc_.get_game_data()) { try { variable_access_const vi = gd->get_variable_access_read(vcfg["find_in"]); bool found_id = false; for (const config& c : vi.as_array()) { if(c["id"] == u.id()) found_id = true; } if(!found_id) { return false; } } catch(const invalid_variablename_exception&) { return false; } } } if (!vcfg["formula"].blank()) { try { const unit_callable main(loc,u); game_logic::map_formula_callable callable(&main); if (u2) { std::shared_ptr<unit_callable> secondary(new unit_callable(*u2)); callable.add("other", variant(secondary.get())); // It's not destroyed upon scope exit because the variant holds a reference } const game_logic::formula form(vcfg["formula"]); if(!form.evaluate(callable).as_bool()) { return false; } return true; } catch(game_logic::formula_error& e) { lg::wml_error() << "Formula error in unit filter: " << e.type << " at " << e.filename << ':' << e.line << ")\n"; // Formulae with syntax errors match nothing return false; } } if (!vcfg["lua_function"].blank()) { if (game_lua_kernel * lk = fc_.get_lua_kernel()) { bool b = lk->run_filter(vcfg["lua_function"].str().c_str(), u); if (!b) return false; } } return true; }