made it so healers can heal allies.
made it so that if a leader dies, all villages for that side become neutral
This commit is contained in:
parent
f734529e45
commit
2d9f035095
14 changed files with 139 additions and 100 deletions
|
@ -121,9 +121,9 @@ error_no_campaigns="There are no campaigns available"
|
|||
choose_campaign="Choose the campaign you want to play:"
|
||||
|
||||
difficulty_level="Select difficulty level:"
|
||||
EASY="Fighter (easy)"
|
||||
NORMAL="*Hero (medium)"
|
||||
HARD="Champion (hard)"
|
||||
EASY="&elvish-fighter.png,Fighter (easy)"
|
||||
NORMAL="*&elvish-hero.png,Hero (medium)"
|
||||
HARD="&elvish-champion.png,Champion (hard)"
|
||||
|
||||
lawful_description="Lawful units fight better at day, and worse at night.
|
||||
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 103 B After Width: | Height: | Size: 127 B |
102
src/actions.cpp
102
src/actions.cpp
|
@ -599,15 +599,17 @@ int tower_owner(const gamemap::location& loc, std::vector<team>& teams)
|
|||
|
||||
|
||||
void get_tower(const gamemap::location& loc, std::vector<team>& teams,
|
||||
int team_num)
|
||||
size_t team_num, const unit_map& units)
|
||||
{
|
||||
for(size_t i = 0; i != teams.size(); ++i) {
|
||||
if(int(i) != team_num && teams[i].owns_tower(loc)) {
|
||||
if(i != team_num && teams[i].owns_tower(loc)) {
|
||||
teams[i].lose_tower(loc);
|
||||
}
|
||||
}
|
||||
|
||||
if(size_t(team_num) < teams.size())
|
||||
//if the side doesn't have a leader, captured villages become neutral
|
||||
const bool has_leader = find_leader(units,int(team_num+1)) != units.end();
|
||||
if(has_leader && team_num < teams.size())
|
||||
teams[team_num].get_tower(loc);
|
||||
}
|
||||
|
||||
|
@ -623,8 +625,71 @@ std::map<gamemap::location,unit>::iterator
|
|||
return units.end();
|
||||
}
|
||||
|
||||
std::map<gamemap::location,unit>::const_iterator
|
||||
find_leader(const std::map<gamemap::location,unit>& units, int side)
|
||||
{
|
||||
for(std::map<gamemap::location,unit>::const_iterator i = units.begin();
|
||||
i != units.end(); ++i) {
|
||||
if(i->second.side() == side && i->second.can_recruit())
|
||||
return i;
|
||||
}
|
||||
|
||||
return units.end();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
//function which returns true iff the unit at 'loc' will heal a unit from side 'side'
|
||||
//on this turn.
|
||||
//
|
||||
//units heal other units if they are (1) on the same side as them; or (2) are on a
|
||||
//different but allied side, and there are no 'higher priority' sides also adjacent
|
||||
//to the healer
|
||||
bool will_heal(const gamemap::location& loc, int side, const std::vector<team>& teams,
|
||||
const unit_map& units)
|
||||
{
|
||||
const unit_map::const_iterator healer_it = units.find(loc);
|
||||
if(healer_it == units.end() || healer_it->second.type().heals() == false)
|
||||
return false;
|
||||
|
||||
const unit& healer = healer_it->second;
|
||||
if(healer.side() == side)
|
||||
return true;
|
||||
|
||||
if(size_t(side-1) >= teams.size() || size_t(healer.side()-1) >= teams.size())
|
||||
return false;
|
||||
|
||||
//if the healer is an enemy, it won't heal
|
||||
if(teams[healer.side()-1].is_enemy(side))
|
||||
return false;
|
||||
|
||||
gamemap::location adjacent[6];
|
||||
get_adjacent_tiles(loc,adjacent);
|
||||
for(int n = 0; n != 6; ++n) {
|
||||
const unit_map::const_iterator u = units.find(adjacent[n]);
|
||||
if(u != units.end() && u->second.hitpoints() < u->second.max_hitpoints()) {
|
||||
const int unit_side = u->second.side();
|
||||
|
||||
//the healer won't heal an ally if there is a wounded unit on the same
|
||||
//side next to her
|
||||
if(unit_side == healer.side())
|
||||
return false;
|
||||
|
||||
//choose an arbitrary order for healing
|
||||
if(unit_side > side)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//there's no-one of higher priority nearby, so the ally will heal
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void calculate_healing(display& disp, const gamemap& map,
|
||||
std::map<gamemap::location,unit>& units, int side)
|
||||
std::map<gamemap::location,unit>& units, int side,
|
||||
const std::vector<team>& teams)
|
||||
{
|
||||
std::map<gamemap::location,int> healed_units, max_healing;
|
||||
|
||||
|
@ -647,17 +712,14 @@ void calculate_healing(display& disp, const gamemap& map,
|
|||
gamemap::location adjacent[6];
|
||||
get_adjacent_tiles(i->first,adjacent);
|
||||
for(int j = 0; j != 6; ++j) {
|
||||
std::map<gamemap::location,unit>::const_iterator healer =
|
||||
units.find(adjacent[j]);
|
||||
if(healer != units.end() && healer->second.side() == side) {
|
||||
max_heal = maximum(max_heal,
|
||||
healer->second.type().max_unit_healing());
|
||||
if(will_heal(adjacent[j],i->second.side(),teams,units)) {
|
||||
const unit_map::const_iterator healer = units.find(adjacent[j]);
|
||||
max_heal = maximum(max_heal,healer->second.type().max_unit_healing());
|
||||
}
|
||||
}
|
||||
|
||||
if(max_heal > 0) {
|
||||
max_healing.insert(std::pair<gamemap::location,int>(i->first,
|
||||
max_heal));
|
||||
max_healing.insert(std::pair<gamemap::location,int>(i->first,max_heal));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -665,7 +727,7 @@ void calculate_healing(display& disp, const gamemap& map,
|
|||
//now see about units that can heal other units
|
||||
for(i = units.begin(); i != units.end(); ++i) {
|
||||
|
||||
if(i->second.side() == side && i->second.type().heals()) {
|
||||
if(will_heal(i->first,side,teams,units)) {
|
||||
gamemap::location adjacent[6];
|
||||
bool gets_healed[6];
|
||||
get_adjacent_tiles(i->first,adjacent);
|
||||
|
@ -677,7 +739,7 @@ void calculate_healing(display& disp, const gamemap& map,
|
|||
units.find(adjacent[j]);
|
||||
if(adj != units.end() &&
|
||||
adj->second.hitpoints() < adj->second.max_hitpoints() &&
|
||||
adj->second.side() == i->second.side() &&
|
||||
adj->second.side() == side &&
|
||||
healed_units[adj->first] < max_healing[adj->first]) {
|
||||
++nhealed;
|
||||
gets_healed[j] = true;
|
||||
|
@ -715,8 +777,7 @@ void calculate_healing(display& disp, const gamemap& map,
|
|||
i->second.hitpoints()-1);
|
||||
|
||||
if(damage > 0) {
|
||||
healed_units.insert(std::pair<gamemap::location,int>(
|
||||
i->first,-damage));
|
||||
healed_units.insert(std::pair<gamemap::location,int>(i->first,-damage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -836,7 +897,7 @@ void advance_unit(const game_data& info,
|
|||
}
|
||||
|
||||
void check_victory(std::map<gamemap::location,unit>& units,
|
||||
const std::vector<team>& teams)
|
||||
std::vector<team>& teams)
|
||||
{
|
||||
std::vector<int> seen_leaders;
|
||||
for(std::map<gamemap::location,unit>::const_iterator i = units.begin();
|
||||
|
@ -845,6 +906,13 @@ void check_victory(std::map<gamemap::location,unit>& units,
|
|||
seen_leaders.push_back(i->second.side());
|
||||
}
|
||||
|
||||
//clear villages for teams that have no leader
|
||||
for(std::vector<team>::iterator tm = teams.begin(); tm != teams.end(); ++tm) {
|
||||
if(std::find(seen_leaders.begin(),seen_leaders.end(),tm-teams.begin() + 1) == seen_leaders.end()) {
|
||||
tm->clear_towers();
|
||||
}
|
||||
}
|
||||
|
||||
bool found_enemies = false;
|
||||
bool found_human = false;
|
||||
|
||||
|
@ -1116,7 +1184,7 @@ size_t move_unit(display* disp, const game_data& gamedata, const gamemap& map,
|
|||
orig_tower_owner = tower_owner(steps.back(),teams);
|
||||
|
||||
if(orig_tower_owner != team_num) {
|
||||
get_tower(steps.back(),teams,team_num);
|
||||
get_tower(steps.back(),teams,team_num,units);
|
||||
u.set_movement(0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,17 +100,22 @@ int tower_owner(const gamemap::location& loc, std::vector<team>& teams);
|
|||
//makes it so the tower at the given location is owned by the given
|
||||
//0-based team number
|
||||
void get_tower(const gamemap::location& loc, std::vector<team>& teams,
|
||||
int team_num);
|
||||
size_t team_num, const unit_map& units);
|
||||
|
||||
//given the 1-based side, will find the leader of that side,
|
||||
//and return an iterator to the leader
|
||||
std::map<gamemap::location,unit>::iterator
|
||||
find_leader(std::map<gamemap::location,unit>& units, int side);
|
||||
|
||||
std::map<gamemap::location,unit>::const_iterator
|
||||
find_leader(const std::map<gamemap::location,unit>& units, int side);
|
||||
|
||||
|
||||
//calculates healing for all units for the given side. Should be called
|
||||
//at the beginning of a side's turn.
|
||||
void calculate_healing(display& disp, const gamemap& map,
|
||||
std::map<gamemap::location,unit>& units, int side);
|
||||
std::map<gamemap::location,unit>& units, int side,
|
||||
const std::vector<team>& teams);
|
||||
|
||||
//function which, given the location of a unit that is advancing, and the
|
||||
//name of the unit it is advancing to, will return the advanced version of
|
||||
|
@ -133,9 +138,9 @@ bool under_leadership(const std::map<gamemap::location,unit>& units,
|
|||
const gamemap::location& loc);
|
||||
|
||||
//checks to see if a side has won, and will throw an end_level_exception
|
||||
//if one has.
|
||||
//if one has. Will also remove control of villages from sides with dead leaders
|
||||
void check_victory(std::map<gamemap::location,unit>& units,
|
||||
const std::vector<team>& teams);
|
||||
std::vector<team>& teams);
|
||||
|
||||
//gets the time of day at a certain tile. Certain tiles may have a time of
|
||||
//day that differs from 'the' time of day, if a unit that illuminates is
|
||||
|
|
42
src/ai.cpp
42
src/ai.cpp
|
@ -148,7 +148,7 @@ void move_unit(const game_data& gameinfo, display& disp,
|
|||
current_unit.set_movement(0);
|
||||
units.insert(std::pair<location,unit>(to,current_unit));
|
||||
if(map.underlying_terrain(map[to.x][to.y]) == gamemap::TOWER)
|
||||
get_tower(to,teams,team_num-1);
|
||||
get_tower(to,teams,team_num-1,units);
|
||||
|
||||
disp.draw_tile(to.x,to.y);
|
||||
disp.draw();
|
||||
|
@ -161,7 +161,7 @@ void move_unit(const game_data& gameinfo, display& disp,
|
|||
}
|
||||
|
||||
void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
|
||||
std::map<gamemap::location,unit>& units,
|
||||
unit_map& units,
|
||||
std::vector<team>& teams, int team_num, const gamestatus& state,
|
||||
bool consider_combat, std::vector<target>* additional_targets)
|
||||
{
|
||||
|
@ -248,26 +248,12 @@ void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
|
|||
}
|
||||
}
|
||||
|
||||
const unit_map::iterator leader = find_leader(units,team_num);
|
||||
|
||||
//no moves left, recruitment phase
|
||||
//take stock of our current set of units
|
||||
if(srcdst.empty()) {
|
||||
std::cout << "recruitment......\n";
|
||||
location leader;
|
||||
int num_units = 0;
|
||||
std::map<std::string,int> unit_types;
|
||||
for(std::map<location,unit>::const_iterator i = units.begin();
|
||||
i != units.end(); ++i) {
|
||||
if(i->second.side() != team_num)
|
||||
continue;
|
||||
|
||||
if(i->second.can_recruit()) {
|
||||
leader = i->first;
|
||||
continue;
|
||||
}
|
||||
|
||||
unit_types[i->second.type().usage()]++;
|
||||
++num_units;
|
||||
}
|
||||
|
||||
//currently just spend all the gold we can!
|
||||
const int min_gold = 0;
|
||||
|
@ -284,8 +270,9 @@ void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
|
|||
int scouts_wanted = current_team.villages_per_scout() > 0 ?
|
||||
neutral_towers/current_team.villages_per_scout() : 0;
|
||||
|
||||
std::map<std::string,int> unit_types;
|
||||
while(unit_types["scout"] < scouts_wanted) {
|
||||
if(recruit(map,leader,"scout",gameinfo,team_num,current_team,
|
||||
if(recruit(map,leader->first,"scout",gameinfo,team_num,current_team,
|
||||
min_gold,units,disp) == false)
|
||||
break;
|
||||
|
||||
|
@ -301,7 +288,7 @@ void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
|
|||
}
|
||||
|
||||
//buy fighters as long as we have room and can afford it
|
||||
while(recruit(map,leader,options[rand()%options.size()].c_str(),
|
||||
while(recruit(map,leader->first,options[rand()%options.size()].c_str(),
|
||||
gameinfo,team_num,current_team,min_gold,units,disp)) {
|
||||
|
||||
}
|
||||
|
@ -431,14 +418,23 @@ void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
|
|||
if(map.underlying_terrain(map[i->first.x][i->first.y]) != gamemap::TOWER)
|
||||
continue;
|
||||
|
||||
bool want_tower = true;
|
||||
bool want_tower = true, owned = false;
|
||||
for(size_t j = 0; j != teams.size(); ++j) {
|
||||
if(!current_team.is_enemy(j+1) && teams[j].owns_tower(i->first)) {
|
||||
owned = teams[j].owns_tower(i->first);
|
||||
if(owned && !current_team.is_enemy(j+1)) {
|
||||
want_tower = false;
|
||||
}
|
||||
|
||||
if(owned) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//if it's a neutral tower, and we have no leader, then the village is no use to us,
|
||||
//and we don't want it.
|
||||
if(!owned && leader == units.end())
|
||||
want_tower = false;
|
||||
|
||||
if(want_tower) {
|
||||
std::cerr << "trying to acquire village: " << i->first.x
|
||||
<< ", " << i->first.y << "\n";
|
||||
|
@ -516,7 +512,7 @@ void do_move(display& disp, const gamemap& map, const game_data& gameinfo,
|
|||
}
|
||||
|
||||
std::cout << "finding targets...\n";
|
||||
std::vector<target> targets = find_targets(map,units,teams,team_num);
|
||||
std::vector<target> targets = find_targets(map,units,teams,team_num,leader != units.end());
|
||||
targets.insert(targets.end(),additional_targets->begin(),
|
||||
additional_targets->end());
|
||||
for(;;) {
|
||||
|
|
|
@ -87,7 +87,7 @@ private:
|
|||
|
||||
std::vector<target> find_targets(
|
||||
const gamemap& map, std::map<location,unit>& units,
|
||||
std::vector<team>& teams, int current_team
|
||||
std::vector<team>& teams, int current_team, bool has_leader
|
||||
)
|
||||
{
|
||||
log_scope("finding targets...");
|
||||
|
@ -96,10 +96,9 @@ std::vector<target> find_targets(
|
|||
|
||||
std::vector<target> targets;
|
||||
|
||||
if(tm.village_value() > 0.0) {
|
||||
if(has_leader && tm.village_value() > 0.0) {
|
||||
const std::vector<location>& towers = map.towers();
|
||||
for(std::vector<location>::const_iterator t = towers.begin();
|
||||
t != towers.end(); ++t) {
|
||||
for(std::vector<location>::const_iterator t = towers.begin(); t != towers.end(); ++t) {
|
||||
assert(map.on_board(*t));
|
||||
bool get_tower = true;
|
||||
for(size_t i = 0; i != teams.size(); ++i) {
|
||||
|
|
|
@ -33,7 +33,7 @@ struct target {
|
|||
|
||||
std::vector<target> find_targets(
|
||||
const gamemap& map, std::map<location,unit>& units,
|
||||
std::vector<team>& teams, int current_team
|
||||
std::vector<team>& teams, int current_team, bool has_leader
|
||||
);
|
||||
|
||||
std::pair<location,location> choose_move(
|
||||
|
|
|
@ -727,7 +727,6 @@ void display::draw_report(reports::TYPE report_num)
|
|||
|
||||
//report and its location is unchanged since last time. Do nothing.
|
||||
if(rect == new_rect && reports_[report_num] == report) {
|
||||
std::cerr << "report unchanged: '" << report.text << "'\n";
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -775,7 +774,6 @@ void display::draw_report(reports::TYPE report_num)
|
|||
|
||||
str += report.text.substr(nchop) + item->postfix();
|
||||
|
||||
std::cerr << "draw report text '" << str << "' at " << rect.x << "," << rect.y << "\n";
|
||||
area = font::draw_text(this,rect,item->font_size(),font::NORMAL_COLOUR,str,rect.x,rect.y);
|
||||
}
|
||||
|
||||
|
@ -1720,45 +1718,11 @@ void display::blit_surface(int x, int y, SDL_Surface* surface)
|
|||
|
||||
if(srcw <= 0 || srch <= 0 || srcx >= surface->w || srcy >= surface->h)
|
||||
return;
|
||||
/* //look at why SDL_BlitSurface doesn't always handle transperancy for us.
|
||||
|
||||
SDL_Rect src_rect = {srcx, srcy, srcw, srch};
|
||||
SDL_Rect dst_rect = {x, y, srcw, srch};
|
||||
|
||||
SDL_BlitSurface(surface,&src_rect,target,&dst_rect);
|
||||
return;
|
||||
*/
|
||||
if(x < 0)
|
||||
x = 0;
|
||||
|
||||
if(y < 0)
|
||||
y = 0;
|
||||
|
||||
//lines are padded to always fit on 4-byte boundaries, so see if there
|
||||
//is padding at the beginning of every line
|
||||
const int padding = is_odd(surface->w);
|
||||
const int surface_width = surface->w + padding;
|
||||
|
||||
surface_lock srclock(surface);
|
||||
surface_lock dstlock(target);
|
||||
|
||||
const short* src = srclock.pixels() + srcy*surface_width + srcx;
|
||||
short* dst = dstlock.pixels() + y*target->w + x;
|
||||
|
||||
static const short transperant = 0;
|
||||
|
||||
for(int i = 0; i != srch; ++i) {
|
||||
const short* s = src + i*surface_width + padding;
|
||||
const short* const end = s + srcw;
|
||||
short* d = dst + i*target->w;
|
||||
while(s != end) {
|
||||
if(*s != transperant) {
|
||||
*d = *s;
|
||||
}
|
||||
|
||||
++s;
|
||||
++d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SDL_Surface* display::getMinimap(int w, int h)
|
||||
|
|
|
@ -306,9 +306,10 @@ SDL_Surface* getMinimap(int w, int h, const gamemap& map, const team* tm)
|
|||
SDL_Rect minirect = {0,0,scale,scale};
|
||||
for(int y = 0; y != map.y(); ++y) {
|
||||
for(int x = 0; x != map.x(); ++x) {
|
||||
const bool shrouded = tm != NULL && tm->shrouded(x,y);
|
||||
if(map.on_board(gamemap::location(x,y)) && !shrouded) {
|
||||
const gamemap::TERRAIN terrain = map[x][y];
|
||||
|
||||
if(map.on_board(gamemap::location(x,y))) {
|
||||
const bool shrouded = tm != NULL && tm->shrouded(x,y);
|
||||
const gamemap::TERRAIN terrain = shrouded ? gamemap::VOID_TERRAIN : map[x][y];
|
||||
cache_map::iterator i = cache.find(terrain);
|
||||
|
||||
if(i == cache.end()) {
|
||||
|
|
|
@ -59,6 +59,10 @@ LEVEL_RESULT play_level(game_data& gameinfo, config& game_config,
|
|||
const config::child_list& unit_cfg = level->get_children("side");
|
||||
for(config::child_list::const_iterator ui = unit_cfg.begin(); ui != unit_cfg.end(); ++ui) {
|
||||
|
||||
if(first_human_team == -1 && (**ui)["controller"] == "human") {
|
||||
first_human_team = ui - unit_cfg.begin();
|
||||
}
|
||||
|
||||
std::string gold = (**ui)["gold"];
|
||||
if(gold.empty())
|
||||
gold = "100";
|
||||
|
@ -111,10 +115,6 @@ LEVEL_RESULT play_level(game_data& gameinfo, config& game_config,
|
|||
state_of_game.can_recruit = teams.back().recruits();
|
||||
}
|
||||
|
||||
if(first_human_team == -1 && teams.back().is_human()) {
|
||||
first_human_team = teams.size()-1;
|
||||
}
|
||||
|
||||
//if there are additional starting units on this side
|
||||
const config::child_list& starting_units = (*ui)->get_children("unit");
|
||||
for(config::child_list::const_iterator su = starting_units.begin();
|
||||
|
@ -231,7 +231,7 @@ LEVEL_RESULT play_level(game_data& gameinfo, config& game_config,
|
|||
team_it->spend_gold(expense);
|
||||
}
|
||||
|
||||
calculate_healing(gui,map,units,player_number);
|
||||
calculate_healing(gui,map,units,player_number,teams);
|
||||
}
|
||||
|
||||
gui.set_playing_team(size_t(player_number-1));
|
||||
|
|
|
@ -805,7 +805,7 @@ void turn_info::undo()
|
|||
|
||||
if(map_.underlying_terrain(map_[route.front().x][route.front().y]) == gamemap::TOWER) {
|
||||
get_tower(route.front(),teams_,
|
||||
undo_stack_.back().original_village_owner);
|
||||
undo_stack_.back().original_village_owner,units_);
|
||||
}
|
||||
|
||||
undo_stack_.back().starting_moves = u->second.movement_left();
|
||||
|
@ -868,7 +868,7 @@ void turn_info::redo()
|
|||
recorder.add_movement(route.front(),route.back());
|
||||
|
||||
if(map_.underlying_terrain(map_[route.back().x][route.back().y]) == gamemap::TOWER) {
|
||||
get_tower(route.back(),teams_,un.side()-1);
|
||||
get_tower(route.back(),teams_,un.side()-1,units_);
|
||||
}
|
||||
|
||||
gui_.draw_tile(route.back().x,route.back().y);
|
||||
|
|
|
@ -530,7 +530,7 @@ bool do_replay(display& disp, const gamemap& map, const game_data& gameinfo,
|
|||
const int orig_owner = tower_owner(dst,teams) + 1;
|
||||
if(orig_owner != team_num) {
|
||||
current_unit.set_movement(0);
|
||||
get_tower(dst,teams,team_num-1);
|
||||
get_tower(dst,teams,team_num-1,units);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -240,6 +240,11 @@ void team::lose_tower(const gamemap::location& loc)
|
|||
towers_.erase(towers_.find(loc));
|
||||
}
|
||||
|
||||
void team::clear_towers()
|
||||
{
|
||||
towers_.clear();
|
||||
}
|
||||
|
||||
const std::set<gamemap::location>& team::towers() const
|
||||
{
|
||||
return towers_;
|
||||
|
|
|
@ -65,6 +65,7 @@ public:
|
|||
|
||||
void get_tower(const gamemap::location&);
|
||||
void lose_tower(const gamemap::location&);
|
||||
void clear_towers();
|
||||
const std::set<gamemap::location>& towers() const;
|
||||
bool owns_tower(const gamemap::location&) const;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue