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