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