The Battle for Wesnoth  1.17.0-dev
teambuilder.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2021
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 << std::endl;
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_ << std::endl;
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_) << std::endl;
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_ << "'\n";
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 << '\n';
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  << std::endl;
162 
163  if(u["type"].empty()) {
164  WRN_NG_TC
165  << "when building level, skipping a unit (id=[" << u["id"] << "]) from " << origin
166  << " with no type information,\n"
167  << "for side:\n"
168  << side_cfg_.debug() << std::endl;
169 
170  return;
171  }
172 
173  const std::string& id = u["id"];
174  if(!id.empty()) {
175  if(seen_ids_.find(id) != seen_ids_.end()) {
176  // seen before
177  config u_tmp = u;
178  u_tmp["side"] = std::to_string(side_);
179  team_.recall_list().add(unit::create(u_tmp, true));
180  } else {
181  // not seen before
182  unit_configs_.push_back(&u);
183  seen_ids_.insert(id);
184  }
185  } else {
186  unit_configs_.push_back(&u);
187  }
188 }
189 
191 {
192  // Make a persistent copy of the config.
193  leader_configs_.push_back(leader);
194  config& stored = leader_configs_.back();
195 
196  // Remove the attributes used to define a side.
197  for(const std::string& attr : team::attributes) {
198  stored.remove_attribute(attr);
199  }
200 
201  // Provide some default values, if not specified.
202  config::attribute_value& a1 = stored["canrecruit"];
203  if(a1.blank()) {
204  a1 = true;
205  }
206 
207  config::attribute_value& a2 = stored["placement"];
208  if(a2.blank()) {
209  a2 = "map,leader";
210  }
211 
212  // Add the leader to the list of units to create.
213  handle_unit(stored, "leader_cfg");
214 }
215 
217 {
218  log_step("leader");
219  // If this side tag describes the leader of the side, we can simply add it to front of unit queue
220  // there was a hack: if this side tag describes the leader of the side,
221  // we may replace the leader with someone from recall list who can recruit, but take positioning from [side]
222  // this hack shall be removed, since it messes up with 'multiple leaders'
223 
224  // If this side tag describes the leader of the side
225  if(!side_cfg_["type"].empty() && side_cfg_["type"] != "null") {
227  }
228 
229  for(const config& l : side_cfg_.child_range("leader")) {
230  handle_leader(l);
231  }
232 }
233 
235 {
236  // if this is a start-of-scenario save then playcampaign.cpp merged
237  // units in [replay_start][side] merged with [side] already
238  // units that are in '[scenario][side]' are 'first'
239 
240  // for create-or-recall semantics to work: for each unit with non-empty
241  // id, unconditionally put OTHER, later, units with same id directly to
242  // recall list, not including them in unit_configs_
243  for(const config& su : side_cfg_.child_range("unit")) {
244  handle_unit(su, "side_cfg");
245  }
246 }
247 
249 {
250  log_step("place units");
252  uc.allow_add_to_recall(true)
253  .allow_discover(true)
254  .allow_get_village(false)
255  .allow_invalidate(false)
256  .allow_rename_side(true)
257  .allow_show(false);
258 
259  for(const config* u : unit_configs_) {
260  try {
261  uc.add_unit(*u);
262  } catch(const unit_type_error& e) {
263  ERR_NG_TC << e.what() << "\n";
264  }
265  }
266 }
unit_creator & allow_rename_side(bool b)
Game board class.
Definition: game_board.hpp:51
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:643
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:180
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:102
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:354
void add_recruit(const std::string &)
Definition: team.cpp:479
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:190
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:72
#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:252
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:227
bool empty() const
Tell if the map is of 0 size.
Definition: map.hpp:65
#define e
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:61
std::string debug() const
Definition: config.cpp:1347