The Battle for Wesnoth  1.19.0-dev
game_board.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 "game_board.hpp"
17 #include "config.hpp"
18 #include "log.hpp"
19 #include "map/map.hpp"
20 #include "preferences/game.hpp"
21 #include "recall_list_manager.hpp"
22 #include "units/unit.hpp"
24 #include "utils/general.hpp"
25 
26 #include <set>
27 #include <vector>
28 
29 static lg::log_domain log_engine("enginerefac");
30 #define DBG_RG LOG_STREAM(debug, log_engine)
31 #define LOG_RG LOG_STREAM(info, log_engine)
32 #define WRN_RG LOG_STREAM(warn, log_engine)
33 #define ERR_RG LOG_STREAM(err, log_engine)
34 
35 static lg::log_domain log_engine_enemies("engine/enemies");
36 #define DBG_EE LOG_STREAM(debug, log_engine_enemies)
37 
39  : teams_()
40  , map_(std::make_unique<gamemap>(level["map_data"]))
41  , unit_id_manager_(level["next_underlying_unit_id"])
42  , units_()
43 {
44 }
45 
47  : teams_(other.teams_)
48  , labels_(other.labels_)
49  , map_(new gamemap(*(other.map_)))
50  , unit_id_manager_(other.unit_id_manager_)
51  , units_(other.units_)
52 {
53 }
54 
56 {
57 }
58 
59 // TODO: Fix this so that we swap pointers to maps
60 // However, then anytime gameboard is overwritten, resources::gamemap must be updated. So might want to
61 // just get rid of resources::gamemap and replace with resources::gameboard->map() at that point.
62 void swap(game_board& one, game_board& other)
63 {
64  std::swap(one.teams_, other.teams_);
65  std::swap(one.units_, other.units_);
67  one.map_.swap(other.map_);
68 }
69 
70 void game_board::new_turn(int player_num)
71 {
72  for(unit& i : units_) {
73  if(i.side() == player_num) {
74  i.new_turn();
75  }
76  }
77 }
78 
79 void game_board::end_turn(int player_num)
80 {
81  for(unit& i : units_) {
82  if(i.side() == player_num) {
83  i.end_turn();
84  }
85  }
86 }
87 
89 {
90  for(unit& i : units_) {
91  i.set_user_end_turn(true);
92  }
93 }
94 
96 {
97  for(auto& u : units_) {
98  if(get_team(u.side()).persistent()) {
99  u.new_turn();
100  u.new_scenario();
101  }
102  }
103 
104  for(auto& t : teams_) {
105  if(t.persistent()) {
106  for(auto& up : t.recall_list()) {
107  up->new_scenario();
108  up->new_turn();
109  }
110  }
111  }
112 }
113 
114 void game_board::check_victory(bool& continue_level,
115  bool& found_player,
116  bool& found_network_player,
117  bool& cleared_villages,
118  std::set<unsigned>& not_defeated,
119  bool remove_from_carryover_on_defeat)
120 {
121  continue_level = true;
122  found_player = false;
123  found_network_player = false;
124  cleared_villages = false;
125 
126  not_defeated = std::set<unsigned>();
127 
128  for(const unit& i : units()) {
129  DBG_EE << "Found a unit: " << i.id() << " on side " << i.side();
130  const team& tm = get_team(i.side());
131  DBG_EE << "That team's defeat condition is: " << defeat_condition::get_string(tm.defeat_cond());
132  if(i.can_recruit() && tm.defeat_cond() == defeat_condition::type::no_leader_left) {
133  not_defeated.insert(i.side());
134  } else if(tm.defeat_cond() == defeat_condition::type::no_units_left) {
135  not_defeated.insert(i.side());
136  }
137  }
138 
139  for(team& tm : teams_) {
140  if(tm.defeat_cond() == defeat_condition::type::never) {
141  not_defeated.insert(tm.side());
142  }
143 
144  // Clear villages for teams that have no leader and
145  // mark side as lost if it should be removed from carryover.
146  if(not_defeated.find(tm.side()) == not_defeated.end()) {
147  tm.clear_villages();
148  // invalidate_all() is overkill and expensive but this code is
149  // run rarely so do it the expensive way.
150  cleared_villages = true;
151 
152  if(remove_from_carryover_on_defeat) {
153  tm.set_lost(true);
154  }
155  } else if(remove_from_carryover_on_defeat) {
156  tm.set_lost(false);
157  }
158  }
159 
160  for(std::set<unsigned>::iterator n = not_defeated.begin(); n != not_defeated.end(); ++n) {
161  std::size_t side = *n - 1;
162  DBG_EE << "Side " << (side + 1) << " is a not-defeated team";
163 
165  for(++m; m != not_defeated.end(); ++m) {
166  if(teams()[side].is_enemy(*m)) {
167  return;
168  }
169 
170  DBG_EE << "Side " << (side + 1) << " and " << *m << " are not enemies.";
171  }
172 
173  if(teams()[side].is_local_human()) {
174  found_player = true;
175  }
176 
177  if(teams()[side].is_network_human()) {
178  found_network_player = true;
179  }
180  }
181 
182  continue_level = false;
183 }
184 
185 unit_map::iterator game_board::find_visible_unit(const map_location& loc, const team& current_team, bool see_all)
186 {
187  if(!map_->on_board(loc)) {
188  return units_.end();
189  }
190 
191  unit_map::iterator u = units_.find(loc);
192  if(!u.valid() || !u->is_visible_to_team(current_team, see_all)) {
193  return units_.end();
194  }
195 
196  return u;
197 }
198 
199 bool game_board::has_visible_unit(const map_location& loc, const team& current_team, bool see_all) const
200 {
201  if(!map_->on_board(loc)) {
202  return false;
203  }
204 
206  if(!u.valid() || !u->is_visible_to_team(current_team, see_all)) {
207  return false;
208  }
209 
210  return true;
211 }
212 
213 unit* game_board::get_visible_unit(const map_location& loc, const team& current_team, bool see_all)
214 {
215  unit_map::iterator ui = find_visible_unit(loc, current_team, see_all);
216  if(ui == units_.end()) {
217  return nullptr;
218  }
219 
220  return &*ui;
221 }
222 
224 {
225  team& tm = get_team(side_num);
226 
227  tm.change_controller(ctrl);
228  tm.change_proxy(proxy);
229  tm.set_local(true);
230 
231  tm.set_current_player(side_controller::get_string(ctrl) + std::to_string(side_num));
232 
233  unit_map::iterator leader = units_.find_leader(side_num);
234  if(leader.valid()) {
235  leader->rename(side_controller::get_string(ctrl) + std::to_string(side_num));
236  }
237 }
238 
240  int side_num, bool is_local, const std::string& pname, const std::string& controller_type)
241 {
242  team& tm = get_team(side_num);
243 
244  tm.set_local(is_local);
245 
246  // only changing the type of controller
247  if(controller_type == side_controller::ai && !tm.is_ai()) {
248  tm.make_ai();
249  return;
250  } else if(controller_type == side_controller::human && !tm.is_human()) {
251  tm.make_human();
252  return;
253  }
254 
255  if(pname.empty() || !tm.is_human()) {
256  return;
257  }
258 
259  tm.set_current_player(pname);
260 
261  unit_map::iterator leader = units_.find_leader(side_num);
262  if(leader.valid()) {
263  leader->rename(pname);
264  }
265 }
266 
268 {
269  switch(t.defeat_cond()) {
270  case defeat_condition::type::always:
271  return true;
272  case defeat_condition::type::no_leader_left:
273  return !units_.find_leader(t.side()).valid();
274  case defeat_condition::type::no_units_left:
275  for(const unit& u : units_) {
276  if(u.side() == t.side())
277  return false;
278  }
279  return true;
280  case defeat_condition::type::never:
281  default:
282  return false;
283  }
284 }
285 
287 {
288  get_team(u->side()).recall_list().add(u);
289  return true;
290 }
291 
292 std::optional<std::string> game_board::replace_map(const gamemap& newmap)
293 {
294  std::optional<std::string> ret;
295 
296  /* Remember the locations where a village is owned by a side. */
297  std::map<map_location, int> villages;
298  for(const auto& village : map_->villages()) {
299  const int owner = village_owner(village);
300  if(owner != 0) {
301  villages[village] = owner;
302  }
303  }
304 
305  for(unit_map::iterator itor = units_.begin(); itor != units_.end();) {
306  if(!newmap.on_board(itor->get_location())) {
307  if(!try_add_unit_to_recall_list(itor->get_location(), itor.get_shared_ptr())) {
308  ret = std::string("replace_map: Cannot add a unit that would become off-map to the recall list\n");
309  }
310  units_.erase(itor++);
311  } else {
312  ++itor;
313  }
314  }
315 
316  /* Disown villages that are no longer villages. */
317  for(const auto& village : villages) {
318  if(!newmap.is_village(village.first)) {
319  get_team(village.second).lose_village(village.first);
320  }
321  }
322 
323  *map_ = newmap;
324  return ret;
325 }
326 
328  const map_location& loc, const std::string& t_str, const std::string& mode_str, bool replace_if_failed)
329 {
330  // Code internalized from the implementation in lua.cpp
332  if(terrain == t_translation::NONE_TERRAIN) {
333  return false;
334  }
335 
337 
338  if(mode_str == "base") {
340  } else if(mode_str == "overlay") {
342  }
343 
344  return change_terrain(loc, terrain, mode, replace_if_failed);
345 }
346 
347 bool game_board::change_terrain(const map_location &loc, const t_translation::terrain_code &terrain, terrain_type_data::merge_mode& mode, bool replace_if_failed) {
348  /*
349  * When a hex changes from a village terrain to a non-village terrain, and
350  * a team owned that village it loses that village. When a hex changes from
351  * a non-village terrain to a village terrain and there is a unit on that
352  * hex it does not automatically capture the village. The reason for not
353  * capturing villages it that there are too many choices to make; should a
354  * unit loose its movement points, should capture events be fired. It is
355  * easier to do this as wanted by the author in WML.
356  */
357  t_translation::terrain_code old_t = map_->get_terrain(loc);
358  t_translation::terrain_code new_t = map_->tdata()->merge_terrains(old_t, terrain, mode, replace_if_failed);
359 
360  if(new_t == t_translation::NONE_TERRAIN) {
361  return false;
362  }
363 
364  preferences::encountered_terrains().insert(new_t);
365 
366  if(map_->tdata()->is_village(old_t) && !map_->tdata()->is_village(new_t)) {
367  int owner = village_owner(loc);
368  if(owner != 0)
369  get_team(owner).lose_village(loc);
370  }
371 
372  map_->set_terrain(loc, new_t);
373 
374  for(const t_translation::terrain_code& ut : map_->underlying_union_terrain(loc)) {
376  }
377 
378  return true;
379 }
380 
382 {
383  cfg["next_underlying_unit_id"] = unit_id_manager_.get_save_id();
384 
385  for(std::vector<team>::const_iterator t = teams_.begin(); t != teams_.end(); ++t) {
386  int side_num = std::distance(teams_.begin(), t) + 1;
387 
388  config& side = cfg.add_child("side");
389  t->write(side);
390  side["no_leader"] = true;
391  side["side"] = std::to_string(side_num);
392 
393  // current units
394  for(const unit& i : units_) {
395  if(i.side() == side_num) {
396  config& u = side.add_child("unit");
397  i.get_location().write(u);
398  i.write(u, false);
399  }
400  }
401 
402  // recall list
403  for(const unit_const_ptr j : t->recall_list()) {
404  config& u = side.add_child("unit");
405  j->write(u);
406  }
407  }
408 
409  // write the map
410  cfg["map_data"] = map_->write();
411 }
412 
414  : m_(m)
415  , loc_(loc)
416  , temp_(m_.extract(loc))
417 {
418  u.mark_clone(true);
419  m_.add(loc, u);
420 }
421 
423  : m_(b.units_)
424  , loc_(loc)
425  , temp_(m_.extract(loc))
426 {
427  u.mark_clone(true);
428  m_.add(loc, u);
429 }
430 
432 {
433  try {
434  m_.erase(loc_);
435  if(temp_) {
436  m_.insert(temp_);
437  }
438  } catch(...) {
439  DBG_RG << "Caught exception in temporary_unit_placer destructor: " << utils::get_unknown_exception_type();
440  }
441 }
442 
444  : m_(m)
445  , loc_(loc)
446  , temp_(m_.extract(loc))
447 {
448 }
449 
451  : m_(b.units_)
452  , loc_(loc)
453  , temp_(m_.extract(loc))
454 {
455 }
456 
458 {
459  try {
460  if(temp_) {
461  m_.insert(temp_);
462  }
463  } catch(...) {
464  DBG_RG << "Caught exception in temporary_unit_remover destructor: " << utils::get_unknown_exception_type();
465  }
466 }
467 
468 /**
469  * Constructor
470  * This version will change the unit's current movement to @a new_moves while
471  * the unit is moved (and restored to its previous value upon this object's
472  * destruction).
473  */
474 temporary_unit_mover::temporary_unit_mover(unit_map& m, const map_location& src, const map_location& dst, int new_moves, bool stand)
475  : m_(m)
476  , src_(src)
477  , dst_(dst)
478  , old_moves_(-1)
479  , temp_(src == dst ? unit_ptr() : m_.extract(dst))
480  , stand_(stand)
481 {
482  auto [iter, success] = m_.move(src_, dst_);
483 
484  // Set the movement.
485  if(success) {
486  old_moves_ = iter->movement_left(true);
487  iter->set_movement(new_moves);
488  if(stand_) {
489  m_.find_unit_ptr(dst_)->anim_comp().set_standing();
490  }
491  }
492 }
493 
495 {
496  try {
497  auto [iter, success] = m_.move(dst_, src_);
498 
499  // Restore the movement?
500  if(success && old_moves_ >= 0) {
501  iter->set_movement(old_moves_);
502  if(stand_) {
503  m_.find_unit_ptr(src_)->anim_comp().set_standing();
504  }
505  }
506 
507  // Restore the extracted unit?
508  if(temp_) {
509  m_.insert(temp_);
510  }
511  } catch(...) {
512  DBG_RG << "Caught exception in temporary_unit_mover destructor: " << utils::get_unknown_exception_type();
513  }
514 }
double t
Definition: astarsearch.cpp:63
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:159
config & add_child(config_key_type key)
Definition: config.cpp:441
int village_owner(const map_location &loc) const
Given the location of a village, will return the 1-based number of the team that currently owns it,...
Game board class.
Definition: game_board.hpp:46
void check_victory(bool &, bool &, bool &, bool &, std::set< unsigned > &, bool)
Definition: game_board.cpp:114
const team & get_team(int side) const
This getter takes a 1-based side number, not a 0-based team number.
bool has_visible_unit(const map_location &loc, const team &team, bool see_all=false) const
Definition: game_board.cpp:199
virtual const std::vector< team > & teams() const override
Definition: game_board.hpp:79
std::vector< team > teams_
Definition: game_board.hpp:47
void side_drop_to(int side_num, side_controller::type ctrl, side_proxy_controller::type proxy=side_proxy_controller::type::human)
Definition: game_board.cpp:223
unit_map units_
Definition: game_board.hpp:52
std::optional< std::string > replace_map(const gamemap &r)
Definition: game_board.cpp:292
std::unique_ptr< gamemap > map_
Definition: game_board.hpp:50
bool change_terrain(const map_location &loc, const std::string &t, const std::string &mode, bool replace_if_failed)
Definition: game_board.cpp:327
game_board(const config &level)
Definition: game_board.cpp:38
void heal_all_survivors()
Definition: game_board.cpp:95
bool try_add_unit_to_recall_list(const map_location &loc, const unit_ptr u)
Definition: game_board.cpp:286
n_unit::id_manager unit_id_manager_
Definition: game_board.hpp:51
unit_map::iterator find_visible_unit(const map_location &loc, const team &current_team, bool see_all=false)
Definition: game_board.cpp:185
bool team_is_defeated(const team &t) const
Calculates whether a team is defeated.
Definition: game_board.cpp:267
void end_turn(int pnum)
Definition: game_board.cpp:79
void side_change_controller(int side_num, bool is_local, const std::string &pname, const std::string &controller_type)
Definition: game_board.cpp:239
unit * get_visible_unit(const map_location &loc, const team &current_team, bool see_all=false)
Definition: game_board.cpp:213
void set_all_units_user_end_turn()
Definition: game_board.cpp:88
void write_config(config &cfg) const
Definition: game_board.cpp:381
virtual const unit_map & units() const override
Definition: game_board.hpp:106
virtual ~game_board()
Definition: game_board.cpp:55
void new_turn(int pnum)
Definition: game_board.cpp:70
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition: map.cpp:384
Encapsulates the map of the game.
Definition: map.hpp:172
bool is_village(const map_location &loc) const
Definition: map.cpp:65
std::size_t get_save_id() const
Used for saving id to savegame.
Definition: id.cpp:42
void add(const unit_ptr &ptr, int pos=-1)
Add a unit to the list.
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:74
bool is_ai() const
Definition: team.hpp:251
int side() const
Definition: team.hpp:174
bool is_human() const
Definition: team.hpp:250
void change_proxy(side_proxy_controller::type proxy)
Definition: team.hpp:277
void set_local(bool local)
Definition: team.hpp:258
void set_current_player(const std::string &player)
Definition: team.hpp:203
defeat_condition::type defeat_cond() const
Definition: team.hpp:328
void change_controller(const std::string &new_controller)
Definition: team.hpp:261
void make_ai()
Definition: team.hpp:260
void make_human()
Definition: team.hpp:259
void clear_villages()
Definition: team.hpp:169
bool persistent() const
Definition: team.hpp:335
recall_list_manager & recall_list()
Definition: team.hpp:201
void lose_village(const map_location &)
Definition: team.cpp:462
void set_lost(bool value=true)
Definition: team.hpp:337
Container associating units to locations.
Definition: map.hpp:98
unit_iterator end()
Definition: map.hpp:428
unit_ptr find_unit_ptr(const T &val)
Definition: map.hpp:387
unit_iterator find(std::size_t id)
Definition: map.cpp:302
unit_iterator begin()
Definition: map.hpp:418
std::size_t erase(const map_location &l)
Erases the unit at location l, if any.
Definition: map.cpp:289
umap_retval_pair_t add(const map_location &l, const unit &u)
Adds a copy of unit u at location l of the map.
Definition: map.cpp:76
unit_iterator find_leader(int side)
Definition: map.cpp:320
umap_retval_pair_t insert(unit_ptr p)
Inserts the unit pointed to by p into the map.
Definition: map.cpp:135
umap_retval_pair_t move(const map_location &src, const map_location &dst)
Moves a unit from location src to location dst.
Definition: map.cpp:92
This class represents a single unit of a specific type.
Definition: unit.hpp:133
std::size_t i
Definition: function.cpp:968
map_location loc_
void swap(game_board &one, game_board &other)
Definition: game_board.cpp:62
static lg::log_domain log_engine("enginerefac")
static lg::log_domain log_engine_enemies("engine/enemies")
#define DBG_RG
Definition: game_board.cpp:30
#define DBG_EE
Definition: game_board.cpp:36
unit & mark_clone(bool is_temporary)
Mark this unit as clone so it can be inserted to unit_map.
Definition: unit.cpp:2673
Standard logging facilities (interface).
std::set< t_translation::terrain_code > & encountered_terrains()
Definition: game.cpp:918
terrain_code read_terrain_code(std::string_view str, const ter_layer filler)
Reads a single terrain from a string.
const terrain_code NONE_TERRAIN
Definition: translation.hpp:58
std::string get_unknown_exception_type()
Utility function for finding the type of thing caught with catch(...).
Definition: general.cpp:23
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
std::shared_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:27
std::shared_ptr< unit > unit_ptr
Definition: ptr.hpp:26
Encapsulates the map of the game.
Definition: location.hpp:38
static std::string get_string(enum_type key)
Converts a enum to its string equivalent.
Definition: enum_base.hpp:46
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
Definition: translation.hpp:49
const map_location src_
Definition: game_board.hpp:230
virtual ~temporary_unit_mover()
Definition: game_board.cpp:494
temporary_unit_mover(unit_map &m, const map_location &src, const map_location &dst, int new_moves, bool stand)
Constructor This version will change the unit's current movement to new_moves while the unit is moved...
Definition: game_board.cpp:474
const map_location dst_
Definition: game_board.hpp:231
virtual ~temporary_unit_placer()
Definition: game_board.cpp:431
temporary_unit_placer(unit_map &m, const map_location &loc, unit &u)
Definition: game_board.cpp:413
const map_location loc_
Definition: game_board.hpp:192
temporary_unit_remover(unit_map &m, const map_location &loc)
Definition: game_board.cpp:443
virtual ~temporary_unit_remover()
Definition: game_board.cpp:457
bool valid() const
Definition: map.hpp:273
static map_location::DIRECTION n
#define b