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