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