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