The Battle for Wesnoth  1.15.7+dev
teambuilder.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2018 by Chris Beck <render787@gmail.com>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 #include "teambuilder.hpp"
15 
16 #include "actions/create.hpp"
17 #include "config.hpp"
18 #include "game_board.hpp"
19 #include "log.hpp"
20 #include "map/map.hpp"
21 #include "team.hpp"
22 #include "units/unit.hpp"
23 #include "units/type_error.hpp"
24 #include "units/map.hpp"
25 #include "resources.hpp"
26 #include "gettext.hpp"
27 #include "game_errors.hpp"
29 
30 #include <deque>
31 #include <vector>
32 
33 static lg::log_domain log_engine_tc("engine/team_construction");
34 #define ERR_NG_TC LOG_STREAM(err, log_engine_tc)
35 #define WRN_NG_TC LOG_STREAM(warn, log_engine_tc)
36 #define LOG_NG_TC LOG_STREAM(info, log_engine_tc)
37 #define DBG_NG_TC LOG_STREAM(debug, log_engine_tc)
38 
39 class team_builder {
40 public:
41  team_builder(const config& side_cfg, std::vector<team>& teams,
42  const config& level, game_board& board, int num)
43  : gold_info_ngold_(0)
44  , leader_configs_()
45  , level_(level)
46  , board_(board)
47  , player_exists_(false)
48  , seen_ids_()
49  , side_(num)
50  , side_cfg_(side_cfg)
51  , t_(nullptr)
52  , teams_(teams)
53  , unit_configs_()
54  {
55  }
56 
58  {
59  //initialize the context variables and flags, find relevant tags, set up everything
60  init();
61 
62  //find out the correct qty of gold and handle gold carryover.
63  gold();
64 
65  //create a new instance of team and push it to back of resources::gameboard->teams() vector
66  new_team();
67 
68  assert(t_!=nullptr);
69 
70  //set team objectives if necessary
71  objectives();
72 
73  // If the game state specifies additional units that can be recruited by the player, add them.
75 
76  //place leader
77  leader();
78 
79  //prepare units, populate obvious recall lists elements
80  prepare_units();
81 
82  }
83 
84 
86  {
87  //place units
88  //this is separate stage because we need to place units only after every other team is constructed
89  place_units();
90 
91  }
92 
93 protected:
94 
96  std::deque<config> leader_configs_;
97  //only used for objectives
98  const config &level_;
100  //only used for debug message
102  std::set<std::string> seen_ids_;
103  int side_;
106  std::vector<team> &teams_;
107  std::vector<const config*> unit_configs_;
108 
109  void log_step(const char *s) const
110  {
111  LOG_NG_TC << "team "<<side_<<" construction: "<< s << std::endl;
112  }
113 
114 
115  void init()
116  {
117  if (side_cfg_["side"].to_int(side_) != side_) {
118  ERR_NG_TC << "found invalid side=" << side_cfg_["side"].to_int(side_) << " in definition of side number " << side_ << std::endl;
119  }
120  t_ = &teams_[side_ - 1];
121  log_step("init");
122 
123  //track whether a [player] tag with persistence information exists (in addition to the [side] tag)
124  player_exists_ = false;
125 
126  if(board_.map().empty()) {
127  throw game::load_game_failed("Map not found");
128  }
129 
130  DBG_NG_TC << "snapshot: " << utils::bool_string(player_exists_) <<std::endl;
131 
132  unit_configs_.clear();
133  seen_ids_.clear();
134 
135  }
136 
137 
138  void gold()
139  {
140  log_step("gold");
141 
142  gold_info_ngold_ = side_cfg_["gold"];
143 
144  DBG_NG_TC << "set gold to '" << gold_info_ngold_ << "'\n";
145  }
146 
147 
148  void new_team()
149  {
150  log_step("new team");
151  t_->build(side_cfg_, board_.map(), gold_info_ngold_);
152  }
153 
154 
155  void objectives()
156  {
157  log_step("objectives");
158  // If this team has no objectives, set its objectives
159  // to the level-global "objectives"
160  // this is only used by the default mp 'Defeat enemy leader' objectives
161  if (t_->objectives().empty())
162  t_->set_objectives(level_["objectives"], false);
163  }
164 
165 
167  {
168  log_step("previous recruits");
169  // If the game state specifies units that
170  // can be recruited for the player, add them.
171  if (!side_cfg_) return;
172  if (const config::attribute_value *v = side_cfg_.get("previous_recruits")) {
173  for (const std::string &rec : utils::split(*v)) {
174  DBG_NG_TC << "adding previous recruit: " << rec << '\n';
175  t_->add_recruit(rec);
176  }
177  }
178  }
179 
180 
181 
182 
183  void handle_unit(const config &u, const char *origin)
184  {
185  DBG_NG_TC
186  << "unit from "<<origin
187  << ": type=["<<u["type"]
188  << "] id=["<<u["id"]
189  << "] placement=["<<u["placement"]
190  << "] x=["<<u["x"]
191  << "] y=["<<u["y"]
192  <<"]"<< std::endl;
193 
194  if (u["type"].empty()) {
195  WRN_NG_TC << "warning: when building level, skipping a unit (id=[" << u["id"] << "]) from " << origin
196  << " with no type information,\n"
197  << "for side:\n" << side_cfg_.debug() << std::endl;
198 
199  return ;
200  }
201 
202  const std::string &id = u["id"];
203  if (!id.empty()) {
204  if ( seen_ids_.find(id)!=seen_ids_.end() ) {
205  //seen before
206  config u_tmp = u;
207  u_tmp["side"] = std::to_string(side_);
208  t_->recall_list().add(unit::create(u_tmp,true));
209  } else {
210  //not seen before
211  unit_configs_.push_back(&u);
212  seen_ids_.insert(id);
213  }
214 
215  } else {
216  unit_configs_.push_back(&u);
217  }
218  }
219 
221  {
222  // Make a persistent copy of the config.
223  leader_configs_.push_back(leader);
224  config & stored = leader_configs_.back();
225 
226  // Remove the attributes used to define a side.
227  for (const std::string & attr : team::attributes) {
228  stored.remove_attribute(attr);
229  }
230 
231  // Provide some default values, if not specified.
232  config::attribute_value &a1 = stored["canrecruit"];
233  if (a1.blank()) a1 = true;
234  config::attribute_value &a2 = stored["placement"];
235  if (a2.blank()) a2 = "map,leader";
236 
237  // Add the leader to the list of units to create.
238  handle_unit(stored, "leader_cfg");
239  }
240 
241  void leader()
242  {
243  log_step("leader");
244  // If this side tag describes the leader of the side, we can simply add it to front of unit queue
245  // there was a hack: if this side tag describes the leader of the side,
246  // we may replace the leader with someone from recall list who can recruit, but take positioning from [side]
247  // this hack shall be removed, since it messes up with 'multiple leaders'
248 
249  // If this side tag describes the leader of the side
250  if (!side_cfg_["type"].empty() && side_cfg_["type"] != "null" ) {
251  handle_leader(side_cfg_);
252  }
253  for (const config &l : side_cfg_.child_range("leader")) {
254  handle_leader(l);
255  }
256  }
257 
258 
260  {
261  //if this is a start-of-scenario save then playcampaign.cpp merged
262  //units in [replay_start][side] merged with [side] already
263  //units that are in '[scenario][side]' are 'first'
264 
265  //for create-or-recall semantics to work: for each unit with non-empty
266  //id, unconditionally put OTHER, later, units with same id directly to
267  //recall list, not including them in unit_configs_
268  for (const config &su : side_cfg_.child_range("unit")) {
269  handle_unit(su, "side_cfg");
270  }
271  }
272 
273 
274  void place_units()
275  {
276  log_step("place units");
277  unit_creator uc(*t_, board_.map().starting_position(side_), &board_);
278  uc
279  .allow_add_to_recall(true)
280  .allow_discover(true)
281  .allow_get_village(false)
282  .allow_invalidate(false)
283  .allow_rename_side(true)
284  .allow_show(false);
285 
286  for (const config *u : unit_configs_) {
287  try {
288  uc.add_unit(*u);
289  }
290  catch (const unit_type_error& e) {
291  ERR_NG_TC << e.what() << "\n";
292  }
293  }
294  }
295 
296 };
297 
299  std::vector<team>& teams,
300  const config& level, game_board& board, int num)
301 {
302  return team_builder_ptr(new team_builder(side_cfg, teams, level, board, num));
303 }
304 
306 {
307  tb_ptr->build_team_stage_one();
308 }
309 
311 {
312  tb_ptr->build_team_stage_two();
313 }
unit_creator & allow_rename_side(bool b)
Game board class.
Definition: game_board.hpp:50
std::deque< config > leader_configs_
Definition: teambuilder.cpp:96
void place_units()
void set_objectives(const t_string &new_objectives, bool silently=false)
Definition: team.cpp:644
unit_creator & allow_invalidate(bool b)
unit_creator & allow_show(bool b)
Variant for storing WML attributes.
Error used when game loading fails.
Definition: game_errors.hpp:30
static const std::set< std::string > attributes
Stores the attributes recognized by [side].
Definition: team.hpp:174
child_itors child_range(config_key_type key)
Definition: config.cpp:362
#define LOG_NG_TC
Definition: teambuilder.cpp:36
unit_creator & allow_discover(bool b)
const attribute_value * get(config_key_type key) const
Returns a pointer to the attribute with the given key or nullptr if it does not exist.
Definition: config.cpp:742
virtual const gamemap & map() const override
Definition: game_board.hpp:109
void build_team_stage_two()
Definition: teambuilder.cpp:85
void build(const config &cfg, const gamemap &map, int gold=default_team_gold_)
Definition: team.cpp:355
std::vector< team > & teams_
void add_recruit(const std::string &)
Definition: team.cpp:480
-file sdl_utils.hpp
void remove_attribute(config_key_type key)
Definition: config.cpp:235
std::vector< const config * > unit_configs_
Definitions for the interface to Wesnoth Markup Language (WML).
unit_creator & allow_add_to_recall(bool b)
static unit_ptr create(const config &cfg, bool use_traits=false, const vconfig *vcfg=nullptr)
Initializes a unit from a config.
Definition: unit.hpp:191
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:44
#define WRN_NG_TC
Definition: teambuilder.cpp:35
bool empty() const
Tell if the map is of 0 size.
Definition: map.hpp:185
void objectives()
static lg::log_domain log_engine_tc("engine/team_construction")
bool blank() const
Tests for an attribute that was never set.
void previous_recruits()
void handle_leader(const config &leader)
#define ERR_NG_TC
Definition: teambuilder.cpp:34
int gold_info_ngold_
Definition: teambuilder.cpp:95
void log_step(const char *s) const
const char * what() const noexcept
Definition: exceptions.hpp:37
void build_team_stage_one()
Definition: teambuilder.cpp:57
const config & level_
Definition: teambuilder.cpp:98
Various functions related to the creation of units (recruits, recalls, and placed units)...
void prepare_units()
unit_creator & allow_get_village(bool b)
std::shared_ptr< team_builder > team_builder_ptr
Definition: teambuilder.hpp:23
static map_location::DIRECTION s
void handle_unit(const config &u, const char *origin)
const t_string & objectives() const
Definition: team.hpp:240
std::string bool_string(const bool value)
Converts a bool value to &#39;true&#39; or &#39;false&#39;.
map_location starting_position(int side) const
Definition: map.cpp:329
game_board & board_
Definition: teambuilder.cpp:99
team_builder(const config &side_cfg, std::vector< team > &teams, const config &level, game_board &board, int num)
Definition: teambuilder.cpp:41
std::set< std::string > seen_ids_
void add_unit(const config &cfg, const vconfig *vcfg=nullptr)
adds a unit on map without firing any events (so, usable during team construction in gamestatus) ...
bool empty() const
Definition: tstring.hpp:182
std::vector< std::string > split(const config_attribute_value &val)
void add(const unit_ptr &ptr, int pos=-1)
Add a unit to the list.
Standard logging facilities (interface).
recall_list_manager & recall_list()
Definition: team.hpp:215
team_builder_ptr create_team_builder(const config &side_cfg, std::vector< team > &teams, const config &level, game_board &board, int num)
#define e
const config & side_cfg_
#define DBG_NG_TC
Definition: teambuilder.cpp:37
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:68
std::string debug() const
Definition: config.cpp:1290