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