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