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