The Battle for Wesnoth  1.19.0-dev
game_state.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2024
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  , victory_when_enemies_defeated_(level["victory_when_enemies_defeated"].to_bool(true))
63  , remove_from_carryover_on_defeat_(level["remove_from_carryover_on_defeat"].to_bool(true))
64  , server_request_number_(level["server_request_number"].to_int())
65 {
66  lua_kernel_->load_core();
67  if(auto endlevel_cfg = level.optional_child("end_level_data")) {
68  end_level_data el_data;
69  el_data.read(*endlevel_cfg);
70  el_data.transient.carryover_report = false;
71  end_level_data_ = el_data;
72  }
73 }
74 
76 
77 static int placing_score(const config& side, const gamemap& map, const map_location& pos)
78 {
79  int positions = 0, liked = 0;
80  const t_translation::ter_list terrain = t_translation::read_list(side["terrain_liked"].str());
81 
82  for(int i = -8; i != 8; ++i) {
83  for(int j = -8; j != +8; ++j) {
84  const map_location pos2 = pos.plus(i, j);
85  if(map.on_board(pos2)) {
86  ++positions;
87  if(std::count(terrain.begin(),terrain.end(),map[pos2])) {
88  ++liked;
89  }
90  }
91  }
92  }
93 
94  return (100*liked)/positions;
95 }
96 
97 struct placing_info {
98 
100  side(0),
101  score(0),
102  pos()
103  {
104  }
105 
106  int side, score;
108 };
109 
110 static bool operator<(const placing_info& a, const placing_info& b) { return a.score > b.score; }
111 
112 
114 {
115  std::vector<placing_info> placings;
116 
117  int num_pos = board_.map().num_valid_starting_positions();
118 
119  int side_num = 1;
120  for(const config &side : level.child_range("side"))
121  {
122  for(int p = 1; p <= num_pos; ++p) {
123  const map_location& pos = board_.map().starting_position(p);
124  int score = placing_score(side, board_.map(), pos);
125  placing_info obj;
126  obj.side = side_num;
127  obj.score = score;
128  obj.pos = pos;
129  placings.push_back(obj);
130  }
131  ++side_num;
132  }
133 
134  std::stable_sort(placings.begin(),placings.end());
135  std::set<int> placed;
136  std::set<map_location> positions_taken;
137 
138  for (std::vector<placing_info>::const_iterator i = placings.begin(); i != placings.end() && static_cast<int>(placed.size()) != side_num - 1; ++i) {
139  if(placed.count(i->side) == 0 && positions_taken.count(i->pos) == 0) {
140  placed.insert(i->side);
141  positions_taken.insert(i->pos);
142  board_.map().set_starting_position(i->side,i->pos);
143  LOG_NG << "placing side " << i->side << " at " << i->pos;
144  }
145  }
146 }
147 
149 {
150  events_manager_->read_scenario(level, *lua_kernel_);
152  if (level["modify_placing"].to_bool()) {
153  LOG_NG << "modifying placing...";
155  }
156 
157  LOG_NG << "initialized time of day regions... " << (SDL_GetTicks() - pc.ticks());
158  for (const config &t : level.child_range("time_area")) {
160  }
161 
162  LOG_NG << "initialized teams... " << (SDL_GetTicks() - pc.ticks());
163 
164  board_.teams().resize(level.child_count("side"));
165  if (player_number_ != 1 && player_number_ > static_cast<int>(board_.teams().size())) {
166  ERR_NG << "invalid player number " << player_number_ << " #sides=" << board_.teams().size();
167  player_number_ = 1;
168  // in case there are no teams, using player_number_ migh still cause problems later.
169  }
170 
171  std::vector<team_builder> team_builders;
172 
173  // Note this isn't strictly necessary since team_builder declares a move constructor which will
174  // be used if a copy is needed (see the class documentation for why a copy causes crashes), but
175  // it can't hurt to be doubly safe.
176  team_builders.reserve(board_.teams().size());
177 
178  int team_num = 0;
179  for (const config &side : level.child_range("side"))
180  {
181  ++team_num;
182 
183  team_builders.emplace_back(side, board_.get_team(team_num), level, board_, team_num);
184  team_builders.back().build_team_stage_one();
185  }
186 
187  //Initialize the lua kernel before the units are created.
188  lua_kernel_->initialize(level);
189 
190  {
191  //sync traits of start units and the random start time.
193 
195 
196  undo_stack_->read(level.child_or_empty("undo_stack"));
197 
198  for(team_builder& tb : team_builders) {
199  tb.build_team_stage_two();
200  }
201  for(team_builder& tb : team_builders) {
202  tb.build_team_stage_three();
203  }
204 
205  for(std::size_t i = 0; i < board_.teams().size(); i++) {
206  // Labels from players in your ignore list default to hidden
207  if(preferences::is_ignored(board_.teams()[i].current_player())) {
208  std::string label_cat = "side:" + std::to_string(i + 1);
209  board_.hidden_label_categories().push_back(label_cat);
210  }
211  }
212  }
213 }
214 
216 {
217  lua_kernel_->set_game_display(gd);
218 }
219 
220 void game_state::write(config& cfg) const
221 {
222  // dont write this before we fired the (pre)start events
223  // This is the case for the 'replay_start' part of the savegame.
225  cfg["playing_team"] = player_number_ - 1;
226  cfg["next_player_number"] = next_player_number_;
227  }
228  cfg["server_request_number"] = server_request_number_;
229  cfg["do_healing"] = do_healing_;
230  cfg["victory_when_enemies_defeated"] = victory_when_enemies_defeated_;
231  cfg["remove_from_carryover_on_defeat"] = remove_from_carryover_on_defeat_;
232  //Call the lua save_game functions
233  lua_kernel_->save_game(cfg);
234 
235  //Write the game events.
236  events_manager_->write_events(cfg);
237 
238  //Write the map, unit_map, and teams info
239  board_.write_config(cfg);
240 
241  //Write the tod manager, and time areas
243 
244  //write out the current state of the map
245  cfg.merge_with(pathfind_manager_->to_config());
246 
247  //Write the game data, including wml vars
249 
250  // Preserve the undo stack so that fog/shroud clearing is kept accurate.
251  undo_stack_->write(cfg.add_child("undo_stack"));
252 
253  if(end_level_data_) {
254  end_level_data_->write(cfg.add_child("end_level_data"));
255  }
256 }
257 
258 namespace {
259  struct castle_cost_calculator : pathfind::cost_calculator
260  {
261  castle_cost_calculator(const gamemap& map, const team & view_team) :
262  map_(map),
263  viewer_(view_team),
264  use_shroud_(view_team.uses_shroud())
265  {}
266 
267  virtual double cost(const map_location& loc, const double) const
268  {
269  if(!map_.is_castle(loc))
270  return 10000;
271 
272  if ( use_shroud_ && viewer_.shrouded(loc) )
273  return 10000;
274 
275  return 1;
276  }
277 
278  private:
279  const gamemap& map_;
280  const team& viewer_;
281  const bool use_shroud_; // Allows faster checks when shroud is disabled.
282  };
283 }//anonymous namespace
284 
285 
286 /**
287  * Checks to see if a leader at @a leader_loc could recruit somewhere.
288  * This takes into account terrain, shroud (for side @a side), and the presence
289  * of visible units.
290  * The behavior for an invalid @a side is subject to change for future needs.
291  */
292 bool game_state::can_recruit_from(const map_location& leader_loc, int side) const
293 {
294  const gamemap& map = board_.map();
295 
296  if(!map.is_keep(leader_loc)) {
297  return false;
298  }
299 
300  try {
301  return pathfind::find_vacant_tile(leader_loc, pathfind::VACANT_CASTLE, nullptr, &board_.get_team(side))
303  } catch(const std::out_of_range&) {
304  // Invalid side specified.
305  // Currently this cannot happen, but it could conceivably be used in
306  // the future to request that shroud and visibility be ignored. Until
307  // that comes to pass, just return.
308  return false;
309  }
310 }
311 
312 bool game_state::can_recruit_from(const unit& leader) const
313 {
314  return can_recruit_from(leader.get_location(), leader.side());
315 }
316 
317 
318 /**
319  * Checks to see if a leader at @a leader_loc could recruit on @a recruit_loc.
320  * This takes into account terrain, shroud (for side @a side), and whether or
321  * not there is already a visible unit at recruit_loc.
322  * The behavior for an invalid @a side is subject to change for future needs.
323  */
324 bool game_state::can_recruit_on(const map_location& leader_loc, const map_location& recruit_loc, int side) const
325 {
326  const gamemap& map = board_.map();
327 
328  if(!map.is_castle(recruit_loc)) {
329  return false;
330  }
331 
332  if(!map.is_keep(leader_loc)) {
333  return false;
334  }
335 
336  try {
337  const team& view_team = board_.get_team(side);
338 
339  if(view_team.shrouded(recruit_loc)) {
340  return false;
341  }
342 
343  if(board_.has_visible_unit(recruit_loc, view_team)) {
344  return false;
345  }
346 
347  castle_cost_calculator calc(map, view_team);
348 
349  // The limit computed in the third argument is more than enough for
350  // any convex castle on the map. Strictly speaking it could be
351  // reduced to sqrt(map.w()**2 + map.h()**2).
353  pathfind::a_star_search(leader_loc, recruit_loc, map.w() + map.h(), calc, map.w(), map.h());
354 
355  return !rt.steps.empty();
356  } catch(const std::out_of_range&) {
357  // Invalid side specified.
358  // Currently this cannot happen, but it could conceivably be used in
359  // the future to request that shroud and visibility be ignored. Until
360  // that comes to pass, just return.
361  return false;
362  }
363 }
364 
365 bool game_state::can_recruit_on(const unit& leader, const map_location& recruit_loc) const
366 {
367  return can_recruit_on(leader.get_location(), recruit_loc, leader.side());
368 }
369 
371 {
372  unit_map::const_iterator leader = board_.units().find(hex);
373  if ( leader != board_.units().end() ) {
374  return leader->can_recruit() && leader->side() == side && can_recruit_from(*leader);
375  } else {
376  // Look for a leader who can recruit on last_hex.
377  for ( leader = board_.units().begin(); leader != board_.units().end(); ++leader) {
378  if ( leader->can_recruit() && leader->side() == side && can_recruit_on(*leader, hex) ) {
379  return true;
380  }
381  }
382  }
383  // No leader found who can recruit at last_hex.
384  return false;
385 }
386 
388 {
389  return this->events_manager_->wml_menu_items();
390 }
391 
393 {
394  return this->events_manager_->wml_menu_items();
395 }
396 
397 
398 namespace
399 {
400  //not really a 'choice' we just need to make sure to inform the server about this.
401 class add_side_wml_choice : public synced_context::server_choice
402 {
403 public:
404  add_side_wml_choice()
405  {
406  }
407 
408  /** We are in a game with no mp server and need to do this choice locally */
409  virtual config local_choice() const
410  {
411  return config{};
412  }
413 
414  /** The request which is sent to the mp server. */
415  virtual config request() const
416  {
417  return config{};
418  }
419 
420  virtual const char* name() const
421  {
422  return "add_side_wml";
423  }
424 
425 private:
426 };
427 } // end anon namespace
428 
429 
431 {
432  cfg["side"] = board_.teams().size() + 1;
433  //if we want to also allow setting the controller we must update the server code.
434  cfg["controller"] = side_controller::none;
435  //TODO: is this it? are there caches which must be cleared?
436  board_.teams().emplace_back();
437  board_.teams().back().build(cfg, board_.map(), cfg["gold"].to_int());
438  config choice = synced_context::ask_server_choice(add_side_wml_choice());
439 }
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:159
void merge_with(const config &c)
Merge config 'c' into this config, overwriting this config's values.
Definition: config.cpp:1127
config & add_child(config_key_type key)
Definition: config.cpp:442
bool has_visible_unit(const map_location &loc, const team &team, bool see_all=false) const
Definition: game_board.cpp:199
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:381
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:220
bool remove_from_carryover_on_defeat_
Definition: game_state.hpp:65
void add_side_wml(config cfg)
creates a new side during a game.
Definition: game_state.cpp:430
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:69
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:292
std::optional< end_level_data > end_level_data_
Definition: game_state.hpp:67
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:324
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:370
bool in_phase(game_data::PHASE phase) const
Definition: game_state.hpp:112
void set_game_display(game_display *)
Definition: game_state.cpp:215
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:148
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:113
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:387
bool victory_when_enemies_defeated_
Definition: game_state.hpp:64
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
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.
config to_config(std::string textdomain="") const
Definition: tod_manager.cpp:98
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:304
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:110
static int placing_score(const config &side, const gamemap &map, const map_location &pos)
Definition: game_state.cpp:77
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:60
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:107
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