The Battle for Wesnoth  1.19.17+dev
teambuilder.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2025
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  : leader_configs_()
38  , level_(level)
39  , board_(board)
40  , seen_ids_()
41  , side_(num)
42  , side_cfg_(side_cfg)
43  , team_(to_build)
44  , unit_configs_()
45 {
46 }
47 
49 {
50  // initialize the context variables and flags, find relevant tags, set up everything
51  init();
52 
53  // builds the team for the given side
54  new_team();
55 
56  // set team objectives if necessary
57  objectives();
58 
59  // If the game state specifies additional units that can be recruited by the player, add them.
61 
62 }
63 
65 {
66  // place leader
67  leader();
68 
69  // prepare units, populate obvious recall lists elements
70  prepare_units();
71 }
72 
74 {
75  // place units
76  // this is separate stage because we need to place units only after every other team is constructed
77  place_units();
78 }
79 
80 void team_builder::log_step(const char* s) const
81 {
82  LOG_NG_TC << "team " << side_ << " construction: " << s;
83 }
84 
86 {
87  if(side_cfg_["side"].to_int(side_) != side_) {
88  ERR_NG_TC << "found invalid side=" << side_cfg_["side"].to_int(side_) << " in definition of side number " << side_;
89  throw game::load_game_failed("Invalid side definition");
90  }
91 
92  log_step("init");
93 
94  if(board_.map().empty()) {
95  throw game::load_game_failed("Map not found");
96  }
97 
98  unit_configs_.clear();
99  seen_ids_.clear();
100 }
101 
102 
104 {
105  log_step("new team");
107 }
108 
110 {
111  log_step("objectives");
112  // If this team has no objectives, set its objectives
113  // to the level-global "objectives"
114  // this is only used by the default mp 'Defeat enemy leader' objectives
115  if(team_.objectives().empty()) {
116  team_.set_objectives(level_["objectives"].t_str(), false);
117  }
118 }
119 
121 {
122  log_step("previous recruits");
123 
124  if(const config::attribute_value* v = side_cfg_.get("previous_recruits")) {
125  for(const std::string& rec : utils::split(*v)) {
126  DBG_NG_TC << "adding previous recruit: " << rec;
127  team_.add_recruit(rec);
128  }
129  }
130 }
131 
132 void team_builder::handle_unit(const config& u, const char* origin)
133 {
134  DBG_NG_TC
135  << "unit from " << origin << ": "
136  << "type=[" << u["type"] << "] "
137  << "id=[" << u["id"] << "] "
138  << "placement=[" << u["placement"] << "] "
139  << "x=[" << u["x"] << "] "
140  << "y=[" << u["y"] << "]";
141 
142  if(u["type"].empty()) {
143  WRN_NG_TC
144  << "when building level, skipping a unit (id=[" << u["id"] << "]) from " << origin
145  << " with no type information,\n"
146  << "for side:\n"
147  << side_cfg_.debug();
148 
149  return;
150  }
151 
152  const std::string& id = u["id"];
153  if(!id.empty()) {
154  if(seen_ids_.find(id) != seen_ids_.end()) {
155  // seen before
156  config u_tmp = u;
157  u_tmp["side"] = std::to_string(side_);
158  team_.recall_list().add(unit::create(u_tmp, true));
159  } else {
160  // not seen before
161  unit_configs_.push_back(&u);
162  seen_ids_.insert(id);
163  }
164  } else {
165  unit_configs_.push_back(&u);
166  }
167 }
168 
170 {
171  // Make a persistent copy of the config.
172  leader_configs_.push_back(leader);
173  config& stored = leader_configs_.back();
174 
175  // Provide some default values, if not specified.
176  config::attribute_value& a1 = stored["canrecruit"];
177  if(a1.blank()) {
178  a1 = true;
179  }
180 
181  config::attribute_value& a2 = stored["placement"];
182  if(a2.blank()) {
183  a2 = "map,leader";
184  }
185 
186  // Add the leader to the list of units to create.
187  handle_unit(stored, "leader_cfg");
188 }
189 
191 {
192  log_step("leader");
193  for(const config& l : side_cfg_.child_range("leader")) {
194  handle_leader(l);
195  }
196 }
197 
199 {
200  // if this is a start-of-scenario save then playcampaign.cpp merged
201  // units in [replay_start][side] merged with [side] already
202  // units that are in '[scenario][side]' are 'first'
203 
204  // for create-or-recall semantics to work: for each unit with non-empty
205  // id, unconditionally put OTHER, later, units with same id directly to
206  // recall list, not including them in unit_configs_
207  for(const config& su : side_cfg_.child_range("unit")) {
208  handle_unit(su, "side_cfg");
209  }
210 }
211 
213 {
214  log_step("place units");
216  uc.allow_add_to_recall(true)
217  .allow_discover(true)
218  .allow_get_village(false)
219  .allow_invalidate(false)
220  .allow_rename_side(true)
221  .allow_show(false);
222 
223  for(const config* u : unit_configs_) {
224  try {
225  uc.add_unit(*u);
226  } catch(const unit_type_error& e) {
227  ERR_NG_TC << e.what();
228  }
229  }
230 }
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:157
child_itors child_range(std::string_view key)
Definition: config.cpp:267
const attribute_value * get(std::string_view key) const
Returns a pointer to the attribute with the given key or nullptr if it does not exist.
Definition: config.cpp:664
std::string debug() const
Definition: config.cpp:1213
Game board class.
Definition: game_board.hpp:47
virtual const gamemap & map() const override
Definition: game_board.hpp:97
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:199
std::deque< config > leader_configs_
Definition: teambuilder.hpp:52
team_builder(const config &side_cfg, team &to_build, const config &level, game_board &board, int num)
Definition: teambuilder.cpp:36
void build_team_stage_three()
Handles the third stage of team initialization (unit placement).
Definition: teambuilder.cpp:73
void handle_leader(const config &leader)
const config & side_cfg_
Definition: teambuilder.hpp:58
void objectives()
void build_team_stage_two()
Handles the second stage of team initialization ((some) unit construction).
Definition: teambuilder.cpp:64
void prepare_units()
std::set< std::string > seen_ids_
Definition: teambuilder.hpp:56
std::vector< const config * > unit_configs_
Definition: teambuilder.hpp:60
void previous_recruits()
void build_team_stage_one()
Handles the first stage of team initialization (everything except unit construction).
Definition: teambuilder.cpp:48
void handle_unit(const config &u, const char *origin)
const config & level_
Definition: teambuilder.hpp:54
void place_units()
void log_step(const char *s) const
Definition: teambuilder.cpp:80
game_board & board_
Definition: teambuilder.hpp:55
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:74
void build(const config &cfg, const gamemap &map)
Definition: team.cpp:344
const t_string & objectives() const
Definition: team.hpp:264
void set_objectives(const t_string &new_objectives, bool silently=false)
Definition: team.cpp:627
void add_recruit(const std::string &)
Definition: team.cpp:467
recall_list_manager & recall_list()
Definition: team.hpp:239
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:200
Various functions related to the creation of units (recruits, recalls, and placed units).
Standard logging facilities (interface).
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