diff --git a/projectfiles/CodeBlocks/wesnoth.cbp b/projectfiles/CodeBlocks/wesnoth.cbp index 44b6e44fea8a..56c2c01b97d3 100644 --- a/projectfiles/CodeBlocks/wesnoth.cbp +++ b/projectfiles/CodeBlocks/wesnoth.cbp @@ -1000,6 +1000,7 @@ + diff --git a/projectfiles/VC9/wesnoth.vcproj b/projectfiles/VC9/wesnoth.vcproj index 153050349f2b..4d3cfab02df8 100644 --- a/projectfiles/VC9/wesnoth.vcproj +++ b/projectfiles/VC9/wesnoth.vcproj @@ -21052,6 +21052,10 @@ RelativePath="..\..\src\team.hpp" > + + diff --git a/src/gamestatus.cpp b/src/gamestatus.cpp index 108dda58fccc..43a4f7d38e02 100644 --- a/src/gamestatus.cpp +++ b/src/gamestatus.cpp @@ -21,7 +21,6 @@ #include "gamestatus.hpp" -#include "actions/create.hpp" #include "carryover.hpp" #include "filesystem.hpp" #include "formula_string_utils.hpp" @@ -36,6 +35,7 @@ #include "serialization/binary_or_text.hpp" #include "statistics.hpp" #include "team.hpp" +#include "teambuilder.hpp" #include "unit.hpp" #include "unit_id.hpp" #include "wesconfig.h" @@ -62,12 +62,6 @@ static lg::log_domain log_engine("engine"); #define LOG_NG LOG_STREAM(info, log_engine) #define DBG_NG LOG_STREAM(debug, log_engine) -static lg::log_domain log_engine_tc("engine/team_construction"); -#define ERR_NG_TC LOG_STREAM(err, log_engine_tc) -#define WRN_NG_TC LOG_STREAM(warn, log_engine_tc) -#define LOG_NG_TC LOG_STREAM(info, log_engine_tc) -#define DBG_NG_TC LOG_STREAM(debug, log_engine_tc) - static lg::log_domain log_enginerefac("enginerefac"); #define LOG_RG LOG_STREAM(info, log_enginerefac) @@ -75,292 +69,6 @@ static lg::log_domain log_enginerefac("enginerefac"); /// The default difficulty setting for campaigns. const std::string DEFAULT_DIFFICULTY("NORMAL"); - -class team_builder { -public: - team_builder(const config& side_cfg, - const std::string &save_id, std::vector& teams, - const config& level, gamemap& map, unit_map& units, - const config &starting_pos) - : gold_info_ngold_(0) - , leader_configs_() - , level_(level) - , map_(map) - , player_exists_(false) - , save_id_(save_id) - , seen_ids_() - , side_(0) - , side_cfg_(side_cfg) - , starting_pos_(starting_pos) - , t_(NULL) - , teams_(teams) - , unit_configs_() - , units_(units) - { - } - - void build_team_stage_one() - { - //initialize the context variables and flags, find relevant tags, set up everything - init(); - - //find out the correct qty of gold and handle gold carryover. - gold(); - - //create a new instance of team and push it to back of resources::teams vector - new_team(); - - assert(t_!=NULL); - - //set team objectives if necessary - objectives(); - - // If the game state specifies additional units that can be recruited by the player, add them. - previous_recruits(); - - //place leader - leader(); - - //prepare units, populate obvious recall lists elements - prepare_units(); - - } - - - void build_team_stage_two() - { - //place units - //this is separate stage because we need to place units only after every other team is constructed - place_units(); - - } - -protected: - - int gold_info_ngold_; - std::deque leader_configs_; - const config &level_; - gamemap &map_; - bool player_exists_; - const std::string save_id_; - std::set seen_ids_; - int side_; - const config &side_cfg_; - const config &starting_pos_; - team *t_; - std::vector &teams_; - std::vector unit_configs_; - unit_map &units_; - - - void log_step(const char *s) const - { - LOG_NG_TC << "team "<= teams_.size()) { - std::stringstream ss; - ss << "Side number " << side_ << " higher than number of sides (" << teams_.size() << ")"; - throw config::error(ss.str()); - } - if (teams_[side_ - 1].side() != 0) { - std::stringstream ss; - ss << "Duplicate definition of side " << side_; - throw config::error(ss.str()); - } - t_ = &teams_[side_ - 1]; - - log_step("init"); - - //track whether a [player] tag with persistence information exists (in addition to the [side] tag) - player_exists_ = false; - - if(map_.empty()) { - throw game::load_game_failed("Map not found"); - } - - DBG_NG_TC << "save id: "<< save_id_ <build(side_cfg_, map_, gold_info_ngold_); - //t_->set_gold_add(gold_info_add_); - } - - - void objectives() - { - log_step("objectives"); - // If this team has no objectives, set its objectives - // to the level-global "objectives" - if (t_->objectives().empty()) - t_->set_objectives(level_["objectives"], false); - } - - - void previous_recruits() - { - log_step("previous recruits"); - // If the game state specifies units that - // can be recruited for the player, add them. - if (!side_cfg_) return; - if (const config::attribute_value *v = side_cfg_.get("previous_recruits")) { - BOOST_FOREACH(const std::string &rec, utils::split(*v)) { - DBG_NG_TC << "adding previous recruit: " << rec << '\n'; - t_->add_recruit(rec); - } - } - } - - - - - void handle_unit(const config &u, const char *origin) - { - DBG_NG_TC - << "unit from "<recall_list().add(UnitPtr(new unit(u_tmp,true))); - } else { - //not seen before - unit_configs_.push_back(&u); - seen_ids_.insert(id); - } - - } else { - unit_configs_.push_back(&u); - } - } - - void handle_leader(const config &leader) - { - // Make a persistent copy of the config. - leader_configs_.push_back(leader); - config & stored = leader_configs_.back(); - - // Remove the attributes used to define a side. - BOOST_FOREACH( const std::string & attr , team::attributes) { - stored.remove_attribute(attr); - } - - // Provide some default values, if not specified. - config::attribute_value &a1 = stored["canrecruit"]; - if (a1.blank()) a1 = true; - config::attribute_value &a2 = stored["placement"]; - if (a2.blank()) a2 = "map,leader"; - - // Add the leader to the list of units to create. - handle_unit(stored, "leader_cfg"); - } - - void leader() - { - log_step("leader"); - // If this side tag describes the leader of the side, we can simply add it to front of unit queue - // there was a hack: if this side tag describes the leader of the side, - // we may replace the leader with someone from recall list who can recruit, but take positioning from [side] - // this hack shall be removed, since it messes up with 'multiple leaders' - - // If this side tag describes the leader of the side - if (!side_cfg_["no_leader"].to_bool() && side_cfg_["controller"] != "null") { - if (side_cfg_["type"] == "random") { - std::vector types = utils::split(side_cfg_["random_leader"]); - if (types.empty()) - types = utils::split(side_cfg_["leader"]); - if (types.empty()) { - utils::string_map i18n_symbols; - i18n_symbols["faction"] = side_cfg_["name"]; - throw config::error(vgettext("Unable to find a leader type for faction $faction", i18n_symbols)); - } - const int choice = rand() % types.size(); - config leader = side_cfg_; - leader["type"] = types[choice]; - handle_leader(leader); - } else - handle_leader(side_cfg_); - } - BOOST_FOREACH(const config &l, side_cfg_.child_range("leader")) { - handle_leader(l); - } - } - - - void prepare_units() - { - //if this is a start-of-scenario save then playcampaign.cpp merged - //units in [replay_start][side] merged with [side] already - //units that are in '[scenario][side]' are 'first' - - //for create-or-recall semantics to work: for each unit with non-empty - //id, unconditionally put OTHER, later, units with same id directly to - //recall list, not including them in unit_configs_ - BOOST_FOREACH(const config &su, side_cfg_.child_range("unit")) { - handle_unit(su, "side_cfg"); - } - } - - - void place_units() - { - log_step("place units"); - unit_creator uc(*t_,map_.starting_position(side_)); - uc - .allow_add_to_recall(true) - .allow_discover(true) - .allow_get_village(true) - .allow_invalidate(false) - .allow_rename_side(true) - .allow_show(false); - - BOOST_FOREACH(const config *u, unit_configs_) { - uc.add_unit(*u); - } - - // Find the first leader and use its name as the player name. - unit_map::iterator u = resources::units->find_first_leader(t_->side()); - if ((u != resources::units->end()) && t_->current_player().empty()) - t_->set_current_player(u->name()); - - } - -}; - game_data::game_data() : scoped_variables() , last_selected(map_location::null_location()) diff --git a/src/teambuilder.hpp b/src/teambuilder.hpp new file mode 100644 index 000000000000..fd8c9183727a --- /dev/null +++ b/src/teambuilder.hpp @@ -0,0 +1,323 @@ +/* + Copyright (C) 2014 by Chris Beck + 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. +*/ + +#ifndef INCL_TEAMBUILDER_HPP_ +#define INCL_TEAMBUILDER_HPP_ + +#include "actions/create.hpp" +#include "config.hpp" +#include "game_board.hpp" +#include "log.hpp" +#include "map.hpp" +#include "team.hpp" +#include "unit.hpp" +#include "unit_map.hpp" + +#include + +#include +#include + +static lg::log_domain log_engine_tc("engine/team_construction"); +#define ERR_NG_TC LOG_STREAM(err, log_engine_tc) +#define WRN_NG_TC LOG_STREAM(warn, log_engine_tc) +#define LOG_NG_TC LOG_STREAM(info, log_engine_tc) +#define DBG_NG_TC LOG_STREAM(debug, log_engine_tc) + +class team_builder { +public: + team_builder(const config& side_cfg, + const std::string &save_id, std::vector& teams, + const config& level, gamemap& map, unit_map& units, + const config &starting_pos) + : gold_info_ngold_(0) + , leader_configs_() + , level_(level) + , map_(map) + , player_exists_(false) + , save_id_(save_id) + , seen_ids_() + , side_(0) + , side_cfg_(side_cfg) + , starting_pos_(starting_pos) + , t_(NULL) + , teams_(teams) + , unit_configs_() + , units_(units) + { + } + + void build_team_stage_one() + { + //initialize the context variables and flags, find relevant tags, set up everything + init(); + + //find out the correct qty of gold and handle gold carryover. + gold(); + + //create a new instance of team and push it to back of resources::teams vector + new_team(); + + assert(t_!=NULL); + + //set team objectives if necessary + objectives(); + + // If the game state specifies additional units that can be recruited by the player, add them. + previous_recruits(); + + //place leader + leader(); + + //prepare units, populate obvious recall lists elements + prepare_units(); + + } + + + void build_team_stage_two() + { + //place units + //this is separate stage because we need to place units only after every other team is constructed + place_units(); + + } + +protected: + + int gold_info_ngold_; + std::deque leader_configs_; + const config &level_; + gamemap &map_; + bool player_exists_; + const std::string save_id_; + std::set seen_ids_; + int side_; + const config &side_cfg_; + const config &starting_pos_; + team *t_; + std::vector &teams_; + std::vector unit_configs_; + unit_map &units_; + + + void log_step(const char *s) const + { + LOG_NG_TC << "team "<= teams_.size()) { + std::stringstream ss; + ss << "Side number " << side_ << " higher than number of sides (" << teams_.size() << ")"; + throw config::error(ss.str()); + } + if (teams_[side_ - 1].side() != 0) { + std::stringstream ss; + ss << "Duplicate definition of side " << side_; + throw config::error(ss.str()); + } + t_ = &teams_[side_ - 1]; + + log_step("init"); + + //track whether a [player] tag with persistence information exists (in addition to the [side] tag) + player_exists_ = false; + + if(map_.empty()) { + throw game::load_game_failed("Map not found"); + } + + DBG_NG_TC << "save id: "<< save_id_ <build(side_cfg_, map_, gold_info_ngold_); + //t_->set_gold_add(gold_info_add_); + } + + + void objectives() + { + log_step("objectives"); + // If this team has no objectives, set its objectives + // to the level-global "objectives" + if (t_->objectives().empty()) + t_->set_objectives(level_["objectives"], false); + } + + + void previous_recruits() + { + log_step("previous recruits"); + // If the game state specifies units that + // can be recruited for the player, add them. + if (!side_cfg_) return; + if (const config::attribute_value *v = side_cfg_.get("previous_recruits")) { + BOOST_FOREACH(const std::string &rec, utils::split(*v)) { + DBG_NG_TC << "adding previous recruit: " << rec << '\n'; + t_->add_recruit(rec); + } + } + } + + + + + void handle_unit(const config &u, const char *origin) + { + DBG_NG_TC + << "unit from "<recall_list().add(UnitPtr(new unit(u_tmp,true))); + } else { + //not seen before + unit_configs_.push_back(&u); + seen_ids_.insert(id); + } + + } else { + unit_configs_.push_back(&u); + } + } + + void handle_leader(const config &leader) + { + // Make a persistent copy of the config. + leader_configs_.push_back(leader); + config & stored = leader_configs_.back(); + + // Remove the attributes used to define a side. + BOOST_FOREACH( const std::string & attr , team::attributes) { + stored.remove_attribute(attr); + } + + // Provide some default values, if not specified. + config::attribute_value &a1 = stored["canrecruit"]; + if (a1.blank()) a1 = true; + config::attribute_value &a2 = stored["placement"]; + if (a2.blank()) a2 = "map,leader"; + + // Add the leader to the list of units to create. + handle_unit(stored, "leader_cfg"); + } + + void leader() + { + log_step("leader"); + // If this side tag describes the leader of the side, we can simply add it to front of unit queue + // there was a hack: if this side tag describes the leader of the side, + // we may replace the leader with someone from recall list who can recruit, but take positioning from [side] + // this hack shall be removed, since it messes up with 'multiple leaders' + + // If this side tag describes the leader of the side + if (!side_cfg_["no_leader"].to_bool() && side_cfg_["controller"] != "null") { + if (side_cfg_["type"] == "random") { + std::vector types = utils::split(side_cfg_["random_leader"]); + if (types.empty()) + types = utils::split(side_cfg_["leader"]); + if (types.empty()) { + utils::string_map i18n_symbols; + i18n_symbols["faction"] = side_cfg_["name"]; + throw config::error(vgettext("Unable to find a leader type for faction $faction", i18n_symbols)); + } + const int choice = rand() % types.size(); + config leader = side_cfg_; + leader["type"] = types[choice]; + handle_leader(leader); + } else + handle_leader(side_cfg_); + } + BOOST_FOREACH(const config &l, side_cfg_.child_range("leader")) { + handle_leader(l); + } + } + + + void prepare_units() + { + //if this is a start-of-scenario save then playcampaign.cpp merged + //units in [replay_start][side] merged with [side] already + //units that are in '[scenario][side]' are 'first' + + //for create-or-recall semantics to work: for each unit with non-empty + //id, unconditionally put OTHER, later, units with same id directly to + //recall list, not including them in unit_configs_ + BOOST_FOREACH(const config &su, side_cfg_.child_range("unit")) { + handle_unit(su, "side_cfg"); + } + } + + + void place_units() + { + log_step("place units"); + unit_creator uc(*t_,map_.starting_position(side_)); + uc + .allow_add_to_recall(true) + .allow_discover(true) + .allow_get_village(true) + .allow_invalidate(false) + .allow_rename_side(true) + .allow_show(false); + + BOOST_FOREACH(const config *u, unit_configs_) { + uc.add_unit(*u); + } + + // Find the first leader and use its name as the player name. + unit_map::iterator u = resources::units->find_first_leader(t_->side()); + if ((u != resources::units->end()) && t_->current_player().empty()) + t_->set_current_player(u->name()); + + } + +}; + +#endif