The Battle for Wesnoth  1.17.17+dev
teambuilder.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2023
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 }
70 
72 {
73  // place leader
74  leader();
75 
76  // prepare units, populate obvious recall lists elements
77  prepare_units();
78 }
79 
81 {
82  // place units
83  // this is separate stage because we need to place units only after every other team is constructed
84  place_units();
85 }
86 
87 void team_builder::log_step(const char* s) const
88 {
89  LOG_NG_TC << "team " << side_ << " construction: " << s;
90 }
91 
93 {
94  if(side_cfg_["side"].to_int(side_) != side_) {
95  ERR_NG_TC << "found invalid side=" << side_cfg_["side"].to_int(side_) << " in definition of side number " << side_;
96  }
97 
98  log_step("init");
99 
100  // track whether a [player] tag with persistence information exists (in addition to the [side] tag)
101  player_exists_ = false;
102 
103  if(board_.map().empty()) {
104  throw game::load_game_failed("Map not found");
105  }
106 
107  DBG_NG_TC << "snapshot: " << utils::bool_string(player_exists_);
108 
109  unit_configs_.clear();
110  seen_ids_.clear();
111 }
112 
114 {
115  log_step("gold");
116 
117  gold_info_ngold_ = side_cfg_["gold"];
118 
119  DBG_NG_TC << "set gold to '" << gold_info_ngold_ << "'";
120 }
121 
123 {
124  log_step("new team");
126 }
127 
129 {
130  log_step("objectives");
131  // If this team has no objectives, set its objectives
132  // to the level-global "objectives"
133  // this is only used by the default mp 'Defeat enemy leader' objectives
134  if(team_.objectives().empty()) {
135  team_.set_objectives(level_["objectives"], false);
136  }
137 }
138 
140 {
141  log_step("previous recruits");
142 
143  if(const config::attribute_value* v = side_cfg_.get("previous_recruits")) {
144  for(const std::string& rec : utils::split(*v)) {
145  DBG_NG_TC << "adding previous recruit: " << rec;
146  team_.add_recruit(rec);
147  }
148  }
149 }
150 
151 void team_builder::handle_unit(const config& u, const char* origin)
152 {
153  DBG_NG_TC
154  << "unit from " << origin << ": "
155  << "type=[" << u["type"] << "] "
156  << "id=[" << u["id"] << "] "
157  << "placement=[" << u["placement"] << "] "
158  << "x=[" << u["x"] << "] "
159  << "y=[" << u["y"] << "]";
160 
161  if(u["type"].empty()) {
162  WRN_NG_TC
163  << "when building level, skipping a unit (id=[" << u["id"] << "]) from " << origin
164  << " with no type information,\n"
165  << "for side:\n"
166  << side_cfg_.debug();
167 
168  return;
169  }
170 
171  const std::string& id = u["id"];
172  if(!id.empty()) {
173  if(seen_ids_.find(id) != seen_ids_.end()) {
174  // seen before
175  config u_tmp = u;
176  u_tmp["side"] = std::to_string(side_);
177  team_.recall_list().add(unit::create(u_tmp, true));
178  } else {
179  // not seen before
180  unit_configs_.push_back(&u);
181  seen_ids_.insert(id);
182  }
183  } else {
184  unit_configs_.push_back(&u);
185  }
186 }
187 
189 {
190  // Make a persistent copy of the config.
191  leader_configs_.push_back(leader);
192  config& stored = leader_configs_.back();
193 
194  // Remove the attributes used to define a side.
195  for(const std::string& attr : team::attributes) {
196  stored.remove_attribute(attr);
197  }
198 
199  // Remove [ai] tag as it is already added for the side
200  stored.remove_children("ai");
201 
202  // Provide some default values, if not specified.
203  config::attribute_value& a1 = stored["canrecruit"];
204  if(a1.blank()) {
205  a1 = true;
206  }
207 
208  config::attribute_value& a2 = stored["placement"];
209  if(a2.blank()) {
210  a2 = "map,leader";
211  }
212 
213  // Add the leader to the list of units to create.
214  handle_unit(stored, "leader_cfg");
215 }
216 
218 {
219  log_step("leader");
220  // If this side tag describes the leader of the side, we can simply add it to front of unit queue
221  // there was a hack: if this side tag describes the leader of the side,
222  // we may replace the leader with someone from recall list who can recruit, but take positioning from [side]
223  // this hack shall be removed, since it messes up with 'multiple leaders'
224 
225  // If this side tag describes the leader of the side
226  if(!side_cfg_["type"].empty() && side_cfg_["type"] != "null") {
228  }
229 
230  for(const config& l : side_cfg_.child_range("leader")) {
231  handle_leader(l);
232  }
233 }
234 
236 {
237  // if this is a start-of-scenario save then playcampaign.cpp merged
238  // units in [replay_start][side] merged with [side] already
239  // units that are in '[scenario][side]' are 'first'
240 
241  // for create-or-recall semantics to work: for each unit with non-empty
242  // id, unconditionally put OTHER, later, units with same id directly to
243  // recall list, not including them in unit_configs_
244  for(const config& su : side_cfg_.child_range("unit")) {
245  handle_unit(su, "side_cfg");
246  }
247 }
248 
250 {
251  log_step("place units");
253  uc.allow_add_to_recall(true)
254  .allow_discover(true)
255  .allow_get_village(false)
256  .allow_invalidate(false)
257  .allow_rename_side(true)
258  .allow_show(false);
259 
260  for(const config* u : unit_configs_) {
261  try {
262  uc.add_unit(*u);
263  } catch(const unit_type_error& e) {
264  ERR_NG_TC << e.what();
265  }
266  }
267 }
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:161
child_itors child_range(config_key_type key)
Definition: config.cpp:277
void remove_attribute(config_key_type key)
Definition: config.cpp:164
std::string debug() const
Definition: config.cpp:1248
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:660
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:691
Game board class.
Definition: game_board.hpp:53
virtual const gamemap & map() const override
Definition: game_board.hpp:103
map_location starting_position(int side) const
Definition: map.cpp:324
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:187
int gold_info_ngold_
Definition: teambuilder.hpp:53
std::deque< config > leader_configs_
Definition: teambuilder.hpp:54
team_builder(const config &side_cfg, team &to_build, const config &level, game_board &board, int num)
Definition: teambuilder.cpp:38
bool player_exists_
Definition: teambuilder.hpp:59
void build_team_stage_three()
Handles the third stage of team initialization (unit placement).
Definition: teambuilder.cpp:80
void handle_leader(const config &leader)
const config & side_cfg_
Definition: teambuilder.hpp:62
void objectives()
void build_team_stage_two()
Handles the second stage of team initialization ((some) unit construction).
Definition: teambuilder.cpp:71
void prepare_units()
std::set< std::string > seen_ids_
Definition: teambuilder.hpp:60
std::vector< const config * > unit_configs_
Definition: teambuilder.hpp:64
void previous_recruits()
void build_team_stage_one()
Handles the first stage of team initialization (everything except unit construction).
Definition: teambuilder.cpp:52
void handle_unit(const config &u, const char *origin)
const config & level_
Definition: teambuilder.hpp:56
void place_units()
void log_step(const char *s) const
Definition: teambuilder.cpp:87
game_board & board_
Definition: teambuilder.hpp:57
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:76
const t_string & objectives() const
Definition: team.hpp:228
void set_objectives(const t_string &new_objectives, bool silently=false)
Definition: team.cpp:639
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
static const std::set< std::string > attributes
Stores the attributes recognized by [side].
Definition: team.hpp:156
recall_list_manager & recall_list()
Definition: team.hpp:203
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:202
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:34
#define LOG_NG_TC
Definition: teambuilder.cpp:35
#define ERR_NG_TC
Definition: teambuilder.cpp:33
static lg::log_domain log_engine_tc("engine/team_construction")
#define DBG_NG_TC
Definition: teambuilder.cpp:36
static map_location::DIRECTION s
#define e