diff --git a/data/translations/english.cfg b/data/translations/english.cfg index fe23c8301f2..077cf2a4e87 100644 --- a/data/translations/english.cfg +++ b/data/translations/english.cfg @@ -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. diff --git a/images/terrain/void.png b/images/terrain/void.png index 00427ec6509..dd41e3d66fb 100644 Binary files a/images/terrain/void.png and b/images/terrain/void.png differ diff --git a/src/actions.cpp b/src/actions.cpp index ddd67963c81..41b44c8c8be 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -599,15 +599,17 @@ int tower_owner(const gamemap::location& loc, std::vector& teams) void get_tower(const gamemap::location& loc, std::vector& 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::iterator return units.end(); } +std::map::const_iterator + find_leader(const std::map& units, int side) +{ + for(std::map::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& 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& units, int side) + std::map& units, int side, + const std::vector& teams) { std::map 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::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(i->first, - max_heal)); + max_healing.insert(std::pair(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( - i->first,-damage)); + healed_units.insert(std::pair(i->first,-damage)); } } } @@ -836,7 +897,7 @@ void advance_unit(const game_data& info, } void check_victory(std::map& units, - const std::vector& teams) + std::vector& teams) { std::vector seen_leaders; for(std::map::const_iterator i = units.begin(); @@ -845,6 +906,13 @@ void check_victory(std::map& units, seen_leaders.push_back(i->second.side()); } + //clear villages for teams that have no leader + for(std::vector::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); } } diff --git a/src/actions.hpp b/src/actions.hpp index 093b4f6d13e..9254cba2864 100644 --- a/src/actions.hpp +++ b/src/actions.hpp @@ -100,17 +100,22 @@ int tower_owner(const gamemap::location& loc, std::vector& 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& 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::iterator find_leader(std::map& units, int side); +std::map::const_iterator + find_leader(const std::map& 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& units, int side); + std::map& units, int side, + const std::vector& 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& 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& units, - const std::vector& teams); + std::vector& 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 diff --git a/src/ai.cpp b/src/ai.cpp index 5f9462c9404..f6107d2ae98 100644 --- a/src/ai.cpp +++ b/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(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& units, + unit_map& units, std::vector& teams, int team_num, const gamestatus& state, bool consider_combat, std::vector* 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 unit_types; - for(std::map::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 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 targets = find_targets(map,units,teams,team_num); + std::vector targets = find_targets(map,units,teams,team_num,leader != units.end()); targets.insert(targets.end(),additional_targets->begin(), additional_targets->end()); for(;;) { diff --git a/src/ai_move.cpp b/src/ai_move.cpp index 0566047c0af..abb744e3ec6 100644 --- a/src/ai_move.cpp +++ b/src/ai_move.cpp @@ -87,7 +87,7 @@ private: std::vector find_targets( const gamemap& map, std::map& units, - std::vector& teams, int current_team + std::vector& teams, int current_team, bool has_leader ) { log_scope("finding targets..."); @@ -96,10 +96,9 @@ std::vector find_targets( std::vector targets; - if(tm.village_value() > 0.0) { + if(has_leader && tm.village_value() > 0.0) { const std::vector& towers = map.towers(); - for(std::vector::const_iterator t = towers.begin(); - t != towers.end(); ++t) { + for(std::vector::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) { diff --git a/src/ai_move.hpp b/src/ai_move.hpp index 123604df835..155ff7d8a7f 100644 --- a/src/ai_move.hpp +++ b/src/ai_move.hpp @@ -33,7 +33,7 @@ struct target { std::vector find_targets( const gamemap& map, std::map& units, - std::vector& teams, int current_team + std::vector& teams, int current_team, bool has_leader ); std::pair choose_move( diff --git a/src/display.cpp b/src/display.cpp index 460e310b804..c4c0c32a84d 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -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) diff --git a/src/image.cpp b/src/image.cpp index 4517eca047b..d2994ddf1e5 100644 --- a/src/image.cpp +++ b/src/image.cpp @@ -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()) { diff --git a/src/playlevel.cpp b/src/playlevel.cpp index 89c6543f8b5..c5195d9cae4 100644 --- a/src/playlevel.cpp +++ b/src/playlevel.cpp @@ -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)); diff --git a/src/playturn.cpp b/src/playturn.cpp index df35c275ba7..b4bc641c24b 100644 --- a/src/playturn.cpp +++ b/src/playturn.cpp @@ -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); diff --git a/src/replay.cpp b/src/replay.cpp index 69d7ff071f3..39aba8ef835 100644 --- a/src/replay.cpp +++ b/src/replay.cpp @@ -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); } } diff --git a/src/team.cpp b/src/team.cpp index 8a5769f82c0..174e46f2ae7 100644 --- a/src/team.cpp +++ b/src/team.cpp @@ -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& team::towers() const { return towers_; diff --git a/src/team.hpp b/src/team.hpp index cd3bb34b675..e7216700ecc 100644 --- a/src/team.hpp +++ b/src/team.hpp @@ -65,6 +65,7 @@ public: void get_tower(const gamemap::location&); void lose_tower(const gamemap::location&); + void clear_towers(); const std::set& towers() const; bool owns_tower(const gamemap::location&) const;