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