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"
35 #include "side_controller.hpp"
36 
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  , undo_stack_(new actions::undo_list())
57  , player_number_(level["playing_team"].to_int() + 1)
58  , next_player_number_(level["next_player_number"].to_int(player_number_ + 1))
59  , do_healing_(level["do_healing"].to_bool(false))
60  , victory_when_enemies_defeated_(level["victory_when_enemies_defeated"].to_bool(true))
61  , remove_from_carryover_on_defeat_(level["remove_from_carryover_on_defeat"].to_bool(true))
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  cfg["victory_when_enemies_defeated"] = victory_when_enemies_defeated_;
229  cfg["remove_from_carryover_on_defeat"] = remove_from_carryover_on_defeat_;
230  //Call the lua save_game functions
231  lua_kernel_->save_game(cfg);
232 
233  //Write the game events.
234  events_manager_->write_events(cfg);
235 
236  //Write the map, unit_map, and teams info
237  board_.write_config(cfg);
238 
239  //Write the tod manager, and time areas
241 
242  //write out the current state of the map
243  cfg.merge_with(pathfind_manager_->to_config());
244 
245  //Write the game data, including wml vars
247 
248  // Preserve the undo stack so that fog/shroud clearing is kept accurate.
249  undo_stack_->write(cfg.add_child("undo_stack"));
250 
251  if(end_level_data_) {
252  end_level_data_->write(cfg.add_child("end_level_data"));
253  }
254 }
255 
256 namespace {
257  struct castle_cost_calculator : pathfind::cost_calculator
258  {
259  castle_cost_calculator(const gamemap& map, const team & view_team) :
260  map_(map),
261  viewer_(view_team),
262  use_shroud_(view_team.uses_shroud())
263  {}
264 
265  virtual double cost(const map_location& loc, const double) const
266  {
267  if(!map_.is_castle(loc))
268  return 10000;
269 
270  if ( use_shroud_ && viewer_.shrouded(loc) )
271  return 10000;
272 
273  return 1;
274  }
275 
276  private:
277  const gamemap& map_;
278  const team& viewer_;
279  const bool use_shroud_; // Allows faster checks when shroud is disabled.
280  };
281 }//anonymous namespace
282 
283 
284 /**
285  * Checks to see if a leader at @a leader_loc could recruit somewhere.
286  * This takes into account terrain, shroud (for side @a side), and the presence
287  * of visible units.
288  * The behavior for an invalid @a side is subject to change for future needs.
289  */
290 bool game_state::can_recruit_from(const map_location& leader_loc, int side) const
291 {
292  const gamemap& map = board_.map();
293 
294  if(!map.is_keep(leader_loc)) {
295  return false;
296  }
297 
298  try {
299  return pathfind::find_vacant_tile(leader_loc, pathfind::VACANT_CASTLE, nullptr, &board_.get_team(side))
301  } catch(const std::out_of_range&) {
302  // Invalid side specified.
303  // Currently this cannot happen, but it could conceivably be used in
304  // the future to request that shroud and visibility be ignored. Until
305  // that comes to pass, just return.
306  return false;
307  }
308 }
309 
310 bool game_state::can_recruit_from(const unit& leader) const
311 {
312  return can_recruit_from(leader.get_location(), leader.side());
313 }
314 
315 
316 /**
317  * Checks to see if a leader at @a leader_loc could recruit on @a recruit_loc.
318  * This takes into account terrain, shroud (for side @a side), and whether or
319  * not there is already a visible unit at recruit_loc.
320  * The behavior for an invalid @a side is subject to change for future needs.
321  */
322 bool game_state::can_recruit_on(const map_location& leader_loc, const map_location& recruit_loc, int side) const
323 {
324  const gamemap& map = board_.map();
325 
326  if(!map.is_castle(recruit_loc)) {
327  return false;
328  }
329 
330  if(!map.is_keep(leader_loc)) {
331  return false;
332  }
333 
334  try {
335  const team& view_team = board_.get_team(side);
336 
337  if(view_team.shrouded(recruit_loc)) {
338  return false;
339  }
340 
341  if(board_.has_visible_unit(recruit_loc, view_team)) {
342  return false;
343  }
344 
345  castle_cost_calculator calc(map, view_team);
346 
347  // The limit computed in the third argument is more than enough for
348  // any convex castle on the map. Strictly speaking it could be
349  // reduced to sqrt(map.w()**2 + map.h()**2).
351  pathfind::a_star_search(leader_loc, recruit_loc, map.w() + map.h(), calc, map.w(), map.h());
352 
353  return !rt.steps.empty();
354  } catch(const std::out_of_range&) {
355  // Invalid side specified.
356  // Currently this cannot happen, but it could conceivably be used in
357  // the future to request that shroud and visibility be ignored. Until
358  // that comes to pass, just return.
359  return false;
360  }
361 }
362 
363 bool game_state::can_recruit_on(const unit& leader, const map_location& recruit_loc) const
364 {
365  return can_recruit_on(leader.get_location(), recruit_loc, leader.side());
366 }
367 
369 {
370  unit_map::const_iterator leader = board_.units().find(hex);
371  if ( leader != board_.units().end() ) {
372  return leader->can_recruit() && leader->side() == side && can_recruit_from(*leader);
373  } else {
374  // Look for a leader who can recruit on last_hex.
375  for ( leader = board_.units().begin(); leader != board_.units().end(); ++leader) {
376  if ( leader->can_recruit() && leader->side() == side && can_recruit_on(*leader, hex) ) {
377  return true;
378  }
379  }
380  }
381  // No leader found who can recruit at last_hex.
382  return false;
383 }
384 
386 {
387  return this->events_manager_->wml_menu_items();
388 }
389 
391 {
392  return this->events_manager_->wml_menu_items();
393 }
394 
395 
396 namespace
397 {
398  //not really a 'choice' we just need to make sure to inform the server about this.
399 class add_side_wml_choice : public synced_context::server_choice
400 {
401 public:
402  add_side_wml_choice()
403  {
404  }
405 
406  /** We are in a game with no mp server and need to do this choice locally */
407  virtual config local_choice() const
408  {
409  return config{};
410  }
411 
412  /** The request which is sent to the mp server. */
413  virtual config request() const
414  {
415  return config{};
416  }
417 
418  virtual const char* name() const
419  {
420  return "add_side_wml";
421  }
422 
423 private:
424 };
425 } // end anon namespace
426 
427 
429 {
430  cfg["side"] = board_.teams().size() + 1;
431  //if we want to also allow setting the controller we must update the server code.
432  cfg["controller"] = side_controller::none;
433  //TODO: is this it? are there caches which must be cleared?
434  board_.teams().emplace_back();
435  board_.teams().back().build(cfg, board_.map(), cfg["gold"].to_int());
436  config choice = synced_context::ask_server_choice(add_side_wml_choice());
437 }
double t
Definition: astarsearch.cpp:63
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:1126
config & add_child(config_key_type key)
Definition: config.cpp:441
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:79
virtual const std::vector< std::string > & hidden_label_categories() const override
Definition: game_board.hpp:116
team & get_team(int i)
Definition: game_board.hpp:91
void write_config(config &cfg) const
Definition: game_board.cpp:381
virtual const unit_map & units() const override
Definition: game_board.hpp:106
virtual const gamemap & map() const override
Definition: game_board.hpp:96
@ PRELOAD
the preload [event] is fired next phase: PRESTART (normal game), TURN_STARTING_WAITING (reloaded game...
Definition: game_data.hpp:76
@ INITIAL
creating intitial [unit]s, executing toplevel [lua] etc.
Definition: game_data.hpp:73
const randomness::mt_rng & rng() const
Definition: game_data.hpp:67
void write_snapshot(config &cfg) const
Definition: game_data.cpp:130
void write(config &cfg) const
Definition: game_state.cpp:218
bool remove_from_carryover_on_defeat_
Definition: game_state.hpp:62
void add_side_wml(config cfg)
creates a new side during a game.
Definition: game_state.cpp:428
int player_number_
Definition: game_state.hpp:57
int next_player_number_
Definition: game_state.hpp:58
int server_request_number_
Definition: game_state.hpp:66
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:290
std::optional< end_level_data > end_level_data_
Definition: game_state.hpp:64
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:322
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:368
bool in_phase(game_data::PHASE phase) const
Definition: game_state.hpp:109
void set_game_display(game_display *)
Definition: game_state.cpp:213
std::unique_ptr< game_lua_kernel > lua_kernel_
Definition: game_state.hpp:48
game_state(const config &level, play_controller &)
Definition: game_state.cpp:47
std::unique_ptr< pathfind::manager > pathfind_manager_
Definition: game_state.hpp:46
const std::unique_ptr< actions::undo_list > undo_stack_
undo_stack_ is never nullptr.
Definition: game_state.hpp:56
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:60
void place_sides_in_preferred_locations(const config &level)
Definition: game_state.cpp:111
game_board board_
Definition: game_state.hpp:44
const std::unique_ptr< game_events::manager > events_manager_
Definition: game_state.hpp:50
game_events::wmi_manager & get_wml_menu_items()
Definition: game_state.cpp:385
bool victory_when_enemies_defeated_
Definition: game_state.hpp:61
tod_manager tod_manager_
Definition: game_state.hpp:45
game_data gamedata_
Definition: game_state.hpp:43
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:379
int h() const
Effective map height.
Definition: map.hpp:53
map_location starting_position(int side) const
Definition: map.cpp:323
int num_valid_starting_positions() const
Counts the number of sides that have valid starting positions on this map.
Definition: map.cpp:334
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition: map.cpp:384
Encapsulates the map of the game.
Definition: map.hpp:172
bool is_castle(const map_location &loc) const
Definition: map.cpp:69
bool is_keep(const map_location &loc) const
Definition: map.cpp:71
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:74
bool shrouded(const map_location &loc) const
Definition: team.cpp:650
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:95
void resolve_random(randomness::rng &r)
handles random_start_time, should be called before the game starts.
Definition: tod_manager.cpp:66
unit_iterator end()
Definition: map.hpp:428
unit_iterator find(std::size_t id)
Definition: map.cpp:302
unit_iterator begin()
Definition: map.hpp:418
This class represents a single unit of a specific type.
Definition: unit.hpp:133
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:45
#define LOG_NG
Definition: game_state.cpp:43
int side() const
The side this unit belongs to.
Definition: unit.hpp:343
const map_location & get_location() const
The current map location this unit is at.
Definition: unit.hpp:1357
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:273
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:85
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