Logo Search packages:      
Sourcecode: wesnoth-1.9 version File versions  Download package

team.cpp

Go to the documentation of this file.
/* $Id: team.cpp 46186 2010-09-01 21:12:38Z silene $ */
/*
   Copyright (C) 2003 - 2010 by David White <dave@whitevine.net>
   Part of the Battle for Wesnoth Project http://www.wesnoth.org/

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY.

   See the COPYING file for more details.
*/

/**
 *  @file
 *  Team-management, allies, setup at start of scenario.
 */

#include "global.hpp"

#include "ai/manager.hpp"
#include "foreach.hpp"
#include "game_events.hpp"
#include "gamestatus.hpp"
#include "log.hpp"
#include "map.hpp"
#include "resources.hpp"
#include "game_preferences.hpp"
#include "team.hpp"
#include "whiteboard/side_actions.hpp"

static lg::log_domain log_engine("engine");
#define DBG_NG LOG_STREAM(debug, log_engine)
#define LOG_NG LOG_STREAM(info, log_engine)
#define WRN_NG LOG_STREAM(warn, log_engine)


static std::vector<team> *&teams = resources::teams;

//static member initialization
const int team::default_team_gold = 100;

const std::vector<team>& teams_manager::get_teams()
{
      assert(teams);
      return *teams;
}

team::team_info::team_info() :
      name(),
      gold(0),
      start_gold(0),
      gold_add(false),
      income(0),
      income_per_village(0),
      recall_cost(0),
      can_recruit(),
      team_name(),
      user_team_name(),
      save_id(),
      current_player(),
      countdown_time(),
      action_bonus_count(0),
      flag(),
      flag_icon(),
      description(),
      scroll_to_leader(true),
      objectives(),
      objectives_changed(false),
      controller(),
      share_maps(false),
      share_view(false),
      disallow_observers(false),
      allow_player(false),
      no_leader(true),
      hidden(true),
      music(),
      color(),
      side(0),
      persistent(false)
{
}

void team::team_info::read(const config &cfg)
{
      name = cfg["name"].str();
      gold = cfg["gold"];
      income = cfg["income"];
      team_name = cfg["team_name"].str();
      user_team_name = cfg["user_team_name"];
      save_id = cfg["save_id"].str();
      current_player = cfg["current_player"].str();
      countdown_time = cfg["countdown_time"].str();
      action_bonus_count = cfg["action_bonus_count"];
      flag = cfg["flag"].str();
      flag_icon = cfg["flag_icon"].str();
      description = cfg["id"].str();
      scroll_to_leader = cfg["scroll_to_leader"].to_bool(true);
      objectives = cfg["objectives"];
      objectives_changed = cfg["objectives_changed"].to_bool();
      disallow_observers = cfg["disallow_observers"].to_bool();
      allow_player = cfg["allow_player"].to_bool(true);
      no_leader = cfg["no_leader"].to_bool();
      hidden = cfg["hidden"].to_bool();
      music = cfg["music"].str();
      side = cfg["side"].to_int(1);

      ///@deprecated 1.9.2 Usage of 'colour' in [side]
      const std::string colour_error = "Usage of 'colour' in [side] is deprecated, support will be removed in 1.9.2.\n";
      if(cfg.has_old_attribute("color","colour",colour_error))
            color = cfg.get_old_attribute("color","colour").str();
      else
            color = cfg["side"].str();

      // If arel starting new scenario overide settings from [ai] tags
      if (!user_team_name.translatable())
            user_team_name = t_string::from_serialized(user_team_name);

      if(cfg.has_attribute("ai_config")) {
            ai::manager::add_ai_for_side_from_file(side, cfg["ai_config"], true);
      } else {
            ai::manager::add_ai_for_side_from_config(side, cfg, true);
      }

      std::vector<std::string> recruits = utils::split(cfg["recruit"]);
      for(std::vector<std::string>::const_iterator i = recruits.begin(); i != recruits.end(); ++i) {
            can_recruit.insert(*i);
      }

      // at the start of a scenario "start_gold" is not set, we need to take the
      // value from the gold setting (or fall back to the gold default)
      if (!cfg["start_gold"].empty())
            start_gold = cfg["start_gold"];
      else if (!cfg["gold"].empty())
            start_gold = gold;
      else
            start_gold = default_team_gold;

      if(team_name.empty()) {
            team_name = cfg["side"].str();
      }

      if(save_id.empty()) {
            save_id = description;
      }
      if (current_player.empty()) {
            current_player = save_id;
      }

      const std::string temp_rgb_str = cfg["team_rgb"];
      std::map<std::string, color_range>::iterator global_rgb = game_config::team_rgb_range.find(cfg["side"]);

      if(!temp_rgb_str.empty()){
            std::vector<Uint32> temp_rgb = string2rgb(temp_rgb_str);
            team_color_range_[side] = color_range(temp_rgb);
      }else if(global_rgb != game_config::team_rgb_range.end()){
            team_color_range_[side] = global_rgb->second;
      }

      income_per_village = cfg["village_gold"].to_int(game_config::village_income);
      recall_cost = cfg["recall_cost"].to_int(game_config::recall_cost);

      std::string control = cfg["controller"];
      //by default, persistence of a team is set depending on the controller
      persistent = true;
      if (control == "human")
            controller = HUMAN;
      else if (control == "human_ai")
            controller = HUMAN_AI;
      else if (control == "network")
            controller = NETWORK;
      else if (control == "network_ai")
            controller = NETWORK_AI;
      else if (control == "null")
      {
            disallow_observers = cfg["disallow_observers"].to_bool(true);
            controller = EMPTY;
            persistent = false;
      }
      else
      {
            controller = AI;
            persistent = false;
      }

      //override persistence flag if it is explicitly defined in the config
      persistent = cfg["persistent"].to_bool(persistent);

      //========================================================
      //END OF MESSY CODE

      // Share_view and share_maps can't both be enabled,
      // so share_view overrides share_maps.
      share_view = cfg["share_view"].to_bool();
      share_maps = !share_view && cfg["share_maps"].to_bool(true);

      LOG_NG << "team_info::team_info(...): team_name: " << team_name
             << ", share_maps: " << share_maps << ", share_view: " << share_view << ".\n";
}

void team::team_info::write(config& cfg) const
{
      cfg["gold"] = gold;
      cfg["start_gold"] = start_gold;
      cfg["gold_add"] = gold_add;
      cfg["income"] = income;
      cfg["name"] = name;
      cfg["team_name"] = team_name;
      cfg["user_team_name"] = user_team_name;
      cfg["save_id"] = save_id;
      cfg["current_player"] = current_player;
      cfg["flag"] = flag;
      cfg["flag_icon"] = flag_icon;
      cfg["id"] = description;
      cfg["objectives"] = objectives;
      cfg["objectives_changed"] = objectives_changed;
      cfg["countdown_time"]= countdown_time;
      cfg["action_bonus_count"]= action_bonus_count;
      cfg["village_gold"] = income_per_village;
      cfg["recall_cost"] = recall_cost;
      cfg["disallow_observers"] = disallow_observers;
      cfg["allow_player"] = allow_player;
      cfg["no_leader"] = no_leader;
      cfg["hidden"] = hidden;
      cfg["scroll_to_leader"] = scroll_to_leader;

      switch(controller) {
      case AI: cfg["controller"] = "ai"; break;
      case HUMAN: cfg["controller"] = "human"; break;
      case HUMAN_AI: cfg["controller"] = "human_ai"; break;
      case NETWORK: cfg["controller"] = "network"; break;
      case NETWORK_AI: cfg["controller"] = "network_ai"; break;
      case EMPTY: cfg["controller"] = "null"; break;
      default: assert(false); return;
      }

      std::stringstream can_recruit_str;
      for(std::set<std::string>::const_iterator cr = can_recruit.begin(); cr != can_recruit.end(); ++cr) {
            if(cr != can_recruit.begin())
                  can_recruit_str << ",";

            can_recruit_str << *cr;
      }

      cfg["recruit"] = can_recruit_str.str();

      cfg["share_maps"] = share_maps;
      cfg["share_view"] = share_view;

      if(music.empty() == false)
            cfg["music"] = music;

      cfg["color"] = str_cast(color);
      ///@deprecated 1.9.2 'colour' also written in team
      cfg["colour"] = str_cast(color);

      cfg["persistent"] = persistent;

      cfg.add_child("ai",ai::manager::to_config(side));
}

00264 void team::merge_shroud_map_data(const std::string& shroud_data)
{
      shroud_.merge(shroud_data);
}

team::team() :
      savegame_config(),
      gold_(0),
      villages_(),
      shroud_(),
      fog_(),
      auto_shroud_updates_(true),
      info_(),
      countdown_time_(0),
      action_bonus_count_(0),
      recall_list_(),
      enemies_(),
      seen_(),
      ally_shroud_(),
      ally_fog_(),
      planned_actions_()
{
}

team::~team()
{
}

void team::build(const config &cfg, const gamemap& map, int gold)
{
      gold_ = gold;
      info_.read(cfg);

      fog_.set_enabled(cfg["fog"].to_bool());
      shroud_.set_enabled(cfg["shroud"].to_bool());
      shroud_.read(cfg["shroud_data"]);

      LOG_NG << "team::team(...): team_name: " << info_.team_name
             << ", shroud: " << uses_shroud() << ", fog: " << uses_fog() << ".\n";

      // To ensure some mimimum starting gold,
      // gold is the maximum of 'gold' and what is given in the config file
      gold_ = std::max(gold, info_.gold);
      if (gold_ != info_.gold)
            info_.start_gold = gold;
      // Old code was doing:
      // info_.start_gold = str_cast(gold) + " (" + info_.start_gold + ")";
      // Was it correct?

      // Load in the villages the side controls at the start
      foreach (const config &v, cfg.child_range("village"))
      {
            map_location loc(v, resources::state_of_game);
            if (map.is_village(loc)) {
                  villages_.insert(loc);
            } else {
                  WRN_NG << "[side] " << name() << " [village] points to a non-village location " << loc << "\n";
            }
      }

      countdown_time_ = cfg["countdown_time"];
      action_bonus_count_ = cfg["action_bonus_count"];

      planned_actions_.reset(new wb::side_actions());
      planned_actions_->set_team_index(info_.side - 1);
}

void team::write(config& cfg) const
{
      info_.write(cfg);
      cfg["shroud"] = uses_shroud();
      cfg["fog"] = uses_fog();
      cfg["gold"] = gold_;

      // Write village locations
      for(std::set<map_location>::const_iterator t = villages_.begin(); t != villages_.end(); ++t) {
            t->write(cfg.add_child("village"));
      }

      cfg["shroud_data"] = shroud_.write();

      cfg["countdown_time"] = countdown_time_;
      cfg["action_bonus_count"] = action_bonus_count_;
}

bool team::get_village(const map_location& loc)
{
      villages_.insert(loc);
      return game_events::fire("capture",loc);
}

void team::lose_village(const map_location& loc)
{
      if(owns_village(loc)) {
            villages_.erase(villages_.find(loc));
      }
}

void team::set_recruits(const std::set<std::string>& recruits)
{
      info_.can_recruit = recruits;
      ai::manager::raise_recruit_list_changed();
}

void team::add_recruit(const std::string &recruit)
{
      info_.can_recruit.insert(recruit);
      ai::manager::raise_recruit_list_changed();
}

bool team::calculate_enemies(size_t index) const
{
      if(teams == NULL || index >= teams->size()) {
            return false;
      }

      while(enemies_.size() <= index) {
            enemies_.push_back(calculate_is_enemy(enemies_.size()));
      }

      return enemies_.back();
}

bool team::calculate_is_enemy(size_t index) const
{
      // We're not enemies of ourselves
      if(&(*teams)[index] == this) {
            return false;
      }

      // We are friends with anyone who we share a teamname with
      std::vector<std::string> our_teams = utils::split(info_.team_name),
                                       their_teams = utils::split((*teams)[index].info_.team_name);
      for(std::vector<std::string>::const_iterator t = our_teams.begin(); t != our_teams.end(); ++t) {
            if(std::find(their_teams.begin(), their_teams.end(), *t) != their_teams.end())
                  return false;
      }
      return true;
}

00404 void team::set_share_maps( bool share_maps ){
      // Share_view and share_maps can't both be enabled,
      // so share_view overrides share_maps.
      // If you want to change them, be sure to change share_view FIRST
      info_.share_maps = !info_.share_view && share_maps;
}

00411 void team::set_share_view( bool share_view ){
      info_.share_view = share_view;
}

void team::change_controller(const std::string& controller)
{
      team::team_info::CONTROLLER cid;
      if (controller == "human")
            cid = team::team_info::HUMAN;
      else if (controller == "human_ai")
            cid = team::team_info::HUMAN_AI;
      else if (controller == "network")
            cid = team::team_info::NETWORK;
      else if (controller == "network_ai")
            cid = team::team_info::NETWORK_AI;
      else if (controller == "null")
            cid = team::team_info::EMPTY;
      else
            cid = team::team_info::AI;

      info_.controller = cid;
}

void team::change_team(const std::string &name, const t_string &user_name)
{
      info_.team_name = name;
      if (!user_name.empty())
      {
            info_.user_team_name = user_name;
      }
      else
      {
            info_.user_team_name = name;
      }

      clear_caches();
}

00449 void team::clear_caches(){
      // Reset the cache of allies for all teams
      if(teams != NULL) {
            for(std::vector<team>::const_iterator i = teams->begin(); i != teams->end(); ++i) {
                  i->enemies_.clear();
                  i->ally_shroud_.clear();
                  i->ally_fog_.clear();
            }
      }
}

void team::set_objectives(const t_string& new_objectives, bool silently)
{
      info_.objectives = new_objectives;
      if(!silently)
            info_.objectives_changed = true;
}

bool team::shrouded(const map_location& loc) const
{
      if(!teams)
            return shroud_.value(loc.x+1,loc.y+1);

      return shroud_.shared_value(ally_shroud(*teams),loc.x+1,loc.y+1);
}

bool team::fogged(const map_location& loc) const
{
      if(shrouded(loc)) return true;

      if(!teams)
            return fog_.value(loc.x+1,loc.y+1);

      return fog_.shared_value(ally_fog(*teams),loc.x+1,loc.y+1);
}

const std::vector<const team::shroud_map*>& team::ally_shroud(const std::vector<team>& teams) const
{
      if(ally_shroud_.empty()) {
            for(size_t i = 0; i < teams.size(); ++i) {
                  if(!is_enemy(i + 1) && (&(teams[i]) == this || teams[i].share_view() || teams[i].share_maps())) {
                        ally_shroud_.push_back(&(teams[i].shroud_));
                  }
            }
      }

      return ally_shroud_;
}

const std::vector<const team::shroud_map*>& team::ally_fog(const std::vector<team>& teams) const
{
      if(ally_fog_.empty()) {
            for(size_t i = 0; i < teams.size(); ++i) {
                  if(!is_enemy(i + 1) && (&(teams[i]) == this || teams[i].share_view())) {
                        ally_fog_.push_back(&(teams[i].fog_));
                  }
            }
      }

      return ally_fog_;
}

bool team::knows_about_team(size_t index, bool is_multiplayer) const
{
      const team& t = (*teams)[index];

      // We know about our own team
      if(this == &t) return true;

      // If we aren't using shroud or fog, then we know about everyone
      if(!uses_shroud() && !uses_fog()) return true;

      // We don't know about enemies
      if(is_enemy(index+1)) return false;

      // We know our allies in multiplayer
      if (is_multiplayer) return true;

      // We know about allies we're sharing maps with
      if(share_maps() && t.uses_shroud()) return true;

      // We know about allies we're sharing view with
      if(share_view() && (t.uses_fog() || t.uses_shroud())) return true;

      return false;
}

bool team::copy_ally_shroud()
{
      if(!teams || !share_maps())
            return false;

      return shroud_.copy_from(ally_shroud(*teams));
}

int team::nteams()
{
      if(teams == NULL) {
            return 0;
      } else {
            return teams->size();
      }
}

bool is_observer()
{
      if(teams == NULL) {
            return true;
      }

      foreach (const team &t, *teams) {
            if (t.is_human())
                  return false;
      }

      return true;
}

void validate_side(int side)
{
      if(teams == NULL) {
            return;
      }

      if(side < 1 || side > int(teams->size())) {
            throw game::game_error("invalid side(" + str_cast(side) + ") found in unit definition");
      }
}

bool team::shroud_map::clear(int x, int y)
{
      if(enabled_ == false || x < 0 || y < 0)
            return false;

      if(x >= static_cast<int>(data_.size()))
            data_.resize(x+1);

      if(y >= static_cast<int>(data_[x].size()))
            data_[x].resize(y+1);

      if(data_[x][y] == false) {
            data_[x][y] = true;
            return true;
      } else {
            return false;
      }
}

void team::shroud_map::place(int x, int y)
{
      if(enabled_ == false || x < 0 || y < 0)
            return;

      if (x >= static_cast<int>(data_.size())) {
            DBG_NG << "Couldn't place shroud on invalid x coordinate: ("
                  << x << ", " << y << ") - max x: " << data_.size() - 1 << "\n";
      } else if (y >= static_cast<int>(data_[x].size())) {
            DBG_NG << "Couldn't place shroud on invalid y coordinate: ("
                  << x << ", " << y << ") - max y: " << data_[x].size() - 1 << "\n";
      } else {
            data_[x][y] = false;
      }
}

void team::shroud_map::reset()
{
      if(enabled_ == false)
            return;

      for(std::vector<std::vector<bool> >::iterator i = data_.begin(); i != data_.end(); ++i) {
            std::fill(i->begin(),i->end(),false);
      }
}

bool team::shroud_map::value(int x, int y) const
{
      if(enabled_ == false || x < 0 || y < 0)
            return false;

      if(x >= static_cast<int>(data_.size()))
            return true;

      if(y >= static_cast<int>(data_[x].size()))
            return true;

      if(data_[x][y])
            return false;
      else
            return true;
}

bool team::shroud_map::shared_value(const std::vector<const shroud_map*>& maps, int x, int y) const
{
      if(enabled_ == false || x < 0 || y < 0)
            return false;

      for(std::vector<const shroud_map*>::const_iterator i = maps.begin(); i != maps.end(); ++i) {
            if((*i)->enabled_ == true && (*i)->value(x,y) == false)
                  return false;
      }
      return true;
}

std::string team::shroud_map::write() const
{
      std::stringstream shroud_str;
      for(std::vector<std::vector<bool> >::const_iterator sh = data_.begin(); sh != data_.end(); ++sh) {
            shroud_str << '|';

            for(std::vector<bool>::const_iterator i = sh->begin(); i != sh->end(); ++i) {
                  shroud_str << (*i ? '1' : '0');
            }

            shroud_str << '\n';
      }

      return shroud_str.str();
}

void team::shroud_map::read(const std::string& str)
{
      data_.clear();
      for(std::string::const_iterator sh = str.begin(); sh != str.end(); ++sh) {
            if(*sh == '|')
                  data_.resize(data_.size()+1);

            if(data_.empty() == false) {
                  if(*sh == '1')
                        data_.back().push_back(true);
                  else if(*sh == '0')
                        data_.back().push_back(false);
            }
      }
}

void team::shroud_map::merge(const std::string& str)
{
      int x=0, y=0;
      for(std::string::const_iterator sh = str.begin(); sh != str.end(); ++sh) {
            if(*sh == '|' && sh != str.begin()) {
                  y=0;
                  x++;
            } else if(*sh == '1') {
                  clear(x,y);
                  y++;
            } else if(*sh == '0') {
                  y++;
            }
      }
}

bool team::shroud_map::copy_from(const std::vector<const shroud_map*>& maps)
{
      if(enabled_ == false)
            return false;

      bool cleared = false;
      for(std::vector<const shroud_map*>::const_iterator i = maps.begin(); i != maps.end(); ++i) {
            if((*i)->enabled_ == false)
                  continue;

            const std::vector<std::vector<bool> >& v = (*i)->data_;
            for(size_t x = 0; x != v.size(); ++x) {
                  for(size_t y = 0; y != v[x].size(); ++y) {
                        if(v[x][y]) {
                              cleared |= clear(x,y);
                        }
                  }
            }
      }
      return cleared;
}

std::map<int, color_range> team::team_color_range_;

const color_range team::get_side_color_range(int side){
  std::string index = get_side_color_index(side);
  std::map<std::string, color_range>::iterator gp=game_config::team_rgb_range.find(index);

  if(gp != game_config::team_rgb_range.end()){
    return(gp->second);
  }

  return(color_range(0x00FF0000,0x00FFFFFF,0x00000000,0x00FF0000));
}

const SDL_Color team::get_minimap_color(int side)
{
      // Note: use mid() instead of rep() unless
      // high contrast is needed over a map or minimap!
      return int_to_color(get_side_color_range(side).rep());
}

std::string team::get_side_color_index(int side)
{
      size_t index = size_t(side-1);

      if(teams != NULL && index < teams->size()) {
            const std::string side_map = (*teams)[index].map_color_to();
            if(side_map.size()) {
                  return side_map;
            }
      }
      return str_cast(side);
}

std::string team::get_side_highlight(int side)
{
      return rgb2highlight(get_side_color_range(side+1).mid());
}

void team::log_recruitable(){
      LOG_NG << "Adding recruitable units: \n";
      for (std::set<std::string>::const_iterator it = info_.can_recruit.begin();
             it != info_.can_recruit.end(); ++it) {
            LOG_NG << *it << std::endl;
      }
      LOG_NG << "Added all recruitable units\n";
}

config team::to_config() const
{
      config cfg;
      config& result = cfg.add_child("side");
      write(result);
      return result;
}

namespace player_teams {
int village_owner(const map_location& loc)
{
      if(! teams) {
            return -1;
      }
      for(size_t i = 0; i != teams->size(); ++i) {
            if((*teams)[i].owns_village(loc))
                  return i;
      }
      return -1;
}
}

Generated by  Doxygen 1.6.0   Back to index