The Battle for Wesnoth  1.15.11+dev
game_state.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_state.hpp"
16 
17 #include "actions/undo.hpp"
18 #include "game_board.hpp"
19 #include "game_data.hpp"
20 #include "game_events/manager.hpp"
21 #include "log.hpp"
22 #include "map/map.hpp"
23 #include "pathfind/pathfind.hpp"
24 #include "pathfind/teleport.hpp"
25 #include "play_controller.hpp"
26 #include "preferences/game.hpp"
27 #include "random_deterministic.hpp"
28 #include "reports.hpp"
30 #include "synced_context.hpp"
31 #include "teambuilder.hpp"
32 #include "units/unit.hpp"
33 #include "whiteboard/manager.hpp"
35 
36 #include <functional>
37 #include <SDL2/SDL_timer.h>
38 
39 #include <algorithm>
40 #include <set>
41 
42 static lg::log_domain log_engine("engine");
43 #define LOG_NG LOG_STREAM(info, log_engine)
44 #define DBG_NG LOG_STREAM(debug, log_engine)
45 #define ERR_NG LOG_STREAM(err, log_engine)
46 
48  : gamedata_(level)
49  , board_(level)
50  , tod_manager_(level)
51  , pathfind_manager_(new pathfind::manager(level))
52  , reports_(new reports())
53  , lua_kernel_(new game_lua_kernel(*this, pc, *reports_))
54  , ai_manager_()
55  , events_manager_(new game_events::manager())
56  // TODO: this construct units (in dimiss undo action) but resrouces:: are not available yet,
57  // so we might want to move the innitialisation of undo_stack_ to game_state::init
58  , undo_stack_(new actions::undo_list(level.child("undo_stack")))
59  , player_number_(level["playing_team"].to_int() + 1)
60  , next_player_number_(level["next_player_number"].to_int(player_number_ + 1))
61  , do_healing_(level["do_healing"].to_bool(false))
62  , init_side_done_(level["init_side_done"].to_bool(false))
63  , start_event_fired_(!level["playing_team"].empty())
64  , server_request_number_(level["server_request_number"].to_int())
65  , first_human_team_(-1)
66 {
67  lua_kernel_->load_core();
68  if(const config& endlevel_cfg = level.child("end_level_data")) {
69  end_level_data el_data;
70  el_data.read(endlevel_cfg);
71  el_data.transient.carryover_report = false;
72  end_level_data_ = el_data;
73  }
74 }
75 
77  : gamedata_(level)
78  , board_(board)
79  , tod_manager_(level)
80  , pathfind_manager_(new pathfind::manager(level))
81  , reports_(new reports())
82  , lua_kernel_(new game_lua_kernel(*this, pc, *reports_))
83  , ai_manager_()
84  , events_manager_(new game_events::manager())
85  , player_number_(level["playing_team"].to_int() + 1)
86  , next_player_number_(level["next_player_number"].to_int(player_number_ + 1))
87  , do_healing_(level["do_healing"].to_bool(false))
88  , end_level_data_()
89  , init_side_done_(level["init_side_done"].to_bool(false))
90  , start_event_fired_(!level["playing_team"].empty())
91  , server_request_number_(level["server_request_number"].to_int())
92  , first_human_team_(-1)
93 {
94  lua_kernel_->load_core();
95  events_manager_->read_scenario(level);
96  if(const config& endlevel_cfg = level.child("end_level_data")) {
97  end_level_data el_data;
98  el_data.read(endlevel_cfg);
99  el_data.transient.carryover_report = false;
100  end_level_data_ = el_data;
101  }
102 }
103 
105 
106 static int placing_score(const config& side, const gamemap& map, const map_location& pos)
107 {
108  int positions = 0, liked = 0;
109  const t_translation::ter_list terrain = t_translation::read_list(side["terrain_liked"].str());
110 
111  for(int i = -8; i != 8; ++i) {
112  for(int j = -8; j != +8; ++j) {
113  const map_location pos2 = pos.plus(i, j);
114  if(map.on_board(pos2)) {
115  ++positions;
116  if(std::count(terrain.begin(),terrain.end(),map[pos2])) {
117  ++liked;
118  }
119  }
120  }
121  }
122 
123  return (100*liked)/positions;
124 }
125 
126 struct placing_info {
127 
129  side(0),
130  score(0),
131  pos()
132  {
133  }
134 
135  int side, score;
137 };
138 
139 static bool operator<(const placing_info& a, const placing_info& b) { return a.score > b.score; }
140 
141 
143 {
144  std::vector<placing_info> placings;
145 
146  int num_pos = board_.map().num_valid_starting_positions();
147 
148  int side_num = 1;
149  for(const config &side : level.child_range("side"))
150  {
151  for(int p = 1; p <= num_pos; ++p) {
152  const map_location& pos = board_.map().starting_position(p);
153  int score = placing_score(side, board_.map(), pos);
154  placing_info obj;
155  obj.side = side_num;
156  obj.score = score;
157  obj.pos = pos;
158  placings.push_back(obj);
159  }
160  ++side_num;
161  }
162 
163  std::stable_sort(placings.begin(),placings.end());
164  std::set<int> placed;
165  std::set<map_location> positions_taken;
166 
167  for (std::vector<placing_info>::const_iterator i = placings.begin(); i != placings.end() && static_cast<int>(placed.size()) != side_num - 1; ++i) {
168  if(placed.count(i->side) == 0 && positions_taken.count(i->pos) == 0) {
169  placed.insert(i->side);
170  positions_taken.insert(i->pos);
171  board_.map().set_starting_position(i->side,i->pos);
172  LOG_NG << "placing side " << i->side << " at " << i->pos << std::endl;
173  }
174  }
175 }
176 
178 {
179  events_manager_->read_scenario(level);
181  if (level["modify_placing"].to_bool()) {
182  LOG_NG << "modifying placing..." << std::endl;
184  }
185 
186  LOG_NG << "initialized time of day regions... " << (SDL_GetTicks() - pc.ticks()) << std::endl;
187  for (const config &t : level.child_range("time_area")) {
189  }
190 
191  LOG_NG << "initialized teams... " << (SDL_GetTicks() - pc.ticks()) << std::endl;
192 
193  board_.teams().resize(level.child_count("side"));
194  if (player_number_ > static_cast<int>(board_.teams().size())) {
195  ERR_NG << "invalid player number " << player_number_ << " #sides=" << board_.teams().size() << "\n";
196  player_number_ = 1;
197  // in case there are no teams, using player_number_ migh still cause problems later.
198  }
199 
200  std::vector<team_builder> team_builders;
201 
202  // Note this isn't strictly necessary since team_builder declares a move constructor which will
203  // be used if a copy is needed (see the class documentation for why a copy causes crashes), but
204  // it can't hurt to be doubly safe.
205  team_builders.reserve(board_.teams().size());
206 
207  int team_num = 0;
208  for (const config &side : level.child_range("side"))
209  {
210  if (first_human_team_ == -1) {
211  const std::string &controller = side["controller"];
212  if (controller == "human" && side["is_local"].to_bool(true)) {
213  first_human_team_ = team_num;
214  }
215  }
216  ++team_num;
217 
218  team_builders.emplace_back(side, board_.get_team(team_num), level, board_, team_num);
219  team_builders.back().build_team_stage_one();
220  }
221 
222  //Initialize the lua kernel before the units are created.
223  lua_kernel_->initialize(level);
224 
225  {
226  //sync traits of start units and the random start time.
228 
230 
231  for(team_builder& tb : team_builders) {
232  tb.build_team_stage_two();
233  }
234 
235  for(std::size_t i = 0; i < board_.teams().size(); i++) {
236  // Labels from players in your ignore list default to hidden
237  if(preferences::is_ignored(board_.teams()[i].current_player())) {
238  std::string label_cat = "side:" + std::to_string(i + 1);
239  board_.hidden_label_categories().push_back(label_cat);
240  }
241  }
242  }
243 }
244 
246 {
247  lua_kernel_->set_game_display(gd);
248 }
249 
250 void game_state::write(config& cfg) const
251 {
252  cfg["init_side_done"] = init_side_done_;
253  if(gamedata_.phase() == game_data::PLAY) {
254  cfg["playing_team"] = player_number_ - 1;
255  cfg["next_player_number"] = next_player_number_;
256  }
257  cfg["server_request_number"] = server_request_number_;
258  cfg["do_healing"] = do_healing_;
259  //Call the lua save_game functions
260  lua_kernel_->save_game(cfg);
261 
262  //Write the game events.
263  events_manager_->write_events(cfg);
264 
265  //Write the map, unit_map, and teams info
266  board_.write_config(cfg);
267 
268  //Write the tod manager, and time areas
270 
271  //write out the current state of the map
272  cfg.merge_with(pathfind_manager_->to_config());
273 
274  //Write the game data, including wml vars
276 
277  // Preserve the undo stack so that fog/shroud clearing is kept accurate.
278  undo_stack_->write(cfg.add_child("undo_stack"));
279 
280  if(end_level_data_) {
281  end_level_data_->write(cfg.add_child("end_level_data"));
282  }
283 }
284 
285 namespace {
286  struct castle_cost_calculator : pathfind::cost_calculator
287  {
288  castle_cost_calculator(const gamemap& map, const team & view_team) :
289  map_(map),
290  viewer_(view_team),
291  use_shroud_(view_team.uses_shroud())
292  {}
293 
294  virtual double cost(const map_location& loc, const double) const
295  {
296  if(!map_.is_castle(loc))
297  return 10000;
298 
299  if ( use_shroud_ && viewer_.shrouded(loc) )
300  return 10000;
301 
302  return 1;
303  }
304 
305  private:
306  const gamemap& map_;
307  const team& viewer_;
308  const bool use_shroud_; // Allows faster checks when shroud is disabled.
309  };
310 }//anonymous namespace
311 
312 
313 /**
314  * Checks to see if a leader at @a leader_loc could recruit somewhere.
315  * This takes into account terrain, shroud (for side @a side), and the presence
316  * of visible units.
317  * The behavior for an invalid @a side is subject to change for future needs.
318  */
319 bool game_state::can_recruit_from(const map_location& leader_loc, int side) const
320 {
321  const gamemap& map = board_.map();
322 
323  if(!map.is_keep(leader_loc)) {
324  return false;
325  }
326 
327  try {
328  return pathfind::find_vacant_tile(leader_loc, pathfind::VACANT_CASTLE, nullptr, &board_.get_team(side))
330  } catch(const std::out_of_range&) {
331  // Invalid side specified.
332  // Currently this cannot happen, but it could conceivably be used in
333  // the future to request that shroud and visibility be ignored. Until
334  // that comes to pass, just return.
335  return false;
336  }
337 }
338 
339 bool game_state::can_recruit_from(const unit& leader) const
340 {
341  return can_recruit_from(leader.get_location(), leader.side());
342 }
343 
344 
345 /**
346  * Checks to see if a leader at @a leader_loc could recruit on @a recruit_loc.
347  * This takes into account terrain, shroud (for side @a side), and whether or
348  * not there is already a visible unit at recruit_loc.
349  * The behavior for an invalid @a side is subject to change for future needs.
350  */
351 bool game_state::can_recruit_on(const map_location& leader_loc, const map_location& recruit_loc, int side) const
352 {
353  const gamemap& map = board_.map();
354 
355  if(!map.is_castle(recruit_loc)) {
356  return false;
357  }
358 
359  if(!map.is_keep(leader_loc)) {
360  return false;
361  }
362 
363  try {
364  const team& view_team = board_.get_team(side);
365 
366  if(view_team.shrouded(recruit_loc)) {
367  return false;
368  }
369 
370  if(board_.has_visible_unit(recruit_loc, view_team)) {
371  return false;
372  }
373 
374  castle_cost_calculator calc(map, view_team);
375 
376  // The limit computed in the third argument is more than enough for
377  // any convex castle on the map. Strictly speaking it could be
378  // reduced to sqrt(map.w()**2 + map.h()**2).
380  pathfind::a_star_search(leader_loc, recruit_loc, map.w() + map.h(), calc, map.w(), map.h());
381 
382  return !rt.steps.empty();
383  } catch(const std::out_of_range&) {
384  // Invalid side specified.
385  // Currently this cannot happen, but it could conceivably be used in
386  // the future to request that shroud and visibility be ignored. Until
387  // that comes to pass, just return.
388  return false;
389  }
390 }
391 
392 bool game_state::can_recruit_on(const unit& leader, const map_location& recruit_loc) const
393 {
394  return can_recruit_on(leader.get_location(), recruit_loc, leader.side());
395 }
396 
398 {
399  unit_map::const_iterator leader = board_.units().find(hex);
400  if ( leader != board_.units().end() ) {
401  return leader->can_recruit() && leader->side() == side && can_recruit_from(*leader);
402  } else {
403  // Look for a leader who can recruit on last_hex.
404  for ( leader = board_.units().begin(); leader != board_.units().end(); ++leader) {
405  if ( leader->can_recruit() && leader->side() == side && can_recruit_on(*leader, hex) ) {
406  return true;
407  }
408  }
409  }
410  // No leader found who can recruit at last_hex.
411  return false;
412 }
413 
415 {
416  return this->events_manager_->wml_menu_items();
417 }
418 
420 {
421  return this->events_manager_->wml_menu_items();
422 }
423 
424 
425 namespace
426 {
427  //not really a 'choice' we just need to make sure to inform the server about this.
428 class add_side_wml_choice : public synced_context::server_choice
429 {
430 public:
431  add_side_wml_choice()
432  {
433  }
434 
435  /** We are in a game with no mp server and need to do this choice locally */
436  virtual config local_choice() const
437  {
438  return config{};
439  }
440 
441  /** The request which is sent to the mp server. */
442  virtual config request() const
443  {
444  return config{};
445  }
446 
447  virtual const char* name() const
448  {
449  return "add_side_wml";
450  }
451 
452 private:
453 };
454 } // end anon namespace
455 
456 
458 {
459  cfg["side"] = board_.teams().size() + 1;
460  //if we want to also allow setting the controller we must update the server code.
461  cfg["controller"] = "null";
462  //TODO: is this it? are there caches which must be cleared?
463  board_.teams().emplace_back();
464  board_.teams().back().build(cfg, board_.map(), cfg["gold"].to_int());
465  config choice = synced_context::ask_server_choice(add_side_wml_choice());
466 }
const std::unique_ptr< reports > reports_
Definition: game_state.hpp:49
void read(const config &cfg)
bool is_keep(const map_location &loc) const
Definition: map.cpp:71
const std::unique_ptr< actions::undo_list > undo_stack_
undo_stack_ is never nullptr.
Definition: game_state.hpp:58
Game board class.
Definition: game_board.hpp:50
virtual const std::vector< std::string > & hidden_label_categories() const override
Definition: game_board.hpp:121
config & child(config_key_type key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:414
unit_iterator end()
Definition: map.hpp:428
virtual const std::vector< team > & teams() const override
Definition: game_board.hpp:84
std::unique_ptr< game_lua_kernel > lua_kernel_
Definition: game_state.hpp:50
virtual const unit_map & units() const override
Definition: game_board.hpp:111
std::optional< end_level_data > end_level_data_
Definition: game_state.hpp:64
ai::manager ai_manager_
Definition: game_state.hpp:51
This class represents a single unit of a specific type.
Definition: unit.hpp:120
bool is_castle(const map_location &loc) const
Definition: map.cpp:69
map_location starting_position(int side) const
Definition: map.cpp:319
bool can_recruit_on(const map_location &leader_loc, const map_location &recruit_loc, int side) const
Checks to see if a leader at leader_loc could recruit on recruit_loc.
Definition: game_state.cpp:351
#define ERR_NG
Definition: game_state.cpp:45
game_events::wmi_manager & get_wml_menu_items()
Definition: game_state.cpp:414
#define a
unsigned child_count(config_key_type key) const
Definition: config.cpp:384
int next_player_number_
Definition: game_state.hpp:60
const randomness::mt_rng & rng() const
Definition: game_data.hpp:67
map_location find_vacant_tile(const map_location &loc, VACANT_TILE_TYPE vacancy, const unit *pass_check, const team *shroud_check, const game_board *board)
Function that will find a location on the board that is as near to loc as possible, but which is unoccupied by any units.
Definition: pathfind.cpp:54
int first_human_team_
Definition: game_state.hpp:74
child_itors child_range(config_key_type key)
Definition: config.cpp:356
int server_request_number_
Definition: game_state.hpp:68
static void progress(loading_stage stage=loading_stage::none)
void resolve_random(randomness::rng &r)
handles random_start_time, should be called before the game starts.
Definition: tod_manager.cpp:68
virtual const gamemap & map() const override
Definition: game_board.hpp:101
unit_iterator begin()
Definition: map.hpp:418
bool do_healing_
True if healing should be done at the beginning of the next side turn.
Definition: game_state.hpp:62
bool init_side_done_
Definition: game_state.hpp:65
const std::unique_ptr< game_events::manager > events_manager_
Definition: game_state.hpp:52
Unit and team statistics.
#define b
void merge_with(const config &c)
Merge config &#39;c&#39; into this config, overwriting this config&#39;s values.
Definition: config.cpp:1199
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:44
team & get_team(int i)
Definition: game_board.hpp:96
map_location plus(int x_diff, int y_diff) const
Definition: location.hpp:159
std::vector< map_location > steps
Definition: pathfind.hpp:134
int w() const
Effective map width.
Definition: map.hpp:49
bool uses_shroud() const
Definition: team.hpp:326
bool can_recruit_from(const map_location &leader_loc, int side) const
Checks to see if a leader at leader_loc could recruit somewhere.
Definition: game_state.cpp:319
RAII class to use rng_deterministic in the current scope.
Structure which holds a single route between one location and another.
Definition: pathfind.hpp:131
Encapsulates the map of the game.
Definition: map.hpp:170
tod_manager tod_manager_
Definition: game_state.hpp:47
config to_config() const
Definition: tod_manager.cpp:97
std::unique_ptr< pathfind::manager > pathfind_manager_
Definition: game_state.hpp:48
void set_starting_position(int side, const map_location &loc)
Manipulate starting positions of the different sides.
Definition: map.cpp:375
bool carryover_report
Should a summary of the scenario outcome be displayed?
static int placing_score(const config &side, const gamemap &map, const map_location &pos)
Definition: game_state.cpp:106
bool has_visible_unit(const map_location &loc, const team &team, bool see_all=false) const
Definition: game_board.cpp:196
bool is_ignored(const std::string &nick)
Definition: game.cpp:284
#define LOG_NG
Definition: game_state.cpp:43
Encapsulates the map of the game.
Definition: location.hpp:37
Domain specific events.
Definition: action_wml.cpp:86
unit_iterator find(std::size_t id)
Definition: map.cpp:309
bool shrouded(const map_location &loc) const
Definition: team.cpp:651
transient_end_level transient
std::size_t i
Definition: function.cpp:940
int num_valid_starting_positions() const
Counts the number of sides that have valid starting positions on this map.
Definition: map.cpp:330
mock_party p
static bool operator<(const placing_info &a, const placing_info &b)
Definition: game_state.cpp:139
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition: map.cpp:380
game_data gamedata_
Definition: game_state.hpp:45
Additional information on the game outcome which can be provided by WML.
rng * generator
This generator is automatically synced during synced context.
Definition: random.cpp:60
void init(const config &level, play_controller &)
Definition: game_state.cpp:177
config & add_child(config_key_type key)
Definition: config.cpp:500
void write_config(config &cfg) const
Definition: game_board.cpp:378
bool side_can_recruit_on(int side, map_location loc) const
Checks if any of the sides leaders can recruit at a location.
Definition: game_state.cpp:397
void set_game_display(game_display *)
Definition: game_state.cpp:245
double t
Definition: astarsearch.cpp:64
void add_time_area(const gamemap &map, const config &cfg)
Adds a new local time area from config, making it follow its own time-of-day sequence.
const map_location & get_location() const
The current map location this unit is at.
Definition: unit.hpp:1348
game_board board_
Definition: game_state.hpp:46
map_location pos
Definition: game_state.cpp:136
Various functions that implement the undoing (and redoing) of in-game commands.
Standard logging facilities (interface).
std::vector< terrain_code > ter_list
Definition: translation.hpp:77
int player_number_
Definition: game_state.hpp:59
static const map_location & null_location()
Definition: location.hpp:80
static lg::log_domain log_engine("engine")
int side() const
The side this unit belongs to.
Definition: unit.hpp:333
void place_sides_in_preferred_locations(const config &level)
Definition: game_state.cpp:142
game_state(const config &level, play_controller &)
Definition: game_state.cpp:47
void write_snapshot(config &cfg) const
Definition: game_data.cpp:125
plain_route a_star_search(const map_location &src, const map_location &dst, double stop_at, const cost_calculator &calc, const std::size_t width, const std::size_t height, const teleport_map *teleports, bool border)
PHASE phase() const
Definition: game_data.hpp:78
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:59
void write(config &cfg) const
Definition: game_state.cpp:250
int h() const
Effective map height.
Definition: map.hpp:52
This module contains various pathfinding functions and utilities.
void add_side_wml(config cfg)
creates a new side during a game.
Definition: game_state.cpp:457
bool start_event_fired_
Definition: game_state.hpp:66
ter_list read_list(std::string_view str, const ter_layer filler)
Reads a list of terrains from a string, when reading the.
static config ask_server_choice(const server_choice &)
If we are in a mp game, ask the server, otherwise generate the answer ourselves.